gecko-dev/lib/libmsg/msgsend.cpp
1998-06-25 05:50:45 +00:00

6361 lines
177 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.
*/
/* compose.c --- generation and delivery of MIME objects.
*/
#include "rosetta.h"
#include "msg.h"
#include "ntypes.h"
#include "structs.h"
#include "xlate.h" /* Text and PostScript converters */
#include "merrors.h"
#include "gui.h" /* for XP_AppCodeName */
#include "mime.h"
#include "xp_time.h" /* For XP_LocalZoneOffset() */
#include "libi18n.h"
#include "xpgetstr.h"
#include "prtime.h"
#include "prtypes.h"
#include "msgcom.h"
#include "msgsend.h"
#include "msgsendp.h"
#include "maildb.h"
#include "mailhdr.h"
#include "msgprefs.h"
#include "msgmast.h"
#include "msgcpane.h"
#ifndef NO_SECURITY
#include HG02902
#endif
#include "grpinfo.h"
#include "msgcflds.h"
#include "prefapi.h"
#include "abdefn.h"
#include "prsembst.h"
#include "secrng.h" /* for RNG_GenerateGlobalRandomBytes() */
#include "addrbook.h"
#include "imaphost.h"
#include "imapoff.h"
#ifdef XP_MAC
#include "errors.h"
#endif
#include "intl_csi.h"
#include "msgimap.h"
#include "msgurlq.h"
#ifdef XP_MAC
#pragma warn_unusedarg off
#endif // XP_MAC
extern "C"
{
extern int MK_MSG_ASSEMBLING_MSG;
extern int MK_MSG_ASSEMB_DONE_MSG;
extern int MK_MSG_LOAD_ATTACHMNT;
extern int MK_MSG_LOAD_ATTACHMNTS;
extern int MK_MSG_DELIV_MAIL;
extern int MK_MSG_DELIV_MAIL_DONE;
extern int MK_MSG_DELIV_NEWS;
extern int MK_MSG_DELIV_NEWS_DONE;
extern int MK_MSG_QUEUEING;
extern int MK_MSG_WRITING_TO_FCC;
extern int MK_MSG_QUEUED;
extern int MK_MIME_ERROR_WRITING_FILE;
extern int MK_MIME_MULTIPART_BLURB;
extern int MK_MIME_NO_RECIPIENTS;
extern int MK_MIME_NO_SENDER;
extern int MK_MSG_COULDNT_OPEN_FCC_FILE;
extern int MK_OUT_OF_MEMORY;
extern int MK_UNABLE_TO_OPEN_TMP_FILE;
extern int MK_MSG_CANT_CREATE_FOLDER;
extern int MK_MSG_SAVE_DRAFT;
extern int MK_ADDR_BOOK_CARD;
extern int MK_MSG_MAC_PROMPT_UUENCODE;
extern int MK_MSG_SAVING_AS_DRAFT;
extern int MK_MSG_SAVING_AS_TEMPLATE;
extern int MK_MSG_UNABLE_TO_SAVE_DRAFT;
extern int MK_MSG_UNABLE_TO_SAVE_TEMPLATE;
extern int MK_UNABLE_TO_OPEN_FILE;
extern int MK_IMAP_UNABLE_TO_SAVE_MESSAGE;
extern int MK_IMAP_NO_ONLINE_FOLDER;
}
#ifdef XP_MAC
#include "m_cvstrm.h"
#endif
#define TEN_K 10240
#define MK_ATTACHMENT_LOAD_FAILED -666
/* Asynchronous mailing of messages with attached URLs.
- If there are any attachments, start their URLs going, and write each
of them to a temp file.
- While writing to their files, examine the data going by and decide
what kind of encoding, if any, they need. Also remember their content
types.
- Once that URLs has been saved to a temp file (or, if there were no
attachments) generate a final temp file, of the actual message:
- Generate a string of the headers.
- Open the final temp file.
- Write the headers.
- Examine the first part, and decide whether to encode it.
- Write the first part to the file, possibly encoded.
- Write the second and subsequent parts to the file, possibly encoded.
(Open the first temp file and copy it to the final temp file, and so
on, through an encoding filter.)
- Delete the attachment temp file(s) as we finish with them.
- Close the final temp file.
- Open the news: url.
- Send the final temp file to NNTP.
If there's an error, run the callback with "failure" status.
- If mail succeeded, open the mailto: url.
- Send the final temp file to SMTP.
If there's an error, run the callback with "failure" status.
- Otherwise, run the callback with "success" status.
- Free everything, delete the final temp file.
The theory behind the encoding logic:
=====================================
If the document is of type text/html, and the user has asked to attach it
as source or postscript, it will be run through the appropriate converter
(which will result in a document of type text/plain.)
An attachment will be encoded if:
- it is of a non-text type (in which case we will use base64); or
- The "use QP" option has been selected and high-bit characters exist; or
- any NULLs exist in the document; or
- any line is longer than 900 bytes.
- If we are encoding, and more than 10% of the document consists of
non-ASCII characters, then we always use base64 instead of QP.
We eschew quoted-printable in favor of base64 for documents which are likely
to always be binary (images, sound) because, on the off chance that a GIF
file (for example) might contain primarily bytes in the ASCII range, using
the quoted-printable representation might cause corruption due to the
translation of CR or LF to CRLF. So, when we don't know that the document
has "lines", we don't use quoted-printable.
*/
/* It's better to send a message as news before sending it as mail, because
the NNTP server is more likely to reject the article (for any number of
reasons) than the SMTP server is. */
#undef MAIL_BEFORE_NEWS
/* Generating a message ID here is a good because it means that if a message
is sent to both mail and news, it will have the same ID in both places. */
#define GENERATE_MESSAGE_ID
/* For maximal compatibility, it helps to emit both
Content-Type: <type>; name="<original-file-name>"
as well as
Content-Disposition: inline; filename="<original-file-name>"
The lossage here is, RFC1341 defined the "name" parameter to Content-Type,
but then RFC1521 deprecated it in anticipation of RFC1806, which defines
Content-Type and the "filename" parameter. But, RFC1521 is "Standards Track"
while RFC1806 is still "Experimental." So, it's probably best to just
implement both.
*/
#define EMIT_NAME_IN_CONTENT_TYPE
/* Whether the contents of the BCC header should be preserved in the FCC'ed
copy of a message. See comments below, in mime_do_fcc_1().
*/
#define SAVE_BCC_IN_FCC_FILE
/* When attaching an HTML document, one must indicate the original URL of
that document, if the receiver is to have any chance of being able to
retreive and display the inline images, or to click on any links in the
HTML.
The way we have done this in the past is by inserting a <BASE> tag as the
first line of all HTML documents we attach. (This is kind of bad in that
we're actually modifying the document, and it really isn't our place to
do that.)
The sanctioned *new* way of doing this is to insert a Content-Base header
field on the attachment. This is (will be) a part of the forthcoming MHTML
spec.
If GENERATE_CONTENT_BASE, we generate a Content-Base header.
We used to have a MANGLE_HTML_ATTACHMENTS_WITH_BASE_TAG symbol that we
defined, which added a BASE tag to the bodies. We stopped doing this in
4.0. */
#define GENERATE_CONTENT_BASE
static XP_Bool mime_use_quoted_printable_p = TRUE;
static XP_Bool mime_headers_use_quoted_printable_p = FALSE;
#ifdef XP_MAC
XP_BEGIN_PROTOS
extern OSErr my_FSSpecFromPathname(char* src_filename, FSSpec* fspec);
extern char * mime_make_separator(const char *prefix);
HG89984
XP_END_PROTOS
static char* NET_GetLocalFileFromURL(char *url)
{
char * finalPath;
XP_ASSERT(strncasecomp(url, "file://", 7) == 0);
finalPath = (char*)XP_ALLOC(strlen(url));
if (finalPath == NULL)
return NULL;
strcpy(finalPath, url+6+1);
return finalPath;
}
static char* NET_GetURLFromLocalFile(char *filename)
{
/* file:///<path>0 */
char * finalPath = (char*)XP_ALLOC(strlen(filename) + 8 + 1);
if (finalPath == NULL)
return NULL;
finalPath[0] = 0;
strcat(finalPath, "file://");
strcat(finalPath, filename);
return finalPath;
}
#endif /* XP_MAC */
void
MIME_ConformToStandard (XP_Bool conform_p)
{
/*
* If we are conforming to mime standard no matter what we set
* for the headers preference when generating mime headers we should
* also conform to the standard. Otherwise, depends the preference
* we set. For now, the headers preference is not accessible from UI.
*/
if (conform_p)
mime_headers_use_quoted_printable_p = TRUE;
else
PREF_GetBoolPref("mail.strictly_mime_headers",
&mime_headers_use_quoted_printable_p);
mime_use_quoted_printable_p = conform_p;
}
MSG_SendMimeDeliveryState::MSG_SendMimeDeliveryState()
{
m_pane = NULL; /* Pane to use when loading the URLs */
m_fe_data = NULL; /* passed in and passed to callback */
m_fields = NULL; /* Where to send the message once it's done */
m_dont_deliver_p = FALSE;
m_deliver_mode = MSG_DeliverNow;
m_attachments_only_p = FALSE;
m_pre_snarfed_attachments_p = FALSE;
m_digest_p = FALSE;
m_be_synchronous_p = FALSE;
HG54689
m_attachment1_type = 0;
m_attachment1_encoding = 0;
m_attachment1_encoder_data = NULL;
m_attachment1_body = 0;
m_attachment1_body_length = 0;
m_attachment_count = 0;
m_attachment_pending_count = 0;
m_attachments = NULL;
m_status = 0;
m_message_delivery_done_callback = NULL;
m_attachments_done_callback = NULL;
m_msg_file_name = NULL;
m_msg_file = 0;
m_plaintext = NULL;
m_html_filename = NULL;
//#ifdef MSG_SEND_MULTIPART_RELATED
m_related_part = NULL;
//#endif
m_imapFolderInfo = NULL;
m_imapOutgoingParser = NULL;
m_imapLocalMailDB = NULL;
}
MSG_SendMimeDeliveryState::~MSG_SendMimeDeliveryState()
{
//#ifdef MSG_SEND_MULTIPART_RELATED
#if 0
if (m_related_part)
{
// m_related_part (if it exists) gets deleted when
// m_related_saver gets torched.
delete m_related_part;
m_related_part = NULL;
}
#endif
//#endif
FREEIF(m_msg_file_name);
}
static char *mime_mailto_stream_read_buffer = 0;
static char *mime_mailto_stream_write_buffer = 0;
static void mime_attachment_url_exit (URL_Struct *url, int status,
MWContext *context);
static void mime_text_attachment_url_exit (PrintSetup *p);
static int mime_sanity_check_fields (const char *from,
const char *reply_to,
const char *to,
const char *cc,
const char *bcc,
const char *fcc,
const char *newsgroups,
const char *followup_to,
const char *subject,
const char *references,
const char *organization,
const char *other_random_headers);
static char *mime_generate_headers (MSG_CompositionFields *fields,
int csid,
MSG_Deliver_Mode deliver_mode);
static char *mime_generate_attachment_headers (const char *type,
const char *encoding,
const char *description,
const char *x_mac_type,
const char *x_mac_creator,
const char *real_name,
const char *base_url,
XP_Bool digest_p,
MSG_DeliverMimeAttachment *ma,
int16 mail_csid);
static char *RFC2231ParmFolding(const char *parmName, const char *charset,
const char *language, const char *parmValue);
#if 0
static XP_Bool mime_type_conversion_possible (const char *from_type,
const char *to_type);
#endif
#ifdef XP_UNIX
extern "C" void XFE_InitializePrintSetup (PrintSetup *p);
#endif /* XP_UNIX */
extern "C" char * NET_ExplainErrorDetails (int code, ...);
MSG_DeliverMimeAttachment::MSG_DeliverMimeAttachment()
{
m_url_string = NULL;
m_url = NULL;
m_done = FALSE;
m_type = NULL;
m_override_type = NULL;
m_override_encoding = NULL;
m_desired_type = NULL;
m_description = NULL;
m_x_mac_type = NULL;
m_x_mac_creator = NULL;
m_encoding = NULL;
m_real_name = NULL;
m_mime_delivery_state = NULL;
m_encoding = NULL;
m_already_encoded_p = FALSE;
m_file_name = NULL;
m_file = 0;
#ifdef XP_MAC
m_ap_filename = NULL;
#endif
HG54897
m_size = 0;
m_unprintable_count = 0;
m_highbit_count = 0;
m_ctl_count = 0;
m_null_count = 0;
m_current_column = 0;
m_max_column = 0;
m_lines = 0;
m_encoder_data = NULL;
XP_MEMSET(&m_print_setup, 0, sizeof(m_print_setup));
m_graph_progress_started = FALSE;
}
MSG_DeliverMimeAttachment::~MSG_DeliverMimeAttachment()
{
}
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]);
}
XP_Bool
MSG_DeliverMimeAttachment::UseUUEncode_p(void)
{
XP_Bool returnVal = (m_mime_delivery_state) &&
(m_mime_delivery_state->m_pane) &&
((MSG_CompositionPane*)(m_mime_delivery_state->m_pane))->
GetCompBoolHeader(MSG_UUENCODE_BINARY_BOOL_HEADER_MASK);
return returnVal;
}
static void msg_escape_file_name (URL_Struct *m_url)
{
XP_ASSERT (m_url->address && !XP_STRNCASECMP(m_url->address, "file:", 5));
if (!m_url->address ||
XP_STRNCASECMP(m_url->address, "file:", 5)) return;
char * new_address = NET_Escape(XP_STRCHR(m_url->address, ':')+1,
URL_PATH);
XP_ASSERT(new_address);
if (!new_address) return;
XP_FREEIF(m_url->address);
m_url->address = PR_smprintf("file:%s", new_address);
XP_FREEIF(new_address);
}
// Returns a newly-allocated string containing the MIME type to be used for
// outgoing attachments of the given document type. The way this is determined
// will be platform-specific, based on the real filename of the file (i.e. not the temp filename)
// and some Mac creator info.
// If there is no default specified in the prefs, then this returns NULL.
static char *msg_GetMissionControlledOutgoingMIMEType(const char *filename,
const char *x_mac_type,
const char *x_mac_creator)
{
if (!filename)
return NULL;
#ifdef XP_WIN
char *whereDot = XP_STRRCHR(filename, '.');
if (whereDot)
{
char *extension = whereDot + 1;
if (extension)
{
char *mcOutgoingMimeType = NULL;
char *prefString = PR_smprintf("mime.table.extension.%s.outgoing_default_type",extension);
if (prefString)
{
PREF_CopyCharPref(prefString, &mcOutgoingMimeType);
XP_FREE(prefString);
}
return mcOutgoingMimeType;
}
else
return NULL; // no file extension
}
else
return NULL; // no file extension
#endif
return NULL;
}
static char *
RFC2231ParmFolding(const char *parmName, const char *charset,
const char *language, const char *parmValue)
{
#define MAX_FOLDING_LEN 75 // this is to gurantee the folded line will
// never be greater than 78 = 75 + CRLFLWSP
char *foldedParm = NULL;
char *dupParm = NULL;
int32 parmNameLen = 0;
int32 parmValueLen = 0;
int32 charsetLen = 0;
int32 languageLen = 0;
XP_Bool needEscape = FALSE;
XP_ASSERT(parmName && *parmName && parmValue && *parmValue);
if (!parmName || !*parmName || !parmValue || !*parmValue)
return NULL;
if ((charset && *charset) || (language && *language))
needEscape = TRUE;
if (needEscape)
dupParm = NET_Escape(parmValue, URL_PATH);
else
dupParm = XP_STRDUP(parmValue);
if (!dupParm)
return NULL;
parmValueLen = XP_STRLEN(dupParm);
parmNameLen = XP_STRLEN(parmName);
if (needEscape)
parmNameLen += 5; // *=__'__'___ or *[0]*=__'__'__ or *[1]*=___
else
parmNameLen += 5; // *[0]="___";
charsetLen = charset ? XP_STRLEN(charset) : 0;
languageLen = language ? XP_STRLEN(language) : 0;
if ((parmValueLen + parmNameLen + charsetLen + languageLen) <
MAX_FOLDING_LEN)
{
StrAllocCopy(foldedParm, parmName);
if (needEscape)
{
StrAllocCat(foldedParm, "*=");
if (charsetLen)
StrAllocCat(foldedParm, charset);
StrAllocCat(foldedParm, "'");
if (languageLen)
StrAllocCat(foldedParm, language);
StrAllocCat(foldedParm, "'");
}
else
StrAllocCat(foldedParm, "=\"");
StrAllocCat(foldedParm, dupParm);
if (!needEscape)
StrAllocCat(foldedParm, "\"");
goto done;
}
else
{
int curLineLen = 0;
int counter = 0;
char digits[32];
char *start = dupParm;
char *end = NULL;
char tmp = 0;
while (parmValueLen > 0)
{
curLineLen = 0;
if (counter == 0)
{
StrAllocCopy (foldedParm, parmName);
}
else
{
if (needEscape)
StrAllocCat(foldedParm, "\r\n ");
else
StrAllocCat(foldedParm, ";\r\n ");
StrAllocCat(foldedParm, parmName);
}
XP_SPRINTF(digits, "*%d", counter);
StrAllocCat(foldedParm, digits);
curLineLen += XP_STRLEN(digits);
if (needEscape)
{
StrAllocCat(foldedParm, "*=");
if (counter == 0)
{
if (charsetLen)
StrAllocCat(foldedParm, charset);
StrAllocCat(foldedParm, "'");
if (languageLen)
StrAllocCat(foldedParm, language);
StrAllocCat (foldedParm, "'");
curLineLen += charsetLen;
curLineLen += languageLen;
}
}
else
{
StrAllocCat(foldedParm, "=\"");
}
counter++;
curLineLen += parmNameLen;
if (parmValueLen <= MAX_FOLDING_LEN - curLineLen)
end = start + parmValueLen;
else
end = start + (MAX_FOLDING_LEN - curLineLen);
tmp = 0;
if (*end && needEscape)
{
// check to see if we are in the middle of escaped char
if (*end == '%')
{
tmp = '%'; *end = NULL;
}
else if (end-1 > start && *(end-1) == '%')
{
end -= 1; tmp = '%'; *end = NULL;
}
else if (end-2 > start && *(end-2) == '%')
{
end -= 2; tmp = '%'; *end = NULL;
}
else
{
tmp = *end; *end = NULL;
}
}
else
{
tmp = *end; *end = NULL;
}
StrAllocCat(foldedParm, start);
if (!needEscape)
StrAllocCat(foldedParm, "\"");
parmValueLen -= (end-start);
if (tmp)
*end = tmp;
start = end;
}
}
done:
XP_FREEIF(dupParm);
return foldedParm;
}
int32
MSG_DeliverMimeAttachment::SnarfAttachment ()
{
int32 status = 0;
XP_ASSERT (! m_done);
m_file_name = WH_TempName (xpFileToPost, "nsmail");
if (! m_file_name)
return (MK_OUT_OF_MEMORY);
m_file = XP_FileOpen (m_file_name, xpFileToPost, XP_FILE_WRITE_BIN);
if (! m_file)
return MK_UNABLE_TO_OPEN_TMP_FILE; /* #### how do we pass file name? */
m_url->fe_data = this;
/* #### m_type is still unknown at this point.
We need to find a way to make the textfe not blow
up on documents that are not text/html!
*/
#ifdef XP_MAC
if (NET_IsLocalFileURL(m_url->address) && // do we need to add IMAP: to this list? NET_IsLocalFileURL returns FALSE always for IMAP - DMB
(strncasecomp(m_url->address, "mailbox:", 8) != 0))
{
/* convert the apple file to AppleDouble first, and then patch the
address in the url.
*/
char* src_filename = NET_GetLocalFileFromURL (m_url->address);
// ### mwelch Only use appledouble if we aren't uuencoding.
if(isMacFile(src_filename) && (! UseUUEncode_p()))
{
char *separator, tmp[128];
NET_StreamClass *ad_encode_stream;
separator = mime_make_separator("ad");
if (!separator)
return MK_OUT_OF_MEMORY;
m_ap_filename = WH_TempName (xpFileToPost, "nsmail");
ad_encode_stream = (NET_StreamClass *) /* need a prototype */
fe_MakeAppleDoubleEncodeStream (FO_CACHE_AND_MAIL_TO,
NULL,
m_url,
m_mime_delivery_state->GetContext(),
src_filename,
m_ap_filename,
separator);
if (ad_encode_stream == NULL)
{
FREEIF(separator);
return MK_OUT_OF_MEMORY;
}
do {
status = (*ad_encode_stream->put_block)
((NET_StreamClass *)ad_encode_stream->data_object, NULL, 1024);
} while (status == noErr);
if (status >= 0)
ad_encode_stream->complete ((NET_StreamClass *)ad_encode_stream->data_object);
else
ad_encode_stream->abort ((NET_StreamClass *)ad_encode_stream->data_object, status);
XP_FREE(ad_encode_stream);
if (status < 0)
{
FREEIF(separator);
return status;
}
XP_FREE(m_url->address);
{
char * temp = WH_FileName(m_ap_filename, xpFileToPost );
m_url->address = XP_PlatformFileToURL(temp); // jrm 97/02/08
if (temp)
XP_FREE(temp);
}
/* and also patch the types.
*/
if (m_type)
XP_FREE (m_type);
XP_SPRINTF(tmp, MULTIPART_APPLEDOUBLE ";\r\n boundary=\"%s\"",
separator);
FREEIF(separator);
m_type = XP_STRDUP(tmp);
}
else
{
if (isMacFile(src_filename))
{
// The only time we want to send just the data fork of a two-fork
// Mac file is if uuencoding has been requested.
XP_ASSERT(UseUUEncode_p());
if (!((MSG_CompositionPane *) m_mime_delivery_state->m_pane)->m_confirmed_uuencode_p)
{
XP_Bool confirmed = FE_Confirm(m_mime_delivery_state->m_pane->GetContext(),
XP_GetString(MK_MSG_MAC_PROMPT_UUENCODE));
// only want to do this once
((MSG_CompositionPane *) m_mime_delivery_state->m_pane)->m_confirmed_uuencode_p = TRUE;
if (! confirmed) // cancelled
return MK_INTERRUPTED;
}
}
/* make sure the file type and create are set. */
char filetype[32];
FSSpec fsSpec;
FInfo info;
Bool useDefault;
char *macType, *macEncoding;
my_FSSpecFromPathname(src_filename, &fsSpec);
if (FSpGetFInfo (&fsSpec, &info) == noErr)
{
XP_SPRINTF(filetype, "%X", info.fdType);
m_x_mac_type = XP_STRDUP(filetype);
XP_SPRINTF(filetype, "%X", info.fdCreator);
m_x_mac_creator = XP_STRDUP(filetype);
if (m_type == NULL ||
!strcasecomp (m_type, TEXT_PLAIN))
{
# define TEXT_TYPE 0x54455854 /* the characters 'T' 'E' 'X' 'T' */
# define text_TYPE 0x74657874 /* the characters 't' 'e' 'x' 't' */
if (info.fdType != TEXT_TYPE && info.fdType != text_TYPE)
{
FE_FileType(m_url->address, &useDefault,
&macType, &macEncoding);
FREEIF(m_type);
m_type = macType;
}
}
}
/* don't bother to set the types if we failed in getting the file
info. */
}
FREEIF(src_filename);
src_filename = 0;
}
#else
/* if we are attaching a local file make sure the file name are escaped
* properly
*/
if (NET_IsLocalFileURL(m_url->address) &&
XP_STRNCASECMP (m_url->address, "file:", 5) == 0)
{
msg_escape_file_name(m_url);
}
#endif /* XP_MAC */
if (m_desired_type &&
!strcasecomp (m_desired_type, TEXT_PLAIN) /* #### &&
mime_type_conversion_possible (m_type, m_desired_type) */ )
{
/* Conversion to plain text desired.
*/
m_print_setup.url = m_url;
m_print_setup.carg = this;
m_print_setup.completion = mime_text_attachment_url_exit;
m_print_setup.filename = NULL;
m_print_setup.out = m_file;
m_print_setup.eol = CRLF;
int32 width = 72;
PREF_GetIntPref("mailnews.wraplength", &width);
if (width == 0) width = 72;
else if (width < 10) width = 10;
else if (width > 30000) width = 30000;
if (m_mime_delivery_state->m_pane->GetPaneType() == MSG_COMPOSITIONPANE)
{
int lineWidth = ((MSG_CompositionPane *) m_mime_delivery_state->m_pane)
->GetLineWidth();
if (lineWidth > width)
width = lineWidth;
}
m_print_setup.width = width;
m_url->savedData.FormList = 0;
#ifdef _USRDLL
if (! NDLLXL_TranslateText (m_mime_delivery_state->GetContext(), m_url,
&m_print_setup))
return MK_ATTACHMENT_LOAD_FAILED;
#else
if (! XL_TranslateText (m_mime_delivery_state->GetContext(), m_url,
&m_print_setup))
return MK_ATTACHMENT_LOAD_FAILED;
#endif
if (m_type) XP_FREE (m_type);
m_type = m_desired_type;
m_desired_type = 0;
if (m_encoding) XP_FREE (m_encoding);
m_encoding = 0;
}
#ifdef XP_UNIX
else if (m_desired_type &&
!strcasecomp (m_desired_type, APPLICATION_POSTSCRIPT) /* #### &&
mime_type_conversion_possible (m_type, m_desired_type) */ )
{
SHIST_SavedData saved_data;
/* Make sure layout saves the current state of form elements. */
LO_SaveFormData(m_mime_delivery_state->GetContext());
/* Hold on to the saved data. */
XP_MEMCPY(&saved_data, &m_url->savedData, sizeof(SHIST_SavedData));
/* Conversion to postscript desired.
*/
XFE_InitializePrintSetup (&m_print_setup);
m_print_setup.url = m_url;
m_print_setup.carg = this;
m_print_setup.completion = mime_text_attachment_url_exit;
m_print_setup.filename = NULL;
m_print_setup.out = m_file;
m_print_setup.eol = CRLF;
XP_MEMSET (&m_url->savedData, 0, sizeof (SHIST_SavedData));
XL_TranslatePostscript (m_mime_delivery_state->GetContext(),
m_url, &saved_data,
&m_print_setup);
if (m_type) XP_FREE (m_type);
m_type = m_desired_type;
m_desired_type = 0;
if (m_encoding) XP_FREE (m_encoding);
m_encoding = 0;
}
#endif /* XP_UNIX */
else
{
int get_url_status;
/* In this case, ignore the status, as that will be handled by
the exit routine. */
/* jwz && tj -> we're assuming that it is safe to return the result
of this call as our status result.
A negative result means that the exit routine was run, either
because the operation completed quickly or failed.
*/
m_url->allow_content_change = FALSE; // don't use modified content
get_url_status = NET_GetURL (m_url, FO_CACHE_AND_MAIL_TO,
m_mime_delivery_state->GetContext(),
mime_attachment_url_exit);
if (get_url_status < 0)
return MK_ATTACHMENT_LOAD_FAILED;
else
return 0;
}
return status;
}
static void
mime_attachment_url_exit (URL_Struct *url, int status, MWContext *context)
{
MSG_DeliverMimeAttachment *ma = (MSG_DeliverMimeAttachment *) url->fe_data;
XP_ASSERT(ma != NULL);
if (ma != NULL)
ma->UrlExit(url, status, context);
}
void MSG_DeliverMimeAttachment::UrlExit(URL_Struct *url, int status,
MWContext *context)
{
char *error_msg = url->error_msg;
url->error_msg = 0;
url->fe_data = 0;
XP_ASSERT(m_mime_delivery_state != NULL);
XP_ASSERT(m_mime_delivery_state->GetContext() != NULL);
XP_ASSERT(m_url != NULL);
if (m_graph_progress_started)
{
m_graph_progress_started = FALSE;
FE_GraphProgressDestroy (m_mime_delivery_state->GetContext(), m_url,
m_url->content_length, m_size);
}
if (status < 0)
/* If any of the attachment URLs fail, kill them all. */
NET_InterruptWindow (context);
/* Close the file, but don't delete it (or the file name.) */
XP_FileClose (m_file);
m_file = 0;
NET_FreeURLStruct (m_url);
/* I'm pretty sure m_url == url */
m_url = 0;
url = 0;
if (status < 0) {
if (m_mime_delivery_state->m_status >= 0)
m_mime_delivery_state->m_status = status;
XP_FileRemove(m_file_name, xpFileToPost);
XP_FREEIF(m_file_name);
}
m_done = TRUE;
XP_ASSERT (m_mime_delivery_state->m_attachment_pending_count > 0);
m_mime_delivery_state->m_attachment_pending_count--;
if (status >= 0 && m_mime_delivery_state->m_be_synchronous_p)
{
/* Find the next attachment which has not yet been loaded,
if any, and start it going.
*/
int32 i;
MSG_DeliverMimeAttachment *next = 0;
for (i = 0; i < m_mime_delivery_state->m_attachment_count; i++)
if (!m_mime_delivery_state->m_attachments[i].m_done)
{
next = &m_mime_delivery_state->m_attachments[i];
break;
}
if (next)
{
int status = next->SnarfAttachment ();
if (status < 0)
{
m_mime_delivery_state->Fail(status, 0);
return;
}
}
}
if (m_mime_delivery_state->m_attachment_pending_count == 0)
{
/* If this is the last attachment, then either complete the
delivery (if successful) or report the error by calling
the exit routine and terminating the delivery.
*/
if (status < 0)
{
m_mime_delivery_state->Fail(status, error_msg);
error_msg = 0;
}
else
{
m_mime_delivery_state->GatherMimeAttachments ();
}
}
else
{
/* If this is not the last attachment, but it got an error,
then report that error and continue (we won't actually
abort the delivery until all the other pending URLs have
caught up with the NET_InterruptWindow() we did up above.)
*/
if (status < 0 && error_msg)
FE_Alert (context, error_msg);
}
FREEIF (error_msg);
}
void
MSG_DeliverMimeAttachment::AnalyzeDataChunk(const char *chunk, int32 length)
{
unsigned char *s = (unsigned char *) chunk;
unsigned char *end = s + length;
for (; s < end; s++)
{
if (*s > 126)
{
m_highbit_count++;
m_unprintable_count++;
}
else if (*s < ' ' && *s != '\t' && *s != CR && *s != LF)
{
m_unprintable_count++;
m_ctl_count++;
if (*s == 0)
m_null_count++;
}
if (*s == CR || *s == LF)
{
if (s+1 < end && s[0] == CR && s[1] == LF)
s++;
if (m_max_column < m_current_column)
m_max_column = m_current_column;
m_current_column = 0;
m_lines++;
}
else
{
m_current_column++;
}
}
}
void
MSG_DeliverMimeAttachment::AnalyzeSnarfedFile(void)
{
char chunk[256];
XP_File fileHdl = NULL;
int32 numRead = 0;
if (m_file_name && *m_file_name)
{
fileHdl = XP_FileOpen(m_file_name, xpFileToPost, XP_FILE_READ_BIN);
if (fileHdl)
{
do
{
numRead = XP_FileRead(chunk, 256, fileHdl);
if (numRead > 0)
AnalyzeDataChunk(chunk, numRead);
}
while (numRead > 0);
XP_FileClose(fileHdl);
}
}
}
static void
mime_text_attachment_url_exit (PrintSetup *p)
{
MSG_DeliverMimeAttachment *ma = (MSG_DeliverMimeAttachment *) p->carg;
XP_ASSERT (p->url == ma->m_url);
ma->m_url->fe_data = ma; /* grr */
mime_attachment_url_exit (p->url, p->status,
ma->m_mime_delivery_state->GetContext());
}
PRIVATE unsigned int
mime_attachment_stream_write_ready (NET_StreamClass *stream)
{
return MAX_WRITE_READY;
}
PRIVATE int
mime_attachment_stream_write (NET_StreamClass *stream, const char *block, int32 length)
{
MSG_DeliverMimeAttachment *ma = (MSG_DeliverMimeAttachment *) stream->data_object;
/*
const unsigned char *s;
const unsigned char *end;
*/
if (ma->m_mime_delivery_state->m_status < 0)
return ma->m_mime_delivery_state->m_status;
ma->m_size += length;
if (!ma->m_graph_progress_started)
{
ma->m_graph_progress_started = TRUE;
FE_GraphProgressInit (ma->m_mime_delivery_state->GetContext(), ma->m_url,
ma->m_url->content_length);
}
FE_GraphProgress (ma->m_mime_delivery_state->GetContext(), ma->m_url,
ma->m_size, length, ma->m_url->content_length);
/* Copy out the content type and encoding if we haven't already.
*/
if (!ma->m_type && ma->m_url->content_type)
{
ma->m_type = XP_STRDUP (ma->m_url->content_type);
/* If the URL has an encoding, and it's not one of the "null" encodings,
then keep it. */
if (ma->m_url->content_encoding &&
strcasecomp (ma->m_url->content_encoding, ENCODING_7BIT) &&
strcasecomp (ma->m_url->content_encoding, ENCODING_8BIT) &&
strcasecomp (ma->m_url->content_encoding, ENCODING_BINARY))
{
if (ma->m_encoding) XP_FREE (ma->m_encoding);
ma->m_encoding = XP_STRDUP (ma->m_url->content_encoding);
ma->m_already_encoded_p = TRUE;
}
/* Make sure there's a string in the type field.
Note that UNKNOWN_CONTENT_TYPE and APPLICATION_OCTET_STREAM are
different; "octet-stream" means that this document was *specified*
as an anonymous binary type; "unknown" means that we will guess
whether it is text or binary based on its contents.
*/
if (!ma->m_type || !*ma->m_type)
StrAllocCopy (ma->m_type, UNKNOWN_CONTENT_TYPE);
#if defined(XP_WIN) || defined(XP_OS2)
/* WinFE tends to spew out bogus internal "zz-" types for things
it doesn't know, so map those to the "real" unknown type.
*/
if (ma->m_type && !strncasecomp (ma->m_type, "zz-", 3))
StrAllocCopy (ma->m_type, UNKNOWN_CONTENT_TYPE);
#endif /* XP_WIN */
/* There are some of "magnus" types in the default
mime.types file that some platforms ship in /usr/local/lib/netscape/. These
types are meaningless to the end user, and the server never returns
them, but they're getting attached to local .cgi files anyway.
Remove them.
*/
if (ma->m_type && !strncasecomp (ma->m_type, "magnus-internal/", 16))
StrAllocCopy (ma->m_type, UNKNOWN_CONTENT_TYPE);
/* kludge.
Unfortunately, the URL_Struct shares the `encoding' slot
amongst the Content-Encoding and Content-Transfer-Encoding headers.
Content-Transfer-Encoding is required to be one of the standard
MIME encodings (x- types are explicitly discourgaged.) But
Content-Encoding can be anything (it's HTTP, not MIME.)
So, to prevent binary compressed data from getting dumped into the
mail stream, we special case some things here. If the encoding is
"x-compress" or "x-gzip", then that must have come from a
Content-Encoding header, So change the type to application/x-compress
and allow it to be encoded in base64.
But what if it's something we don't know? In that case, we just
dump it into the mail. For Content-Transfer-Encodings, like for
example, x-uuencode, that's appropriate. But for Content-Encodings,
like for example, x-some-brand-new-binary-compression-algorithm,
that's wrong.
*/
if (ma->m_encoding &&
(!strcasecomp (ma->m_encoding, ENCODING_COMPRESS) ||
!strcasecomp (ma->m_encoding, ENCODING_COMPRESS2)))
{
StrAllocCopy (ma->m_type, APPLICATION_COMPRESS);
StrAllocCopy (ma->m_encoding, ENCODING_BINARY);
ma->m_already_encoded_p = FALSE;
}
else if (ma->m_encoding &&
(!strcasecomp (ma->m_encoding, ENCODING_GZIP) ||
!strcasecomp (ma->m_encoding, ENCODING_GZIP2)))
{
StrAllocCopy (ma->m_type, APPLICATION_GZIP);
StrAllocCopy (ma->m_encoding, ENCODING_BINARY);
ma->m_already_encoded_p = FALSE;
}
/* If the caller has passed in an overriding type for this URL,
then ignore what the netlib guessed it to be. This is so that
we can hand it a file:/some/tmp/file and tell it that it's of
type message/rfc822 without having to depend on that tmp file
having some particular extension.
*/
if (ma->m_override_type)
{
StrAllocCopy (ma->m_type, ma->m_override_type);
if (ma->m_override_encoding)
StrAllocCopy (ma->m_encoding, ma->m_override_encoding);
}
char *mcType = msg_GetMissionControlledOutgoingMIMEType(ma->m_real_name, ma->m_x_mac_type, ma->m_x_mac_creator); // returns an allocated string
if (mcType)
{
FREEIF(ma->m_type);
ma->m_type = mcType;
}
}
/* Cumulatively examine the data that is passing through this stream,
building up a histogram that will be used when deciding which encoding
(if any) should be used.
*/
ma->AnalyzeDataChunk(block, length); /* calling this instead of the previous 20ish lines */
/* Write it to the file.
*/
while (length > 0)
{
int32 l;
l = XP_FileWrite (block, length, ma->m_file);
if (l < length)
{
ma->m_mime_delivery_state->m_status = MK_MIME_ERROR_WRITING_FILE;
return ma->m_mime_delivery_state->m_status;
}
block += l;
length -= l;
}
return 1;
}
PRIVATE void
mime_attachment_stream_complete (NET_StreamClass *stream)
{
/* Nothing to do here - the URL exit method does our cleanup. */
}
PRIVATE void
mime_attachment_stream_abort (NET_StreamClass *stream, int status)
{
MSG_DeliverMimeAttachment *ma = (MSG_DeliverMimeAttachment *) stream->data_object;
if (ma->m_mime_delivery_state->m_status >= 0)
ma->m_mime_delivery_state->m_status = status;
/* Nothing else to do here - the URL exit method does our cleanup. */
}
#ifdef XP_OS2
//DSR040297 - This looks pretty bad, but some compilers are very type
//strict when it comes to function pointers & the like... So this must be extern & extern "C" to match.
XP_BEGIN_PROTOS
extern NET_StreamClass *
mime_make_attachment_stream (int /*format_out*/, void * /*closure*/,
URL_Struct *url, MWContext *context);
XP_END_PROTOS
extern NET_StreamClass *
#else
static NET_StreamClass *
#endif
mime_make_attachment_stream (int /*format_out*/, void * /*closure*/,
URL_Struct *url, MWContext *context)
{
NET_StreamClass *stream;
TRACEMSG(("Setting up attachment stream. Have URL: %s\n", url->address));
stream = XP_NEW (NET_StreamClass);
if (stream == NULL)
return (NULL);
XP_MEMSET (stream, 0, sizeof (NET_StreamClass));
stream->name = "attachment stream";
stream->complete = mime_attachment_stream_complete;
stream->abort = mime_attachment_stream_abort;
stream->put_block = mime_attachment_stream_write;
stream->is_write_ready = mime_attachment_stream_write_ready;
stream->data_object = url->fe_data;
stream->window_id = context;
TRACEMSG(("Returning stream from mime_make_attachment_stream"));
return stream;
}
HG55451
void
MSG_RegisterConverters (void)
{
NET_RegisterContentTypeConverter ("*", FO_MAIL_TO,
NULL, mime_make_attachment_stream);
NET_RegisterContentTypeConverter ("*", FO_CACHE_AND_MAIL_TO,
NULL, mime_make_attachment_stream);
/* FO_MAIL_MESSAGE_TO is treated the same as FO_MAIL_TO -- this format_out
just means that libmime has already gotten its hands on this document
(which happens to be of type message/rfc822 or message/news) and has
altered it in some way (for example, has decrypted it.) */
NET_RegisterContentTypeConverter ("*", FO_MAIL_MESSAGE_TO,
NULL, mime_make_attachment_stream);
NET_RegisterContentTypeConverter ("*", FO_CACHE_AND_MAIL_MESSAGE_TO,
NULL, mime_make_attachment_stream);
/* Attachment of mail and news messages happens slightly differently:
Rather than FO_MAIL_TO going in to mime_make_attachment_stream, it
goes into MIME_MessageConverter, which will then open a later stream
with FO_MAIL_MESSAGE_TO -- which is how it eventually gets into
mime_make_attachment_stream, after having gone through libmime.
*/
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_MAIL_TO,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_MAIL_TO,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_CACHE_AND_MAIL_TO,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_CACHE_AND_MAIL_TO,
NULL, MIME_MessageConverter);
/* for saving news messages */
NET_RegisterContentTypeConverter ("*", FO_APPEND_TO_FOLDER,
NULL, msg_MakeAppendToFolderStream);
NET_RegisterContentTypeConverter ("*", FO_CACHE_AND_APPEND_TO_FOLDER,
NULL, msg_MakeAppendToFolderStream);
/* Decoders from mimehtml.c for message/rfc822 */
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_PRESENT,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_PRINT,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_EMBED,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_QUOTE_MESSAGE,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_QUOTE_HTML_MESSAGE,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_SAVE_AS,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_SAVE_AS_TEXT,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_INTERNAL_IMAGE,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_FONT,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_CMDLINE_ATTACHMENTS,
NULL, MIME_ToDraftConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_OPEN_DRAFT,
NULL, MIME_ToDraftConverter);
#ifdef XP_UNIX
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_SAVE_AS_POSTSCRIPT,
NULL, MIME_MessageConverter);
#endif /* XP_UNIX */
/* Decoders from mimehtml.c for message/news (same as message/rfc822) */
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_PRESENT,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_PRINT,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_EMBED,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_QUOTE_MESSAGE,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_QUOTE_HTML_MESSAGE,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_SAVE_AS,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_SAVE_AS_TEXT,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_INTERNAL_IMAGE,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_OPEN_DRAFT,
NULL, MIME_ToDraftConverter);
#ifdef XP_UNIX
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_SAVE_AS_POSTSCRIPT,
NULL, MIME_MessageConverter);
#endif /* XP_UNIX */
/* Decoders from mimehtml.c for text/richtext and text/enriched */
NET_RegisterContentTypeConverter (TEXT_RICHTEXT, FO_PRESENT,
NULL, MIME_RichtextConverter);
NET_RegisterContentTypeConverter (TEXT_RICHTEXT, FO_PRINT,
NULL, MIME_RichtextConverter);
NET_RegisterContentTypeConverter (TEXT_ENRICHED, FO_PRESENT,
NULL, MIME_EnrichedTextConverter);
NET_RegisterContentTypeConverter (TEXT_ENRICHED, FO_PRINT,
NULL, MIME_EnrichedTextConverter);
/* Decoders from mimevcrd.c for text/x-vcard and text/enriched */
NET_RegisterContentTypeConverter (TEXT_VCARD, FO_PRESENT,
NULL, MIME_VCardConverter);
NET_RegisterContentTypeConverter (TEXT_VCARD, FO_PRINT,
NULL, MIME_VCardConverter);
/* Decoders from mimejul.c for text/calendar */
#ifdef MOZ_CALENDAR
NET_RegisterContentTypeConverter (TEXT_CALENDAR, FO_PRESENT,
NULL, MIME_JulianConverter);
NET_RegisterContentTypeConverter (TEXT_CALENDAR, FO_PRINT,
NULL, MIME_JulianConverter);
#endif
}
static XP_Bool
mime_7bit_data_p (const char *string, uint32 size)
{
const unsigned char *s = (const unsigned char *) string;
const unsigned char *end = s + size;
if (s)
for (; s < end; s++)
if (*s > 0x7F)
return FALSE;
return TRUE;
}
static int
mime_sanity_check_fields (const char *from,
const char *reply_to,
const char *to,
const char *cc,
const char *bcc,
const char *fcc,
const char *newsgroups,
const char *followup_to,
const char * /*subject*/,
const char * /*references*/,
const char * /*organization*/,
const char * /*other_random_headers*/)
{
if (from) while (XP_IS_SPACE (*from)) from++;
if (reply_to) while (XP_IS_SPACE (*reply_to)) reply_to++;
if (to) while (XP_IS_SPACE (*to)) to++;
if (cc) while (XP_IS_SPACE (*cc)) cc++;
if (bcc) while (XP_IS_SPACE (*bcc)) bcc++;
if (fcc) while (XP_IS_SPACE (*fcc)) fcc++;
if (newsgroups) while (XP_IS_SPACE (*newsgroups)) newsgroups++;
if (followup_to) while (XP_IS_SPACE (*followup_to)) followup_to++;
/* #### check other_random_headers for newline conventions */
if (!from || !*from)
return MK_MIME_NO_SENDER;
else if ((!to || !*to) &&
(!cc || !*cc) &&
(!bcc || !*bcc) &&
(!newsgroups || !*newsgroups))
return MK_MIME_NO_RECIPIENTS;
else
return 0;
}
/* Strips whitespace, and expands newlines into newline-tab for use in
mail headers. Returns a new string or 0 (if it would have been empty.)
If addr_p is true, the addresses will be parsed and reemitted as
rfc822 mailboxes.
*/
static char *
mime_fix_header_1 (const char *string,
XP_Bool addr_p, XP_Bool news_p)
{
char *new_string;
const char *in;
char *out;
int32 i, old_size, new_size;
if (!string || !*string)
return 0;
if (addr_p)
{
char *n = MSG_ReformatRFC822Addresses (string);
if (n) return n;
}
old_size = XP_STRLEN (string);
new_size = old_size;
for (i = 0; i < old_size; i++)
if (string[i] == CR || string[i] == LF)
new_size += 2;
new_string = (char *) XP_ALLOC (new_size + 1);
if (! new_string)
return 0;
in = string;
out = new_string;
/* strip leading whitespace. */
while (XP_IS_SPACE (*in))
in++;
/* replace CR, LF, or CRLF with CRLF-TAB. */
while (*in)
{
if (*in == CR || *in == LF)
{
if (*in == CR && in[1] == LF)
in++;
in++;
*out++ = CR;
*out++ = LF;
*out++ = '\t';
}
else if (news_p && *in == ',')
{
*out++ = *in++;
/* skip over all whitespace after a comma. */
while (XP_IS_SPACE (*in))
in++;
}
else
{
*out++ = *in++;
}
}
*out = 0;
/* strip trailing whitespace. */
while (out > in && XP_IS_SPACE (out[-1]))
*out-- = 0;
/* If we ended up throwing it all away, use 0 instead of "". */
if (!*new_string)
{
XP_FREE (new_string);
new_string = 0;
}
return new_string;
}
static char *
mime_fix_header (const char *string)
{
return mime_fix_header_1 (string, FALSE, FALSE);
}
static char *
mime_fix_addr_header (const char *string)
{
return mime_fix_header_1 (string, TRUE, FALSE);
}
static char *
mime_fix_news_header (const char *string)
{
return mime_fix_header_1 (string, FALSE, TRUE);
}
#if 0
static XP_Bool
mime_type_conversion_possible (const char *from_type, const char *to_type)
{
if (! to_type)
return TRUE;
if (! from_type)
return FALSE;
if (!strcasecomp (from_type, to_type))
/* Don't run text/plain through the text->html converter. */
return FALSE;
if ((!strcasecomp (from_type, INTERNAL_PARSER) ||
!strcasecomp (from_type, TEXT_HTML) ||
!strcasecomp (from_type, TEXT_MDL)) &&
!strcasecomp (to_type, TEXT_PLAIN))
/* Don't run UNKNOWN_CONTENT_TYPE through the text->html converter
(treat it as text/plain already.) */
return TRUE;
#ifdef XP_UNIX
if ((!strcasecomp (from_type, INTERNAL_PARSER) ||
!strcasecomp (from_type, TEXT_PLAIN) ||
!strcasecomp (from_type, TEXT_HTML) ||
!strcasecomp (from_type, TEXT_MDL) ||
!strcasecomp (from_type, IMAGE_GIF) ||
!strcasecomp (from_type, IMAGE_JPG) ||
!strcasecomp (from_type, IMAGE_PJPG) ||
!strcasecomp (from_type, IMAGE_XBM) ||
!strcasecomp (from_type, IMAGE_XBM2) ||
!strcasecomp (from_type, IMAGE_XBM3) ||
/* always treat unknown content types as text/plain */
!strcasecomp (from_type, UNKNOWN_CONTENT_TYPE)
) &&
!strcasecomp (to_type, APPLICATION_POSTSCRIPT))
return TRUE;
#endif /* XP_UNIX */
return FALSE;
}
#endif
static XP_Bool
mime_type_requires_b64_p (const char *type)
{
if (!type || !strcasecomp (type, UNKNOWN_CONTENT_TYPE))
/* Unknown types don't necessarily require encoding. (Note that
"unknown" and "application/octet-stream" aren't the same.) */
return FALSE;
else if (!strncasecomp (type, "image/", 6) ||
!strncasecomp (type, "audio/", 6) ||
!strncasecomp (type, "video/", 6) ||
!strncasecomp (type, "application/", 12))
{
/* The following types are application/ or image/ types that are actually
known to contain textual data (meaning line-based, not binary, where
CRLF conversion is desired rather than disasterous.) So, if the type
is any of these, it does not *require* base64, and if we do need to
encode it for other reasons, we'll probably use quoted-printable.
But, if it's not one of these types, then we assume that any subtypes
of the non-"text/" types are binary data, where CRLF conversion would
corrupt it, so we use base64 right off the bat.
The reason it's desirable to ship these as text instead of just using
base64 all the time is mainly to preserve the readability of them for
non-MIME users: if I mail a /bin/sh script to someone, it might not
need to be encoded at all, so we should leave it readable if we can.
This list of types was derived from the comp.mail.mime FAQ, section
10.2.2, "List of known unregistered MIME types" on 2-Feb-96.
*/
static const char *app_and_image_types_which_are_really_text[] = {
"application/mac-binhex40", /* APPLICATION_BINHEX */
"application/pgp", /* APPLICATION_PGP */
"application/x-pgp-message", /* APPLICATION_PGP2 */
"application/postscript", /* APPLICATION_POSTSCRIPT */
"application/x-uuencode", /* APPLICATION_UUENCODE */
"application/x-uue", /* APPLICATION_UUENCODE2 */
"application/uue", /* APPLICATION_UUENCODE4 */
"application/uuencode", /* APPLICATION_UUENCODE3 */
"application/sgml",
"application/x-csh",
"application/x-javascript",
"application/x-latex",
"application/x-macbinhex40",
"application/x-ns-proxy-autoconfig",
"application/x-www-form-urlencoded",
"application/x-perl",
"application/x-sh",
"application/x-shar",
"application/x-tcl",
"application/x-tex",
"application/x-texinfo",
"application/x-troff",
"application/x-troff-man",
"application/x-troff-me",
"application/x-troff-ms",
"application/x-troff-ms",
"application/x-wais-source",
"image/x-bitmap",
"image/x-pbm",
"image/x-pgm",
"image/x-portable-anymap",
"image/x-portable-bitmap",
"image/x-portable-graymap",
"image/x-portable-pixmap", /* IMAGE_PPM */
"image/x-ppm",
"image/x-xbitmap", /* IMAGE_XBM */
"image/x-xbm", /* IMAGE_XBM2 */
"image/xbm", /* IMAGE_XBM3 */
"image/x-xpixmap",
"image/x-xpm",
0 };
const char **s;
for (s = app_and_image_types_which_are_really_text; *s; s++)
if (!strcasecomp (type, *s))
return FALSE;
/* All others must be assumed to be binary formats, and need Base64. */
return TRUE;
}
else
return FALSE;
}
#ifdef XP_OS2
XP_BEGIN_PROTOS
extern int mime_encoder_output_fn (const char *buf, int32 size, void *closure);
XP_END_PROTOS
#else
static int mime_encoder_output_fn (const char *buf, int32 size, void *closure);
#endif
/* Given a content-type and some info about the contents of the document,
decide what encoding it should have.
*/
int
MSG_DeliverMimeAttachment::PickEncoding (int16 mail_csid)
{
// use the boolean so we only have to test for uuencode vs base64 once
XP_Bool needsB64 = FALSE;
if (m_already_encoded_p)
goto DONE;
if (mime_type_requires_b64_p (m_type))
{
/* If the content-type is "image/" or something else known to be binary,
always use base64 (so that we don't get confused by newline
conversions.)
*/
needsB64 = TRUE;
}
else
{
/* Otherwise, we need to pick an encoding based on the contents of
the document.
*/
XP_Bool encode_p;
if (m_max_column > 900)
encode_p = TRUE;
else if (mime_use_quoted_printable_p && m_unprintable_count)
encode_p = TRUE;
else if (m_null_count) /* If there are nulls, we must always encode,
because sendmail will blow up. */
encode_p = TRUE;
#if 0
else if (m_ctl_count) /* Should we encode if random other control
characters are present? Probably... */
encode_p = TRUE;
#endif
else
encode_p = FALSE;
/* MIME requires a special case that these types never be encoded.
*/
if (!strncasecomp (m_type, "message", 7) ||
!strncasecomp (m_type, "multipart", 9))
{
encode_p = FALSE;
if (m_desired_type && !strcasecomp (m_desired_type, TEXT_PLAIN))
{
XP_FREE (m_desired_type);
m_desired_type = 0;
}
}
/* If the Mail csid is CS_JIS we force it to use Base64 for attachments (bug#104255).
Use 7 bit for other STATFUL ( e.g. CS_2022_KR) csid. */
if ((mail_csid == CS_JIS) &&
(strcasecomp(m_type, TEXT_HTML) == 0))
needsB64 = TRUE;
else if((mail_csid & STATEFUL) &&
((strcasecomp(m_type, TEXT_HTML) == 0) ||
(strcasecomp(m_type, TEXT_MDL) == 0) ||
(strcasecomp(m_type, TEXT_PLAIN) == 0) ||
(strcasecomp(m_type, TEXT_RICHTEXT) == 0) ||
(strcasecomp(m_type, TEXT_ENRICHED) == 0) ||
(strcasecomp(m_type, TEXT_VCARD) == 0) ||
(strcasecomp(m_type, TEXT_CSS) == 0) ||
(strcasecomp(m_type, TEXT_JSSS) == 0) ||
(strcasecomp(m_type, MESSAGE_RFC822) == 0) ||
(strcasecomp(m_type, MESSAGE_NEWS) == 0)))
{
needsB64 = TRUE;
}
else if (encode_p &&
m_size > 500 &&
m_unprintable_count > (m_size / 10))
/* If the document contains more than 10% unprintable characters,
then that seems like a good candidate for base64 instead of
quoted-printable.
*/
needsB64 = TRUE;
else if (encode_p)
StrAllocCopy (m_encoding, ENCODING_QUOTED_PRINTABLE);
else if (m_highbit_count > 0)
StrAllocCopy (m_encoding, ENCODING_8BIT);
else
StrAllocCopy (m_encoding, ENCODING_7BIT);
}
if (needsB64)
{
/*
### mwelch We might have to uuencode instead of
base64 the binary data.
*/
if (UseUUEncode_p())
StrAllocCopy (m_encoding, ENCODING_UUENCODE);
else
StrAllocCopy (m_encoding, ENCODING_BASE64);
}
/* Now that we've picked an encoding, initialize the filter.
*/
XP_ASSERT(!m_encoder_data);
if (!strcasecomp(m_encoding, ENCODING_BASE64))
{
m_encoder_data = MimeB64EncoderInit(mime_encoder_output_fn,
m_mime_delivery_state);
if (!m_encoder_data) return MK_OUT_OF_MEMORY;
}
else if (!strcasecomp(m_encoding, ENCODING_UUENCODE))
{
char *tailName = NULL;
if (m_url_string)
{
tailName = XP_STRRCHR(m_url_string, '/');
if (tailName)
tailName = XP_STRDUP(tailName+1);
}
if (m_url && !tailName)
{
tailName = XP_STRRCHR(m_url->address, '/');
if (tailName)
tailName = XP_STRDUP(tailName+1);
}
m_encoder_data = MimeUUEncoderInit(tailName ? tailName : "",
mime_encoder_output_fn,
m_mime_delivery_state);
XP_FREEIF(tailName);
if (!m_encoder_data) return MK_OUT_OF_MEMORY;
}
else if (!strcasecomp(m_encoding, ENCODING_QUOTED_PRINTABLE))
{
m_encoder_data = MimeQPEncoderInit(mime_encoder_output_fn,
m_mime_delivery_state);
if (!m_encoder_data) return MK_OUT_OF_MEMORY;
}
else
{
m_encoder_data = 0;
}
/* Do some cleanup for documents with unknown content type.
There are two issues: how they look to MIME users, and how they look to
non-MIME users.
If the user attaches a "README" file, which has unknown type because it
has no extension, we still need to send it with no encoding, so that it
is readable to non-MIME users.
But if the user attaches some random binary file, then base64 encoding
will have been chosen for it (above), and in this case, it won't be
immediately readable by non-MIME users. However, if we type it as
text/plain instead of application/octet-stream, it will show up inline
in a MIME viewer, which will probably be ugly, and may possibly have
bad charset things happen as well.
So, the heuristic we use is, if the type is unknown, then the type is
set to application/octet-stream for data which needs base64 (binary data)
and is set to text/plain for data which didn't need base64 (unencoded or
lightly encoded data.)
*/
DONE:
if (!m_type || !*m_type || !strcasecomp(m_type, UNKNOWN_CONTENT_TYPE))
{
if (m_already_encoded_p)
StrAllocCopy (m_type, APPLICATION_OCTET_STREAM);
else if (m_encoding &&
(!strcasecomp(m_encoding, ENCODING_BASE64) ||
!strcasecomp(m_encoding, ENCODING_UUENCODE)))
StrAllocCopy (m_type, APPLICATION_OCTET_STREAM);
else
StrAllocCopy (m_type, TEXT_PLAIN);
}
return 0;
}
/* Some types should have a "charset=" parameter, and some shouldn't.
This is what decides. */
XP_Bool
mime_type_needs_charset (const char *type)
{
/* Only text types should have charset. */
if (!type || !*type)
return FALSE;
else if (!strncasecomp (type, "text", 4))
return TRUE;
else
return FALSE;
}
char*
mime_get_stream_write_buffer(void)
{
if (!mime_mailto_stream_write_buffer) {
mime_mailto_stream_write_buffer = (char *) XP_ALLOC(MIME_BUFFER_SIZE);
}
return mime_mailto_stream_write_buffer;
}
int MSG_SendMimeDeliveryState::InitImapOfflineDB(uint32 flag)
{
int status = 0;
char *folderName = msg_MagicFolderName(m_pane->GetMaster()->GetPrefs(), flag, &status);
if (status < 0)
return status;
else if (!folderName)
return MK_OUT_OF_MEMORY;
else if (NET_URL_Type(folderName) == IMAP_TYPE_URL)
{
MsgERR err = eSUCCESS;
char *dummyEnvelope = msg_GetDummyEnvelope();
XP_ASSERT(!m_imapOutgoingParser && !m_imapLocalMailDB);
MailDB::Open(m_msg_file_name, TRUE, &m_imapLocalMailDB);
if (err != eSUCCESS)
{
if (m_imapLocalMailDB)
m_imapLocalMailDB->Close();
m_imapLocalMailDB = NULL;
status = MK_OUT_OF_MEMORY;
goto done;
}
m_imapOutgoingParser = new ParseOutgoingMessage;
if (!m_imapOutgoingParser)
{
m_imapLocalMailDB->Close();
m_imapLocalMailDB = NULL;
status = MK_OUT_OF_MEMORY;
goto done;
}
m_imapOutgoingParser->Clear();
m_imapOutgoingParser->SetOutFile(m_msg_file);
m_imapOutgoingParser->SetMailDB(m_imapLocalMailDB);
m_imapOutgoingParser->SetWriteToOutFile(FALSE);
m_imapOutgoingParser->Init(0);
m_imapOutgoingParser->StartNewEnvelope(dummyEnvelope,
XP_STRLEN(dummyEnvelope));
}
done:
XP_FREEIF(folderName);
return status;
}
#define PUSH_STRING(S) \
do { XP_STRCPY (buffer_tail, S); buffer_tail += XP_STRLEN (S); } while(0)
#define PUSH_NEWLINE() \
do { *buffer_tail++ = CR; *buffer_tail++ = LF; *buffer_tail = '\0'; } while(0)
/* All of the desired attachments have been written to individual temp files,
and we know what's in them. Now we need to make a final temp file of the
actual mail message, containing all of the other files after having been
encoded as appropriate.
*/
int MSG_SendMimeDeliveryState::GatherMimeAttachments ()
{
int32 status, i;
char *headers = 0;
char *separator = 0;
XP_File in_file = 0;
XP_Bool multipart_p = FALSE;
XP_Bool plaintext_is_mainbody_p = FALSE; // only using text converted from HTML?
char *buffer = 0;
char *buffer_tail = 0;
char* error_msg = 0;
int16 win_csid = INTL_DefaultWinCharSetID(GetContext());
XP_Bool tonews = (m_fields && m_fields->GetNewsgroups() && *m_fields->GetNewsgroups()) ;
// to news is true if we have a m_field and we have a Newsgroup and it is not empty
INTL_MessageSendToNews(tonews); // hack to make Korean Mail/News work correctly
// Look at libi18n/doc_ccc.c for details
// temp solution for bug 30725
int16 mail_csid = tonews ?
INTL_DefaultNewsCharSetID(win_csid) :
INTL_DefaultMailCharSetID(win_csid);
MSG_SendPart* toppart = NULL; // The very top most container of the message
// that we are going to send.
MSG_SendPart* mainbody = NULL;// The leaf node that contains the text of the
// message we're going to contain.
MSG_SendPart* maincontainer = NULL;// The direct child of toppart that will
// contain the mainbody. If mainbody is
// the same as toppart, then this is
// also the same. But if mainbody is
// to end up somewhere inside of a
// multipart/alternative or a
// multipart/related, then this is that
// multipart object.
MSG_SendPart* plainpart = NULL; // If we converted HTML into plaintext,
// the message or child containing the plaintext
// goes here. (Need to use this to determine
// what headers to append/set to the main
// message body.)
char *hdrs = 0;
XP_Bool maincontainerISrelatedpart = FALSE;
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(GetContext());
status = m_status;
if (status < 0)
goto FAIL;
if (m_attachments_only_p)
{
/* If we get here, we shouldn't have the "generating a message" cb. */
XP_ASSERT(!m_dont_deliver_p &&
!m_message_delivery_done_callback);
if (m_attachments_done_callback)
{
struct MSG_AttachedFile *attachments;
XP_ASSERT(m_attachment_count > 0);
if (m_attachment_count <= 0)
{
m_attachments_done_callback (GetContext(),
m_fe_data, 0, 0,
0);
m_attachments_done_callback = 0;
Clear();
goto FAIL;
}
attachments = (struct MSG_AttachedFile *)
XP_ALLOC((m_attachment_count + 1) * sizeof(*attachments));
if (!attachments) goto FAILMEM;
XP_MEMSET(attachments, 0, ((m_attachment_count + 1)
* sizeof(*attachments)));
for (i = 0; i < m_attachment_count; i++)
{
MSG_DeliverMimeAttachment *ma = &m_attachments[i];
#undef SNARF
#define SNARF(x,y) do { if((y) && *(y) && !(x)) { ((x) = (y)); ((y) = 0); }} \
while(0)
/* Rather than copying the strings and dealing with allocation
failures, we'll just "move" them into the other struct (this
should be ok since this file uses FREEIF when discarding
the mime_attachment objects.) */
SNARF(attachments[i].orig_url, ma->m_url_string);
SNARF(attachments[i].file_name, ma->m_file_name);
SNARF(attachments[i].type, ma->m_type);
SNARF(attachments[i].encoding, ma->m_encoding);
SNARF(attachments[i].description, ma->m_description);
SNARF(attachments[i].x_mac_type, ma->m_x_mac_type);
SNARF(attachments[i].x_mac_creator, ma->m_x_mac_creator);
#undef SNARF
HG96484
attachments[i].size = ma->m_size;
attachments[i].unprintable_count = ma->m_unprintable_count;
attachments[i].highbit_count = ma->m_highbit_count;
attachments[i].ctl_count = ma->m_ctl_count;
attachments[i].null_count = ma->m_null_count;
attachments[i].max_line_length = ma->m_max_column;
/* Doesn't really matter, but let's not lie about encoding
in case it does someday. */
if (attachments[i].highbit_count > 0 &&
attachments[i].encoding &&
!strcasecomp(attachments[i].encoding, ENCODING_7BIT))
StrAllocCopy (attachments[i].encoding, ENCODING_8BIT);
}
/* The callback is expected to free the attachments list and all
the strings in it. It's also expected to delete the files!
*/
m_attachments_done_callback (GetContext(),
m_fe_data, 0, 0,
attachments);
m_attachments_done_callback = 0;
Clear();
}
goto FAIL;
}
/* If we get here, we're generating a message, so there shouldn't be an
attachments callback. */
XP_ASSERT(!m_attachments_done_callback);
if (!m_attachment1_type) {
m_attachment1_type = XP_STRDUP(TEXT_PLAIN);
if (!m_attachment1_type) goto FAILMEM;
}
// If we have a text/html main part, and we need a plaintext attachment, then
// we'll do so now. This is an asynchronous thing, so we'll kick it off and
// count on getting back here when it finishes.
if (m_plaintext == NULL &&
(m_fields->GetForcePlainText() ||
m_fields->GetUseMultipartAlternative()) &&
m_attachment1_body && XP_STRCMP(m_attachment1_type, TEXT_HTML) == 0) {
m_html_filename = WH_TempName (xpFileToPost, "nsmail");
if (!m_html_filename) goto FAILMEM;
in_file = XP_FileOpen(m_html_filename, xpFileToPost, XP_FILE_WRITE_BIN);
if (!in_file) {
status = MK_UNABLE_TO_OPEN_TMP_FILE;
goto FAIL;
}
status = XP_FileWrite(m_attachment1_body, m_attachment1_body_length,
in_file);
if (status < int(m_attachment1_body_length)) {
if (status >= 0) {
status = MK_MIME_ERROR_WRITING_FILE;
}
goto FAIL;
}
status = XP_FileClose(in_file);
in_file = NULL;
if (status < 0) goto FAIL;
m_plaintext = new MSG_DeliverMimeAttachment;
if (!m_plaintext) goto FAILMEM;
m_plaintext->m_mime_delivery_state = this;
char* temp = WH_FileName(m_html_filename, xpFileToPost);
m_plaintext->m_url_string = XP_PlatformFileToURL(temp);
if (temp) XP_FREE(temp);
if (!m_plaintext->m_url_string) goto FAILMEM;
m_plaintext->m_url =
NET_CreateURLStruct (m_plaintext->m_url_string, NET_DONT_RELOAD);
if (!m_plaintext->m_url) goto FAILMEM;
StrAllocCopy(m_plaintext->m_url->content_type, TEXT_HTML);
StrAllocCopy(m_plaintext->m_type, TEXT_HTML);
StrAllocCopy(m_plaintext->m_desired_type, TEXT_PLAIN);
m_attachment_pending_count++;
status = m_plaintext->SnarfAttachment();
if (status < 0) goto FAIL;
if (m_attachment_pending_count > 0) return 0;
}
/* hack to avoid having to allocate memory... */
buffer = mime_get_stream_write_buffer();
if (! buffer) goto FAILMEM;
buffer_tail = buffer;
XP_ASSERT (m_attachment_pending_count == 0);
FE_Progress(GetContext(), XP_GetString(MK_MSG_ASSEMBLING_MSG));
/* First, open the message file.
*/
m_msg_file_name = WH_TempName (xpFileToPost, "nsmail");
if (! m_msg_file_name) goto FAILMEM;
m_msg_file = XP_FileOpen (m_msg_file_name, xpFileToPost,
XP_FILE_WRITE_BIN);
if (! m_msg_file)
{
status = MK_UNABLE_TO_OPEN_TMP_FILE;
error_msg = PR_smprintf(XP_GetString(status), m_msg_file_name);
if (!error_msg) status = MK_OUT_OF_MEMORY;
goto FAIL;
}
if (NET_IsOffline() && (m_deliver_mode == MSG_SaveAsDraft ||
m_deliver_mode == MSG_SaveAsTemplate))
{
status = InitImapOfflineDB( m_deliver_mode == MSG_SaveAsDraft ?
MSG_FOLDER_FLAG_DRAFTS :
MSG_FOLDER_FLAG_TEMPLATES );
if (status < 0)
{
StrAllocCopy (error_msg, XP_GetString(status));
goto FAIL;
}
}
#ifdef GENERATE_MESSAGE_ID
if (m_fields->GetMessageId() == NULL) {
m_fields->SetMessageId(msg_generate_message_id ());
}
#endif /* GENERATE_MESSAGE_ID */
mainbody = new MSG_SendPart(this, mail_csid);
if (!mainbody) goto FAILMEM;
mainbody->SetMainPart(TRUE);
mainbody->SetType(m_attachment1_type ? m_attachment1_type : TEXT_PLAIN);
XP_ASSERT(mainbody->GetBuffer() == NULL);
status = mainbody->SetBuffer(m_attachment1_body ? m_attachment1_body : " ");
if (status < 0) goto FAIL;
/* ### mwelch
Determine the encoding of the main message body before we free it.
The proper way to do this should be to test whatever text is in mainbody
just before writing it out, but that will require a fix that is less safe
and takes more memory. */
if ((mail_csid & STATEFUL) || /* CS_JIS or CS_2022_KR */
mime_7bit_data_p (m_attachment1_body,
m_attachment1_body_length))
StrAllocCopy (m_attachment1_encoding, ENCODING_7BIT);
else if (mime_use_quoted_printable_p)
StrAllocCopy (m_attachment1_encoding, ENCODING_QUOTED_PRINTABLE);
else
StrAllocCopy (m_attachment1_encoding, ENCODING_8BIT);
FREEIF (m_attachment1_body);
maincontainer = mainbody;
// If we were given a pre-saved collection of HTML and contained images,
// then we want mainbody to point to the HTML lump therein.
if (m_related_part)
{
// If m_related_part is of type text/html, set both maincontainer
// and mainbody to point to it. If m_related_part is multipart/related,
// however, set mainbody to be the first child within m_related_part.
// To plug a memory leak, delete the original maincontainer/mainbody.
//
// NOTE: We DO NOT want to do this to the HTML or multipart/related
// MSG_SendParts because they are deleted at the end of message
// delivery by the TapeFileSystem object
// (MSG_MimeRelatedSaver / mhtmlstm.cpp).
delete mainbody;
// No matter what, maincontainer points to the outermost related part.
maincontainer = m_related_part;
maincontainerISrelatedpart = TRUE;
const char *relPartType = m_related_part->GetType();
if (relPartType && !strcmp(relPartType, MULTIPART_RELATED))
// outer shell is m/related,
// mainbody wants to be the HTML lump within
mainbody = m_related_part->GetChild(0);
else
// outer shell is text/html,
// so mainbody wants to be the same lump
mainbody = maincontainer;
mainbody->SetMainPart(TRUE);
}
if (m_plaintext) {
// OK. We have a plaintext version of the main body that we want to
// send instead of or with the text/html. Put it in.
plainpart = new MSG_SendPart(this, mail_csid);
if (!plainpart) goto FAILMEM;
status = plainpart->SetType(TEXT_PLAIN);
if (status < 0) goto FAIL;
status = plainpart->SetFile(m_plaintext->m_file_name, xpFileToPost);
if (status < 0) goto FAIL;
m_plaintext->AnalyzeSnarfedFile(); // look for 8 bit text, long lines, etc.
m_plaintext->PickEncoding(mail_csid);
hdrs = mime_generate_attachment_headers(m_plaintext->m_type,
m_plaintext->m_encoding,
m_plaintext->m_description,
m_plaintext->m_x_mac_type,
m_plaintext->m_x_mac_creator,
m_plaintext->m_real_name, 0,
m_digest_p,
m_plaintext,
mail_csid);
if (!hdrs) goto FAILMEM;
status = plainpart->SetOtherHeaders(hdrs);
XP_FREE(hdrs);
hdrs = NULL;
if (status < 0) goto FAIL;
if (m_fields->GetUseMultipartAlternative()) {
MSG_SendPart* htmlpart = maincontainer;
maincontainer = new MSG_SendPart(this);
if (!maincontainer) goto FAILMEM;
status = maincontainer->SetType(MULTIPART_ALTERNATIVE);
if (status < 0) goto FAIL;
status = maincontainer->AddChild(plainpart);
if (status < 0) goto FAIL;
status = maincontainer->AddChild(htmlpart);
if (status < 0) goto FAIL;
// Create the encoder for the plaintext part here,
// because we aren't the main part (attachment1).
// (This, along with the rest of the routine, should really
// be restructured so that no special treatment is given to
// the main body text that came in. Best to put attachment1_text
// etc. into a MSG_SendPart, then reshuffle the parts. Sigh.)
if (!XP_STRCASECMP(m_plaintext->m_encoding, ENCODING_QUOTED_PRINTABLE))
{
MimeEncoderData *plaintext_enc = MimeQPEncoderInit(mime_encoder_output_fn, this);
if (!plaintext_enc)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
plainpart->SetEncoderData(plaintext_enc);
}
} else {
delete maincontainer; //### mwelch - does this cause a crash?!
if (maincontainerISrelatedpart)
m_related_part = NULL;
maincontainer = plainpart;
mainbody = maincontainer;
FREEIF(m_attachment1_type);
m_attachment1_type = XP_STRDUP(TEXT_PLAIN);
if (!m_attachment1_type) goto FAILMEM;
/* Override attachment1_encoding here. */
FREEIF(m_attachment1_encoding);
StrAllocCopy(m_attachment1_encoding, m_plaintext->m_encoding);
plaintext_is_mainbody_p = TRUE; // converted plaintext is mainbody
}
}
// ###tw This used to be this more complicated thing, but for now, if it we
// have any attachments, we generate multipart.
//multipart_p = (m_attachment_count > 1 ||
// (m_attachment_count == 1 &&
// m_attachment1_body_length > 0));
multipart_p = (m_attachment_count > 0);
if (multipart_p) {
toppart = new MSG_SendPart(this);
if (!toppart) goto FAILMEM;
status = toppart->SetType(m_digest_p ? MULTIPART_DIGEST : MULTIPART_MIXED);
if (status < 0) goto FAIL;
status = toppart->AddChild(maincontainer);
if (status < 0) goto FAIL;
HG36459
{
status = toppart->SetBuffer(XP_GetString (MK_MIME_MULTIPART_BLURB));
if (status < 0) goto FAIL;
}
} else {
toppart = maincontainer;
}
/* Write out the message headers.
*/
headers = mime_generate_headers (m_fields,
INTL_GetCSIWinCSID(c),
m_deliver_mode);
if (!headers) goto FAILMEM;
// ### mwelch
// If we converted HTML into plaintext, the plaintext part (plainpart)
// already has its content-type and content-transfer-encoding
// ("other") headers set.
//
// In the specific case where such a plaintext part is the
// top level message part (iff an HTML message is being sent
// as text only and no other attachments exist) we want to
// preserve the original plainpart headers, since they
// contain accurate transfer encoding and Mac type/creator
// information.
//
// So, in the above case we append the main message headers,
// otherwise we overwrite whatever headers may have existed.
//
/* reordering of headers will happen in MSG_SendPart::Write */
if ((plainpart) && (plainpart == toppart))
status = toppart->AppendOtherHeaders(headers);
else
status = toppart->SetOtherHeaders(headers);
XP_FREE(headers);
headers = NULL;
if (status < 0) goto FAIL;
/* Set up the first part (user-typed.) For now, do it even if the first
* part is empty; we need to add things to skip it if this part is empty.
* ###tw
*/
/* Set up encoder for the first part (message body.)
*/
XP_ASSERT(!m_attachment1_encoder_data);
if (!strcasecomp(m_attachment1_encoding, ENCODING_BASE64))
{
m_attachment1_encoder_data =
MimeB64EncoderInit(mime_encoder_output_fn, this);
if (!m_attachment1_encoder_data) goto FAILMEM;
}
else if (!strcasecomp(m_attachment1_encoding,
ENCODING_QUOTED_PRINTABLE))
{
m_attachment1_encoder_data =
MimeQPEncoderInit(mime_encoder_output_fn, this);
if (!m_attachment1_encoder_data) goto FAILMEM;
}
// ### mwelch
// If we converted HTML into plaintext, the plaintext part
// already has its type/encoding headers set. So, in the specific
// case where such a plaintext part is the main message body
// (iff an HTML message is being sent as text only)
// we want to avoid generating type/encoding/digest headers;
// in all other cases, generate such headers here.
//
// We really want to set up headers as a dictionary of some sort
// so that we need not worry about duplicate header lines.
//
if ((!plainpart) || (plainpart != mainbody))
{
hdrs = mime_generate_attachment_headers (m_attachment1_type,
m_attachment1_encoding,
0, 0, 0, 0, 0,
m_digest_p,
NULL, /* no "ma"! */
mail_csid);
if (!hdrs) goto FAILMEM;
status = mainbody->AppendOtherHeaders(hdrs);
if (status < 0) goto FAIL;
}
FREEIF(hdrs);
status = mainbody->SetEncoderData(m_attachment1_encoder_data);
m_attachment1_encoder_data = NULL;
if (status < 0) goto FAIL;
/* Set up the subsequent parts.
*/
if (m_attachment_count > 0)
{
char *buffer;
/* Hack to avoid having to allocate memory... */
if (! mime_mailto_stream_read_buffer)
mime_mailto_stream_read_buffer = (char *) XP_ALLOC (MIME_BUFFER_SIZE);
buffer = mime_mailto_stream_read_buffer;
if (! buffer) goto FAILMEM;
buffer_tail = buffer;
for (i = 0; i < m_attachment_count; i++)
{
MSG_DeliverMimeAttachment *ma = &m_attachments[i];
char *hdrs = 0;
MSG_SendPart* part = NULL;
// If at this point we *still* don't have an content-type, then
// we're never going to get one.
if (ma->m_type == NULL) {
ma->m_type = XP_STRDUP(UNKNOWN_CONTENT_TYPE);
if (ma->m_type == NULL) goto FAILMEM;
}
ma->PickEncoding (mail_csid);
part = new MSG_SendPart(this);
if (!part) goto FAILMEM;
status = toppart->AddChild(part);
if (status < 0) goto FAIL;
status = part->SetType(ma->m_type);
if (status < 0) goto FAIL;
hdrs = mime_generate_attachment_headers (ma->m_type, ma->m_encoding,
ma->m_description,
ma->m_x_mac_type,
ma->m_x_mac_creator,
ma->m_real_name,
ma->m_url_string,
m_digest_p,
ma,
mail_csid);
if (!hdrs) goto FAILMEM;
status = part->SetOtherHeaders(hdrs);
FREEIF(hdrs);
if (status < 0) goto FAIL;
status = part->SetFile(ma->m_file_name, xpFileToPost);
if (status < 0) goto FAIL;
if (ma->m_encoder_data) {
status = part->SetEncoderData(ma->m_encoder_data);
if (status < 0) goto FAIL;
ma->m_encoder_data = NULL;
}
ma->m_current_column = 0;
if (ma->m_type &&
(!strcasecomp (ma->m_type, MESSAGE_RFC822) ||
!strcasecomp (ma->m_type, MESSAGE_NEWS))) {
status = part->SetStripSensitiveHeaders(TRUE);
if (status < 0) goto FAIL;
}
}
}
// OK, now actually write the structure we've carefully built up.
status = toppart->Write();
if (status < 0) goto FAIL;
HG75442
if (m_msg_file)
XP_FileClose (m_msg_file);
m_msg_file = 0;
if (m_imapOutgoingParser)
{
m_imapOutgoingParser->FinishHeader();
XP_ASSERT(m_imapOutgoingParser->m_newMsgHdr);
m_imapOutgoingParser->m_newMsgHdr->OrFlags(kIsRead);
m_imapOutgoingParser->m_newMsgHdr->SetMessageSize
(m_imapOutgoingParser->m_bytes_written);
m_imapOutgoingParser->m_newMsgHdr->SetMessageKey(0);
m_imapLocalMailDB->AddHdrToDB(m_imapOutgoingParser->m_newMsgHdr,
NULL, TRUE);
}
FE_Progress(GetContext(), XP_GetString(MK_MSG_ASSEMB_DONE_MSG));
if (m_dont_deliver_p &&
m_message_delivery_done_callback)
{
m_message_delivery_done_callback (GetContext(),
m_fe_data, 0,
XP_STRDUP (m_msg_file_name));
/* Need to ditch the file name here so that we don't delete the
file, since in this case, the FE needs the file itself. */
FREEIF (m_msg_file_name);
m_msg_file_name = 0;
m_message_delivery_done_callback = 0;
Clear();
}
else
{
DeliverMessage();
}
// Get rid of all the encoder data and temporary files.
for (i = 0; i < m_attachment_count; i++) {
MSG_DeliverMimeAttachment *ma = &m_attachments[i];
if (ma->m_encoder_data) {
status = MimeEncoderDestroy(ma->m_encoder_data, FALSE);
ma->m_encoder_data = 0;
if (status < 0) goto FAIL;
}
if (!m_pre_snarfed_attachments_p) {
if (ma->m_file) {
XP_FileClose(ma->m_file);
ma->m_file = 0;
}
XP_FileRemove(ma->m_file_name, xpFileToPost);
}
XP_FREE (ma->m_file_name);
ma->m_file_name = 0;
}
FAIL:
if (toppart)
delete toppart;
toppart = NULL;
mainbody = NULL;
maincontainer = NULL;
/* Close off encoder for the first part (message body.) */
if (m_attachment1_encoder_data)
{
status = MimeEncoderDestroy(m_attachment1_encoder_data, FALSE);
m_attachment1_encoder_data = 0;
if (status < 0) goto FAIL;
}
if (headers) XP_FREE (headers);
if (separator) XP_FREE (separator);
if (in_file) {
XP_FileClose (in_file);
in_file = NULL;
}
HG59731
if (status < 0)
{
m_status = status;
Fail (status, error_msg);
}
/* If status is >= 0, then the the next event coming up is posting to
a "mailto:" or "news:" URL; the message_delivery_done_callback will
be called from the exit routine of that URL. */
if (m_plaintext) {
if (m_plaintext->m_file)
XP_FileClose(m_plaintext->m_file);
XP_FileRemove(m_plaintext->m_file_name, xpFileToPost);
XP_FREE(m_plaintext->m_file_name);
m_plaintext->m_file_name = NULL;
delete m_plaintext;
m_plaintext = NULL;
}
if (m_html_filename) {
XP_FileRemove(m_html_filename, xpFileToPost);
XP_FREE(m_html_filename);
m_html_filename = NULL;
}
return status;
FAILMEM:
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
#if defined(XP_MAC) && defined(DEBUG)
// Compiler runs out of registers for the debug build.
#pragma global_optimizer on
#pragma optimization_level 4
#endif // XP_MAC && DEBUG
static void FROB (const char *src, char *dest)
{
if (src && *src && dest)
{
if (*dest) XP_STRCAT(dest, ",");
XP_STRCAT(dest, src);
}
}
HG53784
#if defined(XP_MAC) && defined(DEBUG)
#pragma global_optimizer reset
#endif // XP_MAC && DEBUG
int
mime_write_message_body (MSG_SendMimeDeliveryState *state,
char *buf, int32 size)
{
HG56898
{
if (int32(XP_FileWrite (buf, size, state->m_msg_file)) < size)
{
return MK_MIME_ERROR_WRITING_FILE;
}
else
{
if (state->m_imapOutgoingParser)
{
state->m_imapOutgoingParser->ParseFolderLine(buf, size);
state->m_imapOutgoingParser->m_bytes_written += size;
}
return 0;
}
}
}
#ifdef XP_OS2
extern int
#else
static int
#endif
mime_encoder_output_fn (const char *buf, int32 size, void *closure)
{
MSG_SendMimeDeliveryState *state = (MSG_SendMimeDeliveryState *) closure;
return mime_write_message_body (state, (char *) buf, size);
}
char *
msg_generate_message_id (void)
{
time_t now = XP_TIME();
uint32 salt = 0;
const char *host = 0;
const char *from = FE_UsersMailAddress ();
RNG_GenerateGlobalRandomBytes((void *) &salt, sizeof(salt));
if (from)
{
host = XP_STRCHR (from, '@');
if (host)
{
const char *s;
for (s = ++host; *s; s++)
if (!XP_IS_ALPHA(*s) && !XP_IS_DIGIT(*s) &&
*s != '-' && *s != '_' && *s != '.')
{
host = 0;
break;
}
}
}
if (! host)
/* If we couldn't find a valid host name to use, we can't generate a
valid message ID, so bail, and let NNTP and SMTP generate them. */
return 0;
return PR_smprintf("<%lX.%lX@%s>",
(unsigned long) now, (unsigned long) salt, host);
}
static char *
mime_generate_headers (MSG_CompositionFields *fields,
int csid,
MSG_Deliver_Mode deliver_mode)
{
int size = 0;
char *buffer = 0, *buffer_tail = 0;
XP_Bool isDraft = deliver_mode == MSG_SaveAsDraft ||
deliver_mode == MSG_SaveAsTemplate ||
deliver_mode == MSG_QueueForLater;
XP_ASSERT (fields);
if (!fields)
return NULL;
/* Multiply by 3 here to make enough room for MimePartII conversion */
if (fields->GetFrom()) size += 3 * XP_STRLEN (fields->GetFrom());
if (fields->GetReplyTo()) size += 3 * XP_STRLEN (fields->GetReplyTo());
if (fields->GetTo()) size += 3 * XP_STRLEN (fields->GetTo());
if (fields->GetCc()) size += 3 * XP_STRLEN (fields->GetCc());
if (fields->GetNewsgroups()) size += 3 * XP_STRLEN (fields->GetNewsgroups());
if (fields->GetFollowupTo()) size += 3 * XP_STRLEN (fields->GetFollowupTo());
if (fields->GetSubject()) size += 3 * XP_STRLEN (fields->GetSubject());
if (fields->GetReferences()) size += 3 * XP_STRLEN (fields->GetReferences());
if (fields->GetOrganization()) size += 3 * XP_STRLEN (fields->GetOrganization());
if (fields->GetOtherRandomHeaders()) size += 3 * XP_STRLEN (fields->GetOtherRandomHeaders());
if (fields->GetPriority()) size += 3 * XP_STRLEN (fields->GetPriority());
#ifdef GENERATE_MESSAGE_ID
if (fields->GetMessageId()) size += XP_STRLEN (fields->GetMessageId());
#endif /* GENERATE_MESSAGE_ID */
/* Add a bunch of space for the static parts of the headers. */
/* size += 2048; */
size += 2560;
buffer = (char *) XP_ALLOC (size);
if (!buffer)
return 0; /* MK_OUT_OF_MEMORY */
buffer_tail = buffer;
#ifdef GENERATE_MESSAGE_ID
if (fields->GetMessageId() && *fields->GetMessageId())
{
char *convbuf = NULL;
PUSH_STRING ("Message-ID: ");
PUSH_STRING (fields->GetMessageId());
PUSH_NEWLINE ();
/* MDN request header requires to have MessageID header presented
* in the message in order to
* coorelate the MDN reports to the original message. Here will be
* the right place
*/
if (fields->GetReturnReceipt() &&
(fields->GetReturnReceiptType() == 2 ||
fields->GetReturnReceiptType() == 3) &&
(deliver_mode != MSG_SaveAsDraft &&
deliver_mode != MSG_SaveAsTemplate))
{
int32 receipt_header_type = 0;
PREF_GetIntPref("mail.receipt.request_header_type",
&receipt_header_type);
// 0 = MDN Disposition-Notification-To: ; 1 = Return-Receipt-To: ; 2 =
// both MDN DNT & RRT headers
if (receipt_header_type == 1)
{
RRT_HEADER:
PUSH_STRING ("Return-Receipt-To: ");
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetFrom(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREEIF(convbuf);
}
else
PUSH_STRING (fields->GetFrom());
PUSH_NEWLINE ();
}
else
{
PUSH_STRING ("Disposition-Notification-To: ");
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetFrom(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREEIF(convbuf);
}
else
PUSH_STRING (fields->GetFrom());
PUSH_NEWLINE ();
if (receipt_header_type == 2)
goto RRT_HEADER;
}
}
#ifdef SUPPORT_X_TEMPLATE_NAME
if (deliver_mode == MSG_SaveAsTemplate)
{
PUSH_STRING ("X-Template: ");
if (fields->GetTemplateName())
{
convbuf = IntlEncodeMimePartIIStr((char *)
fields->GetTemplateName(),
csid,
mime_headers_use_quoted_printable_p);
if (convbuf)
{
PUSH_STRING (convbuf);
XP_FREEIF(convbuf);
}
else
{
PUSH_STRING(fields->GetTemplateName());
}
}
PUSH_NEWLINE ();
}
#endif /* SUPPORT_X_TEMPLATE_NAME */
}
#endif /* GENERATE_MESSAGE_ID */
{
#if 0
/* Use strftime() to format the date, then figure out what our local
GMT offset it, and append that (since strftime() can't do that.)
Generate four digit years as per RFC 1123 (superceding RFC 822.)
*/
time_t now = time ((time_t *) 0);
int gmtoffset = XP_LocalZoneOffset();
strftime (buffer_tail, 100, "Date: %a, %d %b %Y %H:%M:%S ",
localtime (&now));
#else
int gmtoffset = XP_LocalZoneOffset();
#ifndef NSPR20
PRTime now;
PR_ExplodeTime(&now, PR_Now());
#else
PRExplodedTime now;
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
#endif /* NSPR20 */
/* Use PR_FormatTimeUSEnglish() to format the date in US English format,
then figure out what our local GMT offset is, and append it (since
PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as
per RFC 1123 (superceding RFC 822.)
*/
PR_FormatTimeUSEnglish(buffer_tail, 100,
"Date: %a, %d %b %Y %H:%M:%S ",
&now);
#endif
buffer_tail += XP_STRLEN (buffer_tail);
PR_snprintf(buffer_tail, buffer + size - buffer_tail,
"%c%02d%02d" CRLF,
(gmtoffset >= 0 ? '+' : '-'),
((gmtoffset >= 0 ? gmtoffset : -gmtoffset) / 60),
((gmtoffset >= 0 ? gmtoffset : -gmtoffset) % 60));
buffer_tail += XP_STRLEN (buffer_tail);
}
if (fields->GetFrom() && *fields->GetFrom())
{
char *convbuf;
PUSH_STRING ("From: ");
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetFrom(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (fields->GetFrom());
PUSH_NEWLINE ();
}
if (fields->GetReplyTo() && *fields->GetReplyTo())
{
char *convbuf;
PUSH_STRING ("Reply-To: ");
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetReplyTo(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (fields->GetReplyTo());
PUSH_NEWLINE ();
}
if (fields->GetOrganization() && *fields->GetOrganization())
{
char *convbuf;
PUSH_STRING ("Organization: ");
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetOrganization(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (fields->GetOrganization());
PUSH_NEWLINE ();
}
// X-Sender tag
if (fields->GetOwner())
{
XP_Bool bUseXSender = FALSE;
PREF_GetBoolPref("mail.use_x_sender", &bUseXSender);
if (bUseXSender) {
char *convbuf;
char tmpBuffer[256];
int bufSize = 256;
*tmpBuffer = 0;
PUSH_STRING ("X-Sender: ");
PUSH_STRING("\"");
PREF_GetCharPref("mail.identity.username", tmpBuffer, &bufSize);
convbuf = IntlEncodeMimePartIIStr((char *)tmpBuffer, csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (tmpBuffer);
PUSH_STRING("\" <");
PREF_GetCharPref("mail.pop_name", tmpBuffer, &bufSize);
convbuf = IntlEncodeMimePartIIStr((char *)tmpBuffer, csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (tmpBuffer);
PUSH_STRING ("@");
PREF_GetCharPref("network.hosts.pop_server", tmpBuffer, &bufSize);
convbuf = IntlEncodeMimePartIIStr((char *)tmpBuffer, csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (tmpBuffer);
PUSH_STRING(">");
convbuf = IntlEncodeMimePartIIStr((char *)tmpBuffer, csid,
mime_headers_use_quoted_printable_p);
if (!fields->GetOwner()->GetMaster()->IsUserAuthenticated())
PUSH_STRING (" (Unverified)");
PUSH_NEWLINE ();
}
}
// X-Mozilla-Draft-Info
if (isDraft) {
char *htmlAction = 0;
char *lineWidth = 0; // force plain text hard line break info
PUSH_STRING(X_MOZILLA_DRAFT_INFO);
PUSH_STRING(": internal/draft; ");
if (fields->GetAttachVCard()) {
PUSH_STRING("vcard=1");
}
else {
PUSH_STRING("vcard=0");
}
PUSH_STRING("; ");
if (fields->GetReturnReceipt()) {
char *type = PR_smprintf("%d", (int) fields->GetReturnReceiptType());
if (type)
{
PUSH_STRING("receipt=");
PUSH_STRING(type);
XP_FREEIF(type);
}
}
else {
PUSH_STRING("receipt=0");
}
PUSH_STRING("; ");
if (fields->GetBoolHeader(MSG_UUENCODE_BINARY_BOOL_HEADER_MASK)) {
PUSH_STRING("uuencode=1");
}
else {
PUSH_STRING("uuencode=0");
}
htmlAction = PR_smprintf("html=%d",
((MSG_CompositionPane*)fields->GetOwner())->GetHTMLAction());
if (htmlAction)
{
PUSH_STRING("; ");
PUSH_STRING(htmlAction);
FREEIF(htmlAction);
}
lineWidth = PR_smprintf("; linewidth=%d",
((MSG_CompositionPane*)fields->GetOwner())->GetLineWidth());
if (lineWidth)
{
PUSH_STRING(lineWidth);
FREEIF(lineWidth);
}
PUSH_NEWLINE ();
}
PUSH_STRING ("X-Mailer: ");
PUSH_STRING (XP_AppCodeName);
PUSH_STRING (" ");
PUSH_STRING (XP_AppVersion);
PUSH_NEWLINE ();
/* for Netscape Server, Accept-Language data sent in Mail header */
char *acceptlang = INTL_GetAcceptLanguage();
if( (acceptlang != NULL) && ( *acceptlang != '\0') ){
PUSH_STRING( "X-Accept-Language: " );
PUSH_STRING( acceptlang );
PUSH_NEWLINE();
}
PUSH_STRING ("MIME-Version: 1.0" CRLF);
if (fields->GetNewsgroups() && *fields->GetNewsgroups())
{
/* turn whitespace into a comma list
*/
char *ptr, *ptr2;
char *n2;
char *convbuf;
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetNewsgroups(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf)
n2 = XP_StripLine (convbuf);
else {
ptr = XP_STRDUP(fields->GetNewsgroups());
if (!ptr)
{
FREEIF(buffer);
return 0; /* MK_OUT_OF_MEMORY */
}
n2 = XP_StripLine(ptr);
XP_ASSERT(n2 == ptr); /* Otherwise, the XP_FREE below is
gonna choke badly. */
}
for(ptr=n2; *ptr != '\0'; ptr++)
{
/* find first non white space */
while(!XP_IS_SPACE(*ptr) && *ptr != ',' && *ptr != '\0')
ptr++;
if(*ptr == '\0')
break;
if(*ptr != ',')
*ptr = ',';
/* find next non white space */
ptr2 = ptr+1;
while(XP_IS_SPACE(*ptr2))
ptr2++;
if(ptr2 != ptr+1)
XP_STRCPY(ptr+1, ptr2);
}
PUSH_STRING ("Newsgroups: ");
PUSH_STRING (n2);
XP_FREE (n2);
PUSH_NEWLINE ();
}
/* #### shamelessly duplicated from above */
if (fields->GetFollowupTo() && *fields->GetFollowupTo())
{
/* turn whitespace into a comma list
*/
char *ptr, *ptr2;
char *n2;
char *convbuf;
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetFollowupTo(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf)
n2 = XP_StripLine (convbuf);
else {
ptr = XP_STRDUP(fields->GetFollowupTo());
if (!ptr)
{
FREEIF(buffer);
return 0; /* MK_OUT_OF_MEMORY */
}
n2 = XP_StripLine (ptr);
XP_ASSERT(n2 == ptr); /* Otherwise, the XP_FREE below is
gonna choke badly. */
}
for(ptr=n2; *ptr != '\0'; ptr++)
{
/* find first non white space */
while(!XP_IS_SPACE(*ptr) && *ptr != ',' && *ptr != '\0')
ptr++;
if(*ptr == '\0')
break;
if(*ptr != ',')
*ptr = ',';
/* find next non white space */
ptr2 = ptr+1;
while(XP_IS_SPACE(*ptr2))
ptr2++;
if(ptr2 != ptr+1)
XP_STRCPY(ptr+1, ptr2);
}
PUSH_STRING ("Followup-To: ");
PUSH_STRING (n2);
XP_FREE (n2);
PUSH_NEWLINE ();
}
if (fields->GetTo() && *fields->GetTo())
{
char *convbuf;
PUSH_STRING ("To: ");
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetTo(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (fields->GetTo());
PUSH_NEWLINE ();
}
if (fields->GetCc() && *fields->GetCc())
{
char *convbuf;
PUSH_STRING ("CC: ");
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetCc(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (fields->GetCc());
PUSH_NEWLINE ();
}
if (fields->GetSubject() && *fields->GetSubject())
{
char *convbuf;
PUSH_STRING ("Subject: ");
convbuf = IntlEncodeMimePartIIStr((char *)fields->GetSubject(), csid,
mime_headers_use_quoted_printable_p);
if (convbuf) /* MIME-PartII conversion */
{
PUSH_STRING (convbuf);
XP_FREE(convbuf);
}
else
PUSH_STRING (fields->GetSubject());
PUSH_NEWLINE ();
}
if (fields->GetPriority() && *(fields->GetPriority()))
{
char *priority = (char *) fields->GetPriority();
if (!strcasestr(priority, "normal"))
{
PUSH_STRING ("X-Priority: ");
/* Important: do not change the order of the
* following if statements
*/
if (strcasestr (priority, "highest"))
PUSH_STRING("1 (");
else if (strcasestr(priority, "high"))
PUSH_STRING("2 (");
else if (strcasestr(priority, "lowest"))
PUSH_STRING("5 (");
else if (strcasestr(priority, "low"))
PUSH_STRING("4 (");
PUSH_STRING (priority);
PUSH_STRING(")");
PUSH_NEWLINE ();
}
}
if (fields->GetReferences() && *fields->GetReferences())
{
PUSH_STRING ("References: ");
PUSH_STRING (fields->GetReferences());
PUSH_NEWLINE ();
}
if (fields->GetOtherRandomHeaders() && *fields->GetOtherRandomHeaders())
{
/* Assume they already have the right newlines and continuations
and so on. */
PUSH_STRING (fields->GetOtherRandomHeaders());
}
if (buffer_tail > buffer + size - 1)
abort ();
/* realloc it smaller... */
buffer = (char*)XP_REALLOC (buffer, buffer_tail - buffer + 1);
return buffer;
}
/* Generate headers for a form post to a mailto: URL.
This lets the URL specify additional headers, but is careful to
ignore headers which would be dangerous. It may modify the URL
(because of CC) so a new URL to actually post to is returned.
*/
int
MIME_GenerateMailtoFormPostHeaders (const char *old_post_url,
const char * /*referer*/,
char **new_post_url_return,
char **headers_return)
{
char *from = 0, *to = 0, *cc = 0, *body = 0, *search = 0;
char *extra_headers = 0;
char *s;
XP_Bool subject_p = FALSE;
XP_Bool sign_p = FALSE;
HG29292
char *rest;
int status = 0;
MSG_CompositionFields *fields = NULL;
static const char *forbidden_headers[] = {
"Apparently-To",
"BCC",
"Content-Encoding",
CONTENT_LENGTH,
"Content-Transfer-Encoding",
"Content-Type",
"Date",
"Distribution",
"FCC",
"Followup-To",
"From",
"Lines",
"MIME-Version",
"Message-ID",
"Newsgroups",
"Organization",
"Reply-To",
"Sender",
X_MOZILLA_STATUS,
X_MOZILLA_STATUS2,
X_MOZILLA_NEWSHOST,
X_UIDL,
"XRef",
0 };
from = MIME_MakeFromField ();
if (!from) {
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
to = NET_ParseURL (old_post_url, GET_PATH_PART);
if (!to) {
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
if (!*to)
{
status = MK_MIME_NO_RECIPIENTS; /* rb -1; */
goto FAIL;
}
search = NET_ParseURL (old_post_url, GET_SEARCH_PART);
rest = search;
if (rest && *rest == '?')
{
/* start past the '?' */
rest++;
rest = XP_STRTOK (rest, "&");
while (rest && *rest)
{
char *token = rest;
char *value = 0;
char *eq;
rest = XP_STRTOK (0, "&");
eq = XP_STRCHR (token, '=');
if (eq)
{
value = eq+1;
*eq = 0;
}
if (!strcasecomp (token, "subject"))
subject_p = TRUE;
if (value)
/* Don't allow newlines or control characters in the value. */
for (s = value; *s; s++)
if (*s < ' ' && *s != '\t')
*s = ' ';
if (!strcasecomp (token, "to"))
{
if (to && *to)
{
StrAllocCat (to, ", ");
StrAllocCat (to, value);
}
else
{
StrAllocCopy (to, value);
}
}
else if (!strcasecomp (token, "cc"))
{
if (cc && *cc)
{
StrAllocCat (cc, ", ");
StrAllocCat (cc, value);
}
else
{
StrAllocCopy (cc, value);
}
}
else if (!strcasecomp (token, "body"))
{
if (body && *body)
{
StrAllocCat (body, "\n");
StrAllocCat (body, value);
}
else
{
StrAllocCopy (body, value);
}
}
HG28926
else if (!strcasecomp (token, "sign") ||
!strcasecomp (token, "signed"))
{
sign_p = (!strcasecomp(value, "true") ||
!strcasecomp(value, "yes"));
}
else
{
const char **fh = forbidden_headers;
XP_Bool ok = TRUE;
while (*fh)
if (!strcasecomp (token, *fh++))
{
ok = FALSE;
break;
}
if (ok)
{
XP_Bool upper_p = FALSE;
char *s;
for (s = token; *s; s++)
{
if (*s >= 'A' && *s <= 'Z')
upper_p = TRUE;
else if (*s <= ' ' || *s >= '~' || *s == ':')
goto NOT_OK; /* bad character in header! */
}
if (!upper_p && *token >= 'a' && *token <= 'z')
*token -= ('a' - 'A');
StrAllocCat (extra_headers, token);
StrAllocCat (extra_headers, ": ");
if (value)
StrAllocCat (extra_headers, value);
StrAllocCat (extra_headers, CRLF);
NOT_OK: ;
}
}
}
}
if (!subject_p)
{
/* If the URL didn't provide a subject, we will. */
StrAllocCat (extra_headers, "Subject: Form posted from ");
XP_ASSERT (XP_AppCodeName);
StrAllocCat (extra_headers, XP_AppCodeName);
StrAllocCat (extra_headers, CRLF);
}
/* Note: the `sign', and `body' parameters are currently
ignored in mailto form submissions. */
*new_post_url_return = 0;
fields = MSG_CreateCompositionFields(from, 0, to, cc, 0, 0, 0, 0,
FE_UsersOrganization(), 0, 0,
extra_headers, 0, 0, 0
HG15448);
if (!fields)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
fields->SetDefaultBody(body);
*headers_return = mime_generate_headers (fields, 0, MSG_DeliverNow);
if (*headers_return == 0)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
StrAllocCat ((*new_post_url_return), "mailto:");
if (to)
StrAllocCat ((*new_post_url_return), to);
if (to && cc)
StrAllocCat ((*new_post_url_return), ",");
if (cc)
StrAllocCat ((*new_post_url_return), cc);
FAIL:
FREEIF (from);
FREEIF (to);
FREEIF (cc);
FREEIF (body);
FREEIF (search);
FREEIF (extra_headers);
if (fields)
MSG_DestroyCompositionFields(fields);
return status;
}
static char *
mime_generate_attachment_headers (const char *type, const char *encoding,
const char *description,
const char *x_mac_type,
const char *x_mac_creator,
const char *real_name,
const char *base_url,
XP_Bool /*digest_p*/,
MSG_DeliverMimeAttachment * /*ma*/,
int16 mail_csid)
{
char *buffer = (char *) XP_ALLOC (2048);
char *buffer_tail = buffer;
char charset[30];
if (! buffer)
return 0; /* MK_OUT_OF_MEMORY */
XP_ASSERT (encoding);
charset[0] = 0;
PUSH_STRING ("Content-Type: ");
PUSH_STRING (type);
if (mime_type_needs_charset (type))
{
/* push 7bit encoding out based on current default codeset */
INTL_CharSetIDToName (mail_csid, charset);
/* If the characters are all 7bit, then it's better (and true) to
claim the charset to be US-ASCII rather than Latin1. Should we
do this all the time, for all charsets? I'm not sure. But we
should definitely do it for Latin1. */
if (encoding &&
!strcasecomp (encoding, "7bit") &&
!strcasecomp (charset, "iso-8859-1"))
XP_STRCPY (charset, "us-ascii");
// If csid is JIS and and type is HTML
// then no charset to be specified (apply base64 instead)
// in order to avoid mismatch META_TAG (bug#104255).
if ((mail_csid != CS_JIS) ||
(strcasecomp(type, TEXT_HTML) != 0) ||
(strcasecomp(encoding, ENCODING_BASE64) != 0))
{
PUSH_STRING ("; charset=");
PUSH_STRING (charset);
}
}
if (x_mac_type && *x_mac_type)
{
PUSH_STRING ("; x-mac-type=\"");
PUSH_STRING (x_mac_type);
PUSH_STRING ("\"");
}
if (x_mac_creator && *x_mac_creator)
{
PUSH_STRING ("; x-mac-creator=\"");
PUSH_STRING (x_mac_creator);
PUSH_STRING ("\"");
}
int32 parmFolding = 0;
PREF_GetIntPref("mail.strictly_mime.parm_folding", &parmFolding);
#ifdef EMIT_NAME_IN_CONTENT_TYPE
if (real_name && *real_name)
{
if (parmFolding == 0 || parmFolding == 1)
{
PUSH_STRING (";\r\n name=\"");
PUSH_STRING (real_name);
PUSH_STRING ("\"");
}
else // if (parmFolding == 2)
{
char *rfc2231Parm = RFC2231ParmFolding("name", charset,
INTL_GetAcceptLanguage(), real_name);
if (rfc2231Parm)
{
PUSH_STRING(";\r\n ");
PUSH_STRING(rfc2231Parm);
XP_FREE(rfc2231Parm);
}
}
}
#endif /* EMIT_NAME_IN_CONTENT_TYPE */
PUSH_NEWLINE ();
PUSH_STRING ("Content-Transfer-Encoding: ");
PUSH_STRING (encoding);
PUSH_NEWLINE ();
if (description && *description)
{
char *s = mime_fix_header (description);
if (s)
{
PUSH_STRING ("Content-Description: ");
PUSH_STRING (s);
PUSH_NEWLINE ();
XP_FREE(s);
}
}
if (real_name && *real_name)
{
char *period = XP_STRRCHR(real_name, '.');
int32 pref_content_disposition = 0;
PREF_GetIntPref("mail.content_disposition_type",
&pref_content_disposition);
PUSH_STRING ("Content-Disposition: ");
if (pref_content_disposition == 1)
PUSH_STRING ("attachment");
else if (pref_content_disposition == 2 &&
(!strcasecomp(type, TEXT_PLAIN) ||
(period && !strcasecomp(period, ".txt"))))
PUSH_STRING("attachment");
/* If this document is an anonymous binary file or a vcard,
then always show it as an attachment, never inline. */
else if (!strcasecomp(type, APPLICATION_OCTET_STREAM) ||
!strcasecomp(type, vCardMimeFormat))
PUSH_STRING ("attachment");
else
PUSH_STRING ("inline");
if (parmFolding == 0 || parmFolding == 1)
{
PUSH_STRING (";\r\n filename=\"");
PUSH_STRING (real_name);
PUSH_STRING ("\"" CRLF);
}
else // if (parmFolding == 2)
{
char *rfc2231Parm = RFC2231ParmFolding("name", charset,
INTL_GetAcceptLanguage(), real_name);
if (rfc2231Parm)
{
PUSH_STRING(";\r\n ");
PUSH_STRING(rfc2231Parm);
PUSH_NEWLINE ();
XP_FREE(rfc2231Parm);
}
}
}
else if (type &&
(!strcasecomp (type, MESSAGE_RFC822) ||
!strcasecomp (type, MESSAGE_NEWS)))
{
PUSH_STRING ("Content-Disposition: inline" CRLF);
}
#ifdef GENERATE_CONTENT_BASE
/* If this is an HTML document, and we know the URL it originally
came from, write out a Content-Base header. */
if (type &&
(!strcasecomp (type, TEXT_HTML) ||
!strcasecomp (type, TEXT_MDL)) &&
base_url && *base_url)
{
int32 col = 0;
const char *s = base_url;
const char *colon = XP_STRCHR (s, ':');
XP_Bool useContentLocation = FALSE; /* rhp - add this */
if (!colon) goto GIVE_UP_ON_CONTENT_BASE; /* malformed URL? */
/* Don't emit a content-base that points to (or into) a news or
mail message. */
if (!strncasecomp (s, "news:", 5) ||
!strncasecomp (s, "snews:", 6) ||
!strncasecomp (s, "IMAP:", 5) ||
!strncasecomp (s, "mailbox:", 8))
goto GIVE_UP_ON_CONTENT_BASE;
/* rhp - Put in a pref for using Content-Location instead of Content-Base.
This will get tweaked to default to true in 5.0
*/
PREF_GetBoolPref("mail.use_content_location_on_send", &useContentLocation);
if (useContentLocation)
PUSH_STRING ("Content-Location: \"");
else
PUSH_STRING ("Content-Base: \"");
/* rhp - Pref for Content-Location usage */
/* rhp: this is to work with the Content-Location stuff */
CONTENT_LOC_HACK:
while (*s != 0 && *s != '#')
{
const char *ot = buffer_tail;
/* URLs must be wrapped at 40 characters or less. */
if (col >= 38)
{
PUSH_STRING(CRLF "\t");
col = 0;
}
if (*s == ' ') PUSH_STRING("%20");
else if (*s == '\t') PUSH_STRING("%09");
else if (*s == '\n') PUSH_STRING("%0A");
else if (*s == '\r') PUSH_STRING("%0D");
else
{
*buffer_tail++ = *s;
*buffer_tail = '\0';
}
s++;
col += (buffer_tail - ot);
}
PUSH_STRING ("\"" CRLF);
/* rhp: this is to try to get around this fun problem with Content-Location */
if (!useContentLocation)
{
PUSH_STRING ("Content-Location: \"");
s = base_url;
col = 0;
useContentLocation = TRUE;
goto CONTENT_LOC_HACK;
}
/* rhp: this is to try to get around this fun problem with Content-Location */
GIVE_UP_ON_CONTENT_BASE:
;
}
#endif /* GENERATE_CONTENT_BASE */
/* realloc it smaller... */
buffer = (char*) XP_REALLOC (buffer, buffer_tail - buffer + 1);
return buffer;
}
void MSG_SendMimeDeliveryState::Fail (int failure_code, char *error_msg)
{
if (m_message_delivery_done_callback)
{
if (failure_code < 0 && !error_msg)
error_msg = NET_ExplainErrorDetails(failure_code, 0, 0, 0, 0);
m_message_delivery_done_callback (GetContext(), m_fe_data,
failure_code, error_msg);
FREEIF(error_msg); /* #### Is there a memory leak here? Shouldn't
this free be outside the if? */
}
else if (m_attachments_done_callback)
{
if (failure_code < 0 && !error_msg)
error_msg = NET_ExplainErrorDetails(failure_code, 0, 0, 0, 0);
/* mime_free_message_state will take care of cleaning up the
attachment files and attachment structures */
m_attachments_done_callback (GetContext(),
m_fe_data, failure_code,
error_msg, 0);
FREEIF(error_msg); /* #### Is there a memory leak here? Shouldn't
this free be outside the if? */
}
m_message_delivery_done_callback = 0;
m_attachments_done_callback = 0;
Clear();
}
/* Given a string, convert it to 'qtext' (quoted text) for RFC822 header purposes. */
static char *
msg_make_filename_qtext(const char *srcText, XP_Bool stripCRLFs)
{
/* newString can be at most twice the original string (every char quoted). */
char *newString = (char *) XP_ALLOC(XP_STRLEN(srcText)*2 + 1);
if (!newString) return NULL;
const char *s = srcText;
const char *end = srcText + XP_STRLEN(srcText);
char *d = newString;
while(*s)
{
/* Put backslashes in front of existing backslashes, or double quote
characters.
If stripCRLFs is true, don't write out CRs or LFs. Otherwise,
write out a backslash followed by the CR but not
linear-white-space.
We might already have quoted pair of "\ " or "\\t" skip it.
*/
if (*s == '\\' || *s == '"' ||
(!stripCRLFs &&
(*s == CR && (*(s+1) != LF ||
(*(s+1) == LF && (s+2) < end && !XP_IS_SPACE(*(s+2)))))))
*d++ = '\\';
if (*s == CR)
{
if (stripCRLFs && *(s+1) == LF && (s+2) < end && XP_IS_SPACE(*(s+2)))
s += 2; // skip CRLFLWSP
}
else
{
*d++ = *s;
}
s++;
}
*d = 0;
return newString;
}
/* Rip apart the URL and extract a reasonable value for the `real_name' slot.
*/
static void
msg_pick_real_name (MSG_DeliverMimeAttachment *attachment, int16 csid)
{
const char *s, *s2;
char *s3;
char *url;
if (attachment->m_real_name)
return;
url = attachment->m_url_string;
/* Perhaps the MIME parser knows a better name than the URL itself?
This can happen when one attaches a MIME part from one message
directly into another message.
### mwelch Note that this function simply duplicates and returns an existing
MIME header, so we don't need to process it. */
attachment->m_real_name =
MimeGuessURLContentName(attachment->m_mime_delivery_state->GetContext(),
url);
if (attachment->m_real_name)
return;
/* Otherwise, extract a name from the URL. */
s = url;
s2 = XP_STRCHR (s, ':');
if (s2) s = s2 + 1;
/* If we know the URL doesn't have a sensible file name in it,
don't bother emitting a content-disposition. */
if (!strncasecomp (url, "news:", 5) ||
!strncasecomp (url, "snews:", 6) ||
!strncasecomp (url, "IMAP:", 5) ||
!strncasecomp (url, "mailbox:", 8))
return;
/* Take the part of the file name after the last / or \ */
s2 = XP_STRRCHR (s, '/');
if (s2) s = s2+1;
s2 = XP_STRRCHR (s, '\\');
if (csid & MULTIBYTE)
{
// We don't want to truncate the file name in case of the double
// byte file name
while ( s2 != NULL &&
s2 > s &&
INTL_IsLeadByte(csid, *(s2-1)))
{
s3 = (char *) s2;
*s3 = 0;
s2 = XP_STRRCHR(s, '\\');
*s3 = '\\';
}
}
if (s2) s = s2+1;
/* Copy it into the attachment struct. */
StrAllocCopy (attachment->m_real_name, s);
/* Now trim off any named anchors or search data. */
s3 = XP_STRCHR (attachment->m_real_name, '?');
if (s3) *s3 = 0;
s3 = XP_STRCHR (attachment->m_real_name, '#');
if (s3) *s3 = 0;
/* Now lose the %XX */
NET_UnEscape (attachment->m_real_name);
int32 parmFolding = 0;
PREF_GetIntPref("mail.strictly_mime.parm_folding", &parmFolding);
if (parmFolding == 0 || parmFolding == 1)
{
/* Try to MIME-2 encode the filename... */
char *mime2Name = IntlEncodeMimePartIIStr(attachment->m_real_name, csid,
mime_headers_use_quoted_printable_p);
if (mime2Name && (mime2Name != attachment->m_real_name))
{
XP_FREE(attachment->m_real_name);
attachment->m_real_name = mime2Name;
}
/* ... and then put backslashes before special characters (RFC 822 tells us
to). */
char *qtextName = NULL;
qtextName = msg_make_filename_qtext(attachment->m_real_name,
(parmFolding == 0 ? TRUE : FALSE));
if (qtextName)
{
XP_FREE(attachment->m_real_name);
attachment->m_real_name = qtextName;
}
}
/* Now a special case for attaching uuencoded files...
If we attach a file "foo.txt.uu", we will send it out with
Content-Type: text/plain; Content-Transfer-Encoding: x-uuencode.
When saving such a file, a mail reader will generally decode it first
(thus removing the uuencoding.) So, let's make life a little easier by
removing the indication of uuencoding from the file name itself. (This
will presumably make the file name in the Content-Disposition header be
the same as the file name in the "begin" line of the uuencoded data.)
However, since there are mailers out there (including earlier versions of
Mozilla) that will use "foo.txt.uu" as the file name, we still need to
cope with that; the code which copes with that is in the MIME parser, in
libmime/mimei.c.
*/
if (attachment->m_already_encoded_p &&
attachment->m_encoding)
{
char *result = attachment->m_real_name;
int32 L = XP_STRLEN(result);
const char **exts = 0;
/* #### hack
I'd like to ask the mime.types file, "what extensions correspond
to obj->encoding (which happens to be "x-uuencode") but doing that
in a non-sphagetti way would require brain surgery. So, since
currently uuencode is the only content-transfer-encoding which we
understand which traditionally has an extension, we just special-
case it here!
Note that it's special-cased in a similar way in libmime/mimei.c.
*/
if (!strcasecomp(attachment->m_encoding, ENCODING_UUENCODE) ||
!strcasecomp(attachment->m_encoding, ENCODING_UUENCODE2) ||
!strcasecomp(attachment->m_encoding, ENCODING_UUENCODE3) ||
!strcasecomp(attachment->m_encoding, ENCODING_UUENCODE4))
{
static const char *uue_exts[] = { "uu", "uue", 0 };
exts = uue_exts;
}
while (exts && *exts)
{
const char *ext = *exts;
int32 L2 = XP_STRLEN(ext);
if (L > L2 + 1 && /* long enough */
result[L - L2 - 1] == '.' && /* '.' in right place*/
!strcasecomp(ext, result + (L - L2))) /* ext matches */
{
result[L - L2 - 1] = 0; /* truncate at '.' and stop. */
break;
}
exts++;
}
}
}
int
MSG_SendMimeDeliveryState::HackAttachments(
const struct MSG_AttachmentData *attachments,
const struct MSG_AttachedFile *preloaded_attachments)
{
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(GetContext());
if (preloaded_attachments) XP_ASSERT(!attachments);
if (attachments) XP_ASSERT(!preloaded_attachments);
if (preloaded_attachments && preloaded_attachments[0].orig_url)
{
/* These are attachments which have already been downloaded to tmp files.
We merely need to point the internal attachment data at those tmp
files.
*/
int32 i;
m_pre_snarfed_attachments_p = TRUE;
m_attachment_count = 0;
while (preloaded_attachments[m_attachment_count].orig_url)
m_attachment_count++;
m_attachments = (MSG_DeliverMimeAttachment *)
new MSG_DeliverMimeAttachment[m_attachment_count];
if (! m_attachments)
return MK_OUT_OF_MEMORY;
for (i = 0; i < m_attachment_count; i++)
{
m_attachments[i].m_mime_delivery_state = this;
/* These attachments are already "snarfed". */
m_attachments[i].m_done = TRUE;
XP_ASSERT (preloaded_attachments[i].orig_url);
StrAllocCopy (m_attachments[i].m_url_string,
preloaded_attachments[i].orig_url);
StrAllocCopy (m_attachments[i].m_type,
preloaded_attachments[i].type);
StrAllocCopy (m_attachments[i].m_description,
preloaded_attachments[i].description);
StrAllocCopy (m_attachments[i].m_real_name,
preloaded_attachments[i].real_name);
StrAllocCopy (m_attachments[i].m_x_mac_type,
preloaded_attachments[i].x_mac_type);
StrAllocCopy (m_attachments[i].m_x_mac_creator,
preloaded_attachments[i].x_mac_creator);
StrAllocCopy (m_attachments[i].m_encoding,
preloaded_attachments[i].encoding);
StrAllocCopy (m_attachments[i].m_file_name,
preloaded_attachments[i].file_name);
m_attachments[i].m_size = preloaded_attachments[i].size;
m_attachments[i].m_unprintable_count =
preloaded_attachments[i].unprintable_count;
m_attachments[i].m_highbit_count =
preloaded_attachments[i].highbit_count;
m_attachments[i].m_ctl_count = preloaded_attachments[i].ctl_count;
m_attachments[i].m_null_count =
preloaded_attachments[i].null_count;
m_attachments[i].m_max_column =
preloaded_attachments[i].max_line_length;
/* If the attachment has an encoding, and it's not one of
the "null" encodings, then keep it. */
if (m_attachments[i].m_encoding &&
strcasecomp (m_attachments[i].m_encoding, ENCODING_7BIT) &&
strcasecomp (m_attachments[i].m_encoding, ENCODING_8BIT) &&
strcasecomp (m_attachments[i].m_encoding, ENCODING_BINARY))
m_attachments[i].m_already_encoded_p = TRUE;
msg_pick_real_name(&m_attachments[i], INTL_GetCSIWinCSID(c));
}
}
else if (attachments && attachments[0].url)
{
/* These are attachments which have already been downloaded to tmp files.
We merely need to point the internal attachment data at those tmp
files. We will delete the tmp files as we attach them.
*/
int32 i;
int mailbox_count = 0, news_count = 0;
m_attachment_count = 0;
while (attachments[m_attachment_count].url)
m_attachment_count++;
m_attachments = (MSG_DeliverMimeAttachment *)
new MSG_DeliverMimeAttachment[m_attachment_count];
if (! m_attachments)
return MK_OUT_OF_MEMORY;
for (i = 0; i < m_attachment_count; i++)
{
m_attachments[i].m_mime_delivery_state = this;
XP_ASSERT (attachments[i].url);
StrAllocCopy (m_attachments[i].m_url_string,
attachments[i].url);
StrAllocCopy (m_attachments[i].m_override_type,
attachments[i].real_type);
StrAllocCopy (m_attachments[i].m_override_encoding,
attachments[i].real_encoding);
StrAllocCopy (m_attachments[i].m_desired_type,
attachments[i].desired_type);
StrAllocCopy (m_attachments[i].m_description,
attachments[i].description);
StrAllocCopy (m_attachments[i].m_real_name,
attachments[i].real_name);
StrAllocCopy (m_attachments[i].m_x_mac_type,
attachments[i].x_mac_type);
StrAllocCopy (m_attachments[i].m_x_mac_creator,
attachments[i].x_mac_creator);
StrAllocCopy (m_attachments[i].m_encoding, "7bit");
m_attachments[i].m_url =
NET_CreateURLStruct (m_attachments[i].m_url_string,
NET_DONT_RELOAD);
// real name is set in the case of vcard so don't change it.
// m_attachments[i].m_real_name = 0;
/* Count up attachments which are going to come from mail folders
and from NNTP servers. */
if (strncasecomp(m_attachments[i].m_url_string, "mailbox:",8) ||
strncasecomp(m_attachments[i].m_url_string, "IMAP:",5))
mailbox_count++;
else if (strncasecomp(m_attachments[i].m_url_string, "news:",5) ||
strncasecomp(m_attachments[i].m_url_string, "snews:",6))
news_count++;
msg_pick_real_name(&m_attachments[i], INTL_GetCSIWinCSID(c));
}
/* If there is more than one mailbox URL, or more than one NNTP url,
do the load in serial rather than parallel, for efficiency.
*/
if (mailbox_count > 1 || news_count > 1)
m_be_synchronous_p = TRUE;
m_attachment_pending_count = m_attachment_count;
/* Start the URL attachments loading (eventually, an exit routine will
call the done_callback). */
if (m_attachment_count == 1)
FE_Progress(GetContext(), XP_GetString(MK_MSG_LOAD_ATTACHMNT));
else
FE_Progress(GetContext(), XP_GetString(MK_MSG_LOAD_ATTACHMNTS));
for (i = 0; i < m_attachment_count; i++)
{
/* This only returns a failure code if NET_GetURL was not called
(and thus no exit routine was or will be called.) */
int status = m_attachments [i].SnarfAttachment ();
if (status < 0)
return status;
if (m_be_synchronous_p)
break;
}
}
if (m_attachment_pending_count <= 0)
/* No attachments - finish now (this will call the done_callback). */
GatherMimeAttachments ();
return 0;
}
void
MSG_SendMimeDeliveryState::StartMessageDelivery(
MSG_Pane *pane,
void *fe_data,
MSG_CompositionFields *fields,
XP_Bool digest_p,
XP_Bool dont_deliver_p,
MSG_Deliver_Mode mode,
const char *attachment1_type,
const char *attachment1_body,
uint32 attachment1_body_length,
const struct MSG_AttachmentData *attachments,
const struct MSG_AttachedFile *preloaded_attachments,
//#ifdef MSG_SEND_MULTIPART_RELATED
MSG_SendPart *relatedPart,
//#endif
void (*message_delivery_done_callback)
(MWContext *context,
void *fe_data,
int status,
const char *error_message))
{
int failure = 0;
MSG_SendMimeDeliveryState *state;
if (!attachment1_body || !*attachment1_body)
attachment1_type = attachment1_body = 0;
state = new MSG_SendMimeDeliveryState;
if (! state)
{
failure = MK_OUT_OF_MEMORY;
goto FAIL;
}
failure = state->Init(pane, fe_data, fields,
digest_p, dont_deliver_p, mode,
attachment1_type, attachment1_body,
attachment1_body_length,
attachments, preloaded_attachments,
//#ifdef MSG_SEND_MULTIPART_RELATED
relatedPart,
//#endif
message_delivery_done_callback);
if (failure >= 0)
return;
FAIL:
char *err_msg = NET_ExplainErrorDetails (failure);
message_delivery_done_callback (pane->GetContext(), fe_data, failure,
err_msg);
if (err_msg) XP_FREE (err_msg);
delete state;
}
int MSG_SendMimeDeliveryState::SetMimeHeader(MSG_HEADER_SET header, const char *value)
{
char *dupHeader = NULL;
int ret = MK_OUT_OF_MEMORY;
if (header & (MSG_FROM_HEADER_MASK | MSG_TO_HEADER_MASK | MSG_REPLY_TO_HEADER_MASK | MSG_CC_HEADER_MASK | MSG_BCC_HEADER_MASK))
dupHeader = mime_fix_addr_header(value);
else if (header & (MSG_NEWSGROUPS_HEADER_MASK| MSG_FOLLOWUP_TO_HEADER_MASK))
dupHeader = mime_fix_news_header(value);
else if (header & (MSG_FCC_HEADER_MASK | MSG_ORGANIZATION_HEADER_MASK | MSG_SUBJECT_HEADER_MASK | MSG_REFERENCES_HEADER_MASK | MSG_X_TEMPLATE_HEADER_MASK))
dupHeader = mime_fix_header(value);
else
XP_ASSERT(FALSE); // unhandled header mask
if (dupHeader)
{
ret = m_fields->SetHeader(header, dupHeader);
XP_FREE(dupHeader);
}
return ret;
}
int
MSG_SendMimeDeliveryState::Init(
MSG_Pane *pane,
void *fe_data,
MSG_CompositionFields *fields,
XP_Bool digest_p,
XP_Bool dont_deliver_p,
MSG_Deliver_Mode mode,
const char *attachment1_type,
const char *attachment1_body,
uint32 attachment1_body_length,
const struct MSG_AttachmentData *attachments,
const struct MSG_AttachedFile *preloaded_attachments,
//#ifdef MSG_SEND_MULTIPART_RELATED
MSG_SendPart *relatedPart,
//#endif
void (*message_delivery_done_callback)
(MWContext *context,
void *fe_data,
int status,
const char *error_message))
{
int failure = 0;
m_pane = pane;
m_fe_data = fe_data;
m_message_delivery_done_callback = message_delivery_done_callback;
//#ifdef MSG_SEND_MULTIPART_RELATED
m_related_part = relatedPart;
if (m_related_part)
m_related_part->SetMimeDeliveryState(this);
//#endif
XP_ASSERT (fields);
if (!fields) return MK_OUT_OF_MEMORY; /* rb -1; */
if (m_fields)
{
delete m_fields;
}
m_fields = new MSG_CompositionFields;
if (!m_fields)
return MK_OUT_OF_MEMORY;
m_fields->SetOwner(pane);
#ifdef GENERATE_MESSAGE_ID
if (fields->GetMessageId())
{
m_fields->SetMessageId(XP_STRDUP(fields->GetMessageId()));
/* Don't bother checking for out of memory; if it fails, then we'll just
let the server generate the message-id, and suffer with the
possibility of duplicate messages.*/
}
#endif /* GENERATE_MESSAGE_ID */
/* Strip whitespace from beginning and end of body. */
if (attachment1_body)
{
while (attachment1_body_length > 0 &&
XP_IS_SPACE (*attachment1_body))
{
attachment1_body++;
attachment1_body_length--;
}
while (attachment1_body_length > 0 &&
XP_IS_SPACE (attachment1_body [attachment1_body_length - 1]))
{
attachment1_body_length--;
}
if (attachment1_body_length <= 0)
attachment1_body = 0;
if (attachment1_body)
{
char *newb = (char *) XP_ALLOC (attachment1_body_length + 1);
if (! newb)
{
return MK_OUT_OF_MEMORY;
}
XP_MEMCPY (newb, attachment1_body, attachment1_body_length);
newb [attachment1_body_length] = 0;
m_attachment1_body = newb;
m_attachment1_body_length = attachment1_body_length;
}
}
if (!fields->GetNewspostUrl() || !*fields->GetNewspostUrl())
fields->SetNewspostUrl("news:");
m_fields->SetNewspostUrl(fields->GetNewspostUrl());
m_fields->SetDefaultBody(fields->GetDefaultBody());
StrAllocCopy (m_attachment1_type, attachment1_type);
StrAllocCopy (m_attachment1_encoding, "8bit");
/* strip whitespace from and duplicate header fields. */
SetMimeHeader(MSG_FROM_HEADER_MASK, fields->GetFrom());
SetMimeHeader(MSG_REPLY_TO_HEADER_MASK, fields->GetReplyTo());
SetMimeHeader(MSG_TO_HEADER_MASK, (fields->GetTo()));
SetMimeHeader(MSG_CC_HEADER_MASK, (fields->GetCc()));
SetMimeHeader(MSG_FCC_HEADER_MASK, (fields->GetFcc()));
SetMimeHeader(MSG_BCC_HEADER_MASK, (fields->GetBcc()));
SetMimeHeader(MSG_NEWSGROUPS_HEADER_MASK, (fields->GetNewsgroups()));
SetMimeHeader(MSG_FOLLOWUP_TO_HEADER_MASK, (fields->GetFollowupTo()));
SetMimeHeader(MSG_ORGANIZATION_HEADER_MASK, (fields->GetOrganization()));
SetMimeHeader(MSG_SUBJECT_HEADER_MASK, (fields->GetSubject()));
SetMimeHeader(MSG_REFERENCES_HEADER_MASK, (fields->GetReferences()));
SetMimeHeader(MSG_X_TEMPLATE_HEADER_MASK, (fields->GetTemplateName()));
if (fields->GetOtherRandomHeaders())
m_fields->SetOtherRandomHeaders(fields->GetOtherRandomHeaders());
if (fields->GetPriority())
m_fields->SetPriority(fields->GetPriority());
int i, j = (int) MSG_LAST_BOOL_HEADER_MASK;
for (i = 0; i < j; i++)
m_fields->SetBoolHeader((MSG_BOOL_HEADER_SET) i,
fields->GetBoolHeader((MSG_BOOL_HEADER_SET) i));
#if 0
m_fields->SetReturnReceipt(fields->GetReturnReceipt());
HG29822
m_fields->SetSigned(fields->GetSigned());
m_fields->SetAttachVCard(fields->GetAttachVCard());
#endif
m_fields->SetForcePlainText(fields->GetForcePlainText());
m_fields->SetUseMultipartAlternative(fields->GetUseMultipartAlternative());
if (pane && m_fields->GetReturnReceipt())
{
if (m_fields->GetReturnReceiptType() == 1 ||
m_fields->GetReturnReceiptType() == 3)
pane->SetRequestForReturnReceipt(TRUE);
else
pane->SetRequestForReturnReceipt(FALSE);
}
/* Check the fields for legitimacy, and run the callback if they're not
ok. */
if ( mode != MSG_SaveAsDraft && mode != MSG_SaveAsTemplate )
failure = mime_sanity_check_fields (m_fields->GetFrom(), m_fields->GetReplyTo(),
m_fields->GetTo(), m_fields->GetCc(),
m_fields->GetBcc(), m_fields->GetFcc(),
m_fields->GetNewsgroups(), m_fields->GetFollowupTo(),
m_fields->GetSubject(), m_fields->GetReferences(),
m_fields->GetOrganization(),
m_fields->GetOtherRandomHeaders());
if (failure)
return failure;
m_digest_p = digest_p;
m_dont_deliver_p = dont_deliver_p;
m_deliver_mode = mode;
// m_msg_file_name = WH_TempName (xpFileToPost, "nsmail");
failure = HackAttachments(attachments, preloaded_attachments);
return failure;
}
/* This is the main driving function of this module. It generates a
document of type message/rfc822, which contains the stuff provided.
The first few arguments are the standard header fields that the
generated document should have.
`other_random_headers' is a string of additional headers that should
be inserted beyond the standard ones. If provided, it is just tacked
on to the end of the header block, so it should have newlines at the
end of each line, shouldn't have blank lines, multi-line headers
should be properly continued, etc.
`digest_p' says that most of the documents we are attaching are
themselves messages, and so we should generate a multipart/digest
container instead of multipart/mixed. (It's a minor difference.)
The full text of the first attachment is provided via `attachment1_type',
`attachment1_body' and `attachment1_body_length'. These may all be 0
if all attachments are provided externally.
Subsequent attachments are provided as URLs to load, described in the
MSG_AttachmentData structures.
If `dont_deliver_p' is false, then we actually deliver the message to the
SMTP and/or NNTP server, and the message_delivery_done_callback will be
invoked with the status.
If `dont_deliver_p' is true, then we just generate the message, we don't
actually deliver it, and the message_delivery_done_callback will be called
with the name of the generated file. The callback is responsible for both
freeing the file name string, and deleting the file when it is done with
it. If an error occurred, then `status' will be negative and
`error_message' may be an error message to display. If status is non-
negative, then `error_message' contains the file name (this is kind of
a kludge...)
*/
extern "C" void
MSG_StartMessageDelivery (MSG_Pane *pane,
void *fe_data,
MSG_CompositionFields *fields,
XP_Bool digest_p,
XP_Bool dont_deliver_p,
const char *attachment1_type,
const char *attachment1_body,
uint32 attachment1_body_length,
const struct MSG_AttachmentData *attachments,
void *relatedPart,
void (*message_delivery_done_callback)
(MWContext *context,
void *fe_data,
int status,
const char *error_message))
{
MSG_SendMimeDeliveryState::StartMessageDelivery(pane, fe_data, fields,
digest_p, dont_deliver_p,
MSG_DeliverNow, /* ####??? */
attachment1_type,
attachment1_body, attachment1_body_length,
attachments, 0,
(MSG_SendPart *) relatedPart,
#ifdef XP_OS2
//DSR040297 - Casting away extern "C"
//DSR040297 - Note: this simple little cast switches the function pointers from extern "C"
//to non-extern "C" pointer (aka a C++ function). Don't try to change the static method, I've
//tried it and it only gets uglier. This (for what its worth) is at least only a few casts in
//spots where it makes sense.
(void (*) (MWContext *, void *, int, const char *))
#endif
message_delivery_done_callback);
}
extern "C" void
msg_StartMessageDeliveryWithAttachments (MSG_Pane *pane,
void *fe_data,
MSG_CompositionFields *fields,
XP_Bool digest_p,
XP_Bool dont_deliver_p,
MSG_Deliver_Mode mode,
const char *attachment1_type,
const char *attachment1_body,
uint32 attachment1_body_length,
const struct MSG_AttachedFile
*attachments,
//#ifdef MSG_SEND_MULTIPART_RELATED
void *relatedPart,
//#endif
void (*message_delivery_done_callback)
(MWContext *context,
void *fe_data,
int status,
const char *error_message))
{
MSG_SendMimeDeliveryState::StartMessageDelivery(pane, fe_data, fields,
digest_p, dont_deliver_p, mode,
attachment1_type, attachment1_body,
attachment1_body_length,
0, attachments,
(MSG_SendPart *) relatedPart,
#ifdef XP_OS2
//DSR040297 - see comment above about 'Casting away extern "C"'
(void (*) (MWContext *, void *, int, const char *))
#endif
message_delivery_done_callback);
}
extern "C" int
msg_DownloadAttachments (MSG_Pane *pane,
void *fe_data,
const struct MSG_AttachmentData *attachments,
void (*attachments_done_callback)
(MWContext *context,
void *fe_data,
int status, const char *error_message,
struct MSG_AttachedFile *attachments))
{
MSG_SendMimeDeliveryState *state = 0;
int failure = 0;
XP_ASSERT(attachments && attachments[0].url);
/* if (!attachments || !attachments[0].url)
{
failure = -1;
goto FAIL;
} */ /* The only possible error above is out of memory and it is handled
in MSG_CompositionPane::DownloadAttachments() */
state = new MSG_SendMimeDeliveryState;
if (! state)
{
failure = MK_OUT_OF_MEMORY;
goto FAIL;
}
state->m_pane = pane;
state->m_fe_data = fe_data;
state->m_attachments_only_p = TRUE;
state->m_attachments_done_callback =
#ifdef XP_OS2
//DSR040297 - see comment above about 'Casting away extern "C"'
(void(*)(MWContext*,void*,int,const char*,MSG_AttachedFile*))
#endif
attachments_done_callback;
failure = state->HackAttachments(attachments, 0);
if (failure >= 0)
return 0;
FAIL:
XP_ASSERT (failure);
/* in this case, our NET_GetURL exit routine has already freed
the state */
if (failure != MK_ATTACHMENT_LOAD_FAILED)
{
char *err_msg = NET_ExplainErrorDetails (failure);
attachments_done_callback (state->GetContext(), fe_data, failure,
err_msg, 0);
if (state) delete state;
if (err_msg) XP_FREE (err_msg);
#ifdef XP_MAC
// ### mwelch The MacFE wants this error thrown as an exception.
// This is because of the way that error recovery occurs
// inside the compose session object.
if (failure == MK_INTERRUPTED)
failure = userCanceledErr;
#endif
}
return failure;
}
void MSG_SendMimeDeliveryState::Clear()
{
if (m_fields) {
delete m_fields;
m_fields = NULL;
}
if (m_attachment1_type) XP_FREE (m_attachment1_type);
if (m_attachment1_encoding) XP_FREE (m_attachment1_encoding);
if (m_attachment1_body) XP_FREE (m_attachment1_body);
if (m_attachment1_encoder_data)
{
MimeEncoderDestroy(m_attachment1_encoder_data, TRUE);
m_attachment1_encoder_data = 0;
}
/* if (m_headers) XP_FREE (m_headers); */
if (m_msg_file)
{
XP_FileClose (m_msg_file);
m_msg_file = 0;
XP_ASSERT (m_msg_file_name);
}
if (m_imapOutgoingParser)
{
delete m_imapOutgoingParser;
m_imapOutgoingParser = NULL;
}
if(m_imapLocalMailDB)
{
m_imapLocalMailDB->Close();
m_imapLocalMailDB = NULL;
XP_FileRemove (m_msg_file_name, xpMailFolderSummary);
}
if (m_msg_file_name)
{
XP_FileRemove (m_msg_file_name, xpFileToPost);
XP_FREE (m_msg_file_name);
m_msg_file_name = 0;
}
if (m_attachments)
{
int i;
for (i = 0; i < m_attachment_count; i++)
{
if (m_attachments [i].m_encoder_data)
{
MimeEncoderDestroy(m_attachments [i].m_encoder_data,
TRUE);
m_attachments [i].m_encoder_data = 0;
}
FREEIF (m_attachments [i].m_url_string);
if (m_attachments [i].m_url)
NET_FreeURLStruct (m_attachments [i].m_url);
FREEIF (m_attachments [i].m_type);
FREEIF (m_attachments [i].m_override_type);
FREEIF (m_attachments [i].m_override_encoding);
FREEIF (m_attachments [i].m_desired_type);
FREEIF (m_attachments [i].m_description);
FREEIF (m_attachments [i].m_x_mac_type);
FREEIF (m_attachments [i].m_x_mac_creator);
FREEIF (m_attachments [i].m_real_name);
FREEIF (m_attachments [i].m_encoding);
if (m_attachments [i].m_file)
XP_FileClose (m_attachments [i].m_file);
if (m_attachments [i].m_file_name)
{
if (!m_pre_snarfed_attachments_p)
XP_FileRemove (m_attachments [i].m_file_name, xpFileToPost);
XP_FREE (m_attachments [i].m_file_name);
}
#ifdef XP_MAC
/* remove the appledoubled intermediate file after we done all.
*/
if (m_attachments [i].m_ap_filename)
{
XP_FileRemove (m_attachments [i].m_ap_filename, xpFileToPost);
XP_FREE (m_attachments [i].m_ap_filename);
}
#endif /* XP_MAC */
}
delete[] m_attachments;
m_attachment_count = m_attachment_pending_count = 0;
m_attachments = 0;
}
}
void
MSG_SendMimeDeliveryState::DeliverMessage ()
{
XP_Bool mail_p = ((m_fields->GetTo() && *m_fields->GetTo()) ||
(m_fields->GetCc() && *m_fields->GetCc()) ||
(m_fields->GetBcc() && *m_fields->GetBcc()));
XP_Bool news_p = (m_fields->GetNewsgroups() &&
*(m_fields->GetNewsgroups()) ? TRUE : FALSE);
if ( m_deliver_mode != MSG_SaveAsDraft &&
m_deliver_mode != MSG_SaveAsTemplate )
XP_ASSERT(mail_p || news_p);
#if 0
/* Figure out how many bytes we're actually going to be writing, total.
*/
m_delivery_bytes = 0;
m_delivery_total_bytes = 0;
if (m_fcc && *m_fcc)
m_delivery_total_bytes += m_msg_size;
if (m_queue_for_later_p)
m_delivery_total_bytes += m_msg_size;
else
{
if (mail_p)
m_delivery_total_bytes += m_msg_size;
if (news_p)
m_delivery_total_bytes += m_msg_size;
}
#endif /* 0 */
if (m_deliver_mode == MSG_QueueForLater)
{
QueueForLater();
return;
}
else if (m_deliver_mode == MSG_SaveAsDraft)
{
SaveAsDraft();
return;
}
else if (m_deliver_mode == MSG_SaveAsTemplate)
{
SaveAsTemplate();
return;
}
/*
if (m_fields->GetFcc())
if (!DoFcc())
return;
*/
#ifdef XP_UNIX
{
int status = msg_DeliverMessageExternally(GetContext(), m_msg_file_name);
if (status != 0)
{
if (status < 0)
Fail (status, 0);
else
{
/* The message has now been delivered successfully. */
MWContext *context = GetContext();
if (m_message_delivery_done_callback)
m_message_delivery_done_callback (context,
m_fe_data, 0, NULL);
m_message_delivery_done_callback = 0;
Clear();
/* When attaching, even though the context has
active_url_count == 0, XFE_AllConnectionsComplete() **is**
called. However, when not attaching, and not delivering right
away, we don't actually open any URLs, so we need to destroy
the window ourself. Ugh!!
*/
if (m_attachment_count == 0)
MSG_MailCompositionAllConnectionsComplete(MSG_FindPane(context,
MSG_ANYPANE));
}
return;
}
}
#endif /* XP_UNIX */
#ifdef MAIL_BEFORE_NEWS
if (mail_p)
DeliverFileAsMail (); /* May call ...as_news() next. */
else if (news_p)
DeliverFileAsNews ();
#else /* !MAIL_BEFORE_NEWS */
if (news_p)
DeliverFileAsNews (); /* May call ...as_mail() next. */
else if (mail_p)
DeliverFileAsMail ();
#endif /* !MAIL_BEFORE_NEWS */
else
abort ();
}
#if 0
void
MSG_SendMimeDeliveryState::DeliveryThermo (int32 increment)
{
int32 percent;
m_delivery_bytes += increment;
XP_ASSERT(m_delivery_total_bytes > 0);
if (m_delivery_total_bytes <= 0) return;
percent = 100 * (((double) m_delivery_bytes) /
((double) m_delivery_total_bytes));
FE_SetProgressBarPercent (GetContext(), percent);
#if 0
FE_GraphProgress (GetContext(), 0,
m_delivery_bytes, 0,
m_delivery_total_bytes);
#endif /* 0 */
}
#endif /* 0 */
static void mime_deliver_as_mail_exit (URL_Struct *, int status, MWContext *);
static void mime_deliver_as_news_exit (URL_Struct *url, int status,
MWContext *);
void
MSG_SendMimeDeliveryState::DeliverFileAsMail ()
{
char *buf, *buf2;
URL_Struct *url;
FE_Progress (GetContext(), XP_GetString(MK_MSG_DELIV_MAIL));
buf = (char *) XP_ALLOC ((m_fields->GetTo() ? XP_STRLEN (m_fields->GetTo()) + 10 : 0) +
(m_fields->GetCc() ? XP_STRLEN (m_fields->GetCc()) + 10 : 0) +
(m_fields->GetBcc() ? XP_STRLEN (m_fields->GetBcc()) + 10 : 0) +
10);
if (! buf)
{
Fail (MK_OUT_OF_MEMORY, 0);
return;
}
XP_STRCPY (buf, "mailto:");
buf2 = buf + XP_STRLEN (buf);
if (m_fields->GetTo())
{
XP_STRCAT (buf2, m_fields->GetTo());
}
if (m_fields->GetCc())
{
if (*buf2) XP_STRCAT (buf2, ",");
XP_STRCAT (buf2, m_fields->GetCc());
}
if (m_fields->GetBcc())
{
if (*buf2) XP_STRCAT (buf2, ",");
XP_STRCAT (buf2, m_fields->GetBcc());
}
url = NET_CreateURLStruct (buf, NET_DONT_RELOAD);
XP_FREE (buf);
if (! url)
{
Fail (MK_OUT_OF_MEMORY, 0);
return;
}
/* put the filename of the message into the post data field and set a flag
in the URL struct to specify that it is a file
*/
url->post_data = XP_STRDUP(m_msg_file_name);
url->post_data_size = XP_STRLEN(url->post_data);
url->post_data_is_file = TRUE;
url->method = URL_POST_METHOD;
url->fe_data = this;
url->internal_url = TRUE;
url->msg_pane = m_pane;
/* We can ignore the return value of NET_GetURL() because we have
handled the error in mime_deliver_as_mail_exit(). */
MSG_UrlQueue::AddUrlToPane(url, mime_deliver_as_mail_exit, m_pane, TRUE);
}
void
MSG_SendMimeDeliveryState::DeliverFileAsNews ()
{
URL_Struct *url = NET_CreateURLStruct (m_fields->GetNewspostUrl(), NET_DONT_RELOAD);
if (! url)
{
Fail (MK_OUT_OF_MEMORY, 0);
return;
}
FE_Progress (GetContext(), XP_GetString(MK_MSG_DELIV_NEWS));
/* put the filename of the message into the post data field and set a flag
in the URL struct to specify that it is a file.
*/
url->post_data = XP_STRDUP(m_msg_file_name);
url->post_data_size = XP_STRLEN(url->post_data);
url->post_data_is_file = TRUE;
url->method = URL_POST_METHOD;
url->fe_data = this;
url->internal_url = TRUE;
url->msg_pane = m_pane;
/* We can ignore the return value of NET_GetURL() because we have
handled the error in mime_deliver_as_news_exit(). */
MSG_UrlQueue::AddUrlToPane (url, mime_deliver_as_news_exit, m_pane, TRUE);
}
static void
mime_deliver_as_mail_exit (URL_Struct *url, int status,
MWContext * /*context*/)
{
MSG_SendMimeDeliveryState *state =
(MSG_SendMimeDeliveryState *) url->fe_data;
state->DeliverAsMailExit(url, status);
}
void
MSG_SendMimeDeliveryState::DeliverAsMailExit(URL_Struct *url, int status)
{
char *error_msg = 0;
url->fe_data = 0;
if (status < 0 && url->error_msg)
{
error_msg = url->error_msg;
url->error_msg = 0;
}
// NET_FreeURLStruct (url);
if (status < 0)
{
Fail (status, error_msg);
}
#ifdef MAIL_BEFORE_NEWS
else if (m_newsgroups)
{
/* If we're sending this mail message to news as well, start it now.
Completion and further errors will be handled there.
*/
DeliverFileAsNews ();
}
#endif /* MAIL_BEFORE_NEWS */
else
{
/* The message has now been sent successfully! */
if (m_fields->GetFcc())
if (!DoFcc())
return;
FE_Progress (GetContext(), XP_GetString(MK_MSG_DELIV_MAIL_DONE));
if (m_message_delivery_done_callback)
m_message_delivery_done_callback (GetContext(),
m_fe_data, 0, NULL);
m_message_delivery_done_callback = 0;
Clear();
delete this;
}
}
static void
mime_deliver_as_news_exit (URL_Struct *url, int status,
MWContext * /*context*/)
{
MSG_SendMimeDeliveryState *state =
(MSG_SendMimeDeliveryState *) url->fe_data;
state->DeliverAsNewsExit(url, status);
}
void
MSG_SendMimeDeliveryState::DeliverAsNewsExit(URL_Struct *url, int status)
{
char *error_msg = 0;
url->fe_data = 0;
if (status < 0 && url->error_msg)
{
error_msg = url->error_msg;
url->error_msg = 0;
}
// NET_FreeURLStruct (url);
if (status < 0)
{
Fail (status, error_msg);
}
#ifndef MAIL_BEFORE_NEWS
else if ((m_fields->GetTo() && *m_fields->GetTo()) ||
(m_fields->GetCc() && *m_fields->GetCc()) ||
(m_fields->GetBcc() && *m_fields->GetBcc()))
{
/* If we're sending this news message to mail as well, start it now.
Completion and further errors will be handled there.
*/
DeliverFileAsMail ();
}
#endif /* !MAIL_BEFORE_NEWS */
else
{
/* The message has now been sent successfully! */
if (m_fields->GetFcc())
if (!DoFcc())
return;
FE_Progress (GetContext(), XP_GetString(MK_MSG_DELIV_NEWS_DONE));
if (m_message_delivery_done_callback)
m_message_delivery_done_callback (GetContext(),
m_fe_data, 0, NULL);
m_message_delivery_done_callback = 0;
Clear();
delete this;
}
}
#ifdef XP_OS2
XP_BEGIN_PROTOS
extern int32 msg_do_fcc_handle_line(char* line, uint32 length, void* closure);
XP_END_PROTOS
#else
static int32 msg_do_fcc_handle_line(char* line, uint32 length, void* closure);
#endif
static int
mime_do_fcc_1 (MSG_Pane *pane,
const char *input_file_name, XP_FileType input_file_type,
const char *output_name, XP_FileType output_file_type,
MSG_Deliver_Mode mode,
const char *bcc_header,
const char *fcc_header,
const char *news_url)
{
int status = 0;
XP_File in = 0;
XP_File out = 0;
XP_Bool file_existed_p;
XP_StatStruct st;
char *ibuffer = 0;
int ibuffer_size = TEN_K;
char *obuffer = 0;
int32 obuffer_size = 0, obuffer_fp = 0;
int32 n;
XP_Bool summaryWasValid = FALSE;
XP_Bool summaryIsValid = FALSE;
XP_Bool mark_as_read = TRUE;
ParseOutgoingMessage *outgoingParser = NULL;
MailDB *mail_db = NULL;
MsgERR err = eSUCCESS;
char *envelope;
char *output_file_name = NET_ParseURL(output_name, GET_PATH_PART);
if (!output_file_name || !*output_file_name) // must be real file path
StrAllocCopy(output_file_name, output_name);
if (mode == MSG_QueueForLater)
FE_Progress (pane->GetContext(), XP_GetString(MK_MSG_QUEUEING));
else if ( mode == MSG_SaveAsDraft )
FE_Progress (pane->GetContext(), XP_GetString(MK_MSG_SAVING_AS_DRAFT));
else if ( mode == MSG_SaveAsTemplate )
FE_Progress (pane->GetContext(), XP_GetString(MK_MSG_SAVING_AS_TEMPLATE));
else
FE_Progress (pane->GetContext(), XP_GetString(MK_MSG_WRITING_TO_FCC));
ibuffer = NULL;
while (!ibuffer && (ibuffer_size >= 1024))
{
ibuffer = (char *) XP_ALLOC (ibuffer_size);
if (!ibuffer)
ibuffer_size /= 2;
}
if (!ibuffer)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
file_existed_p = !XP_Stat (output_file_name, &st, output_file_type);
if (file_existed_p)
{
summaryWasValid = msg_IsSummaryValid(output_file_name, &st);
if (!msg_ConfirmMailFile (pane->GetContext(), output_file_name))
{
XP_FREEIF(output_file_name);
return MK_INTERRUPTED; /* #### hack. It turns out we already
were testing for this result code and
silently canceling the send if we ever got
it (because it meant that the user hit the
Stop button). Returning it here has a
similar effect -- the user was asked to
confirm writing to the FCC folder, and he
hit the Cancel button, so we now quietly
do nothing. */
}
}
else
{
pane->GetMaster()->FindMailFolder(output_file_name,
TRUE /*createIfMissing*/);
if (-1 == XP_Stat (output_file_name, &st, output_file_type))
FE_Alert (pane->GetContext(), XP_GetString(MK_MSG_CANT_CREATE_FOLDER));
}
out = XP_FileOpen (output_file_name, output_file_type, XP_FILE_APPEND_BIN);
if (!out)
{
/* #### include file name in error message! */
status = MK_MSG_COULDNT_OPEN_FCC_FILE;
goto FAIL;
}
in = XP_FileOpen (input_file_name, input_file_type, XP_FILE_READ_BIN);
if (!in)
{
status = MK_UNABLE_TO_OPEN_FILE; /* rb -1; */ /* How did this happen? */
goto FAIL;
}
// set up database and outbound message parser to keep db up to date.
outgoingParser = new ParseOutgoingMessage;
err = MailDB::Open(output_file_name, FALSE, &mail_db);
if (err != eSUCCESS)
mail_db = NULL;
if (!outgoingParser)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
outgoingParser->SetOutFile(out);
outgoingParser->SetMailDB(mail_db);
outgoingParser->SetWriteToOutFile(FALSE);
/* Write a BSD mailbox envelope line to the file.
If the file existed, preceed it by a linebreak: this file format wants a
*blank* line before all "From " lines except the first. This assumes
that the previous message in the file was properly closed, that is, that
the last line in the file ended with a linebreak.
*/
XP_FileSeek(out, 0, SEEK_END);
if (file_existed_p && st.st_size > 0)
{
if (XP_FileWrite (LINEBREAK, LINEBREAK_LEN, out) < LINEBREAK_LEN)
{
status = MK_MIME_ERROR_WRITING_FILE;
goto FAIL;
}
}
outgoingParser->Init(XP_FileTell(out));
envelope = msg_GetDummyEnvelope();
if (msg_do_fcc_handle_line (envelope, XP_STRLEN (envelope), outgoingParser)
< 0)
{
status = MK_MIME_ERROR_WRITING_FILE;
goto FAIL;
}
/* Write out an X-Mozilla-Status header.
This is required for the queue file, so that we can overwrite it once
the messages have been delivered, and so that the MSG_FLAG_QUEUED bit
is set.
For FCC files, we don't necessarily need one, but we might as well put
one in so that it's marked as read already.
*/
if (mode == MSG_QueueForLater ||
mode == MSG_SaveAsDraft ||
mode == MSG_SaveAsTemplate ||
mark_as_read)
{
char *buf = 0;
uint16 flags = 0;
mark_as_read = TRUE;
flags |= MSG_FLAG_READ;
if (mode == MSG_QueueForLater )
flags |= MSG_FLAG_QUEUED;
buf = PR_smprintf(X_MOZILLA_STATUS_FORMAT LINEBREAK, flags);
if (buf)
{
status = msg_do_fcc_handle_line(buf, XP_STRLEN(buf), outgoingParser);
XP_FREEIF(buf);
if (status < 0)
goto FAIL;
}
uint32 flags2 = 0;
if (mode == MSG_SaveAsTemplate)
flags2 |= MSG_FLAG_TEMPLATE;
buf = PR_smprintf(X_MOZILLA_STATUS2_FORMAT LINEBREAK, flags2);
if (buf)
{
status = msg_do_fcc_handle_line(buf, XP_STRLEN(buf), outgoingParser);
XP_FREEIF(buf);
if (status < 0)
goto FAIL;
}
}
/* Write out the FCC and BCC headers.
When writing to the Queue file, we *must* write the FCC and BCC
headers, or else that information would be lost. Because, when actually
delivering the message (with "deliver now") we do FCC/BCC right away;
but when queueing for later delivery, we do FCC/BCC at delivery-time.
The question remains of whether FCC and BCC should be written into normal
BCC folders (like the Sent Mail folder.)
For FCC, there seems no point to do that; it's not information that one
would want to refer back to.
For BCC, the question isn't as clear. On the one hand, if I send someone
a BCC'ed copy of the message, and save a copy of it for myself (with FCC)
I might want to be able to look at that message later and see the list of
people to whom I had BCC'ed it.
On the other hand, the contents of the BCC header is sensitive
information, and should perhaps not be stored at all.
Thus the consultation of the #define SAVE_BCC_IN_FCC_FILE.
(Note that, if there is a BCC header present in a message in some random
folder, and that message is forwarded to someone, then the attachment
code will strip out the BCC header before forwarding it.)
*/
if ((mode == MSG_QueueForLater ||
mode == MSG_SaveAsDraft ||
mode == MSG_SaveAsTemplate) &&
fcc_header && *fcc_header)
{
int32 L = XP_STRLEN(fcc_header) + 20;
char *buf = (char *) XP_ALLOC (L);
if (!buf)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
PR_snprintf(buf, L-1, "FCC: %s" LINEBREAK, fcc_header);
status = msg_do_fcc_handle_line(buf, XP_STRLEN(buf), outgoingParser);
if (status < 0)
goto FAIL;
}
if (bcc_header && *bcc_header
#ifndef SAVE_BCC_IN_FCC_FILE
&& (mode == MSG_QueueForLater ||
mode == MSG_SaveAsDraft ||
mode == MSG_SaveAsTemplate)
#endif
)
{
int32 L = XP_STRLEN(bcc_header) + 20;
char *buf = (char *) XP_ALLOC (L);
if (!buf)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
PR_snprintf(buf, L-1, "BCC: %s" LINEBREAK, bcc_header);
status = msg_do_fcc_handle_line(buf, XP_STRLEN(buf), outgoingParser);
if (status < 0)
goto FAIL;
}
/* Write out the X-Mozilla-News-Host header.
This is done only when writing to the queue file, not the FCC file.
We need this to complement the "Newsgroups" header for the case of
queueing a message for a non-default news host.
Convert a URL like "snews://host:123/" to the form "host:123/secure"
or "news://user@host:222" to simply "host:222".
*/
if ((mode == MSG_QueueForLater ||
mode == MSG_SaveAsDraft ||
mode == MSG_SaveAsTemplate) && news_url && *news_url)
{
XP_Bool secure_p = (news_url[0] == 's' || news_url[0] == 'S');
char *orig_hap = NET_ParseURL (news_url, GET_HOST_PART);
char *host_and_port = orig_hap;
if (host_and_port)
{
/* There may be authinfo at the front of the host - it could be of
the form "user:password@host:port", so take off everything before
the first at-sign. We don't want to store authinfo in the queue
folder, I guess, but would want it to be re-prompted-for at
delivery-time.
*/
char *at = XP_STRCHR (host_and_port, '@');
if (at)
host_and_port = at + 1;
}
if ((host_and_port && *host_and_port) || !secure_p)
{
char *line = PR_smprintf(X_MOZILLA_NEWSHOST ": %s%s" LINEBREAK,
host_and_port ? host_and_port : "",
secure_p ? "/secure" : "");
FREEIF(orig_hap);
orig_hap = 0;
if (!line)
{
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
status = msg_do_fcc_handle_line(line, XP_STRLEN(line),
outgoingParser);
FREEIF(line);
if (status < 0)
goto FAIL;
}
FREEIF(orig_hap);
orig_hap = 0;
}
/* Read from the message file, and write to the FCC or Queue file.
There are two tricky parts: the first is that the message file
uses CRLF, and the FCC file should use LINEBREAK. The second
is that the message file may have lines beginning with "From "
but the FCC file must have those lines mangled.
It's unfortunate that we end up writing the FCC file a line
at a time, but it's the easiest way...
*/
while (1)
{
n = XP_FileRead (ibuffer, ibuffer_size, in);
if (n == 0)
break;
if (n < 0) /* read failed (not eof) */
{
status = n;
goto FAIL;
}
n = msg_LineBuffer (ibuffer, n,
&obuffer, (uint32 *)&obuffer_size,
(uint32*)&obuffer_fp,
TRUE, msg_do_fcc_handle_line,
outgoingParser);
if (n < 0) /* write failed */
{
status = n;
goto FAIL;
}
}
/* If there's still stuff in the buffer (meaning the last line had no
newline) push it out. */
if (obuffer_fp > 0)
msg_do_fcc_handle_line (obuffer, obuffer_fp, outgoingParser);
/* Terminate with a final newline. */
if (XP_FileWrite (LINEBREAK, LINEBREAK_LEN, out) < LINEBREAK_LEN)
{
status = MK_MIME_ERROR_WRITING_FILE;
}
else
outgoingParser->AdvanceOutPosition(LINEBREAK_LEN);
if (mail_db != NULL && outgoingParser != NULL &&
outgoingParser->m_newMsgHdr != NULL)
{
outgoingParser->FinishHeader();
mail_db->AddHdrToDB(outgoingParser->m_newMsgHdr, NULL, TRUE);
if (summaryWasValid)
summaryIsValid = TRUE;
}
FAIL:
if (ibuffer)
XP_FREE (ibuffer);
if (obuffer && obuffer != ibuffer)
XP_FREE (obuffer);
if (in)
XP_FileClose (in);
if (out)
{
if (status >= 0)
{
XP_FileClose (out);
if (summaryIsValid)
msg_SetSummaryValid(output_file_name, 0, 0);
}
else if (! file_existed_p)
{
XP_FileClose (out);
XP_FileRemove (output_file_name, output_file_type);
}
else
{
XP_FileClose (out);
XP_FileTruncate (output_file_name, output_file_type, st.st_size); /* restore original size */
}
}
if (mail_db != NULL && status >= 0) {
if ( mode == MSG_SaveAsDraft ) {
MSG_PostDeliveryActionInfo *actionInfo =
pane->GetPostDeliveryActionInfo();
if (actionInfo) {
if (actionInfo->m_flags & MSG_FLAG_EXPUNGED) {
XP_ASSERT(actionInfo->m_msgKeyArray.GetSize()== 1);
mail_db->DeleteMessage(actionInfo->m_msgKeyArray.GetAt(0));
actionInfo->m_msgKeyArray.RemoveAt(0);
}
}
else {
actionInfo = new MSG_PostDeliveryActionInfo((MSG_FolderInfo*)
pane->GetMaster()->
FindMailFolder(output_file_name, TRUE));
if (actionInfo) {
actionInfo->m_flags |= MSG_FLAG_EXPUNGED;
pane->SetPostDeliveryActionInfo(actionInfo);
}
}
if (outgoingParser->m_newMsgHdr && actionInfo)
actionInfo->m_msgKeyArray.Add(outgoingParser->m_newMsgHdr->GetMessageKey());
}
mail_db->Close();
}
FREEIF(output_file_name);
delete outgoingParser;
if (status < 0)
{
/* Fail, and terminate. */
return status;
}
else
{
/* Otherwise, continue on to _deliver_as_mail or _deliver_as_news
or mime_queue_for_later. */
return 0;
}
}
#ifdef XP_OS2
extern int32
#else
static int32
#endif
msg_do_fcc_handle_line(char* line, uint32 length, void* closure)
{
ParseOutgoingMessage *outgoingParser = (ParseOutgoingMessage *) closure;
int32 err = 0;
XP_File out = outgoingParser->GetOutFile();
// if we have a DB, feed the line to the parser
if (outgoingParser->GetMailDB() != NULL)
{
if (outgoingParser->m_bytes_written == 0)
err = outgoingParser->StartNewEnvelope(line, length);
else
err = outgoingParser->ParseFolderLine(line, length);
if (err < 0)
return err;
}
#ifdef MANGLE_INTERNAL_ENVELOPE_LINES
/* Note: it is correct to mangle all lines beginning with "From ",
not just those that look like parsable message delimiters.
Other software expects this. */
// If this really is the envelope, don't escape it. m_bytes_written will
// be 0 in that case, because envelope is always first.
if (outgoingParser->m_bytes_written > 0 && length >= 5 &&
line[0] == 'F' && !XP_STRNCMP(line, "From ", 5))
{
if (XP_FileWrite (">", 1, out) < 1)
return MK_MIME_ERROR_WRITING_FILE;
outgoingParser->AdvanceOutPosition(1);
}
#endif /* MANGLE_INTERNAL_ENVELOPE_LINES */
/* #### if XP_FileWrite is a performance problem, we can put in a
call to msg_ReBuffer() here... */
if (XP_FileWrite (line, length, out) < length)
return MK_MIME_ERROR_WRITING_FILE;
outgoingParser->m_bytes_written += length;
return 0;
}
extern "C" int
msg_DoFCC (MSG_Pane *pane,
const char *input_file, XP_FileType input_file_type,
const char *output_file, XP_FileType output_file_type,
const char *bcc_header_value,
const char *fcc_header_value)
{
XP_ASSERT(pane &&
input_file && *input_file &&
output_file && *output_file);
if (! (pane &&
input_file && *input_file &&
output_file && *output_file))
return MK_MIME_ERROR_WRITING_FILE;
return mime_do_fcc_1 (pane,
input_file, input_file_type,
output_file, output_file_type,
MSG_DeliverNow, bcc_header_value,
fcc_header_value,
0);
}
/* Returns false if an error happened. */
XP_Bool
MSG_SendMimeDeliveryState::DoFcc()
{
if (!m_fields->GetFcc() || !*m_fields->GetFcc())
return TRUE;
else if (NET_URL_Type(m_fields->GetFcc()) == IMAP_TYPE_URL &&
m_pane->GetMaster()->GetPrefs()->GetMailServerIsIMAP4())
{
SendToImapMagicFolder(MSG_FOLDER_FLAG_SENTMAIL);
return FALSE;
}
else
{
int status = msg_DoFCC (m_pane,
m_msg_file_name, xpFileToPost,
m_fields->GetFcc(), xpMailFolder,
m_fields->GetBcc(),
m_fields->GetFcc());
if (status < 0)
Fail (status, 0);
return (status >= 0);
}
}
char *
MSG_SendMimeDeliveryState::GetOnlineFolderName(uint32 flag, const char
**pDefaultName)
{
char *onlineFolderName = NULL;
switch (flag)
{
case MSG_FOLDER_FLAG_DRAFTS:
if (pDefaultName) *pDefaultName = DRAFTS_FOLDER_NAME;
PREF_CopyCharPref ("mail.default_drafts", &onlineFolderName);
break;
case MSG_FOLDER_FLAG_TEMPLATES:
if (pDefaultName) *pDefaultName = TEMPLATES_FOLDER_NAME;
PREF_CopyCharPref("mail.default_templates", &onlineFolderName);
break;
case MSG_FOLDER_FLAG_SENTMAIL:
if (pDefaultName) *pDefaultName = SENT_FOLDER_NAME;
onlineFolderName = XP_STRDUP(m_fields->GetFcc());
break;
default:
XP_ASSERT(0);
break;
}
return onlineFolderName;
}
void
MSG_SendMimeDeliveryState::SaveAsOfflineOp()
{
XP_ASSERT (m_imapOutgoingParser &&
m_imapLocalMailDB && m_imapFolderInfo);
if (!m_imapOutgoingParser || !m_imapLocalMailDB || !m_imapFolderInfo)
{
Fail(MK_IMAP_UNABLE_TO_SAVE_MESSAGE, 0);
return;
}
MsgERR err = eSUCCESS;
MailDB *mailDB = NULL;
MSG_IMAPFolderInfoMail *mailFolderInfo =
m_imapFolderInfo->GetIMAPFolderInfoMail();
XP_ASSERT (mailFolderInfo);
err = MailDB::Open (mailFolderInfo->GetPathname(), FALSE, &mailDB);
if (err == eSUCCESS)
{
MessageKey fakeId = mailDB->GetUnusedFakeId();
MailMessageHdr *mailMsgHdr = NULL, *newMailMsgHdr = NULL;
if (m_deliver_mode == MSG_SaveAsDraft)
{
// Do we have an actionInfo?
MSG_PostDeliveryActionInfo *actionInfo =
m_pane->GetPostDeliveryActionInfo();
if (actionInfo)
{
DBOfflineImapOperation *op = NULL;
MessageKey msgKey = actionInfo->m_msgKeyArray.GetAt(0);
if ((int32) msgKey >= 0) // real message key
{
// we start with an existing draft and save it while offline
// delete the old message and create a new message header
op = mailDB->GetOfflineOpForKey(msgKey, TRUE);
if (op)
{
op->SetImapFlagOperation(op->GetNewMessageFlags() |
kImapMsgDeletedFlag);
delete op;
}
actionInfo->m_msgKeyArray.RemoveAt(0);
actionInfo->m_msgKeyArray.Add(fakeId);
mailDB->DeleteMessage(msgKey);
}
else // faked UID; reuse it
{
fakeId = actionInfo->m_msgKeyArray.GetAt(0);
mailDB->DeleteOfflineOp(fakeId);
mailDB->DeleteMessage(fakeId);
}
}
else
{
// this is a new draft create a new actionInfo and a message
// header
actionInfo = new MSG_PostDeliveryActionInfo(m_imapFolderInfo);
actionInfo->m_flags |= MSG_FLAG_EXPUNGED;
actionInfo->m_msgKeyArray.Add(fakeId);
m_pane->SetPostDeliveryActionInfo(actionInfo);
}
}
newMailMsgHdr = new MailMessageHdr;
mailMsgHdr = m_imapLocalMailDB->GetMailHdrForKey(0);
if (mailMsgHdr)
{
if (newMailMsgHdr)
{
XP_File fileId = XP_FileOpen (m_msg_file_name, xpFileToPost,
XP_FILE_READ_BIN);
int iSize = 10240;
char *ibuffer = NULL;
while (!ibuffer && (iSize >= 512))
{
ibuffer = (char *) XP_ALLOC(iSize);
if (!ibuffer)
iSize /= 2;
}
if (fileId && ibuffer)
{
int32 numRead = 0;
newMailMsgHdr->CopyFromMsgHdr(mailMsgHdr,
m_imapLocalMailDB->GetDB(),
mailDB->GetDB());
newMailMsgHdr->SetMessageKey(fakeId);
err = mailDB->AddHdrToDB(newMailMsgHdr, NULL, TRUE);
// now write the offline message
numRead = XP_FileRead(ibuffer, iSize, fileId);
while(numRead > 0)
{
newMailMsgHdr->AddToOfflineMessage(ibuffer, numRead,
mailDB->GetDB());
numRead = XP_FileRead(ibuffer, iSize, fileId);
}
// now add the offline op to the database
DBOfflineImapOperation *op =
mailDB->GetOfflineOpForKey(fakeId, TRUE);
if (op)
{
op->SetAppendMsgOperation(mailFolderInfo->GetOnlineName(),
m_deliver_mode ==
MSG_SaveAsDraft ?
kAppendDraft :
kAppendTemplate);
delete op;
/* The message has now been queued successfully. */
if (m_message_delivery_done_callback)
m_message_delivery_done_callback (GetContext(),
m_fe_data, 0, NULL);
m_message_delivery_done_callback = 0;
// Clear() clears the Fcc path
Clear();
}
else
{
mailDB->RemoveHeaderFromDB (newMailMsgHdr);
Fail(MK_IMAP_UNABLE_TO_SAVE_MESSAGE, 0);
}
}
else
{
Fail(MK_IMAP_UNABLE_TO_SAVE_MESSAGE, 0);
}
if (fileId)
XP_FileClose(fileId);
XP_FREEIF(ibuffer);
delete newMailMsgHdr;
}
else
{
Fail(MK_IMAP_UNABLE_TO_SAVE_MESSAGE, 0);
}
delete mailMsgHdr;
}
else
{
Fail(MK_IMAP_UNABLE_TO_SAVE_MESSAGE, 0);
}
}
else
{
Fail(MK_IMAP_UNABLE_TO_SAVE_MESSAGE, 0);
}
if (mailDB)
mailDB->Close();
}
void
MSG_SendMimeDeliveryState::ImapAppendAddBccHeadersIfNeeded(URL_Struct *url)
{
XP_ASSERT(url);
const char *bcc_headers = m_fields->GetBcc();
char *post_data = NULL;
if (bcc_headers && *bcc_headers)
{
post_data = WH_TempName(xpFileToPost, "nsmail");
if (post_data)
{
XP_File dstFile = XP_FileOpen(post_data, xpFileToPost, XP_FILE_WRITE_BIN);
if (dstFile)
{
XP_File srcFile = XP_FileOpen(m_msg_file_name, xpFileToPost,
XP_FILE_READ_BIN);
if (srcFile)
{
char *tmpBuffer = NULL;
int bSize = TEN_K;
while (!tmpBuffer && (bSize >= 512))
{
tmpBuffer = (char *)XP_ALLOC(bSize);
if (!tmpBuffer)
bSize /= 2;
}
int bytesRead = 0;
if (tmpBuffer)
{
XP_FileWrite("Bcc: ", 5, dstFile);
XP_FileWrite(bcc_headers, XP_STRLEN(bcc_headers),
dstFile);
XP_FileWrite(CRLF, XP_STRLEN(CRLF), dstFile);
bytesRead = XP_FileRead(tmpBuffer, bSize, srcFile);
while (bytesRead > 0)
{
XP_FileWrite(tmpBuffer, bytesRead, dstFile);
bytesRead = XP_FileRead(tmpBuffer, bSize,
srcFile);
}
XP_FREE(tmpBuffer);
}
XP_FileClose(srcFile);
}
XP_FileClose(dstFile);
}
}
}
else
{
post_data = XP_STRDUP(m_msg_file_name);
}
if (post_data)
{
url->post_data = post_data;
url->post_data_size = XP_STRLEN(post_data);
url->post_data_is_file = TRUE;
url->method = URL_POST_METHOD;
url->fe_data = this;
url->internal_url = TRUE;
url->msg_pane = m_pane;
m_pane->GetContext()->imapURLPane = m_pane;
MSG_UrlQueue::AddUrlToPane
(url, MSG_SendMimeDeliveryState::PostSendToImapMagicFolder, m_pane, TRUE);
}
else
{
NET_FreeURLStruct(url);
}
}
/* Send the message to the magic folder, and runs the completion/failure
callback.
*/
void
MSG_SendMimeDeliveryState::SendToImapMagicFolder ( uint32 flag )
{
char *onlineFolderName = NULL;
const char *defaultName = "";
char *name = NULL;
char *host = NULL;
char *owner = NULL;
URL_Struct* url = NULL;
char* buf = NULL;
if (!m_imapFolderInfo)
{
XP_ASSERT (m_pane);
XP_ASSERT (m_pane->GetMaster()->GetPrefs()->GetMailServerIsIMAP4());
onlineFolderName = GetOnlineFolderName(flag, &defaultName);
if (onlineFolderName && NET_URL_Type(onlineFolderName) == IMAP_TYPE_URL)
{
host = NET_ParseURL(onlineFolderName, GET_HOST_PART);
name = NET_ParseURL(onlineFolderName, GET_PATH_PART);
owner = NET_ParseURL(onlineFolderName, GET_USERNAME_PART);
if (!name || !*name)
{
XP_FREEIF (name); // in case of allocated empty string
name = PR_smprintf("/%s", defaultName);
}
if (!owner || !*owner)
{
MSG_IMAPHost *imapHost = m_pane->GetMaster()->GetIMAPHost(host);
if (imapHost && imapHost->GetDefaultNamespacePrefixOfType(kPersonalNamespace))
StrAllocCopy(owner, imapHost->GetUserName());
}
}
if (name && *name && host && *host)
m_imapFolderInfo = m_pane->GetMaster()->FindImapMailFolder(host,
name+1,
owner,
FALSE);
}
if (m_imapFolderInfo)
{
if (NET_IsOffline())
{
if (flag == MSG_FOLDER_FLAG_DRAFTS || flag == MSG_FOLDER_FLAG_TEMPLATES)
SaveAsOfflineOp();
else
XP_ASSERT(0); // shouldn't be here
}
else
{
buf = CreateImapAppendMessageFromFileUrl( m_imapFolderInfo->GetHostName(),
m_imapFolderInfo->GetOnlineName(),
m_imapFolderInfo->GetOnlineHierarchySeparator(),
m_deliver_mode == MSG_SaveAsDraft);
if (buf)
{
url = NET_CreateURLStruct(buf, NET_NORMAL_RELOAD);
if (url)
{
ImapAppendAddBccHeadersIfNeeded(url);
}
XP_FREEIF(buf);
}
}
}
else if (host && name && *host && *name && !NET_IsOffline())
{
if (m_pane->IMAPListMailboxExist())
{
m_pane->SetIMAPListMailboxExist(FALSE);
buf = CreateImapAppendMessageFromFileUrl(host,
name+1,
kOnlineHierarchySeparatorUnknown,
m_deliver_mode == MSG_SaveAsDraft);
if (buf)
{
url = NET_CreateURLStruct(buf, NET_NORMAL_RELOAD);
if (url)
{
ImapAppendAddBccHeadersIfNeeded(url);
}
XP_FREEIF(buf);
}
}
else
{
buf = CreateImapListUrl(host,
name+1, kOnlineHierarchySeparatorUnknown);
if (buf)
{
url = NET_CreateURLStruct(buf, NET_NORMAL_RELOAD);
if (url)
{
url->fe_data = this;
url->internal_url = TRUE;
url->msg_pane = m_pane;
GetContext()->imapURLPane = m_pane;
m_pane->SetIMAPListInProgress(TRUE);
MSG_UrlQueue::AddUrlToPane
(url, MSG_SendMimeDeliveryState::PostListImapMailboxFolder, m_pane, TRUE);
}
XP_FREEIF(buf);
}
}
}
else
{
switch (m_deliver_mode)
{
case MSG_SaveAsDraft:
FE_Alert(GetContext(),XP_GetString(MK_MSG_UNABLE_TO_SAVE_DRAFT));
break;
case MSG_SaveAsTemplate:
FE_Alert(GetContext(), XP_GetString(MK_MSG_UNABLE_TO_SAVE_TEMPLATE));
break;
case MSG_DeliverNow:
default:
FE_Alert(GetContext(), XP_GetString(MK_MSG_COULDNT_OPEN_FCC_FILE));
break;
}
Fail(MK_IMAP_UNABLE_TO_SAVE_MESSAGE, 0); /* -1 rb */
}
XP_FREEIF(host);
XP_FREEIF(name);
XP_FREEIF(owner);
XP_FREEIF(onlineFolderName);
}
void
MSG_SendMimeDeliveryState::PostCreateImapMagicFolder ( URL_Struct *url,
int status,
MWContext * context)
{
MSG_SendMimeDeliveryState *state =
(MSG_SendMimeDeliveryState*) url->fe_data;
XP_ASSERT(state);
if (status < 0)
{
state->Fail (status, 0);
}
else
{
MSG_Master::PostCreateImapFolderUrlExitFunc (url, status, context);
char *host = NET_ParseURL(url->address, GET_HOST_PART);
char *name = NET_ParseURL(url->address, GET_PATH_PART);
char *owner = NET_ParseURL(url->address, GET_USERNAME_PART);
state->m_imapFolderInfo =
state->m_pane->GetMaster()->FindImapMailFolder(host, name+1,
owner, FALSE);
XP_ASSERT(state->m_imapFolderInfo);
if (state->m_imapFolderInfo)
{
if (state->m_deliver_mode == MSG_SaveAsDraft)
{
state->m_imapFolderInfo->SetFlag(MSG_FOLDER_FLAG_DRAFTS);
state->SendToImapMagicFolder(MSG_FOLDER_FLAG_DRAFTS);
}
else if (state->m_deliver_mode == MSG_DeliverNow)
{
state->m_imapFolderInfo->SetFlag(MSG_FOLDER_FLAG_SENTMAIL);
state->SendToImapMagicFolder(MSG_FOLDER_FLAG_SENTMAIL);
}
else if (state->m_deliver_mode == MSG_SaveAsTemplate)
{
state->m_imapFolderInfo->SetFlag(MSG_FOLDER_FLAG_TEMPLATES);
state->SendToImapMagicFolder(MSG_FOLDER_FLAG_TEMPLATES);
}
else
XP_ASSERT(FALSE);
}
else
{
state->Fail(MK_IMAP_NO_ONLINE_FOLDER, 0); /* -1 rb */
}
XP_FREEIF(host);
XP_FREEIF(name);
XP_FREEIF(owner);
}
NET_FreeURLStruct(url);
}
void
MSG_SendMimeDeliveryState::PostListImapMailboxFolder ( URL_Struct *url,
int status,
MWContext *context)
{
MSG_SendMimeDeliveryState *state =
(MSG_SendMimeDeliveryState*) url->fe_data;
XP_ASSERT(state);
const char *defaultName = "";
if (status < 0)
{
state->Fail (status, 0);
}
else
{
uint32 flag;
switch (state->m_deliver_mode)
{
case MSG_SaveAsDraft:
defaultName = DRAFTS_FOLDER_NAME;
flag = MSG_FOLDER_FLAG_DRAFTS;
break;
case MSG_SaveAsTemplate:
defaultName = TEMPLATES_FOLDER_NAME;
flag = MSG_FOLDER_FLAG_TEMPLATES;
break;
case MSG_DeliverNow:
defaultName = SENT_FOLDER_NAME;
flag = MSG_FOLDER_FLAG_SENTMAIL;
break;
default:
XP_ASSERT(0);
state->Fail(MK_IMAP_UNABLE_TO_SAVE_MESSAGE, 0); /* -1 rb */
return;
break;
}
state->m_pane->SetIMAPListInProgress(FALSE);
if (state->m_pane->IMAPListMailboxExist())
{
state->SendToImapMagicFolder(flag);
}
else
{
char *onlineFolderName = msg_MagicFolderName(state->m_pane->GetMaster()->GetPrefs(), flag, &status);
char *buf = NULL;
char *host = NULL, *name = NULL /*, *owner = NULL */;
MSG_IMAPHost *imapHost = NULL;
if (status < 0)
{
char *error_msg = XP_GetString(status);
state->Fail(status, error_msg ? XP_STRDUP(error_msg) : 0);
return;
}
else if (!onlineFolderName)
{
state->Fail(MK_IMAP_NO_ONLINE_FOLDER, 0); /* -1 rb */
return;
}
XP_ASSERT(NET_URL_Type(onlineFolderName) == IMAP_TYPE_URL);
host = NET_ParseURL(onlineFolderName, GET_HOST_PART);
name = NET_ParseURL(onlineFolderName, GET_PATH_PART);
// owner = NET_ParseURL(onlineFolderName, GET_USERNAME_PART);
if (!name || !*name)
{
XP_FREEIF(name);
name = PR_smprintf("/%s", defaultName);
}
imapHost = state->m_pane->GetMaster()->GetIMAPHost(host);
XP_ASSERT(imapHost);
if (imapHost->GetDefaultNamespacePrefixOfType(kPersonalNamespace))
{
char *nameWithPrefix = PR_smprintf("%s%s",
imapHost->GetDefaultNamespacePrefixOfType(kPersonalNamespace),
name+1);
if (nameWithPrefix)
{
XP_FREE(name);
name = nameWithPrefix;
}
}
// *** Create Imap magic folder
// *** then append message to the folder
buf = CreateImapMailboxCreateUrl(host, *name == '/' ? name+1 : name,
kOnlineHierarchySeparatorUnknown);
if (buf)
{
URL_Struct* url_struct = NULL;
url_struct = NET_CreateURLStruct(buf, NET_NORMAL_RELOAD);
if (url_struct)
{
url_struct->fe_data = state;
url_struct->internal_url = TRUE;
url_struct->msg_pane = state->m_pane;
state->GetContext()->imapURLPane = state->m_pane;
MSG_UrlQueue::AddUrlToPane (url_struct,
MSG_SendMimeDeliveryState::PostCreateImapMagicFolder, state->m_pane, TRUE);
}
XP_FREEIF(buf);
}
XP_FREEIF(onlineFolderName);
XP_FREEIF(host);
XP_FREEIF(name);
// XP_FREEIF(owner);
}
}
NET_FreeURLStruct(url);
}
void
MSG_SendMimeDeliveryState::SetIMAPMessageUID(MessageKey key)
{
XP_ASSERT(m_pane && m_pane->GetPaneType() == MSG_COMPOSITIONPANE);
MSG_CompositionPane *composePane = (MSG_CompositionPane*) m_pane;
composePane->SetIMAPMessageUID(key);
}
void
MSG_SendMimeDeliveryState::PostSendToImapMagicFolder ( URL_Struct *url,
int status,
MWContext *context)
{
MSG_SendMimeDeliveryState *state = (MSG_SendMimeDeliveryState*) url->fe_data;
MSG_CompositionPane *composePane = (MSG_CompositionPane*) state->m_pane;
XP_ASSERT(state && composePane);
MSG_PostDeliveryActionInfo *actionInfo =
composePane->GetPostDeliveryActionInfo();
MailDB *mailDB = NULL;
IDArray *idArray = new IDArray;
char *onlineFolderName = NULL;
if (XP_STRCMP(url->post_data, state->m_msg_file_name))
XP_FileRemove(url->post_data, xpFileToPost);
if (status < 0)
{
state->Fail (status, 0);
}
else
{
/* The message has now been queued successfully. */
if (state->m_message_delivery_done_callback)
state->m_message_delivery_done_callback (state->GetContext(),
state->m_fe_data, 0, NULL);
state->m_message_delivery_done_callback = 0;
// Clear() clears the Fcc path
state->Clear();
}
XP_ASSERT (composePane && composePane->GetPaneType() ==
MSG_COMPOSITIONPANE);
if (actionInfo &&
state->m_deliver_mode == MSG_SaveAsDraft &&
actionInfo->m_msgKeyArray.GetSize() > 1 &&
actionInfo->m_flags & MSG_FLAG_EXPUNGED)
{
MSG_Pane *urlPane = NULL;
if (!urlPane)
actionInfo->m_folderInfo ?
state->m_pane->GetMaster()->FindPaneOfType
(actionInfo->m_folderInfo, MSG_THREADPANE) : NULL;
composePane->DeleteIMAPOldDraftUID(actionInfo, urlPane);
}
if (state->m_imapFolderInfo)
{
char *dbName = WH_FileName(state->m_imapFolderInfo->GetPathname(), xpMailFolderSummary);
if (dbName)
mailDB = (MailDB *) MessageDB::FindInCache(dbName);
XP_FREEIF(dbName);
MSG_Pane *urlPane = NULL;
if (!urlPane)
urlPane = state->m_pane->GetMaster()->FindPaneOfType
(state->m_imapFolderInfo, MSG_THREADPANE);
if (!urlPane)
urlPane = composePane;
if (mailDB && urlPane)
{
char *url_string = CreateImapMailboxLITESelectUrl(
state->m_imapFolderInfo->GetIMAPFolderInfoMail()->GetHostName(),
state->m_imapFolderInfo->GetIMAPFolderInfoMail()->GetOnlineName(),
state->m_imapFolderInfo->GetIMAPFolderInfoMail()->GetOnlineHierarchySeparator());
if (url_string)
{
URL_Struct *url_struct =
NET_CreateURLStruct(url_string,
NET_NORMAL_RELOAD);
XP_ASSERT(urlPane);
if (url_struct)
{
state->m_imapFolderInfo->SetFolderLoadingContext(urlPane->GetContext());
urlPane->SetLoadingImapFolder(state->m_imapFolderInfo);
url_struct->fe_data = (void *) state->m_imapFolderInfo;
url_struct->internal_url = TRUE;
url_struct->msg_pane = urlPane;
urlPane->GetContext()->imapURLPane = urlPane;
MSG_UrlQueue::AddUrlToPane (url_struct,
MSG_Pane::PostLiteSelectExitFunc,
urlPane, TRUE);
}
XP_FREEIF(url_string);
}
// MessageDB::FindInCache() does not add refCount so don't call close
// mailDB->Close();
}
else
{
idArray->Add(0); // add dummy message key
state->m_imapFolderInfo->UpdatePendingCounts(state->m_imapFolderInfo,
idArray, FALSE);
state->m_imapFolderInfo->SummaryChanged();
// make sure we close down the cached imap connection when we done
// with save draft; this is save draft then send case what about the closing
// down the compose window case?
if (urlPane->GetPaneType() != MSG_THREADPANE &&
state->m_imapFolderInfo->GetFlags() & MSG_FOLDER_FLAG_DRAFTS &&
state->m_deliver_mode != MSG_SaveAsDraft)
composePane->GetMaster()->ImapFolderClosed(state->m_imapFolderInfo);
}
}
XP_FREEIF(onlineFolderName);
if (idArray)
delete idArray;
NET_FreeURLStruct(url);
delete state;
}
/* Send the message to the magic folder, and runs the completion/failure
callback.
*/
void
MSG_SendMimeDeliveryState::SendToMagicFolder ( uint32 flag )
{
char *name = 0;
int status = 0;
name = msg_MagicFolderName(m_pane->GetMaster()->GetPrefs(), flag, &status);
if (status < 0)
{
char *error_msg = XP_GetString(status);
Fail (status, error_msg ? XP_STRDUP(error_msg) : 0);
return;
}
else if (!name || *name == 0)
{
XP_FREEIF(name);
status = MK_OUT_OF_MEMORY;
goto FAIL;
}
else if (NET_URL_Type(name) == IMAP_TYPE_URL &&
m_pane->GetMaster()->GetPrefs()->GetMailServerIsIMAP4())
{
SendToImapMagicFolder(flag);
return;
}
status = mime_do_fcc_1 (m_pane,
m_msg_file_name, xpFileToPost,
name, xpMailFolder,
m_deliver_mode, m_fields->GetBcc(), m_fields->GetFcc(),
(m_fields->GetNewsgroups() && *m_fields->GetNewsgroups()
? m_fields->GetNewspostUrl() : 0));
XP_FREEIF (name);
if (status < 0)
goto FAIL;
FAIL:
if (status < 0)
{
Fail (status, 0);
}
else
{
MWContext *context = GetContext();
FE_Progress(context, XP_GetString(MK_MSG_QUEUED));
/* The message has now been queued successfully. */
if (m_message_delivery_done_callback)
m_message_delivery_done_callback (context,
m_fe_data, 0, NULL);
m_message_delivery_done_callback = 0;
Clear();
/* When attaching, even though the context has active_url_count == 0,
XFE_AllConnectionsComplete() **is** called. However, when not
attaching, and not delivering right away, we don't actually open
any URLs, so we need to destroy the window ourself. Ugh!!
*/
/* Unfortunately, Multipart related message falls into the same category.
* If we are sending images within a html message, we'll be chaining URLs
* one after another. Which will get FE_AllConnectionsComplete() to be
* called from NET_ProcessNet(). To prevent from calling
* MSG_MailCompositionAllConnectionsComplete() twice and
* not crashing the browser we only call all connections complete
* when we are not an mhtml message or an mhtml message which does not
* embed any images. This could be done better.
*/
if (m_attachment_count == 0)
if (!m_related_part || !m_related_part->GetNumChildren())
MSG_MailCompositionAllConnectionsComplete(MSG_FindPane(context,
MSG_ANYPANE));
}
}
/* Queues the message for later delivery, and runs the completion/failure
callback.
*/
void MSG_SendMimeDeliveryState::QueueForLater()
{
SendToMagicFolder (MSG_FOLDER_FLAG_QUEUE);
}
/* Save the message to the Drafts folder, and runs the completion/failure
callback.
*/
void MSG_SendMimeDeliveryState::SaveAsDraft()
{
SendToMagicFolder (MSG_FOLDER_FLAG_DRAFTS);
}
/* Save the message to the Template folder, and runs the completion/failure
callback.
*/
void MSG_SendMimeDeliveryState::SaveAsTemplate()
{
SendToMagicFolder (MSG_FOLDER_FLAG_TEMPLATES);
}
#ifdef _USRDLL
PUBLIC void
NET_RegisterDLLContentConverters()
{
NET_RegisterContentTypeConverter ("*", FO_MAIL_TO,
NULL, mime_make_attachment_stream);
NET_RegisterContentTypeConverter ("*", FO_CACHE_AND_MAIL_TO,
NULL, mime_make_attachment_stream);
/* #### What is this function for?
Is this right? I've cloned this stuff from MSG_RegisterConverters()
above. --jwz */
NET_RegisterContentTypeConverter ("*", FO_MAIL_MESSAGE_TO,
NULL, mime_make_attachment_stream);
NET_RegisterContentTypeConverter ("*", FO_CACHE_AND_MAIL_MESSAGE_TO,
NULL, mime_make_attachment_stream);
NET_RegisterContentTypeConverter (MESSAGE_RFC822, FO_MAIL_MESSAGE_TO,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS, FO_MAIL_MESSAGE_TO,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_RFC822,
FO_CACHE_AND_MAIL_MESSAGE_TO,
NULL, MIME_MessageConverter);
NET_RegisterContentTypeConverter (MESSAGE_NEWS,
FO_CACHE_AND_MAIL_MESSAGE_TO,
NULL, MIME_MessageConverter);
}
#endif