summaryrefslogtreecommitdiffstats
path: root/mailnews/compose/src
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
commit302bf1b523012e11b60425d6eee1221ebc2724eb (patch)
treeb191a895f8716efcbe42f454f37597a545a6f421 /mailnews/compose/src
parent21b3f6247403c06f85e1f45d219f87549862198f (diff)
downloadUXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.gz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.lz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.xz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.zip
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
Diffstat (limited to 'mailnews/compose/src')
-rw-r--r--mailnews/compose/src/moz.build55
-rw-r--r--mailnews/compose/src/nsComposeStrings.cpp116
-rw-r--r--mailnews/compose/src/nsComposeStrings.h77
-rw-r--r--mailnews/compose/src/nsMsgAppleCodes.h106
-rw-r--r--mailnews/compose/src/nsMsgAppleDouble.h207
-rw-r--r--mailnews/compose/src/nsMsgAppleDoubleEncode.cpp266
-rw-r--r--mailnews/compose/src/nsMsgAppleEncode.cpp703
-rw-r--r--mailnews/compose/src/nsMsgAttachment.cpp262
-rw-r--r--mailnews/compose/src/nsMsgAttachment.h41
-rw-r--r--mailnews/compose/src/nsMsgAttachmentHandler.cpp1383
-rw-r--r--mailnews/compose/src/nsMsgAttachmentHandler.h194
-rw-r--r--mailnews/compose/src/nsMsgCompFields.cpp693
-rw-r--r--mailnews/compose/src/nsMsgCompFields.h172
-rw-r--r--mailnews/compose/src/nsMsgCompUtils.cpp1803
-rw-r--r--mailnews/compose/src/nsMsgCompUtils.h143
-rw-r--r--mailnews/compose/src/nsMsgCompose.cpp6052
-rw-r--r--mailnews/compose/src/nsMsgCompose.h246
-rw-r--r--mailnews/compose/src/nsMsgComposeContentHandler.cpp125
-rw-r--r--mailnews/compose/src/nsMsgComposeContentHandler.h20
-rw-r--r--mailnews/compose/src/nsMsgComposeParams.cpp170
-rw-r--r--mailnews/compose/src/nsMsgComposeParams.h30
-rw-r--r--mailnews/compose/src/nsMsgComposeProgressParams.cpp46
-rw-r--r--mailnews/compose/src/nsMsgComposeProgressParams.h20
-rw-r--r--mailnews/compose/src/nsMsgComposeService.cpp1479
-rw-r--r--mailnews/compose/src/nsMsgComposeService.h68
-rw-r--r--mailnews/compose/src/nsMsgCopy.cpp553
-rw-r--r--mailnews/compose/src/nsMsgCopy.h120
-rw-r--r--mailnews/compose/src/nsMsgPrompts.cpp115
-rw-r--r--mailnews/compose/src/nsMsgPrompts.h22
-rw-r--r--mailnews/compose/src/nsMsgQuote.cpp233
-rw-r--r--mailnews/compose/src/nsMsgQuote.h52
-rw-r--r--mailnews/compose/src/nsMsgSend.cpp5218
-rw-r--r--mailnews/compose/src/nsMsgSend.h405
-rw-r--r--mailnews/compose/src/nsMsgSendLater.cpp1552
-rw-r--r--mailnews/compose/src/nsMsgSendLater.h144
-rw-r--r--mailnews/compose/src/nsMsgSendPart.cpp779
-rw-r--r--mailnews/compose/src/nsMsgSendPart.h101
-rw-r--r--mailnews/compose/src/nsMsgSendReport.cpp437
-rw-r--r--mailnews/compose/src/nsMsgSendReport.h46
-rw-r--r--mailnews/compose/src/nsSMTPProtocolHandler.js62
-rw-r--r--mailnews/compose/src/nsSMTPProtocolHandler.manifest4
-rw-r--r--mailnews/compose/src/nsSmtpProtocol.cpp2249
-rw-r--r--mailnews/compose/src/nsSmtpProtocol.h225
-rw-r--r--mailnews/compose/src/nsSmtpServer.cpp629
-rw-r--r--mailnews/compose/src/nsSmtpServer.h40
-rw-r--r--mailnews/compose/src/nsSmtpService.cpp772
-rw-r--r--mailnews/compose/src/nsSmtpService.h63
-rw-r--r--mailnews/compose/src/nsSmtpUrl.cpp778
-rw-r--r--mailnews/compose/src/nsSmtpUrl.h100
-rw-r--r--mailnews/compose/src/nsURLFetcher.cpp526
-rw-r--r--mailnews/compose/src/nsURLFetcher.h101
51 files changed, 29803 insertions, 0 deletions
diff --git a/mailnews/compose/src/moz.build b/mailnews/compose/src/moz.build
new file mode 100644
index 000000000..33b62c444
--- /dev/null
+++ b/mailnews/compose/src/moz.build
@@ -0,0 +1,55 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'nsComposeStrings.h',
+ 'nsMsgAttachmentHandler.h',
+ 'nsMsgCompFields.h',
+ 'nsMsgCompose.h',
+ 'nsMsgSend.h',
+]
+
+SOURCES += [
+ 'nsComposeStrings.cpp',
+ 'nsMsgAttachment.cpp',
+ 'nsMsgAttachmentHandler.cpp',
+ 'nsMsgCompFields.cpp',
+ 'nsMsgCompose.cpp',
+ 'nsMsgComposeContentHandler.cpp',
+ 'nsMsgComposeParams.cpp',
+ 'nsMsgComposeProgressParams.cpp',
+ 'nsMsgComposeService.cpp',
+ 'nsMsgCompUtils.cpp',
+ 'nsMsgCopy.cpp',
+ 'nsMsgPrompts.cpp',
+ 'nsMsgQuote.cpp',
+ 'nsMsgSend.cpp',
+ 'nsMsgSendLater.cpp',
+ 'nsMsgSendPart.cpp',
+ 'nsMsgSendReport.cpp',
+ 'nsSmtpProtocol.cpp',
+ 'nsSmtpServer.cpp',
+ 'nsSmtpService.cpp',
+ 'nsSmtpUrl.cpp',
+ 'nsURLFetcher.cpp',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'nsMsgAppleDoubleEncode.cpp',
+ 'nsMsgAppleEncode.cpp',
+ ]
+ EXPORTS += [
+ 'nsMsgAppleCodes.h',
+ 'nsMsgAppleDouble.h',
+ ]
+
+EXTRA_COMPONENTS += [
+ 'nsSMTPProtocolHandler.js',
+ 'nsSMTPProtocolHandler.manifest',
+]
+
+FINAL_LIBRARY = 'mail'
+
diff --git a/mailnews/compose/src/nsComposeStrings.cpp b/mailnews/compose/src/nsComposeStrings.cpp
new file mode 100644
index 000000000..5c8565805
--- /dev/null
+++ b/mailnews/compose/src/nsComposeStrings.cpp
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsComposeStrings.h"
+
+const char16_t* errorStringNameForErrorCode(nsresult aCode)
+{
+#ifdef __GNUC__
+// Temporary workaroung until bug 783526 is fixed.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#endif
+ switch(aCode)
+ {
+ case NS_MSG_UNABLE_TO_OPEN_FILE:
+ return u"unableToOpenFile";
+ case NS_MSG_UNABLE_TO_OPEN_TMP_FILE:
+ return u"unableToOpenTmpFile";
+ case NS_MSG_UNABLE_TO_SAVE_TEMPLATE:
+ return u"unableToSaveTemplate";
+ case NS_MSG_UNABLE_TO_SAVE_DRAFT:
+ return u"unableToSaveDraft";
+ case NS_MSG_COULDNT_OPEN_FCC_FOLDER:
+ return u"couldntOpenFccFolder";
+ case NS_MSG_NO_SENDER:
+ return u"noSender";
+ case NS_MSG_NO_RECIPIENTS:
+ return u"noRecipients";
+ case NS_MSG_ERROR_WRITING_FILE:
+ return u"errorWritingFile";
+ case NS_ERROR_SENDING_FROM_COMMAND:
+ return u"errorSendingFromCommand";
+ case NS_ERROR_SENDING_DATA_COMMAND:
+ return u"errorSendingDataCommand";
+ case NS_ERROR_SENDING_MESSAGE:
+ return u"errorSendingMessage";
+ case NS_ERROR_POST_FAILED:
+ return u"postFailed";
+ case NS_ERROR_QUEUED_DELIVERY_FAILED:
+ return u"errorQueuedDeliveryFailed";
+ case NS_ERROR_SEND_FAILED:
+ return u"sendFailed";
+ case NS_ERROR_SMTP_SERVER_ERROR:
+ return u"smtpServerError";
+ case NS_MSG_UNABLE_TO_SEND_LATER:
+ return u"unableToSendLater";
+ case NS_ERROR_COMMUNICATIONS_ERROR:
+ return u"communicationsError";
+ case NS_ERROR_BUT_DONT_SHOW_ALERT:
+ return u"dontShowAlert";
+ case NS_ERROR_TCP_READ_ERROR:
+ return u"tcpReadError";
+ case NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS:
+ return u"couldNotGetUsersMailAddress";
+ case NS_ERROR_MIME_MPART_ATTACHMENT_ERROR:
+ return u"mimeMpartAttachmentError";
+ case NS_MSG_FAILED_COPY_OPERATION:
+ return u"failedCopyOperation";
+ case NS_ERROR_NNTP_NO_CROSS_POSTING:
+ return u"nntpNoCrossPosting";
+ case NS_MSG_CANCELLING:
+ return u"msgCancelling";
+ case NS_ERROR_SEND_FAILED_BUT_NNTP_OK:
+ return u"sendFailedButNntpOk";
+ case NS_MSG_ERROR_READING_FILE:
+ return u"errorReadingFile";
+ case NS_MSG_ERROR_ATTACHING_FILE:
+ return u"errorAttachingFile";
+ case NS_ERROR_SMTP_GREETING:
+ return u"incorrectSmtpGreeting";
+ case NS_ERROR_SENDING_RCPT_COMMAND:
+ return u"errorSendingRcptCommand";
+ case NS_ERROR_STARTTLS_FAILED_EHLO_STARTTLS:
+ return u"startTlsFailed";
+ case NS_ERROR_SMTP_PASSWORD_UNDEFINED:
+ return u"smtpPasswordUndefined";
+ case NS_ERROR_SMTP_TEMP_SIZE_EXCEEDED:
+ return u"smtpTempSizeExceeded";
+ case NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_1:
+ return u"smtpPermSizeExceeded1";
+ case NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_2:
+ return u"smtpPermSizeExceeded2";
+ case NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_SERVER:
+ return u"smtpSendFailedUnknownServer";
+ case NS_ERROR_SMTP_SEND_FAILED_REFUSED:
+ return u"smtpSendRequestRefused";
+ case NS_ERROR_SMTP_SEND_FAILED_INTERRUPTED:
+ return u"smtpSendInterrupted";
+ case NS_ERROR_SMTP_SEND_FAILED_TIMEOUT:
+ return u"smtpSendTimeout";
+ case NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_REASON:
+ return u"smtpSendFailedUnknownReason";
+ case NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_NO_SSL:
+ return u"smtpHintAuthEncryptToPlainNoSsl";
+ case NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_SSL:
+ return u"smtpHintAuthEncryptToPlainSsl";
+ case NS_ERROR_SMTP_AUTH_CHANGE_PLAIN_TO_ENCRYPT:
+ return u"smtpHintAuthPlainToEncrypt";
+ case NS_ERROR_SMTP_AUTH_FAILURE:
+ return u"smtpAuthFailure";
+ case NS_ERROR_SMTP_AUTH_GSSAPI:
+ return u"smtpAuthGssapi";
+ case NS_ERROR_SMTP_AUTH_MECH_NOT_SUPPORTED:
+ return u"smtpAuthMechNotSupported";
+ case NS_ERROR_SMTP_AUTH_NOT_SUPPORTED:
+ return u"smtpAuthenticationNotSupported";
+ case NS_ERROR_ILLEGAL_LOCALPART:
+ return u"illegalLocalPart";
+ default:
+ return u"sendFailed";
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+}
diff --git a/mailnews/compose/src/nsComposeStrings.h b/mailnews/compose/src/nsComposeStrings.h
new file mode 100644
index 000000000..2a8754493
--- /dev/null
+++ b/mailnews/compose/src/nsComposeStrings.h
@@ -0,0 +1,77 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ String Ids used by mailnews\compose
+ To Do: Convert the callers to use names instead of ids and then make this file obsolete.
+ */
+
+#ifndef _nsComposeStrings_H__
+#define _nsComposeStrings_H__
+
+#include "msgCore.h"
+
+#define NS_MSG_UNABLE_TO_OPEN_FILE NS_MSG_GENERATE_FAILURE(12500)
+#define NS_MSG_UNABLE_TO_OPEN_TMP_FILE NS_MSG_GENERATE_FAILURE(12501)
+#define NS_MSG_UNABLE_TO_SAVE_TEMPLATE NS_MSG_GENERATE_FAILURE(12502)
+#define NS_MSG_UNABLE_TO_SAVE_DRAFT NS_MSG_GENERATE_FAILURE(12503)
+#define NS_MSG_COULDNT_OPEN_FCC_FOLDER NS_MSG_GENERATE_FAILURE(12506)
+#define NS_MSG_NO_SENDER NS_MSG_GENERATE_FAILURE(12510)
+#define NS_MSG_NO_RECIPIENTS NS_MSG_GENERATE_FAILURE(12511)
+#define NS_MSG_ERROR_WRITING_FILE NS_MSG_GENERATE_FAILURE(12512)
+#define NS_ERROR_SENDING_FROM_COMMAND NS_MSG_GENERATE_FAILURE(12514)
+#define NS_ERROR_SENDING_DATA_COMMAND NS_MSG_GENERATE_FAILURE(12516)
+#define NS_ERROR_SENDING_MESSAGE NS_MSG_GENERATE_FAILURE(12517)
+#define NS_ERROR_POST_FAILED NS_MSG_GENERATE_FAILURE(12518)
+#define NS_ERROR_QUEUED_DELIVERY_FAILED NS_MSG_GENERATE_FAILURE(12519)
+#define NS_ERROR_SEND_FAILED NS_MSG_GENERATE_FAILURE(12520)
+#define NS_ERROR_SMTP_SERVER_ERROR NS_MSG_GENERATE_FAILURE(12524)
+#define NS_MSG_UNABLE_TO_SEND_LATER NS_MSG_GENERATE_FAILURE(12525)
+#define NS_ERROR_COMMUNICATIONS_ERROR NS_MSG_GENERATE_FAILURE(12526)
+#define NS_ERROR_BUT_DONT_SHOW_ALERT NS_MSG_GENERATE_FAILURE(12527)
+#define NS_ERROR_TCP_READ_ERROR NS_MSG_GENERATE_FAILURE(12528)
+#define NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS NS_MSG_GENERATE_FAILURE(12529)
+#define NS_ERROR_MIME_MPART_ATTACHMENT_ERROR NS_MSG_GENERATE_FAILURE(12531)
+#define NS_MSG_FAILED_COPY_OPERATION NS_MSG_GENERATE_FAILURE(12532)
+
+/* 12554 is taken by NS_ERROR_NNTP_NO_CROSS_POSTING. use 12555 as the next one */
+
+#define NS_MSG_CANCELLING NS_MSG_GENERATE_SUCCESS(12555)
+
+// For message sending report
+#define NS_ERROR_SEND_FAILED_BUT_NNTP_OK NS_MSG_GENERATE_FAILURE(12560)
+#define NS_MSG_ERROR_READING_FILE NS_MSG_GENERATE_FAILURE(12563)
+
+#define NS_MSG_ERROR_ATTACHING_FILE NS_MSG_GENERATE_FAILURE(12570)
+
+#define NS_ERROR_SMTP_GREETING NS_MSG_GENERATE_FAILURE(12572)
+
+#define NS_ERROR_SENDING_RCPT_COMMAND NS_MSG_GENERATE_FAILURE(12575)
+
+#define NS_ERROR_STARTTLS_FAILED_EHLO_STARTTLS NS_MSG_GENERATE_FAILURE(12582)
+
+#define NS_ERROR_SMTP_PASSWORD_UNDEFINED NS_MSG_GENERATE_FAILURE(12584)
+#define NS_ERROR_SMTP_TEMP_SIZE_EXCEEDED NS_MSG_GENERATE_FAILURE(12586)
+#define NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_1 NS_MSG_GENERATE_FAILURE(12587)
+#define NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_2 NS_MSG_GENERATE_FAILURE(12588)
+
+#define NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_SERVER NS_MSG_GENERATE_FAILURE(12589)
+#define NS_ERROR_SMTP_SEND_FAILED_REFUSED NS_MSG_GENERATE_FAILURE(12590)
+#define NS_ERROR_SMTP_SEND_FAILED_INTERRUPTED NS_MSG_GENERATE_FAILURE(12591)
+#define NS_ERROR_SMTP_SEND_FAILED_TIMEOUT NS_MSG_GENERATE_FAILURE(12592)
+#define NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_REASON NS_MSG_GENERATE_FAILURE(12593)
+
+#define NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_NO_SSL NS_MSG_GENERATE_FAILURE(12594)
+#define NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_SSL NS_MSG_GENERATE_FAILURE(12595)
+#define NS_ERROR_SMTP_AUTH_CHANGE_PLAIN_TO_ENCRYPT NS_MSG_GENERATE_FAILURE(12596)
+#define NS_ERROR_SMTP_AUTH_FAILURE NS_MSG_GENERATE_FAILURE(12597)
+#define NS_ERROR_SMTP_AUTH_GSSAPI NS_MSG_GENERATE_FAILURE(12598)
+#define NS_ERROR_SMTP_AUTH_MECH_NOT_SUPPORTED NS_MSG_GENERATE_FAILURE(12599)
+#define NS_ERROR_SMTP_AUTH_NOT_SUPPORTED NS_MSG_GENERATE_FAILURE(12600)
+
+#define NS_ERROR_ILLEGAL_LOCALPART NS_MSG_GENERATE_FAILURE(12601)
+
+const char16_t* errorStringNameForErrorCode(nsresult aCode);
+
+#endif /* _nsComposeStrings_H__ */
diff --git a/mailnews/compose/src/nsMsgAppleCodes.h b/mailnews/compose/src/nsMsgAppleCodes.h
new file mode 100644
index 000000000..d8ca2f327
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAppleCodes.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** AD_Codes.h
+**
+** ---------------
+**
+** Head file for Apple Decode/Encode essential codes.
+**
+**
+*/
+
+#ifndef ad_codes_h
+#define ad_codes_h
+
+/*
+** applefile definitions used
+*/
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=mac68k
+#endif
+
+#define APPLESINGLE_MAGIC 0x00051600L
+#define APPLEDOUBLE_MAGIC 0x00051607L
+#define VERSION 0x00020000
+
+#define NUM_ENTRIES 6
+
+#define ENT_DFORK 1L
+#define ENT_RFORK 2L
+#define ENT_NAME 3L
+#define ENT_COMMENT 4L
+#define ENT_DATES 8L
+#define ENT_FINFO 9L
+#define CONVERT_TIME 1265437696L
+
+/*
+** data type used in the encoder/decoder.
+*/
+typedef struct ap_header
+{
+ int32_t magic;
+ int32_t version;
+ char fill[16];
+ int16_t entries;
+
+} ap_header;
+
+typedef struct ap_entry
+{
+ int32_t id;
+ int32_t offset;
+ int32_t length;
+
+} ap_entry;
+
+typedef struct ap_dates
+{
+ int32_t create, modify, backup, access;
+
+} ap_dates;
+
+typedef struct myFInfo /* the mac FInfo structure for the cross platform. */
+{
+ int32_t fdType, fdCreator;
+ int16_t fdFlags;
+ int32_t fdLocation; /* it really should be a pointer, but just a place-holder */
+ int16_t fdFldr;
+
+} myFInfo;
+
+PR_BEGIN_EXTERN_C
+/*
+** string utils.
+*/
+int write_stream(appledouble_encode_object *p_ap_encode_obj, const char *s,int len);
+
+int fill_apple_mime_header(appledouble_encode_object *p_ap_encode_obj);
+int ap_encode_file_infor(appledouble_encode_object *p_ap_encode_obj);
+int ap_encode_header(appledouble_encode_object* p_ap_encode_obj, bool firstTime);
+int ap_encode_data( appledouble_encode_object* p_ap_encode_obj, bool firstTime);
+
+/*
+** the prototypes for the ap_decoder.
+*/
+int fetch_a_line(appledouble_decode_object* p_ap_decode_obj, char *buff);
+int ParseFileHeader(appledouble_decode_object* p_ap_decode_obj);
+int ap_seek_part_start(appledouble_decode_object* p_ap_decode_obj);
+void parse_param(char *p, char **param, char**define, char **np);
+int ap_seek_to_boundary(appledouble_decode_object* p_ap_decode_obj, bool firstime);
+int ap_parse_header(appledouble_decode_object* p_ap_decode_obj,bool firstime);
+int ap_decode_file_infor(appledouble_decode_object* p_ap_decode_obj);
+int ap_decode_process_header(appledouble_decode_object* p_ap_decode_obj, bool firstime);
+int ap_decode_process_data( appledouble_decode_object* p_ap_decode_obj, bool firstime);
+
+PR_END_EXTERN_C
+
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=reset
+#endif
+
+#endif /* ad_codes_h */
diff --git a/mailnews/compose/src/nsMsgAppleDouble.h b/mailnews/compose/src/nsMsgAppleDouble.h
new file mode 100644
index 000000000..f4ae934ad
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAppleDouble.h
@@ -0,0 +1,207 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+* AppleDouble.h
+* -------------
+*
+* The header file for a stream based apple single/double encodor/decodor.
+*
+* 2aug95 mym
+*
+*/
+
+
+#ifndef AppleDouble_h
+#define AppleDouble_h
+
+#include "msgCore.h"
+#include "nsComposeStrings.h"
+#include "nsIOutputStream.h"
+#include "nsCOMPtr.h"
+
+#include <CoreServices/CoreServices.h>
+
+#define NOERR 0
+#define errDone 1
+ /* Done with current operation. */
+#define errEOB 2
+ /* End of a buffer. */
+#define errEOP 3
+ /* End of a Part. */
+
+
+#define errFileOpen NS_ERROR_GET_CODE(NS_MSG_UNABLE_TO_OPEN_TMP_FILE)
+#define errFileWrite -202 /*Error writing temporary file.*/
+#define errUsrCancel -2 /*MK_INTERRUPTED */
+#define errDecoding -1
+
+/*
+** The envirment block data type.
+*/
+enum
+{
+ kInit,
+ kDoingHeaderPortion,
+ kDoneHeaderPortion,
+ kDoingDataPortion,
+ kDoneDataPortion
+};
+
+typedef struct _appledouble_encode_object
+{
+ char fname[256];
+ FSIORefNum fileId; /* the id for the open file (data/resource fork) */
+
+ int state;
+ int text_file_type; /* if the file has a text file type with it. */
+ char *boundary; /* the boundary string. */
+
+ int status; /* the error code if anyerror happens. */
+ char b_overflow[200];
+ int s_overflow;
+
+ int state64; /* the left over state of base64 enocding */
+ int ct; /* the character count of base64 encoding */
+ int c1, c2; /* the left of the last base64 encoding */
+
+ char *outbuff; /* the outbuff by the caller. */
+ int s_outbuff; /* the size of the buffer. */
+ int pos_outbuff; /* the offset in the current buffer. */
+
+} appledouble_encode_object;
+
+/* The possible content transfer encodings */
+
+enum
+{
+ kEncodeNone,
+ kEncodeQP,
+ kEncodeBase64,
+ kEncodeUU
+};
+
+enum
+{
+ kGeneralMine,
+ kAppleDouble,
+ kAppleSingle
+};
+
+enum
+{
+ kInline,
+ kDontCare
+};
+
+enum
+{
+ kHeaderPortion,
+ kDataPortion
+};
+
+/* the decode states. */
+enum
+{
+ kBeginParseHeader = 3,
+ kParsingHeader,
+ kBeginSeekBoundary,
+ kSeekingBoundary,
+ kBeginHeaderPortion,
+ kProcessingHeaderPortion,
+ kBeginDataPortion,
+ kProcessingDataPortion,
+ kFinishing
+};
+
+/* uuencode states */
+enum
+{
+ kWaitingForBegin = (int) 0,
+ kBegin,
+ kMainBody,
+ kEnd
+};
+
+typedef struct _appledouble_decode_object
+{
+ int is_binary;
+ int is_apple_single; /* if the object encoded is in apple single */
+ int write_as_binhex;
+
+ int messagetype;
+ char* boundary0; /* the boundary for the enclosure. */
+ int deposition; /* the deposition. */
+ int encoding; /* the encoding method. */
+ int which_part;
+
+ char fname[256];
+ // nsIOFileStream *fileSpec; /* the stream for data fork work. */
+
+ int state;
+
+ int rksize; /* the resource fork size count. */
+ int dksize; /* the data fork size count. */
+
+ int status; /* the error code if anyerror happens. */
+ char b_leftover[256];
+ int s_leftover;
+
+ int encode; /* the encode type of the message. */
+ int state64; /* the left over state of base64 enocding */
+ int left; /* the character count of base64 encoding */
+ int c[4]; /* the left of the last base64 encoding */
+ int uu_starts_line; /* is decoder at the start of a line? (uuencode) */
+ int uu_state; /* state w/r/t the uuencode body */
+ int uu_bytes_written; /* bytes written from the current tuple (uuencode) */
+ int uu_line_bytes; /* encoded bytes remaining in the current line (uuencode) */
+
+ char *inbuff; /* the outbuff by the caller. */
+ int s_inbuff; /* the size of the buffer. */
+ int pos_inbuff; /* the offset in the current buffer. */
+
+
+ nsCOMPtr <nsIFile> tmpFile; /* the temp file to hold the decode data fork */
+ /* when doing the binhex exporting. */
+ nsCOMPtr <nsIOutputStream> tmpFileStream; /* The output File Stream */
+ int32_t data_size; /* the size of the data in the tmp file. */
+
+} appledouble_decode_object;
+
+
+/*
+** The protypes.
+*/
+
+PR_BEGIN_EXTERN_C
+
+int ap_encode_init(appledouble_encode_object *p_ap_encode_obj,
+ const char* fname,
+ char* separator);
+
+int ap_encode_next(appledouble_encode_object* p_ap_encode_obj,
+ char *to_buff,
+ int32_t buff_size,
+ int32_t* real_size);
+
+int ap_encode_end(appledouble_encode_object* p_ap_encode_obj,
+ bool is_aborting);
+
+int ap_decode_init(appledouble_decode_object* p_ap_decode_obj,
+ bool is_apple_single,
+ bool write_as_bin_hex,
+ void *closure);
+
+int ap_decode_next(appledouble_decode_object* p_ap_decode_obj,
+ char *in_buff,
+ int32_t buff_size);
+
+int ap_decode_end(appledouble_decode_object* p_ap_decode_obj,
+ bool is_aborting);
+
+PR_END_EXTERN_C
+
+#endif
diff --git a/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp b/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp
new file mode 100644
index 000000000..4d7178123
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+*
+* apple-double.c
+* --------------
+*
+* The codes to do apple double encoding/decoding.
+*
+* 02aug95 mym created.
+*
+*/
+#include "nsID.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsMsgAppleDouble.h"
+#include "nsMsgAppleCodes.h"
+#include "nsMsgCompUtils.h"
+#include "nsCExternalHandlerService.h"
+#include "nsIMIMEService.h"
+#include "nsMimeTypes.h"
+#include "prmem.h"
+#include "nsNetUtil.h"
+
+
+void
+MacGetFileType(nsIFile *fs,
+ bool *useDefault,
+ char **fileType,
+ char **encoding)
+{
+ if ((fs == NULL) || (fileType == NULL) || (encoding == NULL))
+ return;
+
+ bool exists = false;
+ fs->Exists(&exists);
+ if (!exists)
+ return;
+
+ *useDefault = TRUE;
+ *fileType = NULL;
+ *encoding = NULL;
+
+ nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(fs);
+ FSRef fsRef;
+ FSCatalogInfo catalogInfo;
+ OSErr err = errFileOpen;
+ if (NS_SUCCEEDED(macFile->GetFSRef(&fsRef)))
+ err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo, nullptr, nullptr, nullptr);
+
+ if ( (err != noErr) || (((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'TEXT') )
+ *fileType = strdup(APPLICATION_OCTET_STREAM);
+ else
+ {
+ // At this point, we should call the mime service and
+ // see what we can find out?
+ nsresult rv;
+ nsCOMPtr <nsIURI> tURI;
+ if (NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(tURI), fs)) && tURI)
+ {
+ nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && mimeFinder)
+ {
+ nsAutoCString mimeType;
+ rv = mimeFinder->GetTypeFromURI(tURI, mimeType);
+ if (NS_SUCCEEDED(rv))
+ {
+ *fileType = ToNewCString(mimeType);
+ return;
+ }
+ }
+ }
+
+ // If we hit here, return something...default to this...
+ *fileType = strdup(APPLICATION_OCTET_STREAM);
+ }
+}
+
+//#pragma cplusplus reset
+
+/*
+* ap_encode_init
+* --------------
+*
+* Setup the encode envirment
+*/
+
+int ap_encode_init( appledouble_encode_object *p_ap_encode_obj,
+ const char *fname,
+ char *separator)
+{
+ nsCOMPtr <nsIFile> myFile;
+ NS_NewNativeLocalFile(nsDependentCString(fname), true, getter_AddRefs(myFile));
+ bool exists;
+ if (myFile && NS_SUCCEEDED(myFile->Exists(&exists)) && !exists)
+ return -1;
+
+ nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(myFile);
+ nsAutoCString path;
+ macFile->GetNativePath(path);
+
+ memset(p_ap_encode_obj, 0, sizeof(appledouble_encode_object));
+
+ /*
+ ** Fill out the source file inforamtion.
+ */
+ memcpy(p_ap_encode_obj->fname, path.get(), path.Length());
+ p_ap_encode_obj->fname[path.Length()] = '\0';
+
+ p_ap_encode_obj->boundary = strdup(separator);
+ return noErr;
+}
+
+/*
+** ap_encode_next
+** --------------
+**
+** return :
+** noErr : everything is ok
+** errDone : when encoding is done.
+** errors : otherwise.
+*/
+int ap_encode_next(
+ appledouble_encode_object* p_ap_encode_obj,
+ char *to_buff,
+ int32_t buff_size,
+ int32_t* real_size)
+{
+ int status;
+
+ /*
+ ** install the out buff now.
+ */
+ p_ap_encode_obj->outbuff = to_buff;
+ p_ap_encode_obj->s_outbuff = buff_size;
+ p_ap_encode_obj->pos_outbuff = 0;
+
+ /*
+ ** first copy the outstandind data in the overflow buff to the out buffer.
+ */
+ if (p_ap_encode_obj->s_overflow)
+ {
+ status = write_stream(p_ap_encode_obj,
+ (const char*)(p_ap_encode_obj->b_overflow),
+ p_ap_encode_obj->s_overflow);
+ if (status != noErr)
+ return status;
+
+ p_ap_encode_obj->s_overflow = 0;
+ }
+
+ /*
+ ** go the next processing stage based on the current state.
+ */
+ switch (p_ap_encode_obj->state)
+ {
+ case kInit:
+ /*
+ ** We are in the starting position, fill out the header.
+ */
+ status = fill_apple_mime_header(p_ap_encode_obj);
+ if (status != noErr)
+ break; /* some error happens */
+
+ p_ap_encode_obj->state = kDoingHeaderPortion;
+ status = ap_encode_header(p_ap_encode_obj, true);
+ /* it is the first time to calling */
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneHeaderPortion;
+ }
+ else
+ {
+ break; /* we need more work on header portion. */
+ }
+
+ /*
+ ** we are done with the header, so let's go to the data port.
+ */
+ p_ap_encode_obj->state = kDoingDataPortion;
+ status = ap_encode_data(p_ap_encode_obj, true);
+ /* it is first time call do data portion */
+
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneDataPortion;
+ status = noErr;
+ }
+ break;
+
+ case kDoingHeaderPortion:
+
+ status = ap_encode_header(p_ap_encode_obj, false);
+ /* continue with the header portion. */
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneHeaderPortion;
+ }
+ else
+ {
+ break; /* we need more work on header portion. */
+ }
+
+ /*
+ ** start the data portion.
+ */
+ p_ap_encode_obj->state = kDoingDataPortion;
+ status = ap_encode_data(p_ap_encode_obj, true);
+ /* it is the first time calling */
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneDataPortion;
+ status = noErr;
+ }
+ break;
+
+ case kDoingDataPortion:
+
+ status = ap_encode_data(p_ap_encode_obj, false);
+ /* it is not the first time */
+
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneDataPortion;
+ status = noErr;
+ }
+ break;
+
+ case kDoneDataPortion:
+ status = errDone; /* we are really done. */
+
+ break;
+ }
+
+ *real_size = p_ap_encode_obj->pos_outbuff;
+ return status;
+}
+
+/*
+** ap_encode_end
+** -------------
+**
+** clear the apple encoding.
+*/
+
+int ap_encode_end(
+ appledouble_encode_object *p_ap_encode_obj,
+ bool is_aborting)
+{
+ /*
+ ** clear up the apple doubler.
+ */
+ if (p_ap_encode_obj == NULL)
+ return noErr;
+
+ if (p_ap_encode_obj->fileId) /* close the file if it is open. */
+ ::FSCloseFork(p_ap_encode_obj->fileId);
+
+ PR_FREEIF(p_ap_encode_obj->boundary); /* the boundary string. */
+
+ return noErr;
+}
diff --git a/mailnews/compose/src/nsMsgAppleEncode.cpp b/mailnews/compose/src/nsMsgAppleEncode.cpp
new file mode 100644
index 000000000..27e39a8cd
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAppleEncode.cpp
@@ -0,0 +1,703 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *
+ * apple_double_encode.c
+ * ---------------------
+ *
+ * The routines doing the Apple Double Encoding.
+ *
+ * 2aug95 mym Created.
+ *
+ */
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsMimeTypes.h"
+#include "prprf.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMsgAppleDouble.h"
+#include "nsMsgAppleCodes.h"
+#include "nsILocalFileMac.h"
+
+/*
+** Local Functions prototypes.
+*/
+static int output64chunk( appledouble_encode_object* p_ap_encode_obj,
+ int c1, int c2, int c3, int pads);
+
+static int to64(appledouble_encode_object* p_ap_encode_obj,
+ char *p,
+ int in_size);
+
+static int finish64(appledouble_encode_object* p_ap_encode_obj);
+
+
+#define BUFF_LEFT(p) ((p)->s_outbuff - (p)->pos_outbuff)
+
+/*
+** write_stream.
+*/
+int write_stream(
+ appledouble_encode_object *p_ap_encode_obj,
+ const char *out_string,
+ int len)
+{
+ if (p_ap_encode_obj->pos_outbuff + len < p_ap_encode_obj->s_outbuff)
+ {
+ memcpy(p_ap_encode_obj->outbuff + p_ap_encode_obj->pos_outbuff,
+ out_string,
+ len);
+ p_ap_encode_obj->pos_outbuff += len;
+ return noErr;
+ }
+ else
+ {
+ /*
+ ** If the buff doesn't have enough space, use the overflow buffer then.
+ */
+ int s_len = p_ap_encode_obj->s_outbuff - p_ap_encode_obj->pos_outbuff;
+
+ memcpy(p_ap_encode_obj->outbuff + p_ap_encode_obj->pos_outbuff,
+ out_string,
+ s_len);
+ memcpy(p_ap_encode_obj->b_overflow + p_ap_encode_obj->s_overflow,
+ out_string + s_len,
+ p_ap_encode_obj->s_overflow += (len - s_len));
+ p_ap_encode_obj->pos_outbuff += s_len;
+ return errEOB;
+ }
+}
+
+int fill_apple_mime_header(
+ appledouble_encode_object *p_ap_encode_obj)
+{
+ int status;
+
+ char tmpstr[266];
+
+#if 0
+// strcpy(tmpstr, "Content-Type: multipart/mixed; boundary=\"-\"\n\n---\n");
+// status = write_stream(p_ap_encode_env,
+// tmpstr,
+// strlen(tmpstr));
+// if (status != noErr)
+// return status;
+
+ PR_snprintf(tmpstr, sizeof(tmpstr),
+ "Content-Type: multipart/appledouble; boundary=\"=\"; name=\"");
+ status = write_stream(p_ap_encode_obj, (const char*)tmpstr, strlen(tmpstr));
+ if (status != noErr)
+ return status;
+
+ status = write_stream(p_ap_encode_obj,
+ p_ap_encode_obj->fname,
+ strlen(p_ap_encode_obj->fname));
+ if (status != noErr)
+ return status;
+
+ PR_snprintf(tmpstr, sizeof(tmpstr),
+ "\"\r\nContent-Disposition: inline; filename=\"%s\"\r\n\r\n\r\n--=\r\n",
+ p_ap_encode_obj->fname);
+#endif /* 0 */
+ PR_snprintf(tmpstr, sizeof(tmpstr), "--%s" CRLF, p_ap_encode_obj->boundary);
+ status = write_stream(p_ap_encode_obj, (const char*)tmpstr, strlen(tmpstr));
+ return status;
+}
+
+int ap_encode_file_infor(
+ appledouble_encode_object *p_ap_encode_obj)
+{
+ ap_header head;
+ ap_entry entries[NUM_ENTRIES];
+ ap_dates dates;
+ short i;
+ long comlen;
+ char comment[256];
+ int status;
+
+ nsCOMPtr <nsIFile> resFile;
+ NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true,
+ getter_AddRefs(resFile));
+ if (!resFile)
+ return errFileOpen;
+
+ FSRef ref;
+ nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(resFile);
+ if (NS_FAILED(macFile->GetFSRef(&ref)))
+ return errFileOpen;
+
+ FSCatalogInfo catalogInfo;
+ if (::FSGetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalogInfo, nullptr, nullptr, nullptr) != noErr)
+ {
+ return errFileOpen;
+ }
+
+ /* get a file comment, if possible */
+#if 1
+ // Carbon doesn't support GetWDInfo(). (Bug 555684)
+
+ // not sure why working directories are needed here...
+ comlen = 0;
+#else
+ long procID;
+ procID = 0;
+ GetWDInfo(p_ap_encode_obj->vRefNum, &fpb->ioVRefNum, &fpb->ioDirID, &procID);
+ IOParam vinfo;
+ memset((void *) &vinfo, '\0', sizeof (vinfo));
+ GetVolParmsInfoBuffer vp;
+ vinfo.ioCompletion = nil;
+ vinfo.ioVRefNum = fpb->ioVRefNum;
+ vinfo.ioBuffer = (Ptr) &vp;
+ vinfo.ioReqCount = sizeof (vp);
+ comlen = 0;
+ if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == noErr &&
+ ((vp.vMAttrib >> bHasDesktopMgr) & 1))
+ {
+ DTPBRec dtp;
+ memset((void *) &dtp, '\0', sizeof (dtp));
+ dtp.ioVRefNum = fpb->ioVRefNum;
+ if (PBDTGetPath(&dtp) == noErr)
+ {
+ dtp.ioCompletion = nil;
+ dtp.ioDTBuffer = (Ptr) comment;
+ dtp.ioNamePtr = fpb->ioNamePtr;
+ dtp.ioDirID = fpb->ioFlParID;
+ if (PBDTGetCommentSync(&dtp) == noErr)
+ comlen = dtp.ioDTActCount;
+ }
+ }
+#endif /* ! 1 */
+
+ /* write header */
+// head.magic = dfork ? APPLESINGLE_MAGIC : APPLEDOUBLE_MAGIC;
+ head.magic = APPLEDOUBLE_MAGIC; /* always do apple double */
+ head.version = VERSION;
+ memset(head.fill, '\0', sizeof (head.fill));
+ head.entries = NUM_ENTRIES - 1;
+ status = to64(p_ap_encode_obj,
+ (char *) &head,
+ sizeof (head));
+ if (status != noErr)
+ return status;
+
+ /* write entry descriptors */
+ nsAutoCString leafname;
+ macFile->GetNativeLeafName(leafname);
+ entries[0].offset = sizeof (head) + sizeof (ap_entry) * head.entries;
+ entries[0].id = ENT_NAME;
+ entries[0].length = leafname.Length();
+ entries[1].id = ENT_FINFO;
+ entries[1].length = sizeof (FInfo) + sizeof (FXInfo);
+ entries[2].id = ENT_DATES;
+ entries[2].length = sizeof (ap_dates);
+ entries[3].id = ENT_COMMENT;
+ entries[3].length = comlen;
+ entries[4].id = ENT_RFORK;
+ entries[4].length = catalogInfo.rsrcLogicalSize;
+ entries[5].id = ENT_DFORK;
+ entries[5].length = catalogInfo.dataLogicalSize;
+
+ /* correct the link in the entries. */
+ for (i = 1; i < NUM_ENTRIES; ++i)
+ {
+ entries[i].offset = entries[i-1].offset + entries[i-1].length;
+ }
+ status = to64(p_ap_encode_obj,
+ (char *) entries,
+ sizeof (ap_entry) * head.entries);
+ if (status != noErr)
+ return status;
+
+ /* write name */
+ status = to64(p_ap_encode_obj,
+ (char *) leafname.get(),
+ leafname.Length());
+ if (status != noErr)
+ return status;
+
+ /* write finder info */
+ status = to64(p_ap_encode_obj,
+ (char *) &catalogInfo.finderInfo,
+ sizeof (FInfo));
+ if (status != noErr)
+ return status;
+
+ status = to64(p_ap_encode_obj,
+ (char *) &catalogInfo.extFinderInfo,
+ sizeof (FXInfo));
+ if (status != noErr)
+ return status;
+
+ /* write dates */
+ dates.create = catalogInfo.createDate.lowSeconds + CONVERT_TIME;
+ dates.modify = catalogInfo.contentModDate.lowSeconds + CONVERT_TIME;
+ dates.backup = catalogInfo.backupDate.lowSeconds + CONVERT_TIME;
+ dates.access = catalogInfo.accessDate.lowSeconds + CONVERT_TIME;
+ status = to64(p_ap_encode_obj,
+ (char *) &dates,
+ sizeof (ap_dates));
+ if (status != noErr)
+ return status;
+
+ /* write comment */
+ if (comlen)
+ {
+ status = to64(p_ap_encode_obj,
+ comment,
+ comlen * sizeof(char));
+ }
+ /*
+ ** Get some help information on deciding the file type.
+ */
+ if (((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'TEXT' ||
+ ((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'text')
+ {
+ p_ap_encode_obj->text_file_type = true;
+ }
+
+ return status;
+}
+/*
+** ap_encode_header
+**
+** encode the file header and the resource fork.
+**
+*/
+int ap_encode_header(
+ appledouble_encode_object* p_ap_encode_obj,
+ bool firstime)
+{
+ char rd_buff[256];
+ FSIORefNum fileId;
+ OSErr retval = noErr;
+ int status;
+ ByteCount inCount;
+
+ if (firstime)
+ {
+ PL_strcpy(rd_buff,
+ "Content-Type: application/applefile\r\nContent-Transfer-Encoding: base64\r\n\r\n");
+ status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff));
+ if (status != noErr)
+ return status;
+
+ status = ap_encode_file_infor(p_ap_encode_obj);
+ if (status != noErr)
+ return status;
+
+ /*
+ ** preparing to encode the resource fork.
+ */
+ nsCOMPtr <nsIFile> myFile;
+ NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true, getter_AddRefs(myFile));
+ if (!myFile)
+ return errFileOpen;
+
+ FSRef ref;
+ nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(myFile);
+ if (NS_FAILED(macFile->GetFSRef(&ref)))
+ return errFileOpen;
+
+ HFSUniStr255 forkName;
+ ::FSGetResourceForkName(&forkName);
+ retval = ::FSOpenFork(&ref, forkName.length, forkName.unicode, fsRdPerm, &p_ap_encode_obj->fileId);
+ if (retval != noErr)
+ return retval;
+ }
+
+ fileId = p_ap_encode_obj->fileId;
+ while (retval == noErr)
+ {
+ if (BUFF_LEFT(p_ap_encode_obj) < 400)
+ break;
+
+ inCount = 0;
+ retval = ::FSReadFork(fileId, fsAtMark, 0, 256, rd_buff, &inCount);
+ if (inCount)
+ {
+ status = to64(p_ap_encode_obj,
+ rd_buff,
+ inCount);
+ if (status != noErr)
+ return status;
+ }
+ }
+
+ if (retval == eofErr)
+ {
+ ::FSCloseFork(fileId);
+ p_ap_encode_obj->fileId = 0;
+
+ status = finish64(p_ap_encode_obj);
+ if (status != noErr)
+ return status;
+
+ /*
+ ** write out the boundary
+ */
+ PR_snprintf(rd_buff, sizeof(rd_buff),
+ CRLF "--%s" CRLF,
+ p_ap_encode_obj->boundary);
+
+ status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff));
+ if (status == noErr)
+ status = errDone;
+ }
+ return status;
+}
+
+#if 0
+// This is unused for now and Clang complains about that is it is ifdefed out
+static void replace(char *p, int len, char frm, char to)
+{
+ for (; len > 0; len--, p++)
+ if (*p == frm) *p = to;
+}
+#endif
+
+/* Description of the various file formats and their magic numbers */
+struct magic
+{
+ const char *name; /* Name of the file format */
+ const char *num; /* The magic number */
+ int len; /* Length (0 means strlen(magicnum)) */
+};
+
+/* The magic numbers of the file formats we know about */
+static struct magic magic[] =
+{
+ { "image/gif", "GIF", 0 },
+ { "image/jpeg", "\377\330\377", 0 },
+ { "video/mpeg", "\0\0\001\263", 4 },
+ { "application/postscript", "%!", 0 },
+};
+static int num_magic = MOZ_ARRAY_LENGTH(magic);
+
+static const char *text_type = TEXT_PLAIN; /* the text file type. */
+static const char *default_type = APPLICATION_OCTET_STREAM;
+
+
+/*
+ * Determins the format of the file "inputf". The name
+ * of the file format (or NULL on error) is returned.
+ */
+static const char *magic_look(char *inbuff, int numread)
+{
+ int i, j;
+
+ for (i=0; i<num_magic; i++)
+ {
+ if (magic[i].len == 0)
+ magic[i].len = strlen(magic[i].num);
+ }
+
+ for (i=0; i<num_magic; i++)
+ {
+ if (numread >= magic[i].len)
+ {
+ for (j=0; j<magic[i].len; j++)
+ {
+ if (inbuff[j] != magic[i].num[j]) break;
+ }
+
+ if (j == magic[i].len)
+ return magic[i].name;
+ }
+ }
+
+ return default_type;
+}
+/*
+** ap_encode_data
+**
+** ---------------
+**
+** encode on the data fork.
+**
+*/
+int ap_encode_data(
+ appledouble_encode_object* p_ap_encode_obj,
+ bool firstime)
+{
+ char rd_buff[256];
+ FSIORefNum fileId;
+ OSErr retval = noErr;
+ ByteCount in_count;
+ int status;
+
+ if (firstime)
+ {
+ const char* magic_type;
+
+ /*
+ ** preparing to encode the data fork.
+ */
+ nsCOMPtr <nsIFile> resFile;
+ NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true,
+ getter_AddRefs(resFile));
+ if (!resFile)
+ return errFileOpen;
+
+ FSRef ref;
+ nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(resFile);
+ if (NS_FAILED(macFile->GetFSRef(&ref)))
+ return errFileOpen;
+
+ HFSUniStr255 forkName;
+ ::FSGetDataForkName(&forkName);
+ retval = ::FSOpenFork(&ref, forkName.length, forkName.unicode, fsRdPerm, &fileId);
+ if (retval != noErr)
+ return retval;
+
+ p_ap_encode_obj->fileId = fileId;
+
+
+ if (!p_ap_encode_obj->text_file_type)
+ {
+ /*
+ ** do a smart check for the file type.
+ */
+ in_count = 0;
+ retval = ::FSReadFork(fileId, fsFromStart, 0, 256, rd_buff, &in_count);
+ magic_type = magic_look(rd_buff, in_count);
+
+ /* don't forget to rewind the index to start point. */
+ ::FSSetForkPosition(fileId, fsFromStart, 0);
+ /* and reset retVal just in case... */
+ if (retval == eofErr)
+ retval = noErr;
+ }
+ else
+ {
+ magic_type = text_type; /* we already know it is a text type. */
+ }
+
+ /*
+ ** the data portion header information.
+ */
+ nsAutoCString leafName;
+ resFile->GetNativeLeafName(leafName);
+ PR_snprintf(rd_buff, sizeof(rd_buff),
+ "Content-Type: %s; name=\"%s\"" CRLF "Content-Transfer-Encoding: base64" CRLF "Content-Disposition: inline; filename=\"%s\"" CRLF CRLF,
+ magic_type,
+ leafName.get(),
+ leafName.get());
+
+ status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff));
+ if (status != noErr)
+ return status;
+ }
+
+ while (retval == noErr)
+ {
+ if (BUFF_LEFT(p_ap_encode_obj) < 400)
+ break;
+
+ in_count = 0;
+ retval = ::FSReadFork(p_ap_encode_obj->fileId, fsAtMark, 0, 256, rd_buff, &in_count);
+ if (in_count)
+ {
+#if 0
+/* replace(rd_buff, in_count, '\r', '\n'); */
+#endif
+/* ** may be need to do character set conversion here for localization. ** */
+ status = to64(p_ap_encode_obj,
+ rd_buff,
+ in_count);
+ if (status != noErr)
+ return status;
+ }
+ }
+
+ if (retval == eofErr)
+ {
+ ::FSCloseFork(p_ap_encode_obj->fileId);
+ p_ap_encode_obj->fileId = 0;
+
+ status = finish64(p_ap_encode_obj);
+ if (status != noErr)
+ return status;
+
+ /* write out the boundary */
+
+ PR_snprintf(rd_buff, sizeof(rd_buff),
+ CRLF "--%s--" CRLF CRLF,
+ p_ap_encode_obj->boundary);
+
+ status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff));
+
+ if (status == noErr)
+ status = errDone;
+ }
+ return status;
+}
+
+static char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+** convert the stream in the inbuff to 64 format and put it in the out buff.
+** To make the life easier, the caller will responcable of the cheking of the outbuff's bundary.
+*/
+static int
+to64(appledouble_encode_object* p_ap_encode_obj,
+ char *p,
+ int in_size)
+{
+ int status;
+ int c1, c2, c3, ct;
+ unsigned char *inbuff = (unsigned char*)p;
+
+ ct = p_ap_encode_obj->ct; /* the char count left last time. */
+
+ /*
+ ** resume the left state of the last conversion.
+ */
+ switch (p_ap_encode_obj->state64)
+ {
+ case 0:
+ p_ap_encode_obj->c1 = c1 = *inbuff ++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->state64 = 1;
+ return noErr;
+ }
+ p_ap_encode_obj->c2 = c2 = *inbuff ++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->state64 = 2;
+ return noErr;
+ }
+ c3 = *inbuff ++; --in_size;
+ break;
+ case 1:
+ c1 = p_ap_encode_obj->c1;
+ p_ap_encode_obj->c2 = c2 = *inbuff ++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->state64 = 2;
+ return noErr;
+ }
+ c3 = *inbuff ++; --in_size;
+ break;
+ case 2:
+ c1 = p_ap_encode_obj->c1;
+ c2 = p_ap_encode_obj->c2;
+ c3 = *inbuff ++; --in_size;
+ break;
+ }
+
+ while (in_size >= 0)
+ {
+ status = output64chunk(p_ap_encode_obj,
+ c1,
+ c2,
+ c3,
+ 0);
+ if (status != noErr)
+ return status;
+
+ ct += 4;
+ if (ct > 71)
+ {
+ status = write_stream(p_ap_encode_obj,
+ CRLF,
+ 2);
+ if (status != noErr)
+ return status;
+
+ ct = 0;
+ }
+
+ if (in_size <= 0)
+ {
+ p_ap_encode_obj->state64 = 0;
+ break;
+ }
+
+ c1 = (int)*inbuff++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->c1 = c1;
+ p_ap_encode_obj->state64 = 1;
+ break;
+ }
+ c2 = *inbuff++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->c1 = c1;
+ p_ap_encode_obj->c2 = c2;
+ p_ap_encode_obj->state64 = 2;
+ break;
+ }
+ c3 = *inbuff++;
+ in_size--;
+ }
+ p_ap_encode_obj->ct = ct;
+ return status;
+}
+
+/*
+** clear the left base64 encodes.
+*/
+static int
+finish64(appledouble_encode_object* p_ap_encode_obj)
+{
+ int status;
+
+ switch (p_ap_encode_obj->state64)
+ {
+ case 0:
+ break;
+ case 1:
+ status = output64chunk(p_ap_encode_obj,
+ p_ap_encode_obj->c1,
+ 0,
+ 0,
+ 2);
+ break;
+ case 2:
+ status = output64chunk(p_ap_encode_obj,
+ p_ap_encode_obj->c1,
+ p_ap_encode_obj->c2,
+ 0,
+ 1);
+ break;
+ }
+ status = write_stream(p_ap_encode_obj, CRLF, 2);
+ p_ap_encode_obj->state64 = 0;
+ p_ap_encode_obj->ct = 0;
+ return status;
+}
+
+static int output64chunk(
+ appledouble_encode_object* p_ap_encode_obj,
+ int c1, int c2, int c3, int pads)
+{
+ char tmpstr[32];
+ char *p = tmpstr;
+
+ *p++ = basis_64[c1>>2];
+ *p++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
+ if (pads == 2)
+ {
+ *p++ = '=';
+ *p++ = '=';
+ }
+ else if (pads)
+ {
+ *p++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
+ *p++ = '=';
+ }
+ else
+ {
+ *p++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
+ *p++ = basis_64[c3 & 0x3F];
+ }
+ return write_stream(p_ap_encode_obj, (const char*) tmpstr, p-tmpstr);
+}
diff --git a/mailnews/compose/src/nsMsgAttachment.cpp b/mailnews/compose/src/nsMsgAttachment.cpp
new file mode 100644
index 000000000..3a828c3b0
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAttachment.cpp
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgAttachment.h"
+#include "nsIFile.h"
+#include "nsNetUtil.h"
+
+NS_IMPL_ISUPPORTS(nsMsgAttachment, nsIMsgAttachment)
+
+nsMsgAttachment::nsMsgAttachment()
+{
+ mTemporary = false;
+ mSendViaCloud = false;
+ mSize = -1;
+}
+
+nsMsgAttachment::~nsMsgAttachment()
+{
+ if (mTemporary && !mSendViaCloud)
+ (void)DeleteAttachment();
+}
+
+/* attribute wstring name; */
+NS_IMETHODIMP nsMsgAttachment::GetName(nsAString & aName)
+{
+ aName = mName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::SetName(const nsAString & aName)
+{
+ mName = aName;
+ return NS_OK;
+}
+
+/* attribute string url; */
+NS_IMETHODIMP nsMsgAttachment::GetUrl(nsACString & aUrl)
+{
+ aUrl = mUrl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::SetUrl(const nsACString & aUrl)
+{
+ mUrl = aUrl;
+ return NS_OK;
+}
+
+/* attribute string urlCharset; */
+NS_IMETHODIMP nsMsgAttachment::GetUrlCharset(nsACString & aUrlCharset)
+{
+ aUrlCharset = mUrlCharset;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgAttachment::SetUrlCharset(const nsACString & aUrlCharset)
+{
+ mUrlCharset = aUrlCharset;
+ return NS_OK;
+}
+
+/* attribute boolean temporary; */
+NS_IMETHODIMP nsMsgAttachment::GetTemporary(bool *aTemporary)
+{
+ NS_ENSURE_ARG_POINTER(aTemporary);
+
+ *aTemporary = mTemporary;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgAttachment::SetTemporary(bool aTemporary)
+{
+ mTemporary = aTemporary;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::GetSendViaCloud(bool *aSendViaCloud)
+{
+ NS_ENSURE_ARG_POINTER(aSendViaCloud);
+
+ *aSendViaCloud = mSendViaCloud;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgAttachment::SetSendViaCloud(bool aSendViaCloud)
+{
+ mSendViaCloud = aSendViaCloud;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::SetHtmlAnnotation(const nsAString &aAnnotation)
+{
+ mHtmlAnnotation = aAnnotation;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::GetHtmlAnnotation(nsAString &aAnnotation)
+{
+ aAnnotation = mHtmlAnnotation;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgAttachment::SetCloudProviderKey(const nsACString &aCloudProviderKey)
+{
+ mCloudProviderKey = aCloudProviderKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgAttachment::GetCloudProviderKey(nsACString &aCloudProviderKey)
+{
+ aCloudProviderKey = mCloudProviderKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::GetContentLocation(nsACString &aContentLocation)
+{
+ aContentLocation = mContentLocation;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::SetContentLocation(const nsACString &aContentLocation)
+{
+ mContentLocation = aContentLocation;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::GetContentType(char * *aContentType)
+{
+ NS_ENSURE_ARG_POINTER(aContentType);
+
+ *aContentType = ToNewCString(mContentType);
+ return (*aContentType ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
+}
+
+NS_IMETHODIMP nsMsgAttachment::SetContentType(const char * aContentType)
+{
+ mContentType = aContentType;
+ /* a full content type could also contains parameters but we need to
+ keep only the content type alone. Therefore we need to cleanup it.
+ */
+ int32_t offset = mContentType.FindChar(';');
+ if (offset >= 0)
+ mContentType.SetLength(offset);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachment::GetContentTypeParam(char * *aContentTypeParam)
+{
+ NS_ENSURE_ARG_POINTER(aContentTypeParam);
+
+ *aContentTypeParam = ToNewCString(mContentTypeParam);
+ return (*aContentTypeParam ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
+}
+
+NS_IMETHODIMP nsMsgAttachment::SetContentTypeParam(const char * aContentTypeParam)
+{
+ if (aContentTypeParam)
+ while (*aContentTypeParam == ';' || *aContentTypeParam == ' ')
+ aContentTypeParam ++;
+ mContentTypeParam = aContentTypeParam;
+
+ return NS_OK;
+}
+
+/* attribute string charset; */
+NS_IMETHODIMP nsMsgAttachment::GetCharset(char * *aCharset)
+{
+ NS_ENSURE_ARG_POINTER(aCharset);
+
+ *aCharset = ToNewCString(mCharset);
+ return (*aCharset ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
+}
+NS_IMETHODIMP nsMsgAttachment::SetCharset(const char * aCharset)
+{
+ mCharset = aCharset;
+ return NS_OK;
+}
+
+/* attribute string macType; */
+NS_IMETHODIMP nsMsgAttachment::GetMacType(char * *aMacType)
+{
+ NS_ENSURE_ARG_POINTER(aMacType);
+
+ *aMacType = ToNewCString(mMacType);
+ return (*aMacType ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
+}
+NS_IMETHODIMP nsMsgAttachment::SetMacType(const char * aMacType)
+{
+ mMacType = aMacType;
+ return NS_OK;
+}
+
+/* attribute string macCreator; */
+NS_IMETHODIMP nsMsgAttachment::GetMacCreator(char * *aMacCreator)
+{
+ NS_ENSURE_ARG_POINTER(aMacCreator);
+
+ *aMacCreator = ToNewCString(mMacCreator);
+ return (*aMacCreator ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
+}
+NS_IMETHODIMP nsMsgAttachment::SetMacCreator(const char * aMacCreator)
+{
+ mMacCreator = aMacCreator;
+ return NS_OK;
+}
+
+/* attribute int64_t size; */
+NS_IMETHODIMP nsMsgAttachment::GetSize(int64_t *aSize)
+{
+ NS_ENSURE_ARG_POINTER(aSize);
+
+ *aSize = mSize;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgAttachment::SetSize(int64_t aSize)
+{
+ mSize = aSize;
+ return NS_OK;
+}
+
+/* boolean equalsUrl (in nsIMsgAttachment attachment); */
+NS_IMETHODIMP nsMsgAttachment::EqualsUrl(nsIMsgAttachment *attachment, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(attachment);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsAutoCString url;
+ attachment->GetUrl(url);
+
+ *_retval = mUrl.Equals(url);
+ return NS_OK;
+}
+
+
+nsresult nsMsgAttachment::DeleteAttachment()
+{
+ nsresult rv;
+ bool isAFile = false;
+
+ nsCOMPtr<nsIFile> urlFile;
+ rv = NS_GetFileFromURLSpec(mUrl, getter_AddRefs(urlFile));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Can't nsIFile from URL string");
+ if (NS_SUCCEEDED(rv))
+ {
+ bool bExists = false;
+ rv = urlFile->Exists(&bExists);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Exists() call failed!");
+ if (NS_SUCCEEDED(rv) && bExists)
+ {
+ rv = urlFile->IsFile(&isAFile);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "IsFile() call failed!");
+ }
+ }
+
+ // remove it if it's a valid file
+ if (isAFile)
+ rv = urlFile->Remove(false);
+
+ return rv;
+}
diff --git a/mailnews/compose/src/nsMsgAttachment.h b/mailnews/compose/src/nsMsgAttachment.h
new file mode 100644
index 000000000..9f0d0a3b0
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAttachment.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsMsgAttachment_H_
+#define _nsMsgAttachment_H_
+
+#include "nsIMsgAttachment.h"
+#include "nsStringGlue.h"
+
+class nsMsgAttachment : public nsIMsgAttachment
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGATTACHMENT
+
+ nsMsgAttachment();
+
+private:
+ virtual ~nsMsgAttachment();
+ nsresult DeleteAttachment();
+
+ nsString mName;
+ nsCString mUrl;
+ nsCString mUrlCharset;
+ bool mTemporary;
+ bool mSendViaCloud;
+ nsCString mCloudProviderKey;
+ nsCString mContentLocation;
+ nsCString mContentType;
+ nsCString mContentTypeParam;
+ nsCString mCharset;
+ nsCString mMacType;
+ nsCString mMacCreator;
+ nsString mHtmlAnnotation;
+ int64_t mSize;
+};
+
+
+#endif /* _nsMsgAttachment_H_ */
diff --git a/mailnews/compose/src/nsMsgAttachmentHandler.cpp b/mailnews/compose/src/nsMsgAttachmentHandler.cpp
new file mode 100644
index 000000000..f36f2e29b
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAttachmentHandler.cpp
@@ -0,0 +1,1383 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIPrefLocalizedString.h"
+#include "nsILineInputStream.h"
+#include "nsMsgAttachmentHandler.h"
+#include "prmem.h"
+#include "nsMsgCopy.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsMsgSend.h"
+#include "nsMsgCompUtils.h"
+#include "nsMsgI18N.h"
+#include "nsURLFetcher.h"
+#include "nsMimeTypes.h"
+#include "nsMsgCompCID.h"
+#include "nsIMsgMessageService.h"
+#include "nsMsgUtils.h"
+#include "nsMsgPrompts.h"
+#include "nsTextFormatter.h"
+#include "nsIPrompt.h"
+#include "nsITextToSubURI.h"
+#include "nsIURL.h"
+#include "nsIFileURL.h"
+#include "nsNetCID.h"
+#include "nsIMimeStreamConverter.h"
+#include "nsMsgMimeCID.h"
+#include "nsNetUtil.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsComposeStrings.h"
+#include "nsIZipWriter.h"
+#include "nsIDirectoryEnumerator.h"
+#include "mozilla/Services.h"
+#include "mozilla/mailnews/MimeEncoder.h"
+#include "nsIPrincipal.h"
+
+///////////////////////////////////////////////////////////////////////////
+// Mac Specific Attachment Handling for AppleDouble Encoded Files
+///////////////////////////////////////////////////////////////////////////
+#ifdef XP_MACOSX
+
+#define AD_WORKING_BUFF_SIZE FILE_IO_BUFFER_SIZE
+
+extern void MacGetFileType(nsIFile *fs, bool *useDefault, char **type, char **encoding);
+
+#include "nsILocalFileMac.h"
+
+/* static */
+nsresult nsSimpleZipper::Zip(nsIFile *aInputFile, nsIFile *aOutputFile)
+{
+ // create zipwriter object
+ nsresult rv;
+ nsCOMPtr<nsIZipWriter> zipWriter = do_CreateInstance("@mozilla.org/zipwriter;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = zipWriter->Open(aOutputFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = AddToZip(zipWriter, aInputFile, EmptyCString());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // we're done.
+ zipWriter->Close();
+ return rv;
+}
+
+/* static */
+nsresult nsSimpleZipper::AddToZip(nsIZipWriter *aZipWriter,
+ nsIFile *aFile,
+ const nsACString &aPath)
+{
+ // find out the path this file/dir should have in the zip
+ nsCString leafName;
+ aFile->GetNativeLeafName(leafName);
+ nsCString currentPath(aPath);
+ currentPath += leafName;
+
+ bool isDirectory;
+ aFile->IsDirectory(&isDirectory);
+ // append slash for a directory entry
+ if (isDirectory)
+ currentPath.Append('/');
+
+ // add the file or directory entry to the zip
+ nsresult rv = aZipWriter->AddEntryFile(currentPath, nsIZipWriter::COMPRESSION_DEFAULT, aFile, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if it's a directory, add all its contents too
+ if (isDirectory) {
+ nsCOMPtr<nsISimpleEnumerator> e;
+ nsresult rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDirectoryEnumerator> dirEnumerator = do_QueryInterface(e, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> currentFile;
+ while (NS_SUCCEEDED(dirEnumerator->GetNextFile(getter_AddRefs(currentFile))) && currentFile) {
+ rv = AddToZip(aZipWriter, currentFile, currentPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ return NS_OK;
+}
+#endif // XP_MACOSX
+
+//
+// Class implementation...
+//
+nsMsgAttachmentHandler::nsMsgAttachmentHandler() :
+ mRequest(nullptr),
+ mCompFields(nullptr), // Message composition fields for the sender
+ m_bogus_attachment(false),
+ m_done(false),
+ m_already_encoded_p(false),
+ m_decrypted_p(false),
+ mDeleteFile(false),
+ mMHTMLPart(false),
+ mPartUserOmissionOverride(false),
+ mMainBody(false),
+ mSendViaCloud(false),
+ mNodeIndex(-1),
+ // For analyzing the attachment file...
+ m_size(0),
+ m_unprintable_count(0),
+ m_highbit_count(0),
+ m_ctl_count(0),
+ m_null_count(0),
+ m_have_cr(0),
+ m_have_lf(0),
+ m_have_crlf(0),
+ m_prev_char_was_cr(false),
+ m_current_column(0),
+ m_max_column(0),
+ m_lines(0),
+ m_file_analyzed(false),
+
+ // Mime
+ m_encoder(nullptr)
+{
+}
+
+nsMsgAttachmentHandler::~nsMsgAttachmentHandler()
+{
+ if (mTmpFile && mDeleteFile)
+ mTmpFile->Remove(false);
+
+ if (mOutFile)
+ mOutFile->Close();
+
+ CleanupTempFile();
+}
+
+NS_IMPL_ISUPPORTS(nsMsgAttachmentHandler, nsIMsgAttachmentHandler)
+
+// nsIMsgAttachmentHandler implementation.
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetType(nsACString& aType)
+{
+ aType.Assign(m_type);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetUri(nsACString& aUri)
+{
+ nsAutoCString turl;
+ if (!mURL)
+ {
+ if (!m_uri.IsEmpty())
+ turl = m_uri;
+ }
+ else
+ {
+ nsresult rv = mURL->GetSpec(turl);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ aUri.Assign(turl);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetTmpFile(nsIFile **aTmpFile)
+{
+ NS_ENSURE_ARG_POINTER(aTmpFile);
+ if (!mTmpFile)
+ return NS_ERROR_FAILURE;
+ NS_ADDREF(*aTmpFile = mTmpFile);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetName(nsACString& aName)
+{
+ aName.Assign(m_realName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetSize(uint32_t *aSize)
+{
+ NS_ENSURE_ARG_POINTER(aSize);
+ *aSize = m_size;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetContentId(nsACString& aContentId)
+{
+ aContentId.Assign(m_contentId);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetSendViaCloud(bool* aSendViaCloud)
+{
+ NS_ENSURE_ARG_POINTER(aSendViaCloud);
+ *aSendViaCloud = mSendViaCloud;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetCharset(nsACString& aCharset)
+{
+ aCharset.Assign(m_charset);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetEncoding(nsACString& aEncoding)
+{
+ aEncoding.Assign(m_encoding);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentHandler::GetAlreadyEncoded(bool* aAlreadyEncoded)
+{
+ NS_ENSURE_ARG_POINTER(aAlreadyEncoded);
+ *aAlreadyEncoded = m_already_encoded_p;
+ return NS_OK;
+}
+
+// Local methods.
+
+void
+nsMsgAttachmentHandler::CleanupTempFile()
+{
+#ifdef XP_MACOSX
+ if (mEncodedWorkingFile) {
+ mEncodedWorkingFile->Remove(false);
+ mEncodedWorkingFile = nullptr;
+ }
+#endif // XP_MACOSX
+}
+
+void
+nsMsgAttachmentHandler::AnalyzeDataChunk(const char *chunk, int32_t 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 != '\r' && *s != '\n')
+ {
+ m_unprintable_count++;
+ m_ctl_count++;
+ if (*s == 0)
+ m_null_count++;
+ }
+
+ if (*s == '\r' || *s == '\n')
+ {
+ if (*s == '\r')
+ {
+ if (m_prev_char_was_cr)
+ m_have_cr = 1;
+ else
+ m_prev_char_was_cr = true;
+ }
+ else
+ {
+ if (m_prev_char_was_cr)
+ {
+ if (m_current_column == 0)
+ {
+ m_have_crlf = 1;
+ m_lines--;
+ }
+ else
+ m_have_cr = m_have_lf = 1;
+ m_prev_char_was_cr = false;
+ }
+ else
+ m_have_lf = 1;
+ }
+ if (m_max_column < m_current_column)
+ m_max_column = m_current_column;
+ m_current_column = 0;
+ m_lines++;
+ }
+ else
+ {
+ m_current_column++;
+ }
+ }
+ // Check one last time for the last line. This is also important if there
+ // is only one line that doesn't terminate in \n.
+ if (m_max_column < m_current_column)
+ m_max_column = m_current_column;
+}
+
+void
+nsMsgAttachmentHandler::AnalyzeSnarfedFile(void)
+{
+ char chunk[1024];
+ uint32_t numRead = 0;
+
+ if (m_file_analyzed)
+ return;
+
+ if (mTmpFile)
+ {
+ int64_t fileSize;
+ mTmpFile->GetFileSize(&fileSize);
+ m_size = (uint32_t) fileSize;
+ nsCOMPtr <nsIInputStream> inputFile;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), mTmpFile);
+ if (NS_FAILED(rv))
+ return;
+ {
+ do
+ {
+ rv = inputFile->Read(chunk, sizeof(chunk), &numRead);
+ if (numRead)
+ AnalyzeDataChunk(chunk, numRead);
+ }
+ while (numRead && NS_SUCCEEDED(rv));
+ if (m_prev_char_was_cr)
+ m_have_cr = 1;
+
+ inputFile->Close();
+ m_file_analyzed = true;
+ }
+ }
+}
+
+//
+// Given a content-type and some info about the contents of the document,
+// decide what encoding it should have.
+//
+nsresult
+nsMsgAttachmentHandler::PickEncoding(const char *charset, nsIMsgSend *mime_delivery_state)
+{
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ bool needsB64 = false;
+ bool forceB64 = false;
+ bool isUsingQP = false;
+
+ if (mSendViaCloud)
+ {
+ m_encoding = ENCODING_7BIT;
+ return NS_OK;
+ }
+ if (m_already_encoded_p)
+ goto DONE;
+
+ AnalyzeSnarfedFile();
+
+ // Allow users to override our percentage-wise guess on whether
+ // the file is text or binary.
+ if (pPrefBranch)
+ pPrefBranch->GetBoolPref ("mail.file_attach_binary", &forceB64);
+
+ // If the content-type is "image/" or something else known to be binary or
+ // several flavors of newlines are present, use base64 unless we're attaching
+ // a message (so that we don't get confused by newline conversions).
+ if (!mMainBody &&
+ (forceB64 || mime_type_requires_b64_p(m_type.get()) ||
+ m_have_cr + m_have_lf + m_have_crlf != 1) &&
+ !m_type.LowerCaseEqualsLiteral(MESSAGE_RFC822))
+ {
+ needsB64 = true;
+ }
+ else
+ {
+ // Otherwise, we need to pick an encoding based on the contents of the
+ // document.
+ bool encode_p;
+ bool force_p = false;
+
+ // Force quoted-printable if the sender does not allow conversion to 7bit.
+ if (mCompFields) {
+ if (mCompFields->GetForceMsgEncoding())
+ force_p = true;
+ } else if (mime_delivery_state) {
+ if (((nsMsgComposeAndSend *)mime_delivery_state)->mCompFields->GetForceMsgEncoding()) {
+ force_p = true;
+ }
+ }
+
+ if (force_p || (m_max_column > LINELENGTH_ENCODING_THRESHOLD)) {
+ encode_p = true;
+ } else if (UseQuotedPrintable() && 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;
+ } else {
+ encode_p = false;
+ }
+
+ // MIME requires a special case that these types never be encoded.
+ if (StringBeginsWith(m_type, NS_LITERAL_CSTRING("message"),
+ nsCaseInsensitiveCStringComparator()) ||
+ StringBeginsWith(m_type, NS_LITERAL_CSTRING("multipart"),
+ nsCaseInsensitiveCStringComparator()))
+ {
+ encode_p = false;
+ if (m_desiredType.LowerCaseEqualsLiteral(TEXT_PLAIN))
+ m_desiredType.Truncate();
+ }
+
+ // If the Mail charset is multibyte, we force it to use Base64 for attachments.
+ if ((!mMainBody && charset && nsMsgI18Nmultibyte_charset(charset)) &&
+ (m_type.LowerCaseEqualsLiteral(TEXT_HTML) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_MDL) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_PLAIN) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_RICHTEXT) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_ENRICHED) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_VCARD) ||
+ m_type.LowerCaseEqualsLiteral(APPLICATION_DIRECTORY) || /* text/x-vcard synonym */
+ m_type.LowerCaseEqualsLiteral(TEXT_CSS) ||
+ m_type.LowerCaseEqualsLiteral(TEXT_JSSS))) {
+ needsB64 = true;
+ } else if (charset && nsMsgI18Nstateful_charset(charset)) {
+ m_encoding = ENCODING_7BIT;
+ } else if (encode_p &&
+ 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) {
+ m_encoding = ENCODING_QUOTED_PRINTABLE;
+ isUsingQP = true;
+ } else if (m_highbit_count > 0) {
+ m_encoding = ENCODING_8BIT;
+ } else {
+ m_encoding = ENCODING_7BIT;
+ }
+ }
+
+ // Always base64 binary data.
+ if (needsB64)
+ m_encoding = ENCODING_BASE64;
+
+ // According to RFC 821 we must always have lines shorter than 998 bytes.
+ // To encode "long lines" use a CTE that will transmit shorter lines.
+ // Switch to base64 if we are not already using "quoted printable".
+
+ // We don't do this for message/rfc822 attachments, since we can't
+ // change the original Content-Transfer-Encoding of the message we're
+ // attaching. We rely on the original message complying with RFC 821,
+ // if it doesn't we won't either. Not ideal.
+ if (!m_type.LowerCaseEqualsLiteral(MESSAGE_RFC822) &&
+ m_max_column > LINELENGTH_ENCODING_THRESHOLD && !isUsingQP)
+ m_encoding = ENCODING_BASE64;
+
+ // Now that we've picked an encoding, initialize the filter.
+ NS_ASSERTION(!m_encoder, "not-null m_encoder");
+ if (m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64))
+ {
+ m_encoder = MimeEncoder::GetBase64Encoder(mime_encoder_output_fn,
+ mime_delivery_state);
+ }
+ else if (m_encoding.LowerCaseEqualsLiteral(ENCODING_QUOTED_PRINTABLE))
+ {
+ m_encoder = MimeEncoder::GetQPEncoder(mime_encoder_output_fn,
+ mime_delivery_state);
+ }
+ else
+ {
+ m_encoder = nullptr;
+ }
+
+ /* 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.IsEmpty() || m_type.LowerCaseEqualsLiteral(UNKNOWN_CONTENT_TYPE))
+ {
+ if (m_already_encoded_p)
+ m_type = APPLICATION_OCTET_STREAM;
+ else if (m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64) ||
+ m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE))
+ m_type = APPLICATION_OCTET_STREAM;
+ else
+ m_type = TEXT_PLAIN;
+ }
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::PickCharset()
+{
+ if (!m_charset.IsEmpty() || !m_type.LowerCaseEqualsLiteral(TEXT_PLAIN))
+ return NS_OK;
+
+ nsCOMPtr<nsIFile> tmpFile =
+ do_QueryInterface(mTmpFile);
+ if (!tmpFile)
+ return NS_OK;
+
+ return MsgDetectCharsetFromFile(tmpFile, m_charset);
+}
+
+static nsresult
+FetcherURLDoneCallback(nsresult aStatus,
+ const nsACString &aContentType,
+ const nsACString &aCharset,
+ int32_t totalSize,
+ const char16_t* aMsg, void *tagData)
+{
+ nsMsgAttachmentHandler *ma = (nsMsgAttachmentHandler *) tagData;
+ NS_ASSERTION(ma != nullptr, "not-null mime attachment");
+
+ if (ma != nullptr)
+ {
+ ma->m_size = totalSize;
+ if (!aContentType.IsEmpty())
+ {
+#ifdef XP_MACOSX
+ //Do not change the type if we are dealing with an encoded (e.g., appledouble or zip) file
+ if (!ma->mEncodedWorkingFile)
+#else
+ // can't send appledouble on non-macs
+ if (!aContentType.EqualsLiteral("multipart/appledouble"))
+#endif
+ ma->m_type = aContentType;
+ }
+
+ if (!aCharset.IsEmpty())
+ ma->m_charset = aCharset;
+
+ return ma->UrlExit(aStatus, aMsg);
+ }
+ else
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::SnarfMsgAttachment(nsMsgCompFields *compFields)
+{
+ nsresult rv = NS_ERROR_INVALID_ARG;
+ nsCOMPtr <nsIMsgMessageService> messageService;
+
+ if (m_uri.Find("-message:", CaseInsensitiveCompare) != -1)
+ {
+ nsCOMPtr <nsIFile> tmpFile;
+ rv = nsMsgCreateTempFile("nsmail.tmp", getter_AddRefs(tmpFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ mTmpFile = do_QueryInterface(tmpFile);
+ mDeleteFile = true;
+ mCompFields = compFields;
+ m_type = MESSAGE_RFC822;
+ m_overrideType = MESSAGE_RFC822;
+ if (!mTmpFile)
+ {
+ rv = NS_ERROR_FAILURE;
+ goto done;
+ }
+
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mOutFile), mTmpFile, -1, 00600);
+ if (NS_FAILED(rv) || !mOutFile)
+ {
+ if (m_mime_delivery_state)
+ {
+ nsCOMPtr<nsIMsgSendReport> sendReport;
+ m_mime_delivery_state->GetSendReport(getter_AddRefs(sendReport));
+ if (sendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithTmpFile(mTmpFile, error_msg);
+ sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ }
+ rv = NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
+ goto done;
+ }
+
+ nsCOMPtr<nsIURLFetcher> fetcher = do_CreateInstance(NS_URLFETCHER_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || !fetcher)
+ {
+ if (NS_SUCCEEDED(rv))
+ rv = NS_ERROR_UNEXPECTED;
+ goto done;
+ }
+
+ rv = fetcher->Initialize(mTmpFile, mOutFile, FetcherURLDoneCallback, this);
+ rv = GetMessageServiceFromURI(m_uri, getter_AddRefs(messageService));
+ if (NS_SUCCEEDED(rv) && messageService)
+ {
+ nsAutoCString uri(m_uri);
+ uri += (uri.FindChar('?') == kNotFound) ? '?' : '&';
+ uri.Append("fetchCompleteMessage=true");
+ nsCOMPtr<nsIStreamListener> strListener;
+ fetcher->QueryInterface(NS_GET_IID(nsIStreamListener), getter_AddRefs(strListener));
+
+ // initialize a new stream converter, that uses the strListener as its input
+ // obtain the input stream listener from the new converter,
+ // and pass the converter's input stream listener to DisplayMessage
+
+ m_mime_parser = do_CreateInstance(NS_MAILNEWS_MIME_STREAM_CONVERTER_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ goto done;
+
+ // Set us as the output stream for HTML data from libmime...
+ nsCOMPtr<nsIMimeStreamConverter> mimeConverter = do_QueryInterface(m_mime_parser);
+ if (mimeConverter)
+ {
+ mimeConverter->SetMimeOutputType(nsMimeOutput::nsMimeMessageDecrypt);
+ mimeConverter->SetForwardInline(false);
+ mimeConverter->SetIdentity(nullptr);
+ mimeConverter->SetOriginalMsgURI(nullptr);
+ }
+
+ nsCOMPtr<nsIStreamListener> convertedListener = do_QueryInterface(m_mime_parser, &rv);
+ if (NS_FAILED(rv))
+ goto done;
+
+ nsCOMPtr<nsIURI> aURL;
+ rv = messageService->GetUrlForUri(uri.get(), getter_AddRefs(aURL), nullptr);
+ if (NS_FAILED(rv))
+ goto done;
+
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateInstance of nullprincipal failed.");
+ if (NS_FAILED(rv))
+ goto done;
+
+ rv = NS_NewInputStreamChannel(getter_AddRefs(m_converter_channel),
+ aURL,
+ nullptr,
+ nullPrincipal,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER);
+ if (NS_FAILED(rv))
+ goto done;
+
+ rv = m_mime_parser->AsyncConvertData("message/rfc822", "message/rfc822",
+ strListener, m_converter_channel);
+ if (NS_FAILED(rv))
+ goto done;
+
+ nsCOMPtr<nsIURI> dummyNull;
+ rv = messageService->DisplayMessage(uri.get(), convertedListener, nullptr, nullptr, nullptr,
+ getter_AddRefs(dummyNull));
+ }
+ }
+done:
+ if (NS_FAILED(rv))
+ {
+ if (mOutFile)
+ {
+ mOutFile->Close();
+ mOutFile = nullptr;
+ }
+
+ if (mTmpFile)
+ {
+ mTmpFile->Remove(false);
+ mTmpFile = nullptr;
+ }
+ }
+
+ return rv;
+}
+
+#ifdef XP_MACOSX
+bool nsMsgAttachmentHandler::HasResourceFork(FSRef *fsRef)
+{
+ FSCatalogInfo catalogInfo;
+ OSErr err = FSGetCatalogInfo(fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes, &catalogInfo, nullptr, nullptr, nullptr);
+ return (err == noErr && catalogInfo.rsrcLogicalSize != 0);
+}
+#endif
+
+nsresult
+nsMsgAttachmentHandler::SnarfAttachment(nsMsgCompFields *compFields)
+{
+ NS_ASSERTION (! m_done, "Already done");
+
+ if (!mURL)
+ return SnarfMsgAttachment(compFields);
+
+ mCompFields = compFields;
+
+ // First, get as file spec and create the stream for the
+ // temp file where we will save this data
+ nsCOMPtr <nsIFile> tmpFile;
+ nsresult rv = nsMsgCreateTempFile("nsmail.tmp", getter_AddRefs(tmpFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ mTmpFile = do_QueryInterface(tmpFile);
+ mDeleteFile = true;
+
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mOutFile), mTmpFile, -1, 00600);
+ if (NS_FAILED(rv) || !mOutFile)
+ {
+ if (m_mime_delivery_state)
+ {
+ nsCOMPtr<nsIMsgSendReport> sendReport;
+ m_mime_delivery_state->GetSendReport(getter_AddRefs(sendReport));
+ if (sendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithTmpFile(mTmpFile, error_msg);
+ sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ }
+ mTmpFile->Remove(false);
+ mTmpFile = nullptr;
+ return NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
+ }
+
+ nsCString sourceURISpec;
+ rv = mURL->GetSpec(sourceURISpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+#ifdef XP_MACOSX
+ if (!m_bogus_attachment && StringBeginsWith(sourceURISpec, NS_LITERAL_CSTRING("file://")))
+ {
+ // Unescape the path (i.e. un-URLify it) before making a FSSpec
+ nsAutoCString filePath;
+ filePath.Adopt(nsMsgGetLocalFileFromURL(sourceURISpec.get()));
+ nsAutoCString unescapedFilePath;
+ MsgUnescapeString(filePath, 0, unescapedFilePath);
+
+ nsCOMPtr<nsIFile> sourceFile;
+ NS_NewNativeLocalFile(unescapedFilePath, true, getter_AddRefs(sourceFile));
+ if (!sourceFile)
+ return NS_ERROR_FAILURE;
+
+ // check if it is a bundle. if it is, we'll zip it.
+ // if not, we'll apple encode it (applesingle or appledouble)
+ nsCOMPtr<nsILocalFileMac> macFile(do_QueryInterface(sourceFile));
+ bool isPackage;
+ macFile->IsPackage(&isPackage);
+ if (isPackage)
+ rv = ConvertToZipFile(macFile);
+ else
+ rv = ConvertToAppleEncoding(sourceURISpec, unescapedFilePath, macFile);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+#endif /* XP_MACOSX */
+
+ //
+ // Ok, here we are, we need to fire the URL off and get the data
+ // in the temp file
+ //
+ // Create a fetcher for the URL attachment...
+
+ nsCOMPtr<nsIURLFetcher> fetcher = do_CreateInstance(NS_URLFETCHER_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || !fetcher)
+ {
+ if (NS_SUCCEEDED(rv))
+ return NS_ERROR_UNEXPECTED;
+ else
+ return rv;
+ }
+
+ return fetcher->FireURLRequest(mURL, mTmpFile, mOutFile, FetcherURLDoneCallback, this);
+}
+
+#ifdef XP_MACOSX
+nsresult
+nsMsgAttachmentHandler::ConvertToZipFile(nsILocalFileMac *aSourceFile)
+{
+ // append ".zip" to the real file name
+ nsAutoCString zippedName;
+ nsresult rv = aSourceFile->GetNativeLeafName(zippedName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ zippedName.AppendLiteral(".zip");
+
+ // create a temporary file that we'll work on
+ nsCOMPtr <nsIFile> tmpFile;
+ rv = nsMsgCreateTempFile(zippedName.get(), getter_AddRefs(tmpFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ mEncodedWorkingFile = do_QueryInterface(tmpFile);
+
+ // point our URL at the zipped temp file
+ NS_NewFileURI(getter_AddRefs(mURL), mEncodedWorkingFile);
+
+ // zip it!
+ rv = nsSimpleZipper::Zip(aSourceFile, mEncodedWorkingFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // set some metadata for this attachment, that will affect the MIME headers.
+ m_type = APPLICATION_ZIP;
+ m_realName = zippedName.get();
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::ConvertToAppleEncoding(const nsCString &aFileURI,
+ const nsCString &aFilePath,
+ nsILocalFileMac *aSourceFile)
+{
+ // convert the apple file to AppleDouble first, and then patch the
+ // address in the url.
+
+ //We need to retrieve the file type and creator...
+
+ char fileInfo[32];
+ OSType type, creator;
+
+ nsresult rv = aSourceFile->GetFileType(&type);
+ if (NS_FAILED(rv))
+ return rv;
+ PR_snprintf(fileInfo, sizeof(fileInfo), "%X", type);
+ m_xMacType = fileInfo;
+
+ rv = aSourceFile->GetFileCreator(&creator);
+ if (NS_FAILED(rv))
+ return rv;
+ PR_snprintf(fileInfo, sizeof(fileInfo), "%X", creator);
+ m_xMacCreator = fileInfo;
+
+ FSRef fsRef;
+ aSourceFile->GetFSRef(&fsRef);
+ bool sendResourceFork = HasResourceFork(&fsRef);
+
+ // if we have a resource fork, check the filename extension, maybe we don't need the resource fork!
+ if (sendResourceFork)
+ {
+ nsCOMPtr<nsIURL> fileUrl(do_CreateInstance(NS_STANDARDURL_CONTRACTID));
+ if (fileUrl)
+ {
+ rv = fileUrl->SetSpec(aFileURI);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString ext;
+ rv = fileUrl->GetFileExtension(ext);
+ if (NS_SUCCEEDED(rv) && !ext.IsEmpty())
+ {
+ sendResourceFork =
+ PL_strcasecmp(ext.get(), "TXT") &&
+ PL_strcasecmp(ext.get(), "JPG") &&
+ PL_strcasecmp(ext.get(), "GIF") &&
+ PL_strcasecmp(ext.get(), "TIF") &&
+ PL_strcasecmp(ext.get(), "HTM") &&
+ PL_strcasecmp(ext.get(), "HTML") &&
+ PL_strcasecmp(ext.get(), "ART") &&
+ PL_strcasecmp(ext.get(), "XUL") &&
+ PL_strcasecmp(ext.get(), "XML") &&
+ PL_strcasecmp(ext.get(), "CSS") &&
+ PL_strcasecmp(ext.get(), "JS");
+ }
+ }
+ }
+ }
+
+ // Only use appledouble if we aren't uuencoding.
+ if( sendResourceFork )
+ {
+ char *separator;
+
+ separator = mime_make_separator("ad");
+ if (!separator)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr <nsIFile> tmpFile;
+ nsresult rv = nsMsgCreateTempFile("appledouble", getter_AddRefs(tmpFile));
+ if (NS_SUCCEEDED(rv))
+ mEncodedWorkingFile = do_QueryInterface(tmpFile);
+ if (!mEncodedWorkingFile)
+ {
+ PR_FREEIF(separator);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ //
+ // RICHIE_MAC - ok, here's the deal, we have a file that we need
+ // to encode in appledouble encoding for the resource fork and put that
+ // into the mEncodedWorkingFile location. Then, we need to patch the new file
+ // spec into the array and send this as part of the 2 part appledouble/mime
+ // encoded mime part.
+ //
+ AppleDoubleEncodeObject *obj = new (AppleDoubleEncodeObject);
+ if (obj == NULL)
+ {
+ mEncodedWorkingFile = nullptr;
+ PR_FREEIF(separator);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = MsgGetFileStream(mEncodedWorkingFile, getter_AddRefs(obj->fileStream));
+ if (NS_FAILED(rv) || !obj->fileStream)
+ {
+ PR_FREEIF(separator);
+ delete obj;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ char *working_buff = (char *) PR_Malloc(AD_WORKING_BUFF_SIZE);
+ if (!working_buff)
+ {
+ PR_FREEIF(separator);
+ delete obj;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ obj->buff = working_buff;
+ obj->s_buff = AD_WORKING_BUFF_SIZE;
+
+ //
+ // Setup all the need information on the apple double encoder.
+ //
+ ap_encode_init(&(obj->ap_encode_obj), aFilePath.get(), separator);
+
+ int32_t count;
+
+ OSErr status = noErr;
+ m_size = 0;
+ while (status == noErr)
+ {
+ status = ap_encode_next(&(obj->ap_encode_obj), obj->buff, obj->s_buff, &count);
+ if (status == noErr || status == errDone)
+ {
+ //
+ // we got the encode data, so call the next stream to write it to the disk.
+ //
+ uint32_t bytesWritten;
+ obj->fileStream->Write(obj->buff, count, &bytesWritten);
+ if (bytesWritten != (uint32_t) count)
+ status = errFileWrite;
+ }
+ }
+
+ ap_encode_end(&(obj->ap_encode_obj), (status >= 0)); // if this is true, ok, false abort
+ if (obj->fileStream)
+ obj->fileStream->Close();
+
+ PR_FREEIF(obj->buff); /* free the working buff. */
+ PR_FREEIF(obj);
+
+ nsCOMPtr <nsIURI> fileURI;
+ NS_NewFileURI(getter_AddRefs(fileURI), mEncodedWorkingFile);
+
+ nsCOMPtr<nsIFileURL> theFileURL = do_QueryInterface(fileURI, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCString newURLSpec;
+ rv = fileURI->GetSpec(newURLSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (newURLSpec.IsEmpty())
+ {
+ PR_FREEIF(separator);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (NS_FAILED(nsMsgNewURL(getter_AddRefs(mURL), newURLSpec.get())))
+ {
+ PR_FREEIF(separator);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Now after conversion, also patch the types.
+ char tmp[128];
+ PR_snprintf(tmp, sizeof(tmp), MULTIPART_APPLEDOUBLE ";\r\n boundary=\"%s\"", separator);
+ PR_FREEIF(separator);
+ m_type = tmp;
+ }
+ else
+ {
+ if ( sendResourceFork )
+ {
+ // For now, just do the encoding, but in the old world we would ask the
+ // user about doing this conversion
+ printf("...we could ask the user about this conversion, but for now, nahh..\n");
+ }
+
+ bool useDefault;
+ char *macType, *macEncoding;
+ if (m_type.IsEmpty() || m_type.LowerCaseEqualsLiteral(TEXT_PLAIN))
+ {
+# define TEXT_TYPE 0x54455854 /* the characters 'T' 'E' 'X' 'T' */
+# define text_TYPE 0x74657874 /* the characters 't' 'e' 'x' 't' */
+
+ if (type != TEXT_TYPE && type != text_TYPE)
+ {
+ MacGetFileType(aSourceFile, &useDefault, &macType, &macEncoding);
+ m_type = macType;
+ }
+ }
+ // don't bother to set the types if we failed in getting the file info.
+ }
+
+ return NS_OK;
+}
+#endif // XP_MACOSX
+
+nsresult
+nsMsgAttachmentHandler::LoadDataFromFile(nsIFile *file, nsString &sigData, bool charsetConversion)
+{
+ int32_t readSize;
+ char *readBuf;
+
+ nsCOMPtr <nsIInputStream> inputFile;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), file);
+ if (NS_FAILED(rv))
+ return NS_MSG_ERROR_WRITING_FILE;
+
+ int64_t fileSize;
+ file->GetFileSize(&fileSize);
+ readSize = (uint32_t) fileSize;
+
+ readBuf = (char *)PR_Malloc(readSize + 1);
+ if (!readBuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+ memset(readBuf, 0, readSize + 1);
+
+ uint32_t bytesRead;
+ inputFile->Read(readBuf, readSize, &bytesRead);
+ inputFile->Close();
+
+ nsDependentCString cstringReadBuf(readBuf, bytesRead);
+ if (charsetConversion)
+ {
+ if (NS_FAILED(ConvertToUnicode(m_charset.get(), cstringReadBuf, sigData)))
+ CopyASCIItoUTF16(cstringReadBuf, sigData);
+ }
+ else
+ CopyASCIItoUTF16(cstringReadBuf, sigData);
+
+ PR_FREEIF(readBuf);
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::Abort()
+{
+ nsCOMPtr<nsIRequest> saveRequest;
+ saveRequest.swap(mRequest);
+
+ if (mTmpFile)
+ {
+ if (mDeleteFile)
+ mTmpFile->Remove(false);
+ mTmpFile = nullptr;
+ }
+
+ NS_ASSERTION(m_mime_delivery_state != nullptr, "not-null m_mime_delivery_state");
+
+ if (m_done)
+ return NS_OK;
+
+ if (saveRequest)
+ return saveRequest->Cancel(NS_ERROR_ABORT);
+ else
+ if (m_mime_delivery_state)
+ {
+ m_mime_delivery_state->SetStatus(NS_ERROR_ABORT);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, NS_ERROR_ABORT, 0, nullptr);
+ }
+ return NS_OK;
+
+}
+
+nsresult
+nsMsgAttachmentHandler::UrlExit(nsresult status, const char16_t* aMsg)
+{
+ NS_ASSERTION(m_mime_delivery_state != nullptr, "not-null m_mime_delivery_state");
+
+ // Close the file, but don't delete the disk file (or the file spec.)
+ if (mOutFile)
+ {
+ mOutFile->Close();
+ mOutFile = nullptr;
+ }
+ // this silliness is because Windows nsIFile caches its file size
+ // so if an output stream writes to it, it will still return the original
+ // cached size.
+ if (mTmpFile)
+ {
+ nsCOMPtr <nsIFile> tmpFile;
+ mTmpFile->Clone(getter_AddRefs(tmpFile));
+ mTmpFile = do_QueryInterface(tmpFile);
+ }
+ mRequest = nullptr;
+
+ // First things first, we are now going to see if this is an HTML
+ // Doc and if it is, we need to see if we can determine the charset
+ // for this part by sniffing the HTML file.
+ // This is needed only when the charset is not set already.
+ // (e.g. a charset may be specified in HTTP header)
+ //
+ if (!m_type.IsEmpty() && m_charset.IsEmpty() &&
+ m_type.LowerCaseEqualsLiteral(TEXT_HTML))
+ m_charset = nsMsgI18NParseMetaCharset(mTmpFile);
+
+ nsresult mimeDeliveryStatus;
+ m_mime_delivery_state->GetStatus(&mimeDeliveryStatus);
+
+ if (mimeDeliveryStatus == NS_ERROR_ABORT)
+ status = NS_ERROR_ABORT;
+
+ // If the attachment is empty, let's call that a failure.
+ if (!m_size)
+ status = NS_ERROR_FAILURE;
+
+ if (NS_FAILED(status) && status != NS_ERROR_ABORT && NS_SUCCEEDED(mimeDeliveryStatus))
+ {
+ // At this point, we should probably ask a question to the user
+ // if we should continue without this attachment.
+ //
+ bool keepOnGoing = true;
+ nsCString turl;
+ nsString msg;
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsMsgDeliverMode mode = nsIMsgSend::nsMsgDeliverNow;
+ m_mime_delivery_state->GetDeliveryMode(&mode);
+ nsCString params;
+ if (!m_realName.IsEmpty())
+ params = m_realName;
+ else if (NS_SUCCEEDED(mURL->GetSpec(turl)) && !turl.IsEmpty())
+ {
+ nsAutoCString unescapedUrl;
+ MsgUnescapeString(turl, 0, unescapedUrl);
+ if (unescapedUrl.IsEmpty())
+ params = turl;
+ else
+ params = unescapedUrl;
+ }
+ else
+ params.AssignLiteral("?");
+
+ NS_ConvertUTF8toUTF16 UTF16params(params);
+ const char16_t* formatParams[] = { UTF16params.get() };
+ if (mode == nsIMsgSend::nsMsgSaveAsDraft || mode == nsIMsgSend::nsMsgSaveAsTemplate)
+ bundle->FormatStringFromName(u"failureOnObjectEmbeddingWhileSaving",
+ formatParams, 1, getter_Copies(msg));
+ else
+ bundle->FormatStringFromName(u"failureOnObjectEmbeddingWhileSending",
+ formatParams, 1, getter_Copies(msg));
+
+ nsCOMPtr<nsIPrompt> aPrompt;
+ if (m_mime_delivery_state)
+ m_mime_delivery_state->GetDefaultPrompt(getter_AddRefs(aPrompt));
+ nsMsgAskBooleanQuestionByString(aPrompt, msg.get(), &keepOnGoing);
+
+ if (keepOnGoing)
+ {
+ status = NS_OK;
+ m_bogus_attachment = true; //That will cause this attachment to be ignored.
+ }
+ else
+ {
+ status = NS_ERROR_ABORT;
+ m_mime_delivery_state->SetStatus(status);
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, nullptr, &ignoreMe);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, status, 0, nullptr);
+ SetMimeDeliveryState(nullptr);
+ return status;
+ }
+ }
+
+ m_done = true;
+
+ //
+ // Ok, now that we have the file here on disk, we need to see if there was
+ // a need to do conversion to plain text...if so, the magic happens here,
+ // otherwise, just move on to other attachments...
+ //
+ if (NS_SUCCEEDED(status) && !m_type.LowerCaseEqualsLiteral(TEXT_PLAIN) &&
+ m_desiredType.LowerCaseEqualsLiteral(TEXT_PLAIN))
+ {
+ //
+ // Conversion to plain text desired.
+ // Now use the converter service here to do the right
+ // thing and convert this data to plain text for us!
+ //
+ nsAutoString conData;
+
+ if (NS_SUCCEEDED(LoadDataFromFile(mTmpFile, conData, true)))
+ {
+ bool flowed, delsp, formatted, disallowBreaks;
+ GetSerialiserFlags(m_charset.get(), &flowed, &delsp, &formatted, &disallowBreaks);
+
+ if (NS_SUCCEEDED(ConvertBufToPlainText(conData, flowed, delsp, formatted, disallowBreaks)))
+ {
+ if (mDeleteFile)
+ mTmpFile->Remove(false);
+
+ nsCOMPtr<nsIOutputStream> outputStream;
+ nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream), mTmpFile,
+ PR_WRONLY | PR_CREATE_FILE, 00600);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString tData;
+ if (NS_FAILED(ConvertFromUnicode(m_charset.get(), conData, tData)))
+ LossyCopyUTF16toASCII(conData, tData);
+ if (!tData.IsEmpty())
+ {
+ uint32_t bytesWritten;
+ (void) outputStream->Write(tData.get(), tData.Length(), &bytesWritten);
+ }
+ outputStream->Close();
+ // this silliness is because Windows nsIFile caches its file size
+ // so if an output stream writes to it, it will still return the original
+ // cached size.
+ if (mTmpFile)
+ {
+ nsCOMPtr <nsIFile> tmpFile;
+ mTmpFile->Clone(getter_AddRefs(tmpFile));
+ mTmpFile = do_QueryInterface(tmpFile);
+ }
+
+ }
+ }
+ }
+
+ m_type = m_desiredType;
+ m_desiredType.Truncate();
+ m_encoding.Truncate();
+ }
+
+ uint32_t pendingAttachmentCount = 0;
+ m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
+ NS_ASSERTION (pendingAttachmentCount > 0, "no more pending attachment");
+
+ m_mime_delivery_state->SetPendingAttachmentCount(pendingAttachmentCount - 1);
+
+ bool processAttachmentsSynchronously = false;
+ m_mime_delivery_state->GetProcessAttachmentsSynchronously(&processAttachmentsSynchronously);
+ if (NS_SUCCEEDED(status) && processAttachmentsSynchronously)
+ {
+ /* Find the next attachment which has not yet been loaded,
+ if any, and start it going.
+ */
+ uint32_t i;
+ nsMsgAttachmentHandler *next = 0;
+ nsTArray<RefPtr<nsMsgAttachmentHandler>> *attachments;
+
+ m_mime_delivery_state->GetAttachmentHandlers(&attachments);
+
+ for (i = 0; i < attachments->Length(); i++)
+ {
+ if (!(*attachments)[i]->m_done)
+ {
+ next = (*attachments)[i];
+ //
+ // rhp: We need to get a little more understanding to failed URL
+ // requests. So, at this point if most of next is NULL, then we
+ // should just mark it fetched and move on! We probably ignored
+ // this earlier on in the send process.
+ //
+ if ( (!next->mURL) && (next->m_uri.IsEmpty()) )
+ {
+ (*attachments)[i]->m_done = true;
+ (*attachments)[i]->SetMimeDeliveryState(nullptr);
+ m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
+ m_mime_delivery_state->SetPendingAttachmentCount(pendingAttachmentCount - 1);
+ next->mPartUserOmissionOverride = true;
+ next = nullptr;
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ if (next)
+ {
+ nsresult status = next->SnarfAttachment(mCompFields);
+ if (NS_FAILED(status))
+ {
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, nullptr, &ignoreMe);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, status, 0, nullptr);
+ SetMimeDeliveryState(nullptr);
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ }
+
+ m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
+ if (pendingAttachmentCount == 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 (NS_FAILED(status))
+ {
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, status, aMsg, nullptr);
+ SetMimeDeliveryState(nullptr);
+ return NS_ERROR_UNEXPECTED;
+ }
+ else
+ {
+ status = m_mime_delivery_state->GatherMimeAttachments ();
+ if (NS_FAILED(status))
+ {
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
+ m_mime_delivery_state->NotifyListenerOnStopSending(nullptr, status, aMsg, nullptr);
+ SetMimeDeliveryState(nullptr);
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ }
+ else
+ {
+ // If this is not the last attachment, but it got an error,
+ // then report that error and continue
+ if (NS_FAILED(status))
+ {
+ nsresult ignoreMe;
+ m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
+ }
+ }
+
+ SetMimeDeliveryState(nullptr);
+ return NS_OK;
+}
+
+
+nsresult
+nsMsgAttachmentHandler::GetMimeDeliveryState(nsIMsgSend** _retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = m_mime_delivery_state;
+ NS_IF_ADDREF(*_retval);
+ return NS_OK;
+}
+
+nsresult
+nsMsgAttachmentHandler::SetMimeDeliveryState(nsIMsgSend* mime_delivery_state)
+{
+ /*
+ Because setting m_mime_delivery_state to null could destroy ourself as
+ m_mime_delivery_state it's our parent, we need to protect ourself against
+ that!
+
+ This extra comptr is necessary,
+ see bug http://bugzilla.mozilla.org/show_bug.cgi?id=78967
+ */
+ nsCOMPtr<nsIMsgSend> temp = m_mime_delivery_state; /* Should lock our parent until the end of the function */
+ m_mime_delivery_state = mime_delivery_state;
+ return NS_OK;
+}
diff --git a/mailnews/compose/src/nsMsgAttachmentHandler.h b/mailnews/compose/src/nsMsgAttachmentHandler.h
new file mode 100644
index 000000000..79e526627
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAttachmentHandler.h
@@ -0,0 +1,194 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsMsgAttachmentHandler_H_
+#define _nsMsgAttachmentHandler_H_
+
+#include "nsIURL.h"
+#include "nsMsgCompFields.h"
+#include "nsIMsgStatusFeedback.h"
+#include "nsIChannel.h"
+#include "nsIMsgSend.h"
+#include "nsIFileStreams.h"
+#include "nsIStreamConverter.h"
+#include "nsAutoPtr.h"
+#include "nsIMsgAttachmentHandler.h"
+
+#ifdef XP_MACOSX
+
+#include "nsMsgAppleDouble.h"
+#include "nsILocalFileMac.h"
+
+class AppleDoubleEncodeObject
+{
+public:
+ appledouble_encode_object ap_encode_obj;
+ char *buff; // the working buff
+ int32_t s_buff; // the working buff size
+ nsCOMPtr <nsIOutputStream> fileStream; // file to hold the encoding
+};
+
+class nsILocalFileMac;
+class nsIZipWriter;
+
+/* Simple utility class that will synchronously zip any file
+ (or folder hierarchy) you give it. */
+class nsSimpleZipper
+{
+ public:
+
+ // Synchronously zips the input file/folder and writes all
+ // data to the output file.
+ static nsresult Zip(nsIFile *aInputFile, nsIFile *aOutputFile);
+
+ private:
+
+ // Recursively adds the file or folder to aZipWriter.
+ static nsresult AddToZip(nsIZipWriter *aZipWriter,
+ nsIFile *aFile,
+ const nsACString &aPath);
+};
+#endif // XP_MACOSX
+
+namespace mozilla {
+namespace mailnews {
+class MimeEncoder;
+}
+}
+
+//
+// This is a class that deals with processing remote attachments. It implements
+// an nsIStreamListener interface to deal with incoming data
+//
+class nsMsgAttachmentHandler : public nsIMsgAttachmentHandler
+{
+
+ typedef mozilla::mailnews::MimeEncoder MimeEncoder;
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIMSGATTACHMENTHANDLER
+
+ nsMsgAttachmentHandler();
+public:
+ nsresult SnarfAttachment(nsMsgCompFields *compFields);
+ nsresult PickEncoding(const char *charset, nsIMsgSend* mime_delivery_state);
+ nsresult PickCharset();
+ void AnalyzeSnarfedFile (); // Analyze a previously-snarfed file.
+ // (Currently only used for plaintext
+ // converted from HTML.)
+ nsresult Abort();
+ nsresult UrlExit(nsresult status, const char16_t* aMsg);
+
+ // if there's an intermediate temp file left, takes care to remove it from disk.
+ //
+ // NOTE: this takes care of the mEncodedWorkingFile temp file, but not mTmpFile which seems
+ // to be used by lots of other classes at the moment.
+ void CleanupTempFile();
+
+private:
+ virtual ~nsMsgAttachmentHandler();
+
+ // use when a message (e.g. original message in a reply) is attached as a rfc822 attachment.
+ nsresult SnarfMsgAttachment(nsMsgCompFields *compFields);
+ bool UseUUEncode_p(void);
+ void AnalyzeDataChunk (const char *chunk, int32_t chunkSize);
+ nsresult LoadDataFromFile(nsIFile *file, nsString &sigData, bool charsetConversion); //A similar function already exist in nsMsgCompose!
+#ifdef XP_MACOSX
+ nsresult ConvertToAppleEncoding(const nsCString &aFileSpecURI,
+ const nsCString &aFilePath,
+ nsILocalFileMac *aSourceFile);
+ // zips this attachment and does the work to make this attachment handler handle it properly.
+ nsresult ConvertToZipFile(nsILocalFileMac *aSourceFile);
+ bool HasResourceFork(FSRef *fsRef);
+#endif
+
+ //
+public:
+ nsCOMPtr<nsIURI> mURL;
+ nsCOMPtr<nsIFile> mTmpFile; // The temp file to which we save it
+ nsCOMPtr<nsIOutputStream> mOutFile;
+ nsCOMPtr<nsIRequest> mRequest; // The live request used while fetching an attachment
+ nsMsgCompFields *mCompFields; // Message composition fields for the sender
+ bool m_bogus_attachment; // This is to catch problem children...
+
+#ifdef XP_MACOSX
+ // if we need to encode this file into for example an appledouble, or zip file,
+ // this file is our working file. currently only needed on mac.
+ nsCOMPtr<nsIFile> mEncodedWorkingFile;
+#endif
+
+ nsCString m_xMacType; // Mac file type
+ nsCString m_xMacCreator; // Mac file creator
+
+ bool m_done;
+ nsCString m_charset; // charset name
+ nsCString m_contentId; // This is for mutipart/related Content-ID's
+ nsCString m_type; // The real type, once we know it.
+ nsCString m_typeParam; // Any addition parameters to add to the content-type (other than charset, macType and maccreator)
+ nsCString m_overrideType; // The type we should assume it to be
+ // or 0, if we should get it from the
+ // server)
+ nsCString m_overrideEncoding; // Goes along with override_type
+
+ nsCString m_desiredType; // The type it should be converted to.
+ nsCString m_description; // For Content-Description header
+ nsCString m_realName; // The name for the headers, if different
+ // from the URL.
+ nsCString m_encoding; // The encoding, once we've decided. */
+ bool m_already_encoded_p; // If we attach a document that is already
+ // encoded, we just pass it through.
+
+ bool m_decrypted_p; /* S/MIME -- when attaching a message that was
+ encrypted, it's necessary to decrypt it first
+ (since nobody but the original recipient can
+ read it -- if you forward it to someone in the
+ raw, it will be useless to them.) This flag
+ indicates whether decryption occurred, so that
+ libmsg can issue appropriate warnings about
+ doing a cleartext forward of a message that was
+ originally encrypted. */
+
+ bool mDeleteFile; // If this is true, Delete the file...its
+ // NOT the original file!
+
+ bool mMHTMLPart; // This is true if its an MHTML part, otherwise, false
+ bool mPartUserOmissionOverride; // This is true if the user send send the email without this part
+ bool mMainBody; // True if this is a main body.
+ // true if this should be sent as a link to a file.
+ bool mSendViaCloud;
+ nsString mHtmlAnnotation;
+ nsCString mCloudProviderKey;
+ nsCString mCloudUrl;
+ int32_t mNodeIndex; //If this is an embedded image, this is the index of the
+ // corresponding domNode in the editor's
+ //GetEmbeddedObjects. Otherwise, it will be -1.
+ //
+ // Vars for analyzing file data...
+ //
+ uint32_t m_size; /* Some state used while filtering it */
+ uint32_t m_unprintable_count;
+ uint32_t m_highbit_count;
+ uint32_t m_ctl_count;
+ uint32_t m_null_count;
+ uint8_t m_have_cr, m_have_lf, m_have_crlf;
+ bool m_prev_char_was_cr;
+ uint32_t m_current_column;
+ uint32_t m_max_column;
+ uint32_t m_lines;
+ bool m_file_analyzed;
+
+ nsAutoPtr<MimeEncoder> m_encoder;
+ nsCString m_uri; // original uri string
+
+ nsresult GetMimeDeliveryState(nsIMsgSend** _retval);
+ nsresult SetMimeDeliveryState(nsIMsgSend* mime_delivery_state);
+private:
+ nsCOMPtr<nsIMsgSend> m_mime_delivery_state;
+ nsCOMPtr<nsIStreamConverter> m_mime_parser;
+ nsCOMPtr<nsIChannel> m_converter_channel;
+};
+
+
+#endif /* _nsMsgAttachmentHandler_H_ */
diff --git a/mailnews/compose/src/nsMsgCompFields.cpp b/mailnews/compose/src/nsMsgCompFields.cpp
new file mode 100644
index 000000000..c65e6ca17
--- /dev/null
+++ b/mailnews/compose/src/nsMsgCompFields.cpp
@@ -0,0 +1,693 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgCompose.h"
+#include "nsMsgCompFields.h"
+#include "nsMsgI18N.h"
+#include "nsMsgCompUtils.h"
+#include "nsMsgUtils.h"
+#include "prmem.h"
+#include "nsIFileChannel.h"
+#include "nsIMsgAttachment.h"
+#include "nsIMsgMdnGenerator.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMsgMimeCID.h"
+#include "nsArrayEnumerator.h"
+#include "nsMemory.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+
+using namespace mozilla::mailnews;
+
+struct HeaderInfo {
+ /// Header name
+ const char *mName;
+ /// If true, nsMsgCompFields should reflect the raw header value instead of
+ /// the unstructured header value.
+ bool mStructured;
+};
+
+// This is a mapping of the m_headers local set to the actual header name we
+// store on the structured header object.
+static HeaderInfo kHeaders[] = {
+ { "From", true },
+ { "Reply-To", true },
+ { "To", true },
+ { "Cc", true },
+ { "Bcc", true },
+ { nullptr, false }, // FCC
+ { nullptr, false }, // FCC2
+ { "Newsgroups", true },
+ { "Followup-To", true },
+ { "Subject", false },
+ { "Organization", false },
+ { "References", true },
+ { "X-Mozilla-News-Host", false },
+ { "X-Priority", false },
+ { nullptr, false }, // CHARACTER_SET
+ { "Message-Id", true },
+ { "X-Template", true },
+ { nullptr, false }, // DRAFT_ID
+ { "Content-Language", true },
+ { nullptr, false } // CREATOR IDENTITY KEY
+};
+
+static_assert(MOZ_ARRAY_LENGTH(kHeaders) ==
+ nsMsgCompFields::MSG_MAX_HEADERS,
+ "These two arrays need to be kept in sync or bad things will happen!");
+
+NS_IMPL_ISUPPORTS(nsMsgCompFields, nsIMsgCompFields, msgIStructuredHeaders,
+ msgIWritableStructuredHeaders)
+
+nsMsgCompFields::nsMsgCompFields()
+: mStructuredHeaders(do_CreateInstance(NS_ISTRUCTUREDHEADERS_CONTRACTID))
+{
+ m_body.Truncate();
+
+ m_attachVCard = false;
+ m_forcePlainText = false;
+ m_useMultipartAlternative = false;
+ m_returnReceipt = false;
+ m_receiptHeaderType = nsIMsgMdnGenerator::eDntType;
+ m_DSN = false;
+ m_bodyIsAsciiOnly = false;
+ m_forceMsgEncoding = false;
+ m_needToCheckCharset = true;
+ m_attachmentReminder = false;
+ m_deliveryFormat = nsIMsgCompSendFormat::AskUser;
+
+ // Get the default charset from pref, use this as a mail charset.
+ nsString charset;
+ NS_GetLocalizedUnicharPreferenceWithDefault(nullptr, "mailnews.send_default_charset",
+ NS_LITERAL_STRING("UTF-8"), charset);
+
+ LossyCopyUTF16toASCII(charset, m_DefaultCharacterSet); // Charsets better be ASCII
+ SetCharacterSet(m_DefaultCharacterSet.get());
+}
+
+nsMsgCompFields::~nsMsgCompFields()
+{
+}
+
+nsresult nsMsgCompFields::SetAsciiHeader(MsgHeaderID header, const char *value)
+{
+ NS_ASSERTION(header >= 0 && header < MSG_MAX_HEADERS,
+ "Invalid message header index!");
+
+ // If we are storing this on the structured header object, we need to set the
+ // value on that object as well. Note that the value may be null, which we'll
+ // take as an attempt to delete the header.
+ const char *headerName = kHeaders[header].mName;
+ if (headerName)
+ {
+ if (!value || !*value)
+ return mStructuredHeaders->DeleteHeader(headerName);
+
+ return mStructuredHeaders->SetRawHeader(headerName,
+ nsDependentCString(value), "UTF-8");
+ }
+
+ // Not on the structurd header object, so save it locally.
+ m_headers[header] = value;
+
+ return NS_OK;
+}
+
+const char* nsMsgCompFields::GetAsciiHeader(MsgHeaderID header)
+{
+ NS_ASSERTION(header >= 0 && header < MSG_MAX_HEADERS,
+ "Invalid message header index!");
+
+ const char *headerName = kHeaders[header].mName;
+ if (headerName)
+ {
+ // We may be out of sync with the structured header object. Retrieve the
+ // header value.
+ if (kHeaders[header].mStructured)
+ {
+ mStructuredHeaders->GetRawHeader(headerName, m_headers[header]);
+ }
+ else
+ {
+ nsString value;
+ mStructuredHeaders->GetUnstructuredHeader(headerName, value);
+ CopyUTF16toUTF8(value, m_headers[header]);
+ }
+ }
+
+ return m_headers[header].get();
+}
+
+nsresult nsMsgCompFields::SetUnicodeHeader(MsgHeaderID header, const nsAString& value)
+{
+ return SetAsciiHeader(header, NS_ConvertUTF16toUTF8(value).get());
+}
+
+nsresult nsMsgCompFields::GetUnicodeHeader(MsgHeaderID header, nsAString& aResult)
+{
+ CopyUTF8toUTF16(nsDependentCString(GetAsciiHeader(header)), aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetFrom(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_FROM_HEADER_ID, value);
+}
+
+
+NS_IMETHODIMP nsMsgCompFields::GetFrom(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_FROM_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetReplyTo(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_REPLY_TO_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetReplyTo(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_REPLY_TO_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetTo(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_TO_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetTo(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_TO_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetCc(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_CC_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetCc(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_CC_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetBcc(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_BCC_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetBcc(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_BCC_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetFcc(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_FCC_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetFcc(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_FCC_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetFcc2(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_FCC2_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetFcc2(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_FCC2_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetNewsgroups(const nsAString &aValue)
+{
+ return SetUnicodeHeader(MSG_NEWSGROUPS_HEADER_ID, aValue);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetNewsgroups(nsAString &aGroup)
+{
+ return GetUnicodeHeader(MSG_NEWSGROUPS_HEADER_ID, aGroup);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetFollowupTo(const nsAString &aValue)
+{
+ return SetUnicodeHeader(MSG_FOLLOWUP_TO_HEADER_ID, aValue);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetFollowupTo(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_FOLLOWUP_TO_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetHasRecipients(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = NS_SUCCEEDED(mime_sanity_check_fields_recipients(
+ GetTo(), GetCc(), GetBcc(), GetNewsgroups()));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetCreatorIdentityKey(const char *value)
+{
+ return SetAsciiHeader(MSG_CREATOR_IDENTITY_KEY_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetCreatorIdentityKey(char **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = strdup(GetAsciiHeader(MSG_CREATOR_IDENTITY_KEY_ID));
+ return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetSubject(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_SUBJECT_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetSubject(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_SUBJECT_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetOrganization(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_ORGANIZATION_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetOrganization(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_ORGANIZATION_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetReferences(const char *value)
+{
+ return SetAsciiHeader(MSG_REFERENCES_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetReferences(char **_retval)
+{
+ *_retval = strdup(GetAsciiHeader(MSG_REFERENCES_HEADER_ID));
+ return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetNewspostUrl(const char *value)
+{
+ return SetAsciiHeader(MSG_NEWSPOSTURL_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetNewspostUrl(char **_retval)
+{
+ *_retval = strdup(GetAsciiHeader(MSG_NEWSPOSTURL_HEADER_ID));
+ return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetPriority(const char *value)
+{
+ return SetAsciiHeader(MSG_PRIORITY_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetPriority(char **_retval)
+{
+ *_retval = strdup(GetAsciiHeader(MSG_PRIORITY_HEADER_ID));
+ return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetCharacterSet(const char *value)
+{
+ return SetAsciiHeader(MSG_CHARACTER_SET_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetCharacterSet(char **_retval)
+{
+ *_retval = strdup(GetAsciiHeader(MSG_CHARACTER_SET_HEADER_ID));
+ return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetMessageId(const char *value)
+{
+ return SetAsciiHeader(MSG_MESSAGE_ID_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetMessageId(char **_retval)
+{
+ *_retval = strdup(GetAsciiHeader(MSG_MESSAGE_ID_HEADER_ID));
+ return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetTemplateName(const nsAString &value)
+{
+ return SetUnicodeHeader(MSG_X_TEMPLATE_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetTemplateName(nsAString &_retval)
+{
+ return GetUnicodeHeader(MSG_X_TEMPLATE_HEADER_ID, _retval);
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetDraftId(const char *value)
+{
+ return SetAsciiHeader(MSG_DRAFT_ID_HEADER_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetDraftId(char **_retval)
+{
+ *_retval = strdup(GetAsciiHeader(MSG_DRAFT_ID_HEADER_ID));
+ return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetReturnReceipt(bool value)
+{
+ m_returnReceipt = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetReturnReceipt(bool *_retval)
+{
+ *_retval = m_returnReceipt;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetReceiptHeaderType(int32_t value)
+{
+ m_receiptHeaderType = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetReceiptHeaderType(int32_t *_retval)
+{
+ *_retval = m_receiptHeaderType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetDSN(bool value)
+{
+ m_DSN = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetDSN(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = m_DSN;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetAttachVCard(bool value)
+{
+ m_attachVCard = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetAttachVCard(bool *_retval)
+{
+ *_retval = m_attachVCard;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetAttachmentReminder(bool *_retval)
+{
+ *_retval = m_attachmentReminder;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetAttachmentReminder(bool value)
+{
+ m_attachmentReminder = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetDeliveryFormat(int32_t value)
+{
+ switch (value) {
+ case nsIMsgCompSendFormat::AskUser:
+ case nsIMsgCompSendFormat::PlainText:
+ case nsIMsgCompSendFormat::HTML:
+ case nsIMsgCompSendFormat::Both:
+ m_deliveryFormat = value;
+ break;
+ default:
+ m_deliveryFormat = nsIMsgCompSendFormat::AskUser;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetDeliveryFormat(int32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = m_deliveryFormat;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetContentLanguage(const char *value)
+{
+ return SetAsciiHeader(MSG_CONTENT_LANGUAGE_ID, value);
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetContentLanguage(char **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = strdup(GetAsciiHeader(MSG_CONTENT_LANGUAGE_ID));
+ return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetForcePlainText(bool value)
+{
+ m_forcePlainText = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetForcePlainText(bool *_retval)
+{
+ *_retval = m_forcePlainText;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetForceMsgEncoding(bool value)
+{
+ m_forceMsgEncoding = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetForceMsgEncoding(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = m_forceMsgEncoding;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetUseMultipartAlternative(bool value)
+{
+ m_useMultipartAlternative = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetUseMultipartAlternative(bool *_retval)
+{
+ *_retval = m_useMultipartAlternative;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetBodyIsAsciiOnly(bool value)
+{
+ m_bodyIsAsciiOnly = value;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetBodyIsAsciiOnly(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = m_bodyIsAsciiOnly;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetBody(const nsAString &value)
+{
+ CopyUTF16toUTF8(value, m_body);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetBody(nsAString &_retval)
+{
+ CopyUTF8toUTF16(m_body, _retval);
+ return NS_OK;
+}
+
+nsresult nsMsgCompFields::SetBody(const char *value)
+{
+ if (value)
+ m_body = value;
+ else
+ m_body.Truncate();
+ return NS_OK;
+}
+
+const char* nsMsgCompFields::GetBody()
+{
+ return m_body.get();
+}
+
+/* readonly attribute nsISimpleEnumerator attachmentsArray; */
+NS_IMETHODIMP nsMsgCompFields::GetAttachments(nsISimpleEnumerator * *aAttachmentsEnum)
+{
+ return aAttachmentsEnum ? NS_NewArrayEnumerator(aAttachmentsEnum, m_attachments) : NS_ERROR_NULL_POINTER;
+}
+
+/* void addAttachment (in nsIMsgAttachment attachment); */
+NS_IMETHODIMP nsMsgCompFields::AddAttachment(nsIMsgAttachment *attachment)
+{
+ int32_t attachmentCount = m_attachments.Count();
+
+ //Don't add twice the same attachment.
+ nsCOMPtr<nsIMsgAttachment> element;
+ bool sameUrl;
+ for (int32_t i = 0; i < attachmentCount; i ++)
+ {
+ m_attachments[i]->EqualsUrl(attachment, &sameUrl);
+ if (sameUrl)
+ return NS_OK;
+ }
+ m_attachments.AppendObject(attachment);
+
+ return NS_OK;
+}
+
+/* void removeAttachment (in nsIMsgAttachment attachment); */
+NS_IMETHODIMP nsMsgCompFields::RemoveAttachment(nsIMsgAttachment *attachment)
+{
+ int32_t attachmentCount = m_attachments.Count();
+
+ nsCOMPtr<nsIMsgAttachment> element;
+ bool sameUrl;
+ for (int32_t i = 0; i < attachmentCount; i ++)
+ {
+ m_attachments[i]->EqualsUrl(attachment, &sameUrl);
+ if (sameUrl)
+ {
+ m_attachments.RemoveObjectAt(i);
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+/* void removeAttachments (); */
+NS_IMETHODIMP nsMsgCompFields::RemoveAttachments()
+{
+ m_attachments.Clear();
+
+ return NS_OK;
+}
+
+
+// This method is called during the creation of a new window.
+NS_IMETHODIMP
+nsMsgCompFields::SplitRecipients(const nsAString &aRecipients,
+ bool aEmailAddressOnly,
+ uint32_t *aLength,
+ char16_t*** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aLength);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aLength = 0;
+ *aResult = nullptr;
+
+ nsCOMArray<msgIAddressObject> header(EncodedHeader(NS_ConvertUTF16toUTF8(aRecipients)));
+ nsTArray<nsString> results;
+ if (aEmailAddressOnly)
+ ExtractEmails(header, results);
+ else
+ ExtractDisplayAddresses(header, results);
+
+ uint32_t count = results.Length();
+ char16_t **result = (char16_t **)NS_Alloc(sizeof(char16_t *) * count);
+ for (uint32_t i = 0; i < count; ++i)
+ result[i] = ToNewUnicode(results[i]);
+
+ *aResult = result;
+ *aLength = count;
+ return NS_OK;
+}
+
+
+// This method is called during the sending of message from nsMsgCompose::CheckAndPopulateRecipients()
+nsresult nsMsgCompFields::SplitRecipientsEx(const nsAString &recipients,
+ nsTArray<nsMsgRecipient> &aResult)
+{
+ nsTArray<nsString> names, addresses;
+ ExtractAllAddresses(EncodedHeader(NS_ConvertUTF16toUTF8(recipients)), names,
+ addresses);
+
+ uint32_t numAddresses = names.Length();
+ for (uint32_t i = 0; i < numAddresses; ++i)
+ {
+ nsMsgRecipient msgRecipient;
+ msgRecipient.mEmail = addresses[i];
+ msgRecipient.mName = names[i];
+ aResult.AppendElement(msgRecipient);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::ConvertBodyToPlainText()
+{
+ nsresult rv = NS_OK;
+
+ if (!m_body.IsEmpty())
+ {
+ nsAutoString body;
+ rv = GetBody(body);
+ if (NS_SUCCEEDED(rv))
+ {
+ bool flowed, delsp, formatted, disallowBreaks;
+ GetSerialiserFlags(GetCharacterSet(), &flowed, &delsp, &formatted, &disallowBreaks);
+ rv = ConvertBufToPlainText(body, flowed, delsp, formatted, disallowBreaks);
+ if (NS_SUCCEEDED(rv))
+ rv = SetBody(body);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetSecurityInfo(nsISupports ** aSecurityInfo)
+{
+ NS_ENSURE_ARG_POINTER(aSecurityInfo);
+ *aSecurityInfo = mSecureCompFields;
+ NS_IF_ADDREF(*aSecurityInfo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetSecurityInfo(nsISupports * aSecurityInfo)
+{
+ mSecureCompFields = aSecurityInfo;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetDefaultCharacterSet(char * *aDefaultCharacterSet)
+{
+ NS_ENSURE_ARG_POINTER(aDefaultCharacterSet);
+ *aDefaultCharacterSet = ToNewCString(m_DefaultCharacterSet);
+ return *aDefaultCharacterSet ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompFields::GetNeedToCheckCharset(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = m_needToCheckCharset;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompFields::SetNeedToCheckCharset(bool aCheck)
+{
+ m_needToCheckCharset = aCheck;
+ return NS_OK;
+}
diff --git a/mailnews/compose/src/nsMsgCompFields.h b/mailnews/compose/src/nsMsgCompFields.h
new file mode 100644
index 000000000..ecc562499
--- /dev/null
+++ b/mailnews/compose/src/nsMsgCompFields.h
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MsgCompFields_H_
+#define _MsgCompFields_H_
+
+#include "nsIMsgCompFields.h"
+#include "msgCore.h"
+#include "nsIAbCard.h"
+#include "nsIAbDirectory.h"
+#include "nsTArray.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+
+struct nsMsgRecipient
+{
+ nsString mName;
+ nsString mEmail;
+ nsCOMPtr<nsIAbCard> mCard;
+ nsCOMPtr<nsIAbDirectory> mDirectory;
+};
+
+/* Note that all the "Get" methods never return NULL (except in case of serious
+ error, like an illegal parameter); rather, they return "" if things were set
+ to NULL. This makes it real handy for the callers. */
+
+class nsMsgCompFields : public nsIMsgCompFields {
+public:
+ nsMsgCompFields();
+
+ /* this macro defines QueryInterface, AddRef and Release for this class */
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_MSGISTRUCTUREDHEADERS(mStructuredHeaders->)
+ NS_FORWARD_MSGIWRITABLESTRUCTUREDHEADERS(mStructuredHeaders->)
+ NS_DECL_NSIMSGCOMPFIELDS
+
+ // Allow the C++ utility methods for people who use a concrete class instead
+ // of the interfaces.
+ using msgIStructuredHeaders::GetAddressingHeader;
+ using msgIWritableStructuredHeaders::SetAddressingHeader;
+
+ typedef enum MsgHeaderID
+ {
+ MSG_FROM_HEADER_ID = 0,
+ MSG_REPLY_TO_HEADER_ID,
+ MSG_TO_HEADER_ID,
+ MSG_CC_HEADER_ID,
+ MSG_BCC_HEADER_ID,
+ MSG_FCC_HEADER_ID,
+ MSG_FCC2_HEADER_ID,
+ MSG_NEWSGROUPS_HEADER_ID,
+ MSG_FOLLOWUP_TO_HEADER_ID,
+ MSG_SUBJECT_HEADER_ID,
+ MSG_ORGANIZATION_HEADER_ID,
+ MSG_REFERENCES_HEADER_ID,
+ MSG_NEWSPOSTURL_HEADER_ID,
+ MSG_PRIORITY_HEADER_ID,
+ MSG_CHARACTER_SET_HEADER_ID,
+ MSG_MESSAGE_ID_HEADER_ID,
+ MSG_X_TEMPLATE_HEADER_ID,
+ MSG_DRAFT_ID_HEADER_ID,
+ MSG_CONTENT_LANGUAGE_ID,
+ MSG_CREATOR_IDENTITY_KEY_ID,
+
+ MSG_MAX_HEADERS //Must be the last one.
+ } MsgHeaderID;
+
+ nsresult SetAsciiHeader(MsgHeaderID header, const char *value);
+ const char* GetAsciiHeader(MsgHeaderID header); //just return the address of the internal header variable, don't dispose it
+
+ nsresult SetUnicodeHeader(MsgHeaderID header, const nsAString &value);
+ nsresult GetUnicodeHeader(MsgHeaderID header, nsAString &_retval);
+
+ /* Convenience routines to get and set header's value...
+
+ IMPORTANT:
+ all routines const char* GetXxx(void) will return a pointer to the header, please don't free it.
+ */
+
+ nsresult SetFrom(const char *value) {return SetAsciiHeader(MSG_FROM_HEADER_ID, value);}
+ const char* GetFrom(void) {return GetAsciiHeader(MSG_FROM_HEADER_ID);}
+
+ nsresult SetReplyTo(const char *value) {return SetAsciiHeader(MSG_REPLY_TO_HEADER_ID, value);}
+ const char* GetReplyTo() {return GetAsciiHeader(MSG_REPLY_TO_HEADER_ID);}
+
+ nsresult SetTo(const char *value) {return SetAsciiHeader(MSG_TO_HEADER_ID, value);}
+ const char* GetTo() {return GetAsciiHeader(MSG_TO_HEADER_ID);}
+
+ nsresult SetCc(const char *value) {return SetAsciiHeader(MSG_CC_HEADER_ID, value);}
+ const char* GetCc() {return GetAsciiHeader(MSG_CC_HEADER_ID);}
+
+ nsresult SetBcc(const char *value) {return SetAsciiHeader(MSG_BCC_HEADER_ID, value);}
+ const char* GetBcc() {return GetAsciiHeader(MSG_BCC_HEADER_ID);}
+
+ nsresult SetFcc(const char *value) {return SetAsciiHeader(MSG_FCC_HEADER_ID, value);}
+ const char* GetFcc() {return GetAsciiHeader(MSG_FCC_HEADER_ID);}
+
+ nsresult SetFcc2(const char *value) {return SetAsciiHeader(MSG_FCC2_HEADER_ID, value);}
+ const char* GetFcc2() {return GetAsciiHeader(MSG_FCC2_HEADER_ID);}
+
+ nsresult SetNewsgroups(const char *aValue) {return SetAsciiHeader(MSG_NEWSGROUPS_HEADER_ID, aValue);}
+ const char* GetNewsgroups() {return GetAsciiHeader(MSG_NEWSGROUPS_HEADER_ID);}
+
+ nsresult SetFollowupTo(const char *aValue) {return SetAsciiHeader(MSG_FOLLOWUP_TO_HEADER_ID, aValue);}
+ const char* GetFollowupTo() {return GetAsciiHeader(MSG_FOLLOWUP_TO_HEADER_ID);}
+
+ nsresult SetSubject(const char *value) {return SetAsciiHeader(MSG_SUBJECT_HEADER_ID, value);}
+ const char* GetSubject() {return GetAsciiHeader(MSG_SUBJECT_HEADER_ID);}
+
+ nsresult SetOrganization(const char *value) {return SetAsciiHeader(MSG_ORGANIZATION_HEADER_ID, value);}
+ const char* GetOrganization() {return GetAsciiHeader(MSG_ORGANIZATION_HEADER_ID);}
+
+ const char* GetReferences() {return GetAsciiHeader(MSG_REFERENCES_HEADER_ID);}
+
+ const char* GetNewspostUrl() {return GetAsciiHeader(MSG_NEWSPOSTURL_HEADER_ID);}
+
+ const char* GetPriority() {return GetAsciiHeader(MSG_PRIORITY_HEADER_ID);}
+
+ const char* GetCharacterSet() {return GetAsciiHeader(MSG_CHARACTER_SET_HEADER_ID);}
+
+ const char* GetMessageId() {return GetAsciiHeader(MSG_MESSAGE_ID_HEADER_ID);}
+
+ nsresult SetTemplateName(const char *value) {return SetAsciiHeader(MSG_X_TEMPLATE_HEADER_ID, value);}
+ const char* GetTemplateName() {return GetAsciiHeader(MSG_X_TEMPLATE_HEADER_ID);}
+
+ const char* GetDraftId() {return GetAsciiHeader(MSG_DRAFT_ID_HEADER_ID);}
+
+ const char* GetContentLanguage() {return GetAsciiHeader(MSG_CONTENT_LANGUAGE_ID);}
+
+ bool GetReturnReceipt() {return m_returnReceipt;}
+ bool GetDSN() {return m_DSN;}
+ bool GetAttachVCard() {return m_attachVCard;}
+ bool GetAttachmentReminder() {return m_attachmentReminder;}
+ int32_t GetDeliveryFormat() {return m_deliveryFormat;}
+ bool GetForcePlainText() {return m_forcePlainText;}
+ bool GetUseMultipartAlternative() {return m_useMultipartAlternative;}
+ bool GetBodyIsAsciiOnly() {return m_bodyIsAsciiOnly;}
+ bool GetForceMsgEncoding() {return m_forceMsgEncoding;}
+
+ nsresult SetBody(const char *value);
+ const char* GetBody();
+
+ nsresult SplitRecipientsEx(const nsAString &recipients,
+ nsTArray<nsMsgRecipient> &aResult);
+
+protected:
+ virtual ~nsMsgCompFields();
+ nsCString m_headers[MSG_MAX_HEADERS];
+ nsCString m_body;
+ nsCOMArray<nsIMsgAttachment> m_attachments;
+ bool m_attachVCard;
+ bool m_attachmentReminder;
+ int32_t m_deliveryFormat;
+ bool m_forcePlainText;
+ bool m_useMultipartAlternative;
+ bool m_returnReceipt;
+ bool m_DSN;
+ bool m_bodyIsAsciiOnly;
+ bool m_forceMsgEncoding;
+ int32_t m_receiptHeaderType; /* receipt header type */
+ nsCString m_DefaultCharacterSet;
+ bool m_needToCheckCharset;
+
+ nsCOMPtr<nsISupports> mSecureCompFields;
+ nsCOMPtr<msgIWritableStructuredHeaders> mStructuredHeaders;
+};
+
+
+#endif /* _MsgCompFields_H_ */
diff --git a/mailnews/compose/src/nsMsgCompUtils.cpp b/mailnews/compose/src/nsMsgCompUtils.cpp
new file mode 100644
index 000000000..4615f0f36
--- /dev/null
+++ b/mailnews/compose/src/nsMsgCompUtils.cpp
@@ -0,0 +1,1803 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsCOMPtr.h"
+#include "nsMsgCompUtils.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "prmem.h"
+#include "nsMsgSend.h"
+#include "nsIIOService.h"
+#include "nsIHttpProtocolHandler.h"
+#include "nsMailHeaders.h"
+#include "nsMsgI18N.h"
+#include "nsINntpService.h"
+#include "nsMimeTypes.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIURI.h"
+#include "nsNetCID.h"
+#include "nsMsgPrompts.h"
+#include "nsMsgUtils.h"
+#include "nsCExternalHandlerService.h"
+#include "nsIMIMEService.h"
+#include "nsComposeStrings.h"
+#include "nsIMsgCompUtils.h"
+#include "nsIMsgMdnGenerator.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMemory.h"
+#include "nsCRTGlue.h"
+#include <ctype.h>
+#include "mozilla/mailnews/Services.h"
+#include "mozilla/Services.h"
+#include "nsIMIMEInfo.h"
+#include "nsIMsgHeaderParser.h"
+#include "nsIRandomGenerator.h"
+#include "nsID.h"
+
+NS_IMPL_ISUPPORTS(nsMsgCompUtils, nsIMsgCompUtils)
+
+nsMsgCompUtils::nsMsgCompUtils()
+{
+}
+
+nsMsgCompUtils::~nsMsgCompUtils()
+{
+}
+
+NS_IMETHODIMP nsMsgCompUtils::MimeMakeSeparator(const char *prefix,
+ char **_retval)
+{
+ NS_ENSURE_ARG_POINTER(prefix);
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mime_make_separator(prefix);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompUtils::MsgGenerateMessageId(nsIMsgIdentity *identity,
+ char **_retval)
+{
+ NS_ENSURE_ARG_POINTER(identity);
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = msg_generate_message_id(identity);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompUtils::GetMsgMimeConformToStandard(bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nsMsgMIMEGetConformToStandard();
+ return NS_OK;
+}
+
+//
+// Create a file for the a unique temp file
+// on the local machine. Caller must free memory
+//
+nsresult
+nsMsgCreateTempFile(const char *tFileName, nsIFile **tFile)
+{
+ if ((!tFileName) || (!*tFileName))
+ tFileName = "nsmail.tmp";
+
+ nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+ tFileName,
+ tFile);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = (*tFile)->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
+ if (NS_FAILED(rv))
+ NS_RELEASE(*tFile);
+
+ return rv;
+}
+
+//
+// Create a file spec for the a unique temp file
+// on the local machine. Caller must free memory
+// returned
+//
+char *
+nsMsgCreateTempFileName(const char *tFileName)
+{
+ if ((!tFileName) || (!*tFileName))
+ tFileName = "nsmail.tmp";
+
+ nsCOMPtr<nsIFile> tmpFile;
+
+ nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+ tFileName,
+ getter_AddRefs(tmpFile));
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ nsCString tempString;
+ rv = tmpFile->GetNativePath(tempString);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ char *tString = ToNewCString(tempString);
+ if (!tString)
+ return PL_strdup("mozmail.tmp"); // No need to I18N
+
+ return tString;
+}
+
+// This is the value a caller will Get if they don't Set first (like MDN
+// sending a return receipt), so init to the default value of the
+// mail.strictly_mime_headers preference.
+static bool mime_headers_use_quoted_printable_p = true;
+
+bool
+nsMsgMIMEGetConformToStandard (void)
+{
+ return mime_headers_use_quoted_printable_p;
+}
+
+void
+nsMsgMIMESetConformToStandard (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 {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ prefs->GetBoolPref("mail.strictly_mime_headers", &mime_headers_use_quoted_printable_p);
+ }
+ }
+}
+
+/**
+ * Checks if the recipient fields have sane values for message send.
+ */
+nsresult mime_sanity_check_fields_recipients (
+ const char *to,
+ const char *cc,
+ const char *bcc,
+ const char *newsgroups)
+{
+ if (to)
+ while (IS_SPACE(*to))
+ to++;
+ if (cc)
+ while (IS_SPACE(*cc))
+ cc++;
+ if (bcc)
+ while (IS_SPACE(*bcc))
+ bcc++;
+ if (newsgroups)
+ while (IS_SPACE(*newsgroups))
+ newsgroups++;
+
+ if ((!to || !*to) && (!cc || !*cc) &&
+ (!bcc || !*bcc) && (!newsgroups || !*newsgroups))
+ return NS_MSG_NO_RECIPIENTS;
+
+ return NS_OK;
+}
+
+/**
+ * Checks if the compose fields have sane values for message send.
+ */
+nsresult 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 (IS_SPACE(*from))
+ from++;
+ if (reply_to)
+ while (IS_SPACE(*reply_to))
+ reply_to++;
+ if (fcc)
+ while (IS_SPACE(*fcc))
+ fcc++;
+ if (followup_to)
+ while (IS_SPACE(*followup_to))
+ followup_to++;
+
+ // TODO: sanity check other_random_headers for newline conventions
+ if (!from || !*from)
+ return NS_MSG_NO_SENDER;
+
+ return mime_sanity_check_fields_recipients(to, cc, bcc, newsgroups);
+}
+
+//
+// Generate the message headers for the new RFC822 message
+//
+#define UA_PREF_PREFIX "general.useragent."
+
+// Helper macro for generating the X-Mozilla-Draft-Info header.
+#define APPEND_BOOL(method, param) \
+ do { \
+ bool val = false; \
+ fields->Get##method(&val); \
+ if (val) \
+ draftInfo.AppendLiteral(param "=1"); \
+ else \
+ draftInfo.AppendLiteral(param "=0"); \
+ } while (false)
+
+nsresult mime_generate_headers(nsIMsgCompFields *fields,
+ nsMsgDeliverMode deliver_mode,
+ msgIWritableStructuredHeaders *finalHeaders)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isDraft =
+ deliver_mode == nsIMsgSend::nsMsgSaveAsDraft ||
+ deliver_mode == nsIMsgSend::nsMsgSaveAsTemplate ||
+ deliver_mode == nsIMsgSend::nsMsgQueueForLater ||
+ deliver_mode == nsIMsgSend::nsMsgDeliverBackground;
+
+ bool hasDisclosedRecipient = false;
+
+ MOZ_ASSERT(fields, "null fields");
+ NS_ENSURE_ARG_POINTER(fields);
+
+ nsCOMArray<msgIAddressObject> from;
+ fields->GetAddressingHeader("From", from, true);
+
+ // Copy all headers from the original compose field.
+ rv = finalHeaders->AddAllHeaders(fields);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMessageId = false;
+ if (NS_SUCCEEDED(fields->HasHeader("Message-ID", &hasMessageId)) &&
+ hasMessageId)
+ {
+ /* 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
+ */
+
+ bool returnReceipt = false;
+ fields->GetReturnReceipt(&returnReceipt);
+ if (returnReceipt &&
+ (deliver_mode != nsIMsgSend::nsMsgSaveAsDraft &&
+ deliver_mode != nsIMsgSend::nsMsgSaveAsTemplate))
+ {
+ int32_t receipt_header_type = nsIMsgMdnGenerator::eDntType;
+ fields->GetReceiptHeaderType(&receipt_header_type);
+
+ // nsIMsgMdnGenerator::eDntType = MDN Disposition-Notification-To: ;
+ // nsIMsgMdnGenerator::eRrtType = Return-Receipt-To: ;
+ // nsIMsgMdnGenerator::eDntRrtType = both MDN DNT and RRT headers .
+ if (receipt_header_type != nsIMsgMdnGenerator::eRrtType)
+ finalHeaders->SetAddressingHeader("Disposition-Notification-To", from);
+ if (receipt_header_type != nsIMsgMdnGenerator::eDntType)
+ finalHeaders->SetAddressingHeader("Return-Receipt-To", from);
+ }
+ }
+
+ PRExplodedTime now;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
+ int gmtoffset = (now.tm_params.tp_gmt_offset + now.tm_params.tp_dst_offset) / 60;
+
+ /* 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.)
+ */
+ char dateString[130];
+ PR_FormatTimeUSEnglish(dateString, sizeof(dateString),
+ "%a, %d %b %Y %H:%M:%S ",
+ &now);
+
+ char *entryPoint = dateString + strlen(dateString);
+ PR_snprintf(entryPoint, sizeof(dateString) - (entryPoint - dateString),
+ "%c%02d%02d" CRLF,
+ (gmtoffset >= 0 ? '+' : '-'),
+ ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) / 60),
+ ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) % 60));
+ finalHeaders->SetRawHeader("Date", nsDependentCString(dateString), nullptr);
+
+ // X-Mozilla-Draft-Info
+ if (isDraft)
+ {
+ nsAutoCString draftInfo;
+ draftInfo.AppendLiteral("internal/draft; ");
+ APPEND_BOOL(AttachVCard, "vcard");
+ draftInfo.AppendLiteral("; ");
+ bool hasReturnReceipt = false;
+ fields->GetReturnReceipt(&hasReturnReceipt);
+ if (hasReturnReceipt)
+ {
+ // slight change compared to 4.x; we used to use receipt= to tell
+ // whether the draft/template has request for either MDN or DNS or both
+ // return receipt; since the DNS is out of the picture we now use the
+ // header type + 1 to tell whether user has requested the return receipt
+ int32_t headerType = 0;
+ fields->GetReceiptHeaderType(&headerType);
+ draftInfo.AppendLiteral("receipt=");
+ draftInfo.AppendInt(headerType + 1);
+ }
+ else
+ draftInfo.AppendLiteral("receipt=0");
+ draftInfo.AppendLiteral("; ");
+ APPEND_BOOL(DSN, "DSN");
+ draftInfo.AppendLiteral("; ");
+ draftInfo.AppendLiteral("uuencode=0");
+ draftInfo.AppendLiteral("; ");
+ APPEND_BOOL(AttachmentReminder, "attachmentreminder");
+ draftInfo.AppendLiteral("; ");
+ int32_t deliveryFormat;
+ fields->GetDeliveryFormat(&deliveryFormat);
+ draftInfo.AppendLiteral("deliveryformat=");
+ draftInfo.AppendInt(deliveryFormat);
+
+ finalHeaders->SetRawHeader(HEADER_X_MOZILLA_DRAFT_INFO, draftInfo, nullptr);
+ }
+
+ nsCOMPtr<nsIHttpProtocolHandler> pHTTPHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv);
+ if (NS_SUCCEEDED(rv) && pHTTPHandler)
+ {
+ nsAutoCString userAgentString;
+ pHTTPHandler->GetUserAgent(userAgentString);
+
+ if (!userAgentString.IsEmpty())
+ finalHeaders->SetUnstructuredHeader("User-Agent",
+ NS_ConvertUTF8toUTF16(userAgentString));
+ }
+
+ finalHeaders->SetUnstructuredHeader("MIME-Version", NS_LITERAL_STRING("1.0"));
+
+ nsAutoCString newsgroups;
+ finalHeaders->GetRawHeader("Newsgroups", newsgroups);
+ if (!newsgroups.IsEmpty())
+ {
+ // Since the newsgroup header can contain data in the form of:
+ // "news://news.mozilla.org/netscape.test,news://news.mozilla.org/netscape.junk"
+ // we need to turn that into: "netscape.test,netscape.junk"
+ // (XXX: can it really?)
+ nsCOMPtr<nsINntpService> nntpService =
+ do_GetService("@mozilla.org/messenger/nntpservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString newsgroupsHeaderVal;
+ nsCString newshostHeaderVal;
+ rv = nntpService->GenerateNewsHeaderValsForPosting(newsgroups,
+ getter_Copies(newsgroupsHeaderVal), getter_Copies(newshostHeaderVal));
+ NS_ENSURE_SUCCESS(rv, rv);
+ finalHeaders->SetRawHeader("Newsgroups", newsgroupsHeaderVal, nullptr);
+
+ // If we are here, we are NOT going to send this now. (i.e. it is a Draft,
+ // Send Later file, etc...). Because of that, we need to store what the user
+ // typed in on the original composition window for use later when rebuilding
+ // the headers
+ if (deliver_mode != nsIMsgSend::nsMsgDeliverNow &&
+ deliver_mode != nsIMsgSend::nsMsgSendUnsent)
+ {
+ // This is going to be saved for later, that means we should just store
+ // what the user typed into the "Newsgroup" line in the HEADER_X_MOZILLA_NEWSHOST
+ // header for later use by "Send Unsent Messages", "Drafts" or "Templates"
+ finalHeaders->SetRawHeader(HEADER_X_MOZILLA_NEWSHOST, newshostHeaderVal,
+ nullptr);
+ }
+
+ // Newsgroups are a recipient...
+ hasDisclosedRecipient = true;
+ }
+
+ nsCOMArray<msgIAddressObject> recipients;
+ finalHeaders->GetAddressingHeader("To", recipients);
+ hasDisclosedRecipient |= !recipients.IsEmpty();
+ finalHeaders->GetAddressingHeader("Cc", recipients);
+ hasDisclosedRecipient |= !recipients.IsEmpty();
+
+ // If we don't have disclosed recipient (only Bcc), address the message to
+ // undisclosed-recipients to prevent problem with some servers
+
+ // If we are saving the message as a draft, don't bother inserting the undisclosed recipients field. We'll take care of that when we
+ // really send the message.
+ if (!hasDisclosedRecipient && !isDraft)
+ {
+ bool bAddUndisclosedRecipients = true;
+ prefs->GetBoolPref("mail.compose.add_undisclosed_recipients", &bAddUndisclosedRecipients);
+ if (bAddUndisclosedRecipients)
+ {
+ bool hasBcc = false;
+ fields->HasHeader("Bcc", &hasBcc);
+ if (hasBcc)
+ {
+ nsCOMPtr<nsIStringBundleService> stringService =
+ mozilla::services::GetStringBundleService();
+ if (stringService)
+ {
+ nsCOMPtr<nsIStringBundle> composeStringBundle;
+ rv = stringService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(composeStringBundle));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsString undisclosedRecipients;
+ rv = composeStringBundle->GetStringFromName(u"undisclosedRecipients",
+ getter_Copies(undisclosedRecipients));
+ if (NS_SUCCEEDED(rv) && !undisclosedRecipients.IsEmpty())
+ {
+ nsCOMPtr<nsIMsgHeaderParser> headerParser(
+ mozilla::services::GetHeaderParser());
+ nsCOMPtr<msgIAddressObject> group;
+ headerParser->MakeGroupObject(undisclosedRecipients,
+ nullptr, 0, getter_AddRefs(group));
+ recipients.AppendElement(group);
+ finalHeaders->SetAddressingHeader("To", recipients);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // We don't want to emit a Bcc header to the output. If we are saving this to
+ // Drafts/Sent, this is readded later in nsMsgSend.cpp.
+ finalHeaders->DeleteHeader("bcc");
+
+ // Skip no or empty priority.
+ nsAutoCString priority;
+ rv = fields->GetRawHeader("X-Priority", priority);
+ if (NS_SUCCEEDED(rv) && !priority.IsEmpty())
+ {
+ nsMsgPriorityValue priorityValue;
+
+ NS_MsgGetPriorityFromString(priority.get(), priorityValue);
+
+ // Skip default priority.
+ if (priorityValue != nsMsgPriority::Default) {
+ nsAutoCString priorityName;
+ nsAutoCString priorityValueString;
+
+ NS_MsgGetPriorityValueString(priorityValue, priorityValueString);
+ NS_MsgGetUntranslatedPriorityName(priorityValue, priorityName);
+
+ // Output format: [X-Priority: <pValue> (<pName>)]
+ priorityValueString.AppendLiteral(" (");
+ priorityValueString += priorityName;
+ priorityValueString.AppendLiteral(")");
+ finalHeaders->SetRawHeader("X-Priority", priorityValueString, nullptr);
+ }
+ }
+
+ nsAutoCString references;
+ finalHeaders->GetRawHeader("References", references);
+ if (!references.IsEmpty())
+ {
+ // The References header should be kept under 998 characters: if it's too
+ // long, trim out the earliest references to make it smaller.
+ if (references.Length() > 986)
+ {
+ int32_t firstRef = references.FindChar('<');
+ int32_t secondRef = references.FindChar('<', firstRef + 1);
+ if (secondRef > 0)
+ {
+ nsAutoCString newReferences(StringHead(references, secondRef));
+ int32_t bracket = references.FindChar('<',
+ references.Length() + newReferences.Length() - 986);
+ if (bracket > 0)
+ {
+ newReferences.Append(Substring(references, bracket));
+ finalHeaders->SetRawHeader("References", newReferences, nullptr);
+ }
+ }
+ }
+ // The In-Reply-To header is the last entry in the references header...
+ int32_t bracket = references.RFind("<");
+ if (bracket >= 0)
+ finalHeaders->SetRawHeader("In-Reply-To", Substring(references, bracket),
+ nullptr);
+ }
+
+ return NS_OK;
+}
+
+#undef APPEND_BOOL // X-Mozilla-Draft-Info helper macro
+
+static void
+GenerateGlobalRandomBytes(unsigned char *buf, int32_t len)
+{
+ // Attempt to generate bytes from system entropy-based RNG.
+ nsCOMPtr<nsIRandomGenerator> randomGenerator(do_GetService("@mozilla.org/security/random-generator;1"));
+ MOZ_ASSERT(randomGenerator, "nsIRandomGenerator service not retrievable");
+ uint8_t *tempBuffer;
+ nsresult rv = randomGenerator->GenerateRandomBytes(len, &tempBuffer);
+ if (NS_SUCCEEDED(rv))
+ {
+ memcpy(buf, tempBuffer, len);
+ free(tempBuffer);
+ return;
+ }
+ // nsIRandomGenerator failed -- fall back to low entropy PRNG.
+ static bool firstTime = true;
+ if (firstTime)
+ {
+ // Seed the random-number generator with current time so that
+ // the numbers will be different every time we run.
+ srand( (unsigned)PR_Now() );
+ firstTime = false;
+ }
+
+ for( int32_t i = 0; i < len; i++ )
+ buf[i] = rand() % 256;
+}
+
+char
+*mime_make_separator(const char *prefix)
+{
+ unsigned char rand_buf[13];
+ GenerateGlobalRandomBytes(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]);
+}
+
+static char *
+RFC2231ParmFolding(const char *parmName, const nsCString& charset,
+ const char *language, const nsString& parmValue);
+
+static char *
+LegacyParmFolding(const nsCString& aCharset,
+ const nsCString& aFileName, int32_t aParmFolding);
+
+char *
+mime_generate_attachment_headers (const char *type,
+ const char *type_param,
+ const char *encoding,
+ const char *description,
+ const char *x_mac_type,
+ const char *x_mac_creator,
+ const char *real_name,
+ const char *base_url,
+ bool /*digest_p*/,
+ nsMsgAttachmentHandler * /*ma*/,
+ const char *attachmentCharset,
+ const char *bodyCharset,
+ bool bodyIsAsciiOnly,
+ const char *content_id,
+ bool aBodyDocument)
+{
+ NS_ASSERTION (encoding, "null encoding");
+
+ int32_t parmFolding = 2; // RFC 2231-compliant
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefs)
+ prefs->GetIntPref("mail.strictly_mime.parm_folding", &parmFolding);
+
+ /* Let's encode the real name */
+ char *encodedRealName = nullptr;
+ nsCString charset; // actual charset used for MIME encode
+ nsAutoString realName;
+ if (real_name)
+ {
+ // first try main body's charset to encode the file name,
+ // then try local file system charset if fails
+ CopyUTF8toUTF16(nsDependentCString(real_name), realName);
+ if (bodyCharset && *bodyCharset &&
+ nsMsgI18Ncheck_data_in_charset_range(bodyCharset, realName.get()))
+ charset.Assign(bodyCharset);
+ else
+ {
+ charset.Assign(nsMsgI18NFileSystemCharset());
+ if (!nsMsgI18Ncheck_data_in_charset_range(charset.get(), realName.get()))
+ charset.Assign("UTF-8"); // set to UTF-8 if fails again
+ }
+
+ encodedRealName = RFC2231ParmFolding("filename", charset, nullptr,
+ realName);
+ // somehow RFC2231ParamFolding failed. fall back to legacy method
+ if (!encodedRealName || !*encodedRealName) {
+ PR_FREEIF(encodedRealName);
+ parmFolding = 0;
+ // Not RFC 2231 style encoding (it's not standard-compliant)
+ encodedRealName =
+ LegacyParmFolding(charset, nsDependentCString(real_name), parmFolding);
+ }
+ }
+
+ nsCString buf; // very likely to be longer than 64 characters
+ buf.Append("Content-Type: ");
+ buf.Append(type);
+ if (type_param && *type_param)
+ {
+ if (*type_param != ';')
+ buf.Append("; ");
+ buf.Append(type_param);
+ }
+
+ if (mime_type_needs_charset (type))
+ {
+
+ char charset_label[65] = ""; // Content-Type: charset
+ if (attachmentCharset)
+ {
+ PL_strncpy(charset_label, attachmentCharset, sizeof(charset_label)-1);
+ charset_label[sizeof(charset_label)-1] = '\0';
+ }
+
+ /* If the characters are all 7bit, arguably it's better to
+ claim the charset to be US-ASCII. However, it causes
+ a major 'interoperability problem' with MS OE, which makes it hard
+ to sell Mozilla/TB to people most of whose correspondents use
+ MS OE. MS OE turns all non-ASCII characters to question marks
+ in replies to messages labeled as US-ASCII if users select 'send as is'
+ with MIME turned on. (with MIME turned off, this happens without
+ any warning.) To avoid this, we use the label 'US-ASCII' only when
+ it's explicitly requested by setting the hidden pref.
+ 'mail.label_ascii_only_mail_as_us_ascii'. (bug 247958) */
+ bool labelAsciiAsAscii = false;
+ if (prefs)
+ prefs->GetBoolPref("mail.label_ascii_only_mail_as_us_ascii",
+ &labelAsciiAsAscii);
+ if (labelAsciiAsAscii && encoding &&
+ !PL_strcasecmp (encoding, "7bit") && bodyIsAsciiOnly)
+ PL_strcpy (charset_label, "us-ascii");
+
+ // If charset is multibyte then no charset to be specified (apply base64 instead).
+ // The list of file types match with PickEncoding() where we put base64 label.
+ if ( ((attachmentCharset && !nsMsgI18Nmultibyte_charset(attachmentCharset)) ||
+ ((PL_strcasecmp(type, TEXT_HTML) == 0) ||
+ (PL_strcasecmp(type, TEXT_MDL) == 0) ||
+ (PL_strcasecmp(type, TEXT_PLAIN) == 0) ||
+ (PL_strcasecmp(type, TEXT_RICHTEXT) == 0) ||
+ (PL_strcasecmp(type, TEXT_ENRICHED) == 0) ||
+ (PL_strcasecmp(type, TEXT_VCARD) == 0) ||
+ (PL_strcasecmp(type, APPLICATION_DIRECTORY) == 0) || /* text/x-vcard synonym */
+ (PL_strcasecmp(type, TEXT_CSS) == 0) ||
+ (PL_strcasecmp(type, TEXT_JSSS) == 0)) ||
+ (PL_strcasecmp(encoding, ENCODING_BASE64) != 0)) &&
+ (*charset_label))
+ {
+ buf.Append("; charset=");
+ buf.Append(charset_label);
+ }
+ }
+
+ // Only do this if we are in the body of a message
+ if (aBodyDocument)
+ {
+ // Add format=flowed as in RFC 2646 if we are using that
+ if(type && !PL_strcasecmp(type, "text/plain"))
+ {
+ bool flowed, delsp, formatted, disallowBreaks;
+ GetSerialiserFlags(bodyCharset, &flowed, &delsp, &formatted, &disallowBreaks);
+ if(flowed)
+ buf.Append("; format=flowed");
+ if (delsp)
+ buf.Append("; delsp=yes");
+ // else
+ // {
+ // Don't add a markup. Could use
+ // PUSH_STRING ("; format=fixed");
+ // but it is equivalent to nothing at all and we do want
+ // to save bandwidth. Don't we?
+ // }
+ }
+ }
+
+ if (x_mac_type && *x_mac_type) {
+ buf.Append("; x-mac-type=\"");
+ buf.Append(x_mac_type);
+ buf.Append("\"");
+ }
+
+ if (x_mac_creator && *x_mac_creator) {
+ buf.Append("; x-mac-creator=\"");
+ buf.Append(x_mac_creator);
+ buf.Append("\"");
+ }
+
+#ifdef EMIT_NAME_IN_CONTENT_TYPE
+ if (encodedRealName && *encodedRealName) {
+ // Note that we don't need to output the name field if the name encoding is
+ // RFC 2231. If the MUA knows the RFC 2231, it should know the RFC 2183 too.
+ if (parmFolding != 2) {
+ // The underlying JS MIME code will only handle UTF-8 here.
+ char *nameValue = LegacyParmFolding(NS_LITERAL_CSTRING("UTF-8"),
+ nsDependentCString(real_name),
+ parmFolding);
+ if (!nameValue || !*nameValue) {
+ PR_FREEIF(nameValue);
+ nameValue = encodedRealName;
+ }
+ buf.Append(";\r\n name=\"");
+ buf.Append(nameValue);
+ buf.Append("\"");
+ if (nameValue != encodedRealName)
+ PR_FREEIF(nameValue);
+ }
+ }
+#endif /* EMIT_NAME_IN_CONTENT_TYPE */
+ buf.Append(CRLF);
+
+ buf.Append("Content-Transfer-Encoding: ");
+ buf.Append(encoding);
+ buf.Append(CRLF);
+
+ if (description && *description) {
+ char *s = mime_fix_header (description);
+ if (s) {
+ buf.Append("Content-Description: ");
+ buf.Append(s);
+ buf.Append(CRLF);
+ PR_Free(s);
+ }
+ }
+
+ if ( (content_id) && (*content_id) )
+ {
+ buf.Append("Content-ID: <");
+ buf.Append(content_id);
+ buf.Append(">");
+ buf.Append(CRLF);
+ }
+
+ if (encodedRealName && *encodedRealName) {
+ char *period = PL_strrchr(encodedRealName, '.');
+ int32_t pref_content_disposition = 0;
+
+ if (prefs) {
+ mozilla::DebugOnly<nsresult> rv = prefs->GetIntPref("mail.content_disposition_type",
+ &pref_content_disposition);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get mail.content_disposition_type");
+ }
+
+ buf.Append("Content-Disposition: ");
+
+ // If this is an attachment which is part of the message body and therefore has a
+ // Content-ID (e.g, image in HTML msg), then Content-Disposition has to be inline
+ if (content_id && *content_id)
+ buf.Append("inline");
+ else if (pref_content_disposition == 1)
+ buf.Append("attachment");
+ else
+ if (pref_content_disposition == 2 &&
+ (!PL_strcasecmp(type, TEXT_PLAIN) ||
+ (period && !PL_strcasecmp(period, ".txt"))))
+ buf.Append("attachment");
+
+ /* If this document is an anonymous binary file or a vcard,
+ then always show it as an attachment, never inline. */
+ else
+ if (!PL_strcasecmp(type, APPLICATION_OCTET_STREAM) ||
+ !PL_strcasecmp(type, TEXT_VCARD) ||
+ !PL_strcasecmp(type, APPLICATION_DIRECTORY)) /* text/x-vcard synonym */
+ buf.Append("attachment");
+ else
+ buf.Append("inline");
+
+ buf.Append(";\r\n ");
+ buf.Append(encodedRealName);
+ buf.Append(CRLF);
+ }
+ else
+ if (type &&
+ (!PL_strcasecmp (type, MESSAGE_RFC822) ||
+ !PL_strcasecmp (type, MESSAGE_NEWS)))
+ buf.Append("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 &&
+ (!PL_strcasecmp (type, TEXT_HTML) ||
+ !PL_strcasecmp (type, TEXT_MDL)) &&
+ base_url && *base_url)
+ {
+ int32_t col = 0;
+ const char *s = base_url;
+ const char *colon = PL_strchr (s, ':');
+ 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 (!PL_strncasecmp (s, "news:", 5) ||
+ !PL_strncasecmp (s, "snews:", 6) ||
+ !PL_strncasecmp (s, "IMAP:", 5) ||
+ !PL_strncasecmp (s, "file:", 5) || /* rhp: fix targets from included HTML files */
+ !PL_strncasecmp (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
+ */
+ if (prefs)
+ prefs->GetBoolPref("mail.use_content_location_on_send", &useContentLocation);
+
+ if (useContentLocation)
+ buf.Append("Content-Location: \"");
+ else
+ buf.Append("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 != '#')
+ {
+ uint32_t ot=buf.Length();
+ char tmp[]="\x00\x00";
+ /* URLs must be wrapped at 40 characters or less. */
+ if (col >= 38) {
+ buf.Append(CRLF "\t");
+ col = 0;
+ }
+
+ if (*s == ' ')
+ buf.Append("%20");
+ else if (*s == '\t')
+ buf.Append("%09");
+ else if (*s == '\n')
+ buf.Append("%0A");
+ else if (*s == '\r')
+ buf.Append("%0D");
+ else {
+ tmp[0]=*s;
+ buf.Append(tmp);
+ }
+ s++;
+ col += (buf.Length() - ot);
+ }
+ buf.Append("\"" CRLF);
+
+ /* rhp: this is to try to get around this fun problem with Content-Location */
+ if (!useContentLocation) {
+ buf.Append("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... */
+
+#ifdef DEBUG_jungshik
+ printf ("header=%s\n", buf.get());
+#endif
+ PR_Free(encodedRealName);
+ return PL_strdup(buf.get());
+}
+
+static bool isValidHost( const char* host )
+{
+ if ( host )
+ for (const char *s = host; *s; ++s)
+ if ( !isalpha(*s)
+ && !isdigit(*s)
+ && *s != '-'
+ && *s != '_'
+ && *s != '.'
+ )
+ {
+ host = nullptr;
+ break;
+ }
+
+ return nullptr != host;
+}
+
+char *
+msg_generate_message_id (nsIMsgIdentity *identity)
+{
+ const char *host = 0;
+
+ nsCString forcedFQDN;
+ nsCString from;
+ nsresult rv = NS_OK;
+
+ rv = identity->GetCharAttribute("FQDN", forcedFQDN);
+
+ if (NS_SUCCEEDED(rv) && !forcedFQDN.IsEmpty())
+ host = forcedFQDN.get();
+
+ if (!isValidHost(host))
+ {
+ nsresult rv = identity->GetEmail(from);
+ if (NS_SUCCEEDED(rv) && !from.IsEmpty())
+ host = strchr(from.get(),'@');
+
+ // No '@'? Munged address, anti-spam?
+ // see bug #197203
+ if (host)
+ ++host;
+ }
+
+ if (!isValidHost(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;
+
+ // Generate 128-bit UUID for the local part. We use the high-entropy
+ // GenerateGlobalRandomBytes to make tracking more difficult.
+ nsID uuid;
+ GenerateGlobalRandomBytes((unsigned char*) &uuid, sizeof(nsID));
+ char uuidString[NSID_LENGTH];
+ uuid.ToProvidedString(uuidString);
+ // Drop first and last characters (curly braces).
+ uuidString[NSID_LENGTH - 2] = 0;
+ return PR_smprintf("<%s@%s>", uuidString + 1, host);
+}
+
+
+inline static bool is7bitCharset(const nsCString& charset)
+{
+ // charset name is canonical (no worry about case-sensitivity)
+ return Substring(charset, 0, 8).EqualsLiteral("ISO-2022-");
+}
+
+#define PR_MAX_FOLDING_LEN 75 // this is to gurantee the folded line will
+ // never be greater than 78 = 75 + CRLFLWSP
+/*static */ char *
+RFC2231ParmFolding(const char *parmName, const nsCString& charset,
+ const char *language, const nsString& parmValue)
+{
+ NS_ENSURE_TRUE(parmName && *parmName && !parmValue.IsEmpty(), nullptr);
+
+ bool needEscape;
+ nsCString dupParm;
+
+ if (!NS_IsAscii(parmValue.get()) || is7bitCharset(charset)) {
+ needEscape = true;
+ nsAutoCString nativeParmValue;
+ ConvertFromUnicode(charset.get(), parmValue, nativeParmValue);
+ MsgEscapeString(nativeParmValue, nsINetUtil::ESCAPE_ALL, dupParm);
+ }
+ else {
+ needEscape = false;
+ dupParm.Adopt(
+ msg_make_filename_qtext(NS_LossyConvertUTF16toASCII(parmValue).get(),
+ true));
+ }
+
+ if (dupParm.IsEmpty())
+ return nullptr;
+
+ int32_t parmNameLen = PL_strlen(parmName);
+ int32_t parmValueLen = dupParm.Length();
+
+ parmNameLen += 5; // *=__'__'___ or *[0]*=__'__'__ or *[1]*=___ or *[0]="___"
+
+ int32_t languageLen = language ? PL_strlen(language) : 0;
+ int32_t charsetLen = charset.Length();
+ char *foldedParm = nullptr;
+
+ if ((parmValueLen + parmNameLen + charsetLen + languageLen) <
+ PR_MAX_FOLDING_LEN)
+ {
+ foldedParm = PL_strdup(parmName);
+ if (needEscape)
+ {
+ NS_MsgSACat(&foldedParm, "*=");
+ if (charsetLen)
+ NS_MsgSACat(&foldedParm, charset.get());
+ NS_MsgSACat(&foldedParm, "'");
+ if (languageLen)
+ NS_MsgSACat(&foldedParm, language);
+ NS_MsgSACat(&foldedParm, "'");
+ }
+ else
+ NS_MsgSACat(&foldedParm, "=\"");
+ NS_MsgSACat(&foldedParm, dupParm.get());
+ if (!needEscape)
+ NS_MsgSACat(&foldedParm, "\"");
+ }
+ else
+ {
+ int curLineLen = 0;
+ int counter = 0;
+ char digits[32];
+ char *start = dupParm.BeginWriting();
+ char *end = NULL;
+ char tmp = 0;
+
+ while (parmValueLen > 0)
+ {
+ curLineLen = 0;
+ if (counter == 0) {
+ PR_FREEIF(foldedParm)
+ foldedParm = PL_strdup(parmName);
+ }
+ else {
+ NS_MsgSACat(&foldedParm, ";\r\n ");
+ NS_MsgSACat(&foldedParm, parmName);
+ }
+ PR_snprintf(digits, sizeof(digits), "*%d", counter);
+ NS_MsgSACat(&foldedParm, digits);
+ curLineLen += PL_strlen(digits);
+ if (needEscape)
+ {
+ NS_MsgSACat(&foldedParm, "*=");
+ if (counter == 0)
+ {
+ if (charsetLen)
+ NS_MsgSACat(&foldedParm, charset.get());
+ NS_MsgSACat(&foldedParm, "'");
+ if (languageLen)
+ NS_MsgSACat(&foldedParm, language);
+ NS_MsgSACat(&foldedParm, "'");
+ curLineLen += charsetLen;
+ curLineLen += languageLen;
+ }
+ }
+ else
+ {
+ NS_MsgSACat(&foldedParm, "=\"");
+ }
+ counter++;
+ curLineLen += parmNameLen;
+ if (parmValueLen <= PR_MAX_FOLDING_LEN - curLineLen)
+ end = start + parmValueLen;
+ else
+ end = start + (PR_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 = 0;
+ }
+ else if (end-1 > start && *(end-1) == '%')
+ {
+ end -= 1; tmp = '%'; *end = 0;
+ }
+ else if (end-2 > start && *(end-2) == '%')
+ {
+ end -= 2; tmp = '%'; *end = 0;
+ }
+ else
+ {
+ tmp = *end; *end = 0;
+ }
+ }
+ else
+ {
+ // XXX should check if we are in the middle of escaped char (RFC 822)
+ tmp = *end; *end = 0;
+ }
+ NS_MsgSACat(&foldedParm, start);
+ if (!needEscape)
+ NS_MsgSACat(&foldedParm, "\"");
+
+ parmValueLen -= (end-start);
+ if (tmp)
+ *end = tmp;
+ start = end;
+ }
+ }
+
+ return foldedParm;
+}
+
+/*static */ char *
+LegacyParmFolding(const nsCString& aCharset,
+ const nsCString& aFileName, int32_t aParmFolding)
+{
+ bool usemime = nsMsgMIMEGetConformToStandard();
+ char *encodedRealName =
+ nsMsgI18NEncodeMimePartIIStr(aFileName.get(), false, aCharset.get(),
+ 0, usemime);
+
+ if (!encodedRealName || !*encodedRealName) {
+ PR_FREEIF(encodedRealName);
+ encodedRealName = (char *) PR_Malloc(aFileName.Length() + 1);
+ if (encodedRealName)
+ PL_strcpy(encodedRealName, aFileName.get());
+ }
+
+ // Now put backslashes before special characters per RFC 822
+ char *qtextName =
+ msg_make_filename_qtext(encodedRealName, aParmFolding == 0);
+ if (qtextName) {
+ PR_FREEIF(encodedRealName);
+ encodedRealName = qtextName;
+ }
+ return encodedRealName;
+}
+
+bool
+mime_7bit_data_p (const char *string, uint32_t size)
+{
+ if ((!string) || (!*string))
+ return true;
+
+ char *ptr = (char *)string;
+ for (uint32_t i=0; i<size; i++)
+ {
+ if ((unsigned char) ptr[i] > 0x7F)
+ return false;
+ }
+ return true;
+}
+
+/* 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.
+ */
+char *
+mime_fix_header_1 (const char *string, bool addr_p, bool news_p)
+{
+ char *new_string;
+ const char *in;
+ char *out;
+ int32_t i, old_size, new_size;
+
+ if (!string || !*string)
+ return 0;
+
+ if (addr_p) {
+ return strdup(string);
+ }
+
+ old_size = PL_strlen (string);
+ new_size = old_size;
+ for (i = 0; i < old_size; i++)
+ if (string[i] == '\r' || string[i] == '\n')
+ new_size += 2;
+
+ new_string = (char *) PR_Malloc (new_size + 1);
+ if (! new_string)
+ return 0;
+
+ in = string;
+ out = new_string;
+
+ /* strip leading whitespace. */
+ while (IS_SPACE (*in))
+ in++;
+
+ /* replace CR, LF, or CRLF with CRLF-TAB. */
+ while (*in) {
+ if (*in == '\r' || *in == '\n') {
+ if (*in == '\r' && in[1] == '\n')
+ in++;
+ in++;
+ *out++ = '\r';
+ *out++ = '\n';
+ *out++ = '\t';
+ }
+ else
+ if (news_p && *in == ',') {
+ *out++ = *in++;
+ /* skip over all whitespace after a comma. */
+ while (IS_SPACE (*in))
+ in++;
+ }
+ else
+ *out++ = *in++;
+ }
+ *out = 0;
+
+ /* strip trailing whitespace. */
+ while (out > in && IS_SPACE (out[-1]))
+ *out-- = 0;
+
+ /* If we ended up throwing it all away, use 0 instead of "". */
+ if (!*new_string) {
+ PR_Free (new_string);
+ new_string = 0;
+ }
+
+ return new_string;
+}
+
+char *
+mime_fix_header (const char *string)
+{
+ return mime_fix_header_1 (string, false, false);
+}
+
+char *
+mime_fix_addr_header (const char *string)
+{
+ return mime_fix_header_1 (string, true, false);
+}
+
+char *
+mime_fix_news_header (const char *string)
+{
+ return mime_fix_header_1 (string, false, true);
+}
+
+bool
+mime_type_requires_b64_p (const char *type)
+{
+ if (!type || !PL_strcasecmp (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 (!PL_strncasecmp (type, "image/", 6) ||
+ !PL_strncasecmp (type, "audio/", 6) ||
+ !PL_strncasecmp (type, "video/", 6) ||
+ !PL_strncasecmp (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/pgp-keys",
+ "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/javascript",
+ "application/ecmascript",
+ "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 (!PL_strcasecmp (type, *s))
+ return false;
+
+ /* All others must be assumed to be binary formats, and need Base64. */
+ return true;
+ }
+
+ else
+ return false;
+}
+
+//
+// Some types should have a "charset=" parameter, and some shouldn't.
+// This is what decides.
+//
+bool
+mime_type_needs_charset (const char *type)
+{
+ /* Only text types should have charset. */
+ if (!type || !*type)
+ return false;
+ else
+ if (!PL_strncasecmp (type, "text", 4))
+ return true;
+ else
+ return false;
+}
+
+/* Given a string, convert it to 'qtext' (quoted text) for RFC822 header purposes. */
+char *
+msg_make_filename_qtext(const char *srcText, bool stripCRLFs)
+{
+ /* newString can be at most twice the original string (every char quoted). */
+ char *newString = (char *) PR_Malloc(PL_strlen(srcText)*2 + 1);
+ if (!newString) return NULL;
+
+ const char *s = srcText;
+ const char *end = srcText + PL_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 == '\r' && (s[1] != '\n' ||
+ (s[1] == '\n' && (s+2) < end && !IS_SPACE(s[2]))))))
+ *d++ = '\\';
+
+ if (stripCRLFs && *s == '\r' && s[1] == '\n' && (s+2) < end && IS_SPACE(s[2]))
+ {
+ s += 3; // skip CRLFLWSP
+ }
+ else
+ {
+ *d++ = *s++;
+ }
+ }
+ *d = 0;
+
+ return newString;
+}
+
+/* Rip apart the URL and extract a reasonable value for the `real_name' slot.
+ */
+void
+msg_pick_real_name (nsMsgAttachmentHandler *attachment, const char16_t *proposedName, const char *charset)
+{
+ const char *s, *s2;
+
+ if (!attachment->m_realName.IsEmpty())
+ return;
+
+ if (proposedName && *proposedName)
+ {
+ attachment->m_realName.Adopt(ToNewUTF8String(nsAutoString(proposedName)));
+ }
+ else //Let's extract the name from the URL
+ {
+ nsCString url;
+ nsresult rv = attachment->mURL->GetSpec(url);
+ if (NS_FAILED(rv))
+ return;
+
+ s = url.get();
+ s2 = PL_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 (StringBeginsWith (url, NS_LITERAL_CSTRING("news:"), nsCaseInsensitiveCStringComparator()) ||
+ StringBeginsWith (url, NS_LITERAL_CSTRING("snews:"), nsCaseInsensitiveCStringComparator()) ||
+ StringBeginsWith (url, NS_LITERAL_CSTRING("IMAP:"), nsCaseInsensitiveCStringComparator()) ||
+ StringBeginsWith (url, NS_LITERAL_CSTRING("mailbox:"), nsCaseInsensitiveCStringComparator()))
+ return;
+
+ if (StringBeginsWith(url, NS_LITERAL_CSTRING("data:"),
+ nsCaseInsensitiveCStringComparator()))
+ {
+ int32_t endNonData = url.FindChar(',');
+ if (endNonData == -1)
+ return;
+ nsCString nonDataPart(Substring(url, 5, endNonData - 5));
+ int32_t filenamePos = nonDataPart.Find("filename=");
+ if (filenamePos != -1)
+ {
+ filenamePos += 9;
+ int32_t endFilename = nonDataPart.FindChar(';', filenamePos);
+ if (endFilename == -1)
+ endFilename = endNonData;
+ attachment->m_realName = Substring(nonDataPart, filenamePos,
+ endFilename - filenamePos);
+ }
+ else
+ {
+ // no filename; need to construct one based on the content type.
+ nsCOMPtr<nsIMIMEService> mimeService(do_GetService(NS_MIMESERVICE_CONTRACTID));
+ if (!mimeService)
+ return;
+ nsCOMPtr<nsIMIMEInfo> mimeInfo;
+ nsCString mediaType(Substring(nonDataPart, 0, nonDataPart.FindChar(';')));
+ mimeService->GetFromTypeAndExtension(mediaType, EmptyCString(), getter_AddRefs(mimeInfo));
+ if (!mimeInfo)
+ return;
+ nsCString filename;
+ nsCString extension;
+ mimeInfo->GetPrimaryExtension(extension);
+ unsigned char filePrefixBytes[8];
+ GenerateGlobalRandomBytes(filePrefixBytes, 8);
+ // Create a filename prefix with 16 lowercase letters,
+ // representing 8 bytes.
+ for (int32_t i = 0; i < 8; i++)
+ {
+ // A pair of letters, each any of (a-p).
+ filename.Append((filePrefixBytes[i] & 0xF) + 'a');
+ filename.Append((filePrefixBytes[i] >> 4) + 'a');
+ }
+ filename.Append('.');
+ filename.Append(extension);
+ attachment->m_realName = filename;
+ }
+ }
+ else
+ {
+ /* Take the part of the file name after the last / or \ */
+ s2 = PL_strrchr (s, '/');
+ if (s2) s = s2+1;
+ s2 = PL_strrchr (s, '\\');
+
+ if (s2) s = s2+1;
+ /* Copy it into the attachment struct. */
+ attachment->m_realName = s;
+ int32_t charPos = attachment->m_realName.FindChar('?');
+ if (charPos != -1)
+ attachment->m_realName.SetLength(charPos);
+ /* Now trim off any named anchors or search data. */
+ charPos = attachment->m_realName.FindChar('#');
+ if (charPos != -1)
+ attachment->m_realName.SetLength(charPos);
+ }
+ /* Now lose the %XX crap. */
+ nsCString unescaped_real_name;
+ MsgUnescapeString(attachment->m_realName, 0, unescaped_real_name);
+ attachment->m_realName = unescaped_real_name;
+ }
+
+ /* 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.IsEmpty())
+ {
+ /* #### TOTAL KLUDGE.
+ 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 (attachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE) ||
+ attachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE2) ||
+ attachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE3) ||
+ attachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE4))
+ {
+ if (StringEndsWith(attachment->m_realName, NS_LITERAL_CSTRING(".uu")))
+ attachment->m_realName.Cut(attachment->m_realName.Length() - 3, 3);
+ else if (StringEndsWith(attachment->m_realName, NS_LITERAL_CSTRING(".uue")))
+ attachment->m_realName.Cut(attachment->m_realName.Length() - 4, 4);
+ }
+ }
+}
+
+// Utility to create a nsIURI object...
+nsresult
+nsMsgNewURL(nsIURI** aInstancePtrResult, const char * aSpec)
+{
+ nsresult rv = NS_OK;
+ if (nullptr == aInstancePtrResult)
+ return NS_ERROR_NULL_POINTER;
+ nsCOMPtr<nsIIOService> pNetService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(pNetService, NS_ERROR_UNEXPECTED);
+ if (PL_strstr(aSpec, "://") == nullptr && strncmp(aSpec, "data:", 5))
+ {
+ //XXXjag Temporary fix for bug 139362 until the real problem(bug 70083) get fixed
+ nsAutoCString uri(NS_LITERAL_CSTRING("http://"));
+ uri.Append(aSpec);
+ rv = pNetService->NewURI(uri, nullptr, nullptr, aInstancePtrResult);
+ }
+ else
+ rv = pNetService->NewURI(nsDependentCString(aSpec), nullptr, nullptr, aInstancePtrResult);
+ return rv;
+}
+
+bool
+nsMsgIsLocalFile(const char *url)
+{
+ /*
+ A url is considered as a local file if it's start with file://
+ But on Window, we need to filter UNC file url because there
+ are not really local file. Those start with file:////
+ */
+ if (PL_strncasecmp(url, "file://", 7) == 0)
+ {
+#ifdef XP_WIN
+ if (PL_strncasecmp(url, "file:////", 9) == 0)
+ return false;
+#endif
+ return true;
+ }
+ else
+ return false;
+}
+
+char
+*nsMsgGetLocalFileFromURL(const char *url)
+{
+ char * finalPath;
+ NS_ASSERTION(PL_strncasecmp(url, "file://", 7) == 0, "invalid url");
+ finalPath = (char*)PR_Malloc(strlen(url));
+ if (finalPath == NULL)
+ return NULL;
+ strcpy(finalPath, url+6+1);
+ return finalPath;
+}
+
+char *
+nsMsgParseURLHost(const char *url)
+{
+ nsIURI *workURI = nullptr;
+ nsresult rv;
+
+ rv = nsMsgNewURL(&workURI, url);
+ if (NS_FAILED(rv) || !workURI)
+ return nullptr;
+
+ nsAutoCString host;
+ rv = workURI->GetHost(host);
+ NS_IF_RELEASE(workURI);
+ if (NS_FAILED(rv))
+ return nullptr;
+
+ return ToNewCString(host);
+}
+
+char *
+GenerateFileNameFromURI(nsIURI *aURL)
+{
+ nsresult rv;
+ nsCString file;
+ nsCString spec;
+ char *returnString;
+ char *cp = nullptr;
+ char *cp1 = nullptr;
+
+ rv = aURL->GetPath(file);
+ if ( NS_SUCCEEDED(rv) && !file.IsEmpty())
+ {
+ char *newFile = ToNewCString(file);
+ if (!newFile)
+ return nullptr;
+
+ // strip '/'
+ cp = PL_strrchr(newFile, '/');
+ if (cp)
+ ++cp;
+ else
+ cp = newFile;
+
+ if (*cp)
+ {
+ if ((cp1 = PL_strchr(cp, '/'))) *cp1 = 0;
+ if ((cp1 = PL_strchr(cp, '?'))) *cp1 = 0;
+ if ((cp1 = PL_strchr(cp, '>'))) *cp1 = 0;
+ if (*cp != '\0')
+ {
+ returnString = PL_strdup(cp);
+ PR_FREEIF(newFile);
+ return returnString;
+ }
+ }
+ else
+ return nullptr;
+ }
+
+ cp = nullptr;
+ cp1 = nullptr;
+
+
+ rv = aURL->GetSpec(spec);
+ if ( NS_SUCCEEDED(rv) && !spec.IsEmpty())
+ {
+ char *newSpec = ToNewCString(spec);
+ if (!newSpec)
+ return nullptr;
+
+ char *cp2 = NULL, *cp3=NULL ;
+
+ // strip '"'
+ cp2 = newSpec;
+ while (*cp2 == '"')
+ cp2++;
+ if ((cp3 = PL_strchr(cp2, '"')))
+ *cp3 = 0;
+
+ char *hostStr = nsMsgParseURLHost(cp2);
+ if (!hostStr)
+ hostStr = PL_strdup(cp2);
+
+ bool isHTTP = false;
+ if (NS_SUCCEEDED(aURL->SchemeIs("http", &isHTTP)) && isHTTP)
+ {
+ returnString = PR_smprintf("%s.html", hostStr);
+ PR_FREEIF(hostStr);
+ }
+ else
+ returnString = hostStr;
+
+ PR_FREEIF(newSpec);
+ return returnString;
+ }
+
+ return nullptr;
+}
+
+//
+// This routine will generate a content id for use in a mail part.
+// It will take the part number passed in as well as the email
+// address. If the email address is null or invalid, we will simply
+// use netscape.com for the interesting part. The content ID's will
+// look like the following:
+//
+// Content-ID: <part1.36DF1DCE.73B5A330@netscape.com>
+//
+char *
+mime_gen_content_id(uint32_t aPartNum, const char *aEmailAddress)
+{
+ int32_t randLen = 5;
+ unsigned char rand_buf1[5];
+ unsigned char rand_buf2[5];
+ const char *domain = nullptr;
+ const char *defaultDomain = "@netscape.com";
+
+ memset(rand_buf1, 0, randLen-1);
+ memset(rand_buf2, 0, randLen-1);
+
+ GenerateGlobalRandomBytes(rand_buf1, randLen);
+ GenerateGlobalRandomBytes(rand_buf2, randLen);
+
+ // Find the @domain.com string...
+ if (aEmailAddress && *aEmailAddress)
+ domain = const_cast<const char*>(PL_strchr(aEmailAddress, '@'));
+
+ if (!domain)
+ domain = defaultDomain;
+
+ char *retVal = PR_smprintf("part%d."
+ "%02X%02X%02X%02X"
+ "."
+ "%02X%02X%02X%02X"
+ "%s",
+ aPartNum,
+ rand_buf1[0], rand_buf1[1], rand_buf1[2], rand_buf1[3],
+ rand_buf2[0], rand_buf2[1], rand_buf2[2], rand_buf2[3],
+ domain);
+
+ return retVal;
+}
+
+void
+GetFolderURIFromUserPrefs(nsMsgDeliverMode aMode, nsIMsgIdentity* identity, nsCString& uri)
+{
+ nsresult rv;
+ uri.Truncate();
+
+ // QueueForLater (Outbox)
+ if (aMode == nsIMsgSend::nsMsgQueueForLater ||
+ aMode == nsIMsgSend::nsMsgDeliverBackground)
+ {
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+ rv = prefs->GetCharPref("mail.default_sendlater_uri", getter_Copies(uri));
+ if (NS_FAILED(rv) || uri.IsEmpty())
+ uri.AssignLiteral(ANY_SERVER);
+ else
+ {
+ // check if uri is unescaped, and if so, escape it and reset the pef.
+ if (uri.FindChar(' ') != kNotFound)
+ {
+ MsgReplaceSubstring(uri, " ", "%20");
+ prefs->SetCharPref("mail.default_sendlater_uri", uri.get());
+ }
+ }
+ return;
+ }
+
+ if (!identity)
+ return;
+
+ if (aMode == nsIMsgSend::nsMsgSaveAsDraft) // SaveAsDraft (Drafts)
+ rv = identity->GetDraftFolder(uri);
+ else if (aMode == nsIMsgSend::nsMsgSaveAsTemplate) // SaveAsTemplate (Templates)
+ rv = identity->GetStationeryFolder(uri);
+ else
+ {
+ bool doFcc = false;
+ rv = identity->GetDoFcc(&doFcc);
+ if (doFcc)
+ rv = identity->GetFccFolder(uri);
+ }
+ return;
+}
+
+/**
+ * Check if we should use format=flowed (RFC 2646) for a mail.
+ * We will use format=flowed unless the preference tells us not to do so.
+ * In this function we set all the serialiser flags.
+ * 'formatted' is always 'true'.
+ */
+void GetSerialiserFlags(const char* charset, bool* flowed, bool* delsp, bool* formatted, bool* disallowBreaks)
+{
+ *flowed = false;
+ *delsp = false;
+ *formatted = true;
+ *disallowBreaks = true;
+
+ // Set format=flowed as in RFC 2646 according to the preference.
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv)) {
+ prefs->GetBoolPref("mailnews.send_plaintext_flowed", flowed);
+ }
+
+ // We could test statefulCharset(charset) here, but since ISO-2022-JP is the
+ // only one left we support, we might as well check for it directly.
+ if (PL_strcasecmp(charset, "ISO-2022-JP") == 0) {
+ // Make sure we honour RFC 1468. For encoding in ISO-2022-JP we need to
+ // send short lines to allow 7bit transfer encoding.
+ *disallowBreaks = false;
+ if (*flowed)
+ *delsp = true;
+ }
+}
diff --git a/mailnews/compose/src/nsMsgCompUtils.h b/mailnews/compose/src/nsMsgCompUtils.h
new file mode 100644
index 000000000..13d6ddd83
--- /dev/null
+++ b/mailnews/compose/src/nsMsgCompUtils.h
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsMsgCompUtils_H_
+#define _nsMsgCompUtils_H_
+
+#include "nscore.h"
+#include "nsMsgSend.h"
+#include "nsMsgCompFields.h"
+#include "nsIMsgSend.h"
+#include "nsIMsgCompUtils.h"
+
+class nsIPrompt;
+
+#define ANY_SERVER "anyfolder://"
+
+// these are msg hdr property names for storing the original
+// msg uri's and disposition(replied/forwarded) when queuing
+// messages to send later.
+#define ORIG_URI_PROPERTY "origURIs"
+#define QUEUED_DISPOSITION_PROPERTY "queuedDisposition"
+
+class nsMsgCompUtils : public nsIMsgCompUtils
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGCOMPUTILS
+
+ nsMsgCompUtils();
+
+private:
+ virtual ~nsMsgCompUtils();
+};
+
+PR_BEGIN_EXTERN_C
+
+//
+// Create a file spec or file name using the name passed
+// in as a template
+//
+nsresult nsMsgCreateTempFile(const char *tFileName, nsIFile **tFile);
+char *nsMsgCreateTempFileName(const char *tFileName);
+
+
+//
+// Various utilities for building parts of MIME encoded
+// messages during message composition
+//
+
+nsresult mime_sanity_check_fields_recipients (
+ const char *to,
+ const char *cc,
+ const char *bcc,
+ const char *newsgroups);
+
+nsresult 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*/);
+
+nsresult mime_generate_headers(nsIMsgCompFields *fields,
+ nsMsgDeliverMode deliver_mode,
+ msgIWritableStructuredHeaders *headers);
+
+char *mime_make_separator(const char *prefix);
+char *mime_gen_content_id(uint32_t aPartNum, const char *aEmailAddress);
+
+char *mime_generate_attachment_headers (
+ const char *type,
+ const char *type_param,
+ const char *encoding,
+ const char *description,
+ const char *x_mac_type,
+ const char *x_mac_creator,
+ const char *real_name,
+ const char *base_url,
+ bool digest_p,
+ nsMsgAttachmentHandler *ma,
+ const char *attachmentCharset, // charset of the attachment (can be null)
+ const char *bodyCharset, // charset of the main body
+ bool bodyIsAsciiOnly,
+ const char *content_id,
+ bool aBodyDocument);
+
+char *msg_generate_message_id (nsIMsgIdentity*);
+
+bool mime_7bit_data_p (const char *string, uint32_t size);
+
+char *mime_fix_header_1 (const char *string, bool addr_p, bool news_p);
+char *mime_fix_header (const char *string);
+char *mime_fix_addr_header (const char *string);
+char *mime_fix_news_header (const char *string);
+
+bool mime_type_requires_b64_p (const char *type);
+bool mime_type_needs_charset (const char *type);
+
+char *msg_make_filename_qtext(const char *srcText, bool stripCRLFs);
+
+// Rip apart the URL and extract a reasonable value for the `real_name' slot.
+void msg_pick_real_name (nsMsgAttachmentHandler *attachment, const char16_t *proposedName, const char *charset);
+
+//
+// Informational calls...
+//
+void nsMsgMIMESetConformToStandard (bool conform_p);
+bool nsMsgMIMEGetConformToStandard (void);
+
+//
+// network service type calls...
+//
+nsresult nsMsgNewURL(nsIURI** aInstancePtrResult, const char * aSpec);
+bool nsMsgIsLocalFile(const char *url);
+char *nsMsgGetLocalFileFromURL(const char *url);
+
+char *nsMsgParseURLHost(const char *url);
+
+char *GenerateFileNameFromURI(nsIURI *aURL);
+
+//
+// Folder calls...
+//
+void GetFolderURIFromUserPrefs(nsMsgDeliverMode aMode, nsIMsgIdentity *identity, nsCString& uri);
+
+// Check if we should use format=flowed
+void GetSerialiserFlags(const char *charset, bool *flowed, bool *delsp, bool *formatted, bool *disallowBreaks);
+
+
+PR_END_EXTERN_C
+
+
+#endif /* _nsMsgCompUtils_H_ */
+
diff --git a/mailnews/compose/src/nsMsgCompose.cpp b/mailnews/compose/src/nsMsgCompose.cpp
new file mode 100644
index 000000000..58340bffa
--- /dev/null
+++ b/mailnews/compose/src/nsMsgCompose.cpp
@@ -0,0 +1,6052 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgCompose.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMText.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsIDOMHTMLLinkElement.h"
+#include "nsIDOMHTMLAnchorElement.h"
+#include "nsPIDOMWindow.h"
+#include "mozIDOMWindow.h"
+#include "nsISelectionController.h"
+#include "nsMsgI18N.h"
+#include "nsMsgCompCID.h"
+#include "nsMsgQuote.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIDocumentEncoder.h" // for editor output flags
+#include "nsMsgCompUtils.h"
+#include "nsComposeStrings.h"
+#include "nsIMsgSend.h"
+#include "nsMailHeaders.h"
+#include "nsMsgPrompts.h"
+#include "nsMimeTypes.h"
+#include "nsICharsetConverterManager.h"
+#include "nsTextFormatter.h"
+#include "nsIPlaintextEditor.h"
+#include "nsIHTMLEditor.h"
+#include "nsIEditorMailSupport.h"
+#include "plstr.h"
+#include "prmem.h"
+#include "nsIDocShell.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "nsAbBaseCID.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsCExternalHandlerService.h"
+#include "nsIMIMEService.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIWindowMediator.h"
+#include "nsIURL.h"
+#include "nsIMsgMailSession.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgMimeCID.h"
+#include "nsDateTimeFormatCID.h"
+#include "nsIDateTimeFormat.h"
+#include "nsILocaleService.h"
+#include "nsILocale.h"
+#include "nsIMsgComposeService.h"
+#include "nsIMsgComposeProgressParams.h"
+#include "nsMsgUtils.h"
+#include "nsIMsgImapMailFolder.h"
+#include "nsImapCore.h"
+#include "nsUnicharUtils.h"
+#include "nsNetUtil.h"
+#include "nsIContentViewer.h"
+#include "nsIMsgMdnGenerator.h"
+#include "plbase64.h"
+#include "nsUConvCID.h"
+#include "nsIUnicodeNormalizer.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgAttachment.h"
+#include "nsIMsgProgress.h"
+#include "nsMsgFolderFlags.h"
+#include "nsIMsgDatabase.h"
+#include "nsStringStream.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "nsIMsgWindow.h"
+#include "nsITextToSubURI.h"
+#include "nsIAbManager.h"
+#include "nsCRT.h"
+#include "mozilla/Services.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "mozilla/Preferences.h"
+#include "nsStreamConverter.h"
+#include "nsISelection.h"
+#include "nsJSEnvironment.h"
+#include "nsIObserverService.h"
+#include "nsIProtocolHandler.h"
+#include "nsContentUtils.h"
+#include "nsIFileURL.h"
+
+using namespace mozilla;
+using namespace mozilla::mailnews;
+
+static nsresult GetReplyHeaderInfo(int32_t* reply_header_type,
+ nsString& reply_header_locale,
+ nsString& reply_header_authorwrote,
+ nsString& reply_header_ondateauthorwrote,
+ nsString& reply_header_authorwroteondate,
+ nsString& reply_header_originalmessage)
+{
+ nsresult rv;
+ *reply_header_type = 0;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If fetching any of the preferences fails,
+ // we return early with header_type = 0 meaning "no header".
+ rv = NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.reply_header_locale", EmptyString(), reply_header_locale);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_GetLocalizedUnicharPreference(prefBranch, "mailnews.reply_header_authorwrotesingle",
+ reply_header_authorwrote);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_GetLocalizedUnicharPreference(prefBranch, "mailnews.reply_header_ondateauthorwrote",
+ reply_header_ondateauthorwrote);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_GetLocalizedUnicharPreference(prefBranch, "mailnews.reply_header_authorwroteondate",
+ reply_header_authorwroteondate);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_GetLocalizedUnicharPreference(prefBranch, "mailnews.reply_header_originalmessage",
+ reply_header_originalmessage);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return prefBranch->GetIntPref("mailnews.reply_header_type", reply_header_type);
+}
+
+static void TranslateLineEnding(nsString& data)
+{
+ char16_t* rPtr; //Read pointer
+ char16_t* wPtr; //Write pointer
+ char16_t* sPtr; //Start data pointer
+ char16_t* ePtr; //End data pointer
+
+ rPtr = wPtr = sPtr = data.BeginWriting();
+ ePtr = rPtr + data.Length();
+
+ while (rPtr < ePtr)
+ {
+ if (*rPtr == nsCRT::CR) {
+ *wPtr = nsCRT::LF;
+ if (rPtr + 1 < ePtr && *(rPtr + 1) == nsCRT::LF)
+ rPtr ++;
+ }
+ else
+ *wPtr = *rPtr;
+
+ rPtr ++;
+ wPtr ++;
+ }
+
+ data.SetLength(wPtr - sPtr);
+}
+
+static void GetTopmostMsgWindowCharacterSet(nsCString& charset, bool* charsetOverride)
+{
+ // HACK: if we are replying to a message and that message used a charset over ride
+ // (as specified in the top most window (assuming the reply originated from that window)
+ // then use that over ride charset instead of the charset specified in the message
+ nsCOMPtr <nsIMsgMailSession> mailSession (do_GetService(NS_MSGMAILSESSION_CONTRACTID));
+ if (mailSession)
+ {
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+ if (msgWindow)
+ {
+ msgWindow->GetMailCharacterSet(charset);
+ msgWindow->GetCharsetOverride(charsetOverride);
+ }
+ }
+}
+
+nsMsgCompose::nsMsgCompose()
+{
+
+ mQuotingToFollow = false;
+ mInsertingQuotedContent = false;
+ mWhatHolder = 1;
+ m_window = nullptr;
+ m_editor = nullptr;
+ mQuoteStreamListener=nullptr;
+ mCharsetOverride = false;
+ mAnswerDefaultCharset = false;
+ mDeleteDraft = false;
+ m_compFields = nullptr; //m_compFields will be set during nsMsgCompose::Initialize
+ mType = nsIMsgCompType::New;
+
+ // For TagConvertible
+ // Read and cache pref
+ mConvertStructs = false;
+ nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefBranch)
+ prefBranch->GetBoolPref("converter.html2txt.structs", &mConvertStructs);
+
+ m_composeHTML = false;
+}
+
+
+nsMsgCompose::~nsMsgCompose()
+{
+ NS_IF_RELEASE(m_compFields);
+ NS_IF_RELEASE(mQuoteStreamListener);
+}
+
+/* the following macro actually implement addref, release and query interface for our component. */
+NS_IMPL_ISUPPORTS(nsMsgCompose, nsIMsgCompose, nsIMsgSendListener,
+ nsISupportsWeakReference)
+
+//
+// Once we are here, convert the data which we know to be UTF-8 to UTF-16
+// for insertion into the editor
+//
+nsresult
+GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, int32_t &aOffset)
+{
+ NS_ASSERTION((aChild && aParent), "bad args");
+ nsresult result = NS_ERROR_NULL_POINTER;
+ if (aChild && aParent)
+ {
+ nsCOMPtr<nsIDOMNodeList> childNodes;
+ result = aParent->GetChildNodes(getter_AddRefs(childNodes));
+ if ((NS_SUCCEEDED(result)) && (childNodes))
+ {
+ int32_t i=0;
+ for ( ; NS_SUCCEEDED(result); i++)
+ {
+ nsCOMPtr<nsIDOMNode> childNode;
+ result = childNodes->Item(i, getter_AddRefs(childNode));
+ if ((NS_SUCCEEDED(result)) && (childNode))
+ {
+ if (childNode.get()==aChild)
+ {
+ aOffset = i;
+ break;
+ }
+ }
+ else if (!childNode)
+ result = NS_ERROR_NULL_POINTER;
+ }
+ }
+ else if (!childNodes)
+ result = NS_ERROR_NULL_POINTER;
+ }
+ return result;
+}
+
+nsresult
+GetNodeLocation(nsIDOMNode *inChild, nsCOMPtr<nsIDOMNode> *outParent, int32_t *outOffset)
+{
+ NS_ASSERTION((outParent && outOffset), "bad args");
+ nsresult result = NS_ERROR_NULL_POINTER;
+ if (inChild && outParent && outOffset)
+ {
+ result = inChild->GetParentNode(getter_AddRefs(*outParent));
+ if ( (NS_SUCCEEDED(result)) && (*outParent) )
+ {
+ result = GetChildOffset(inChild, *outParent, *outOffset);
+ }
+ }
+
+ return result;
+}
+
+bool nsMsgCompose::IsEmbeddedObjectSafe(const char * originalScheme,
+ const char * originalHost,
+ const char * originalPath,
+ nsIDOMNode * object)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIDOMHTMLImageElement> image;
+ nsCOMPtr<nsIDOMHTMLLinkElement> link;
+ nsCOMPtr<nsIDOMHTMLAnchorElement> anchor;
+ nsAutoString objURL;
+
+ if (!object || !originalScheme || !originalPath) //having a null host is ok...
+ return false;
+
+ if ((image = do_QueryInterface(object)))
+ {
+ if (NS_FAILED(image->GetSrc(objURL)))
+ return false;
+ }
+ else if ((link = do_QueryInterface(object)))
+ {
+ if (NS_FAILED(link->GetHref(objURL)))
+ return false;
+ }
+ else if ((anchor = do_QueryInterface(object)))
+ {
+ if (NS_FAILED(anchor->GetHref(objURL)))
+ return false;
+ }
+ else
+ return false;
+
+ if (!objURL.IsEmpty())
+ {
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), objURL);
+ if (NS_SUCCEEDED(rv) && uri)
+ {
+ nsAutoCString scheme;
+ rv = uri->GetScheme(scheme);
+ if (NS_SUCCEEDED(rv) && scheme.Equals(originalScheme, nsCaseInsensitiveCStringComparator()))
+ {
+ nsAutoCString host;
+ rv = uri->GetAsciiHost(host);
+ // mailbox url don't have a host therefore don't be too strict.
+ if (NS_SUCCEEDED(rv) && (host.IsEmpty() || originalHost || host.Equals(originalHost, nsCaseInsensitiveCStringComparator())))
+ {
+ nsAutoCString path;
+ rv = uri->GetPath(path);
+ if (NS_SUCCEEDED(rv))
+ {
+ const char * query = strrchr(path.get(), '?');
+ if (query && PL_strncasecmp(path.get(), originalPath, query - path.get()) == 0)
+ return true; //This object is a part of the original message, we can send it safely.
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/* Reset the uri's of embedded objects because we've saved the draft message, and the
+ original message doesn't exist anymore.
+ */
+nsresult nsMsgCompose::ResetUrisForEmbeddedObjects()
+{
+ nsCOMPtr<nsIArray> aNodeList;
+ uint32_t numNodes;
+ uint32_t i;
+
+ nsCOMPtr<nsIEditorMailSupport> mailEditor (do_QueryInterface(m_editor));
+ if (!mailEditor)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = mailEditor->GetEmbeddedObjects(getter_AddRefs(aNodeList));
+ if (NS_FAILED(rv) || !aNodeList)
+ return NS_ERROR_FAILURE;
+
+ if (NS_FAILED(aNodeList->GetLength(&numNodes)))
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIDOMNode> node;
+ nsCString curDraftIdURL;
+
+ rv = m_compFields->GetDraftId(getter_Copies(curDraftIdURL));
+
+ // Skip if no draft id (probably a new draft msg).
+ if (NS_SUCCEEDED(rv) && mMsgSend && !curDraftIdURL.IsEmpty())
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgDBHdr;
+ rv = GetMsgDBHdrFromURI(curDraftIdURL.get(), getter_AddRefs(msgDBHdr));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get msg header DB interface pointer.");
+ if (NS_SUCCEEDED(rv) && msgDBHdr)
+ {
+ // build up the old and new ?number= parts. This code assumes it is
+ // called *before* RemoveCurrentDraftMessage, so that curDraftIdURL
+ // is the previous draft.
+ // This code works for both imap and local messages.
+ nsMsgKey newMsgKey;
+ nsCString folderUri;
+ nsCString baseMsgUri;
+ mMsgSend->GetMessageKey(&newMsgKey);
+ mMsgSend->GetFolderUri(folderUri);
+ nsCOMPtr<nsIMsgFolder> folder;
+ rv = GetExistingFolder(folderUri, getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ folder->GetBaseMessageURI(baseMsgUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMElement> domElement;
+ for (i = 0; i < numNodes; i ++)
+ {
+ domElement = do_QueryElementAt(aNodeList, i);
+ if (!domElement)
+ continue;
+
+ nsCOMPtr<nsIDOMHTMLImageElement> image = do_QueryInterface(domElement);
+ if (!image)
+ continue;
+ nsCString partNum;
+ mMsgSend->GetPartForDomIndex(i, partNum);
+ // do we care about anything besides images?
+ nsAutoString objURL;
+ image->GetSrc(objURL);
+
+ // First we need to make sure that the URL is associated with a message
+ // protocol so we don't accidentally manipulate a URL like:
+ // http://www.site.com/retrieve.html?C=image.jpg.
+ nsCOMPtr<nsIIOService> ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString scheme;
+ ioService->ExtractScheme(NS_ConvertUTF16toUTF8(objURL), scheme);
+
+ // Detect message protocols where attachments can occur.
+ nsCOMPtr<nsIProtocolHandler> handler;
+ ioService->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
+ if (!handler)
+ continue;
+ nsCOMPtr<nsIMsgMessageFetchPartService> mailHandler = do_QueryInterface(handler);
+ if (!mailHandler)
+ continue;
+
+ // the objURL is the full path to the embedded content. We need
+ // to update it with uri for the folder we just saved to, and the new
+ // msg key.
+ int32_t restOfUrlIndex = objURL.Find("?number=");
+ if (restOfUrlIndex == kNotFound)
+ restOfUrlIndex = objURL.FindChar('?');
+ else
+ restOfUrlIndex = objURL.FindChar('&', restOfUrlIndex);
+
+ if (restOfUrlIndex == kNotFound)
+ continue;
+
+ nsCString newURI(baseMsgUri);
+ newURI.Append('#');
+ newURI.AppendInt(newMsgKey);
+ nsString restOfUrl(Substring(objURL, restOfUrlIndex, objURL.Length() - restOfUrlIndex));
+ int32_t partIndex = restOfUrl.Find("part=");
+ if (partIndex != kNotFound)
+ {
+ partIndex += 5;
+ int32_t endPart = restOfUrl.FindChar('&', partIndex);
+ int32_t existingPartLen = (endPart == kNotFound) ? -1 : endPart - partIndex;
+ restOfUrl.Replace(partIndex, existingPartLen, NS_ConvertASCIItoUTF16(partNum));
+ }
+
+ nsCOMPtr<nsIMsgMessageService> msgService;
+ rv = GetMessageServiceFromURI(newURI, getter_AddRefs(msgService));
+ if (NS_FAILED(rv))
+ continue;
+ nsCOMPtr<nsIURI> newUrl;
+ rv = msgService->GetUrlForUri(newURI.get(), getter_AddRefs(newUrl), nullptr);
+ if (!newUrl)
+ continue;
+ nsCString spec;
+ rv = newUrl->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsString newSrc;
+ // mailbox urls will have ?number=xxx; imap urls won't. We need to
+ // handle both cases because we may be going from a mailbox url to
+ // and imap url, or vice versa, depending on the original folder,
+ // and the destination drafts folder.
+ bool specHasQ = (spec.FindChar('?') != kNotFound);
+ if (specHasQ && restOfUrl.CharAt(0) == '?')
+ restOfUrl.SetCharAt('&', 0);
+ else if (!specHasQ && restOfUrl.CharAt(0) == '&')
+ restOfUrl.SetCharAt('?', 0);
+ AppendUTF8toUTF16(spec, newSrc);
+ newSrc.Append(restOfUrl);
+ image->SetSrc(newSrc);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+
+/* The purpose of this function is to mark any embedded object that wasn't a RFC822 part
+ of the original message as moz-do-not-send.
+ That will prevent us to attach data not specified by the user or not present in the
+ original message.
+*/
+nsresult nsMsgCompose::TagEmbeddedObjects(nsIEditorMailSupport *aEditor)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIArray> aNodeList;
+ uint32_t count;
+ uint32_t i;
+
+ if (!aEditor)
+ return NS_ERROR_FAILURE;
+
+ rv = aEditor->GetEmbeddedObjects(getter_AddRefs(aNodeList));
+ if (NS_FAILED(rv) || !aNodeList)
+ return NS_ERROR_FAILURE;
+
+ if (NS_FAILED(aNodeList->GetLength(&count)))
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIURI> originalUrl;
+ nsCString originalScheme;
+ nsCString originalHost;
+ nsCString originalPath;
+
+ // first, convert the rdf original msg uri into a url that represents the message...
+ nsCOMPtr <nsIMsgMessageService> msgService;
+ rv = GetMessageServiceFromURI(mOriginalMsgURI, getter_AddRefs(msgService));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = msgService->GetUrlForUri(mOriginalMsgURI.get(), getter_AddRefs(originalUrl), nullptr);
+ if (NS_SUCCEEDED(rv) && originalUrl)
+ {
+ originalUrl->GetScheme(originalScheme);
+ originalUrl->GetAsciiHost(originalHost);
+ originalUrl->GetPath(originalPath);
+ }
+ }
+
+ // Then compare the url of each embedded objects with the original message.
+ // If they a not coming from the original message, they should not be sent
+ // with the message.
+ for (i = 0; i < count; i ++)
+ {
+ nsCOMPtr<nsIDOMNode> node = do_QueryElementAt(aNodeList, i);
+ if (!node)
+ continue;
+ if (IsEmbeddedObjectSafe(originalScheme.get(), originalHost.get(),
+ originalPath.get(), node))
+ continue; //Don't need to tag this object, it safe to send it.
+
+ //The source of this object should not be sent with the message
+ nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(node);
+ if (domElement)
+ domElement->SetAttribute(NS_LITERAL_STRING("moz-do-not-send"), NS_LITERAL_STRING("true"));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::GetInsertingQuotedContent(bool * aInsertingQuotedText)
+{
+ NS_ENSURE_ARG_POINTER(aInsertingQuotedText);
+ *aInsertingQuotedText = mInsertingQuotedContent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::SetInsertingQuotedContent(bool aInsertingQuotedText)
+{
+ mInsertingQuotedContent = aInsertingQuotedText;
+ return NS_OK;
+}
+
+void
+nsMsgCompose::InsertDivWrappedTextAtSelection(const nsAString &aText,
+ const nsAString &classStr)
+{
+ NS_ASSERTION(m_editor, "InsertDivWrappedTextAtSelection called, but no editor exists\n");
+ if (!m_editor)
+ return;
+
+ nsCOMPtr<nsIDOMElement> divElem;
+ nsCOMPtr<nsIHTMLEditor> htmlEditor(do_QueryInterface(m_editor));
+
+ nsresult rv = htmlEditor->CreateElementWithDefaults(NS_LITERAL_STRING("div"),
+ getter_AddRefs(divElem));
+
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCOMPtr<nsIDOMNode> divNode (do_QueryInterface(divElem));
+
+ // We need the document
+ nsCOMPtr<nsIDOMDocument> doc;
+ rv = m_editor->GetDocument(getter_AddRefs(doc));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ // Break up the text by newlines, and then insert text nodes followed
+ // by <br> nodes.
+ int32_t start = 0;
+ int32_t end = aText.Length();
+
+ for (;;)
+ {
+ int32_t delimiter = aText.FindChar('\n', start);
+ if (delimiter == kNotFound)
+ delimiter = end;
+
+ nsCOMPtr<nsIDOMText> textNode;
+ rv = doc->CreateTextNode(Substring(aText, start, delimiter - start), getter_AddRefs(textNode));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCOMPtr<nsIDOMNode> newTextNode = do_QueryInterface(textNode);
+ nsCOMPtr<nsIDOMNode> resultNode;
+ rv = divElem->AppendChild(newTextNode, getter_AddRefs(resultNode));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ // Now create and insert a BR
+ nsCOMPtr<nsIDOMElement> brElem;
+ rv = htmlEditor->CreateElementWithDefaults(NS_LITERAL_STRING("br"),
+ getter_AddRefs(brElem));
+ rv = divElem->AppendChild(brElem, getter_AddRefs(resultNode));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ if (delimiter == end)
+ break;
+ start = ++delimiter;
+ if (start == end)
+ break;
+ }
+
+ htmlEditor->InsertElementAtSelection(divElem, true);
+ nsCOMPtr<nsIDOMNode> parent;
+ int32_t offset;
+
+ rv = GetNodeLocation(divNode, address_of(parent), &offset);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsISelection> selection;
+ m_editor->GetSelection(getter_AddRefs(selection));
+
+ if (selection)
+ selection->Collapse(parent, offset + 1);
+ }
+ if (divElem)
+ divElem->SetAttribute(NS_LITERAL_STRING("class"), classStr);
+}
+
+/*
+ * The following function replaces <plaintext> tags with <x-plaintext>.
+ * <plaintext> is a funny beast: It leads to everything following it
+ * being displayed verbatim, even a </plaintext> tag is ignored.
+ */
+static void
+remove_plaintext_tag(nsString &body)
+{
+ // Replace all <plaintext> and </plaintext> tags.
+ int32_t index = 0;
+ bool replaced = false;
+ while ((index = body.Find("<plaintext", /* ignoreCase = */ true, index)) != kNotFound) {
+ body.Insert(u"x-", index+1);
+ index += 12;
+ replaced = true;
+ }
+ if (replaced) {
+ index = 0;
+ while ((index = body.Find("</plaintext", /* ignoreCase = */ true, index)) != kNotFound) {
+ body.Insert(u"x-", index+2);
+ index += 13;
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsMsgCompose::ConvertAndLoadComposeWindow(nsString& aPrefix,
+ nsString& aBuf,
+ nsString& aSignature,
+ bool aQuoted,
+ bool aHTMLEditor)
+{
+ NS_ASSERTION(m_editor, "ConvertAndLoadComposeWindow but no editor\n");
+ NS_ENSURE_TRUE(m_editor && m_identity, NS_ERROR_NOT_INITIALIZED);
+
+ // First, get the nsIEditor interface for future use
+ nsCOMPtr<nsIDOMNode> nodeInserted;
+
+ TranslateLineEnding(aPrefix);
+ TranslateLineEnding(aBuf);
+ TranslateLineEnding(aSignature);
+
+ m_editor->EnableUndo(false);
+
+ // Ok - now we need to figure out the charset of the aBuf we are going to send
+ // into the editor shell. There are I18N calls to sniff the data and then we need
+ // to call the new routine in the editor that will allow us to send in the charset
+ //
+
+ // Now, insert it into the editor...
+ nsCOMPtr<nsIHTMLEditor> htmlEditor (do_QueryInterface(m_editor));
+ nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(m_editor));
+ nsCOMPtr<nsIEditorMailSupport> mailEditor (do_QueryInterface(m_editor));
+ int32_t reply_on_top = 0;
+ bool sig_bottom = true;
+ m_identity->GetReplyOnTop(&reply_on_top);
+ m_identity->GetSigBottom(&sig_bottom);
+ bool sigOnTop = (reply_on_top == 1 && !sig_bottom);
+ bool isForwarded = (mType == nsIMsgCompType::ForwardInline);
+
+ if (aQuoted)
+ {
+ mInsertingQuotedContent = true;
+ if (!aPrefix.IsEmpty())
+ {
+ if (!aHTMLEditor)
+ aPrefix.AppendLiteral("\n");
+
+ int32_t reply_on_top = 0;
+ m_identity->GetReplyOnTop(&reply_on_top);
+ if (reply_on_top == 1)
+ {
+ // HTML editor eats one line break
+ if (aHTMLEditor)
+ textEditor->InsertLineBreak();
+
+ // add one newline if a signature comes before the quote, two otherwise
+ bool includeSignature = true;
+ bool sig_bottom = true;
+ bool attachFile = false;
+ nsString prefSigText;
+
+ m_identity->GetSigOnReply(&includeSignature);
+ m_identity->GetSigBottom(&sig_bottom);
+ m_identity->GetHtmlSigText(prefSigText);
+ nsresult rv = m_identity->GetAttachSignature(&attachFile);
+ if (includeSignature && !sig_bottom &&
+ ((NS_SUCCEEDED(rv) && attachFile) || !prefSigText.IsEmpty()))
+ textEditor->InsertLineBreak();
+ else {
+ textEditor->InsertLineBreak();
+ textEditor->InsertLineBreak();
+ }
+ }
+
+ InsertDivWrappedTextAtSelection(aPrefix,
+ NS_LITERAL_STRING("moz-cite-prefix"));
+ }
+
+ if (!aBuf.IsEmpty() && mailEditor)
+ {
+ // This leaves the caret at the right place to insert a bottom signature.
+ if (aHTMLEditor) {
+ nsAutoString body(aBuf);
+ remove_plaintext_tag(body);
+ mailEditor->InsertAsCitedQuotation(body,
+ mCiteReference,
+ true,
+ getter_AddRefs(nodeInserted));
+ } else {
+ mailEditor->InsertAsQuotation(aBuf,
+ getter_AddRefs(nodeInserted));
+ }
+ }
+
+ mInsertingQuotedContent = false;
+
+ (void)TagEmbeddedObjects(mailEditor);
+
+ if (!aSignature.IsEmpty())
+ {
+ //we cannot add it on top earlier, because TagEmbeddedObjects will mark all images in the signature as "moz-do-not-send"
+ if( sigOnTop )
+ m_editor->BeginningOfDocument();
+
+ if (aHTMLEditor && htmlEditor)
+ htmlEditor->InsertHTML(aSignature);
+ else if (htmlEditor)
+ {
+ textEditor->InsertLineBreak();
+ InsertDivWrappedTextAtSelection(aSignature,
+ NS_LITERAL_STRING("moz-signature"));
+ }
+
+ if( sigOnTop )
+ m_editor->EndOfDocument();
+ }
+ }
+ else
+ {
+ if (aHTMLEditor && htmlEditor)
+ {
+ mInsertingQuotedContent = true;
+ if (isForwarded && Substring(aBuf, 0, sizeof(MIME_FORWARD_HTML_PREFIX)-1)
+ .EqualsLiteral(MIME_FORWARD_HTML_PREFIX)) {
+ // We assign the opening tag inside "<HTML><BODY><BR><BR>" before the
+ // two <br> elements.
+ // This is a bit hacky but we know that the MIME code prepares the
+ // forwarded content like this:
+ // <HTML><BODY><BR><BR> + forwarded header + header table.
+ // Note: We only do this when we prepare the message to be forwarded,
+ // a re-opened saved draft of a forwarded message does not repeat this.
+ nsString newBody(aBuf);
+ nsString divTag;
+ divTag.AssignLiteral("<div class=\"moz-forward-container\">");
+ newBody.Insert(divTag, sizeof(MIME_FORWARD_HTML_PREFIX)-1-8);
+ remove_plaintext_tag(newBody);
+ htmlEditor->RebuildDocumentFromSource(newBody);
+ } else {
+ htmlEditor->RebuildDocumentFromSource(aBuf);
+ }
+ mInsertingQuotedContent = false;
+
+ // when forwarding a message as inline, tag any embedded objects
+ // which refer to local images or files so we know not to include
+ // send them
+ if (isForwarded)
+ (void)TagEmbeddedObjects(mailEditor);
+
+ if (!aSignature.IsEmpty())
+ {
+ if (isForwarded && sigOnTop) {
+ // Use our own function, nsEditor::BeginningOfDocument() would position
+ // into the <div class="moz-forward-container"> we've just created.
+ MoveToBeginningOfDocument();
+ } else {
+ // Use our own function, nsEditor::EndOfDocument() would position
+ // into the <div class="moz-forward-container"> we've just created.
+ MoveToEndOfDocument();
+ }
+ htmlEditor->InsertHTML(aSignature);
+ if (isForwarded && sigOnTop)
+ m_editor->EndOfDocument();
+ }
+ else
+ m_editor->EndOfDocument();
+ }
+ else if (htmlEditor)
+ {
+ bool sigOnTopInserted = false;
+ if (isForwarded && sigOnTop && !aSignature.IsEmpty())
+ {
+ textEditor->InsertLineBreak();
+ InsertDivWrappedTextAtSelection(aSignature,
+ NS_LITERAL_STRING("moz-signature"));
+ m_editor->EndOfDocument();
+ sigOnTopInserted = true;
+ }
+
+ if (!aBuf.IsEmpty())
+ {
+ nsresult rv;
+ nsCOMPtr<nsIDOMElement> divElem;
+ nsCOMPtr<nsIDOMNode> extraBr;
+
+ if (isForwarded) {
+ // Special treatment for forwarded messages: Part 1.
+ // Create a <div> of the required class.
+ rv = htmlEditor->CreateElementWithDefaults(NS_LITERAL_STRING("div"),
+ getter_AddRefs(divElem));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString attributeName;
+ nsAutoString attributeValue;
+ attributeName.AssignLiteral("class");
+ attributeValue.AssignLiteral("moz-forward-container");
+ divElem->SetAttribute(attributeName, attributeValue);
+
+ // We can't insert an empty <div>, so fill it with something.
+ nsCOMPtr<nsIDOMElement> brElem;
+ rv = htmlEditor->CreateElementWithDefaults(NS_LITERAL_STRING("br"),
+ getter_AddRefs(brElem));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = divElem->AppendChild(brElem, getter_AddRefs(extraBr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Insert the non-empty <div> into the DOM.
+ rv = htmlEditor->InsertElementAtSelection(divElem, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Position into the div, so out content goes there.
+ nsCOMPtr<nsISelection> selection;
+ m_editor->GetSelection(getter_AddRefs(selection));
+ rv = selection->Collapse(divElem, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (mailEditor) {
+ rv = mailEditor->InsertTextWithQuotations(aBuf);
+ } else {
+ // Will we ever get here?
+ rv = textEditor->InsertText(aBuf);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isForwarded) {
+ // Special treatment for forwarded messages: Part 2.
+ if (sigOnTopInserted) {
+ // Sadly the M-C editor inserts a <br> between the <div> for the signature
+ // and this <div>, so remove the <br> we don't want.
+ nsCOMPtr<nsIDOMNode> brBeforeDiv;
+ nsAutoString tagLocalName;
+ rv = divElem->GetPreviousSibling(getter_AddRefs(brBeforeDiv));
+ if (NS_SUCCEEDED(rv) && brBeforeDiv) {
+ brBeforeDiv->GetLocalName(tagLocalName);
+ if (tagLocalName.EqualsLiteral("br")) {
+ rv = m_editor->DeleteNode(brBeforeDiv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ }
+
+ // Clean up the <br> we inserted.
+ rv = m_editor->DeleteNode(extraBr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Use our own function instead of nsEditor::EndOfDocument() because
+ // we don't want to position at the end of the div we've just created.
+ // It's OK to use, even if we're not forwarding and didn't create a
+ // <div>.
+ rv = MoveToEndOfDocument();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if ((!isForwarded || !sigOnTop) && !aSignature.IsEmpty()) {
+ textEditor->InsertLineBreak();
+ InsertDivWrappedTextAtSelection(aSignature,
+ NS_LITERAL_STRING("moz-signature"));
+ }
+ }
+ }
+
+ if (aBuf.IsEmpty())
+ m_editor->BeginningOfDocument();
+ else
+ {
+ switch (reply_on_top)
+ {
+ // This should set the cursor after the body but before the sig
+ case 0:
+ {
+ if (!textEditor)
+ {
+ m_editor->BeginningOfDocument();
+ break;
+ }
+
+ nsCOMPtr<nsISelection> selection = nullptr;
+ nsCOMPtr<nsIDOMNode> parent = nullptr;
+ int32_t offset;
+ nsresult rv;
+
+ // get parent and offset of mailcite
+ rv = GetNodeLocation(nodeInserted, address_of(parent), &offset);
+ if (NS_FAILED(rv) || (!parent))
+ {
+ m_editor->BeginningOfDocument();
+ break;
+ }
+
+ // get selection
+ m_editor->GetSelection(getter_AddRefs(selection));
+ if (!selection)
+ {
+ m_editor->BeginningOfDocument();
+ break;
+ }
+
+ // place selection after mailcite
+ selection->Collapse(parent, offset+1);
+
+ // insert a break at current selection
+ textEditor->InsertLineBreak();
+
+ // i'm not sure if you need to move the selection back to before the
+ // break. expirement.
+ selection->Collapse(parent, offset+1);
+
+ break;
+ }
+
+ case 2:
+ {
+ m_editor->SelectAll();
+ break;
+ }
+
+ // This should set the cursor to the top!
+ default:
+ {
+ m_editor->BeginningOfDocument();
+ break;
+ }
+ }
+ }
+
+ nsCOMPtr<nsISelectionController> selCon;
+ m_editor->GetSelectionController(getter_AddRefs(selCon));
+
+ if (selCon)
+ selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_ANCHOR_REGION, true);
+
+ m_editor->EnableUndo(true);
+ SetBodyModified(false);
+
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+ nsCOMPtr<nsIMsgComposeService> composeService (do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID));
+ composeService->TimeStamp("Finished inserting data into the editor. The window is finally ready!", false);
+#endif
+ return NS_OK;
+}
+
+/**
+ * Check the identity pref to include signature on replies and forwards.
+ */
+bool nsMsgCompose::CheckIncludeSignaturePrefs(nsIMsgIdentity *identity)
+{
+ bool includeSignature = true;
+ switch (mType)
+ {
+ case nsIMsgCompType::ForwardInline:
+ case nsIMsgCompType::ForwardAsAttachment:
+ identity->GetSigOnForward(&includeSignature);
+ break;
+ case nsIMsgCompType::Reply:
+ case nsIMsgCompType::ReplyAll:
+ case nsIMsgCompType::ReplyToList:
+ case nsIMsgCompType::ReplyToGroup:
+ case nsIMsgCompType::ReplyToSender:
+ case nsIMsgCompType::ReplyToSenderAndGroup:
+ identity->GetSigOnReply(&includeSignature);
+ break;
+ }
+ return includeSignature;
+}
+
+nsresult
+nsMsgCompose::SetQuotingToFollow(bool aVal)
+{
+ mQuotingToFollow = aVal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::GetQuotingToFollow(bool* quotingToFollow)
+{
+ NS_ENSURE_ARG(quotingToFollow);
+ *quotingToFollow = mQuotingToFollow;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::Initialize(nsIMsgComposeParams *aParams,
+ mozIDOMWindowProxy *aWindow,
+ nsIDocShell *aDocShell)
+{
+ NS_ENSURE_ARG_POINTER(aParams);
+ nsresult rv;
+
+ aParams->GetIdentity(getter_AddRefs(m_identity));
+
+ if (aWindow)
+ {
+ m_window = aWindow;
+ nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow);
+ NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIDocShellTreeItem> treeItem =
+ do_QueryInterface(window->GetDocShell());
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ rv = treeItem->GetTreeOwner(getter_AddRefs(treeOwner));
+ if (NS_FAILED(rv)) return rv;
+
+ m_baseWindow = do_QueryInterface(treeOwner);
+ }
+
+ MSG_ComposeFormat format;
+ aParams->GetFormat(&format);
+
+ MSG_ComposeType type;
+ aParams->GetType(&type);
+
+ nsCString originalMsgURI;
+ aParams->GetOriginalMsgURI(getter_Copies(originalMsgURI));
+ aParams->GetOrigMsgHdr(getter_AddRefs(mOrigMsgHdr));
+
+ nsCOMPtr<nsIMsgCompFields> composeFields;
+ aParams->GetComposeFields(getter_AddRefs(composeFields));
+
+ nsCOMPtr<nsIMsgComposeService> composeService = do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = composeService->DetermineComposeHTML(m_identity, format, &m_composeHTML);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (composeFields)
+ {
+ nsAutoCString draftId; // will get set for drafts and templates
+ rv = composeFields->GetDraftId(getter_Copies(draftId));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Set return receipt flag and type, and if we should attach a vCard
+ // by checking the identity prefs - but don't clobber the values for
+ // drafts and templates as they were set up already by mime when
+ // initializing the message.
+ if (m_identity && draftId.IsEmpty() && type != nsIMsgCompType::Template)
+ {
+ bool requestReturnReceipt = false;
+ rv = m_identity->GetRequestReturnReceipt(&requestReturnReceipt);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = composeFields->SetReturnReceipt(requestReturnReceipt);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t receiptType = nsIMsgMdnGenerator::eDntType;
+ rv = m_identity->GetReceiptHeaderType(&receiptType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = composeFields->SetReceiptHeaderType(receiptType);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool requestDSN = false;
+ rv = m_identity->GetRequestDSN(&requestDSN);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = composeFields->SetDSN(requestDSN);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool attachVCard;
+ rv = m_identity->GetAttachVCard(&attachVCard);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = composeFields->SetAttachVCard(attachVCard);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ nsCOMPtr<nsIMsgSendListener> externalSendListener;
+ aParams->GetSendListener(getter_AddRefs(externalSendListener));
+ if(externalSendListener)
+ AddMsgSendListener( externalSendListener );
+
+ nsCString smtpPassword;
+ aParams->GetSmtpPassword(getter_Copies(smtpPassword));
+ mSmtpPassword = smtpPassword;
+
+ aParams->GetHtmlToQuote(mHtmlToQuote);
+
+ if (aDocShell)
+ {
+ mDocShell = aDocShell;
+ // register the compose object with the compose service
+ rv = composeService->RegisterComposeDocShell(aDocShell, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return CreateMessage(originalMsgURI.get(), type, composeFields);
+}
+
+nsresult nsMsgCompose::SetDocumentCharset(const char *aCharset)
+{
+ NS_ENSURE_TRUE(m_compFields && m_editor, NS_ERROR_NOT_INITIALIZED);
+
+ // Set charset, this will be used for the MIME charset labeling.
+ m_compFields->SetCharacterSet(aCharset);
+
+ // notify the change to editor
+ nsCString charset;
+ if (aCharset)
+ charset = nsDependentCString(aCharset);
+ if (m_editor)
+ m_editor->SetDocumentCharacterSet(charset);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::RegisterStateListener(nsIMsgComposeStateListener *aStateListener)
+{
+ NS_ENSURE_ARG_POINTER(aStateListener);
+
+ return mStateListeners.AppendElement(aStateListener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::UnregisterStateListener(nsIMsgComposeStateListener *aStateListener)
+{
+ NS_ENSURE_ARG_POINTER(aStateListener);
+
+ return mStateListeners.RemoveElement(aStateListener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+// Added to allow easier use of the nsIMsgSendListener
+NS_IMETHODIMP nsMsgCompose::AddMsgSendListener( nsIMsgSendListener *aMsgSendListener )
+{
+ NS_ENSURE_ARG_POINTER(aMsgSendListener);
+ return mExternalSendListeners.AppendElement(aMsgSendListener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsMsgCompose::RemoveMsgSendListener( nsIMsgSendListener *aMsgSendListener )
+{
+ NS_ENSURE_ARG_POINTER(aMsgSendListener);
+ return mExternalSendListeners.RemoveElement(aMsgSendListener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::SendMsgToServer(MSG_DeliverMode deliverMode, nsIMsgIdentity *identity,
+ const char *accountKey)
+{
+ nsresult rv = NS_OK;
+
+ // clear saved message id if sending, so we don't send out the same message-id.
+ if (deliverMode == nsIMsgCompDeliverMode::Now ||
+ deliverMode == nsIMsgCompDeliverMode::Later ||
+ deliverMode == nsIMsgCompDeliverMode::Background)
+ m_compFields->SetMessageId("");
+
+ if (m_compFields && identity)
+ {
+ // Pref values are supposed to be stored as UTF-8, so no conversion
+ nsCString email;
+ nsString fullName;
+ nsString organization;
+
+ identity->GetEmail(email);
+ identity->GetFullName(fullName);
+ identity->GetOrganization(organization);
+
+ const char* pFrom = m_compFields->GetFrom();
+ if (!pFrom || !*pFrom)
+ {
+ nsCString sender;
+ MakeMimeAddress(NS_ConvertUTF16toUTF8(fullName), email, sender);
+ m_compFields->SetFrom(sender.IsEmpty() ? email.get() : sender.get());
+ }
+
+ m_compFields->SetOrganization(organization);
+
+ // We need an nsIMsgSend instance to send the message. Allow extensions
+ // to override the default SMTP sender by observing mail-set-sender.
+ mMsgSend = nullptr;
+ mDeliverMode = deliverMode; // save for possible access by observer.
+
+ // Allow extensions to specify an outgoing server.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ NS_ENSURE_STATE(observerService);
+
+ // Assemble a string with sending parameters.
+ nsAutoString sendParms;
+
+ // First parameter: account key. This may be null.
+ sendParms.AppendASCII(accountKey && *accountKey ? accountKey : "");
+ sendParms.AppendLiteral(",");
+
+ // Second parameter: deliverMode.
+ sendParms.AppendInt(deliverMode);
+ sendParms.AppendLiteral(",");
+
+ // Third parameter: identity (as identity key).
+ nsAutoCString identityKey;
+ identity->GetKey(identityKey);
+ sendParms.AppendASCII(identityKey.get());
+
+ observerService->NotifyObservers(
+ NS_ISUPPORTS_CAST(nsIMsgCompose*, this),
+ "mail-set-sender",
+ sendParms.get());
+
+ if (!mMsgSend)
+ mMsgSend = do_CreateInstance(NS_MSGSEND_CONTRACTID);
+
+ if (mMsgSend)
+ {
+ nsCString bodyString(m_compFields->GetBody());
+
+ // Create the listener for the send operation...
+ nsCOMPtr<nsIMsgComposeSendListener> composeSendListener = do_CreateInstance(NS_MSGCOMPOSESENDLISTENER_CONTRACTID);
+ if (!composeSendListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // right now, AutoSaveAsDraft is identical to SaveAsDraft as
+ // far as the msg send code is concerned. This way, we don't have
+ // to add an nsMsgDeliverMode for autosaveasdraft, and add cases for
+ // it in the msg send code.
+ if (deliverMode == nsIMsgCompDeliverMode::AutoSaveAsDraft)
+ deliverMode = nsIMsgCompDeliverMode::SaveAsDraft;
+
+ RefPtr<nsIMsgCompose> msgCompose(this);
+ composeSendListener->SetMsgCompose(msgCompose);
+ composeSendListener->SetDeliverMode(deliverMode);
+
+ if (mProgress)
+ {
+ nsCOMPtr<nsIWebProgressListener> progressListener = do_QueryInterface(composeSendListener);
+ mProgress->RegisterListener(progressListener);
+ }
+
+ // If we are composing HTML, then this should be sent as
+ // multipart/related which means we pass the editor into the
+ // backend...if not, just pass nullptr
+ //
+ nsCOMPtr<nsIMsgSendListener> sendListener = do_QueryInterface(composeSendListener);
+ rv = mMsgSend->CreateAndSendMessage(
+ m_composeHTML ? m_editor.get() : nullptr,
+ identity,
+ accountKey,
+ m_compFields,
+ false,
+ false,
+ (nsMsgDeliverMode)deliverMode,
+ nullptr,
+ m_composeHTML ? TEXT_HTML : TEXT_PLAIN,
+ bodyString,
+ nullptr,
+ nullptr,
+ m_window,
+ mProgress,
+ sendListener,
+ mSmtpPassword.get(),
+ mOriginalMsgURI,
+ mType);
+ }
+ else
+ rv = NS_ERROR_FAILURE;
+ }
+ else
+ rv = NS_ERROR_NOT_INITIALIZED;
+
+ if (NS_FAILED(rv))
+ NotifyStateListeners(nsIMsgComposeNotificationType::ComposeProcessDone, rv);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgCompose::SendMsg(MSG_DeliverMode deliverMode, nsIMsgIdentity *identity, const char *accountKey, nsIMsgWindow *aMsgWindow, nsIMsgProgress *progress)
+{
+ NS_ENSURE_TRUE(m_compFields, NS_ERROR_NOT_INITIALIZED);
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIPrompt> prompt;
+
+ // i'm assuming the compose window is still up at this point...
+ if (m_window) {
+ nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(m_window);
+ window->GetPrompter(getter_AddRefs(prompt));
+ }
+
+ // Set content type based on which type of compose window we had.
+ nsString contentType = (m_composeHTML) ? NS_LITERAL_STRING("text/html"):
+ NS_LITERAL_STRING("text/plain");
+ nsString msgBody;
+ if (m_editor)
+ {
+ // Reset message body previously stored in the compose fields
+ // There is 2 nsIMsgCompFields::SetBody() functions using a pointer as argument,
+ // therefore a casting is required.
+ m_compFields->SetBody((const char *)nullptr);
+
+ const char *charset = m_compFields->GetCharacterSet();
+
+ uint32_t flags = nsIDocumentEncoder::OutputCRLineBreak |
+ nsIDocumentEncoder::OutputLFLineBreak;
+
+ if (m_composeHTML) {
+ flags |= nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputDisallowLineBreaking;
+ } else {
+ bool flowed, delsp, formatted, disallowBreaks;
+ GetSerialiserFlags(charset, &flowed, &delsp, &formatted, &disallowBreaks);
+ if (flowed)
+ flags |= nsIDocumentEncoder::OutputFormatFlowed;
+ if (delsp)
+ flags |= nsIDocumentEncoder::OutputFormatDelSp;
+ if (formatted)
+ flags |= nsIDocumentEncoder::OutputFormatted;
+ if (disallowBreaks)
+ flags |= nsIDocumentEncoder::OutputDisallowLineBreaking;
+ // Don't lose NBSP in the plain text encoder.
+ flags |= nsIDocumentEncoder::OutputPersistNBSP;
+ }
+ rv = m_editor->OutputToString(contentType, flags, msgBody);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else
+ {
+ m_compFields->GetBody(msgBody);
+ }
+ if (!msgBody.IsEmpty())
+ {
+ // Convert body to mail charset
+ nsCString outCString;
+ rv = nsMsgI18NConvertFromUnicode(m_compFields->GetCharacterSet(),
+ msgBody, outCString, false, true);
+ bool isAsciiOnly = NS_IsAscii(outCString.get()) &&
+ !nsMsgI18Nstateful_charset(m_compFields->GetCharacterSet());
+ if (m_compFields->GetForceMsgEncoding())
+ isAsciiOnly = false;
+ if (NS_SUCCEEDED(rv) && !outCString.IsEmpty())
+ {
+ // If the body contains characters outside the repertoire of the current
+ // charset, just convert to UTF-8 and be done with it
+ // unless disable_fallback_to_utf8 is set for this charset.
+ if (NS_ERROR_UENC_NOMAPPING == rv)
+ {
+ bool needToCheckCharset;
+ m_compFields->GetNeedToCheckCharset(&needToCheckCharset);
+ if (needToCheckCharset)
+ {
+ bool disableFallback = false;
+ nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (prefBranch)
+ {
+ nsCString prefName("mailnews.disable_fallback_to_utf8.");
+ prefName.Append(m_compFields->GetCharacterSet());
+ prefBranch->GetBoolPref(prefName.get(), &disableFallback);
+ }
+ if (!disableFallback)
+ {
+ CopyUTF16toUTF8(msgBody, outCString);
+ m_compFields->SetCharacterSet("UTF-8");
+ SetDocumentCharset("UTF-8");
+ }
+ }
+ }
+ m_compFields->SetBodyIsAsciiOnly(isAsciiOnly);
+ m_compFields->SetBody(outCString.get());
+ }
+ else
+ {
+ m_compFields->SetBody(NS_ConvertUTF16toUTF8(msgBody).get());
+ m_compFields->SetCharacterSet("UTF-8");
+ SetDocumentCharset("UTF-8");
+ }
+ }
+
+ // Let's open the progress dialog
+ if (progress)
+ {
+ mProgress = progress;
+
+ if (deliverMode != nsIMsgCompDeliverMode::AutoSaveAsDraft)
+ {
+ nsAutoString msgSubject;
+ m_compFields->GetSubject(msgSubject);
+
+ bool showProgress = false;
+ nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefBranch)
+ {
+ prefBranch->GetBoolPref("mailnews.show_send_progress", &showProgress);
+ if (showProgress)
+ {
+ nsCOMPtr<nsIMsgComposeProgressParams> params = do_CreateInstance(NS_MSGCOMPOSEPROGRESSPARAMS_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || !params)
+ return NS_ERROR_FAILURE;
+
+ params->SetSubject(msgSubject.get());
+ params->SetDeliveryMode(deliverMode);
+
+ mProgress->OpenProgressDialog(m_window, aMsgWindow,
+ "chrome://messenger/content/messengercompose/sendProgress.xul",
+ false, params);
+ }
+ }
+ }
+
+ mProgress->OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_START, NS_OK);
+ }
+
+ bool attachVCard = false;
+ m_compFields->GetAttachVCard(&attachVCard);
+
+ if (attachVCard && identity &&
+ (deliverMode == nsIMsgCompDeliverMode::Now ||
+ deliverMode == nsIMsgCompDeliverMode::Later ||
+ deliverMode == nsIMsgCompDeliverMode::Background))
+ {
+ nsCString escapedVCard;
+ // make sure, if there is no card, this returns an empty string, or NS_ERROR_FAILURE
+ rv = identity->GetEscapedVCard(escapedVCard);
+
+ if (NS_SUCCEEDED(rv) && !escapedVCard.IsEmpty())
+ {
+ nsCString vCardUrl;
+ vCardUrl = "data:text/x-vcard;charset=utf-8;base64,";
+ nsCString unescapedData;
+ MsgUnescapeString(escapedVCard, 0, unescapedData);
+ char *result = PL_Base64Encode(unescapedData.get(), 0, nullptr);
+ vCardUrl += result;
+ PR_Free(result);
+
+ nsCOMPtr<nsIMsgAttachment> attachment = do_CreateInstance(NS_MSGATTACHMENT_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && attachment)
+ {
+ // [comment from 4.x]
+ // Send the vCard out with a filename which distinguishes this user. e.g. jsmith.vcf
+ // The main reason to do this is for interop with Eudora, which saves off
+ // the attachments separately from the message body
+ nsCString userid;
+ (void)identity->GetEmail(userid);
+ int32_t index = userid.FindChar('@');
+ if (index != kNotFound)
+ userid.SetLength(index);
+
+ if (userid.IsEmpty())
+ attachment->SetName(NS_LITERAL_STRING("vcard.vcf"));
+ else
+ {
+ // Replace any dot with underscore to stop vCards
+ // generating false positives with some heuristic scanners
+ MsgReplaceChar(userid, '.', '_');
+ userid.AppendLiteral(".vcf");
+ attachment->SetName(NS_ConvertASCIItoUTF16(userid));
+ }
+
+ attachment->SetUrl(vCardUrl);
+ m_compFields->AddAttachment(attachment);
+ }
+ }
+ }
+
+ // Save the identity being sent for later use.
+ m_identity = identity;
+
+ rv = SendMsgToServer(deliverMode, identity, accountKey);
+ if (NS_FAILED(rv))
+ {
+ nsCOMPtr<nsIMsgSendReport> sendReport;
+ if (mMsgSend)
+ mMsgSend->GetSendReport(getter_AddRefs(sendReport));
+ if (sendReport)
+ {
+ nsresult theError;
+ sendReport->DisplayReport(prompt, true, true, &theError);
+ }
+ else
+ {
+ /* If we come here it's because we got an error before we could intialize a
+ send report! Let's try our best...
+ */
+ switch (deliverMode)
+ {
+ case nsIMsgCompDeliverMode::Later:
+ nsMsgDisplayMessageByName(prompt, u"unableToSendLater");
+ break;
+ case nsIMsgCompDeliverMode::AutoSaveAsDraft:
+ case nsIMsgCompDeliverMode::SaveAsDraft:
+ nsMsgDisplayMessageByName(prompt, u"unableToSaveDraft");
+ break;
+ case nsIMsgCompDeliverMode::SaveAsTemplate:
+ nsMsgDisplayMessageByName(prompt, u"unableToSaveTemplate");
+ break;
+
+ default:
+ nsMsgDisplayMessageByName(prompt, u"sendFailed");
+ break;
+ }
+ }
+
+ if (progress)
+ progress->CloseProgressDialog(true);
+ }
+
+ return rv;
+}
+
+/* attribute boolean deleteDraft */
+NS_IMETHODIMP nsMsgCompose::GetDeleteDraft(bool *aDeleteDraft)
+{
+ NS_ENSURE_ARG_POINTER(aDeleteDraft);
+ *aDeleteDraft = mDeleteDraft;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::SetDeleteDraft(bool aDeleteDraft)
+{
+ mDeleteDraft = aDeleteDraft;
+ return NS_OK;
+}
+
+bool nsMsgCompose::IsLastWindow()
+{
+ nsresult rv;
+ bool more;
+ nsCOMPtr<nsIWindowMediator> windowMediator =
+ do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
+ rv = windowMediator->GetEnumerator(nullptr,
+ getter_AddRefs(windowEnumerator));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsISupports> isupports;
+
+ if (NS_SUCCEEDED(windowEnumerator->GetNext(getter_AddRefs(isupports))))
+ if (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)))
+ return !more;
+ }
+ }
+ return true;
+}
+
+NS_IMETHODIMP nsMsgCompose::CloseWindow(void)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIMsgComposeService> composeService = do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // unregister the compose object with the compose service
+ rv = composeService->UnregisterComposeDocShell(mDocShell);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mDocShell = nullptr;
+
+ // ensure that the destructor of nsMsgSend is invoked to remove
+ // temporary files.
+ mMsgSend = nullptr;
+
+ //We are going away for real, we need to do some clean up first
+ if (m_baseWindow)
+ {
+ if (m_editor)
+ {
+ // The editor will be destroyed during the close window.
+ // Set it to null to be sure we won't use it anymore.
+ m_editor = nullptr;
+ }
+ nsIBaseWindow * window = m_baseWindow;
+ m_baseWindow = nullptr;
+ rv = window->Destroy();
+ }
+
+ m_window = nullptr;
+ return rv;
+}
+
+nsresult nsMsgCompose::Abort()
+{
+ if (mMsgSend)
+ mMsgSend->Abort();
+
+ if (mProgress)
+ mProgress->CloseProgressDialog(true);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::GetEditor(nsIEditor * *aEditor)
+{
+ NS_IF_ADDREF(*aEditor = m_editor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::SetEditor(nsIEditor *aEditor)
+{
+ m_editor = aEditor;
+ return NS_OK;
+}
+
+static nsresult fixCharset(nsCString &aCharset)
+{
+ // No matter what, we should block x-windows-949 (our internal name)
+ // from being used for outgoing emails (bug 234958).
+ if (aCharset.Equals("x-windows-949", nsCaseInsensitiveCStringComparator()))
+ aCharset = "EUC-KR";
+
+ // Convert to a canonical charset name.
+ // Bug 1297118 will revisit this call site.
+ nsresult rv;
+ nsCOMPtr<nsICharsetConverterManager> ccm =
+ do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString charset(aCharset);
+ rv = ccm->GetCharsetAlias(charset.get(), aCharset);
+
+ // Don't accept UTF-16 ever. UTF-16 should never be selected as an
+ // outgoing encoding for e-mail. MIME can't handle those messages
+ // encoded in ASCII-incompatible encodings.
+ if (NS_FAILED(rv) ||
+ StringBeginsWith(aCharset, NS_LITERAL_CSTRING("UTF-16"))) {
+ aCharset.AssignLiteral("UTF-8");
+ }
+ return NS_OK;
+}
+
+// This used to be called BEFORE editor was created
+// (it did the loadUrl that triggered editor creation)
+// It is called from JS after editor creation
+// (loadUrl is done in JS)
+NS_IMETHODIMP nsMsgCompose::InitEditor(nsIEditor* aEditor, mozIDOMWindowProxy* aContentWindow)
+{
+ NS_ENSURE_ARG_POINTER(aEditor);
+ NS_ENSURE_ARG_POINTER(aContentWindow);
+ nsresult rv;
+
+ m_editor = aEditor;
+
+ nsAutoCString msgCharSet(m_compFields->GetCharacterSet());
+ rv = fixCharset(msgCharSet);
+ NS_ENSURE_SUCCESS(rv, rv);
+ m_compFields->SetCharacterSet(msgCharSet.get());
+ m_editor->SetDocumentCharacterSet(msgCharSet);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aContentWindow);
+
+ nsIDocShell *docShell = window->GetDocShell();
+ NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIContentViewer> childCV;
+ NS_ENSURE_SUCCESS(docShell->GetContentViewer(getter_AddRefs(childCV)), NS_ERROR_FAILURE);
+ if (childCV)
+ {
+ // SetForceCharacterSet will complain about "UTF-7" or "x-mac-croatian"
+ // (see test-charset-edit.js), but we deal with this elsewhere.
+ rv = childCV->SetForceCharacterSet(msgCharSet);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetForceCharacterSet() failed");
+ }
+
+ // This is what used to be done in mDocumentListener,
+ // nsMsgDocumentStateListener::NotifyDocumentCreated()
+ bool quotingToFollow = false;
+ GetQuotingToFollow(&quotingToFollow);
+ if (quotingToFollow)
+ return BuildQuotedMessageAndSignature();
+ else
+ {
+ NotifyStateListeners(nsIMsgComposeNotificationType::ComposeFieldsReady, NS_OK);
+ rv = BuildBodyMessageAndSignature();
+ NotifyStateListeners(nsIMsgComposeNotificationType::ComposeBodyReady, NS_OK);
+ return rv;
+ }
+}
+
+NS_IMETHODIMP nsMsgCompose::GetBodyRaw(nsACString& aBodyRaw)
+{
+ aBodyRaw.Assign((char *)m_compFields->GetBody());
+ return NS_OK;
+}
+
+nsresult nsMsgCompose::GetBodyModified(bool * modified)
+{
+ nsresult rv;
+
+ if (! modified)
+ return NS_ERROR_NULL_POINTER;
+
+ *modified = true;
+
+ if (m_editor)
+ {
+ rv = m_editor->GetDocumentModified(modified);
+ if (NS_FAILED(rv))
+ *modified = true;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsMsgCompose::SetBodyModified(bool modified)
+{
+ nsresult rv = NS_OK;
+
+ if (m_editor)
+ {
+ if (modified)
+ {
+ int32_t modCount = 0;
+ m_editor->GetModificationCount(&modCount);
+ if (modCount == 0)
+ m_editor->IncrementModificationCount(1);
+ }
+ else
+ m_editor->ResetModificationCount();
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::GetDomWindow(mozIDOMWindowProxy * *aDomWindow)
+{
+ NS_IF_ADDREF(*aDomWindow = m_window);
+ return NS_OK;
+}
+
+nsresult nsMsgCompose::GetCompFields(nsIMsgCompFields * *aCompFields)
+{
+ *aCompFields = (nsIMsgCompFields*)m_compFields;
+ NS_IF_ADDREF(*aCompFields);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::GetComposeHTML(bool *aComposeHTML)
+{
+ *aComposeHTML = m_composeHTML;
+ return NS_OK;
+}
+
+nsresult nsMsgCompose::GetWrapLength(int32_t *aWrapLength)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ return prefBranch->GetIntPref("mailnews.wraplength", aWrapLength);
+}
+
+nsresult nsMsgCompose::CreateMessage(const char * originalMsgURI,
+ MSG_ComposeType type,
+ nsIMsgCompFields * compFields)
+{
+ nsresult rv = NS_OK;
+ mType = type;
+ mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_None;
+
+ mDeleteDraft = (type == nsIMsgCompType::Draft);
+ nsAutoCString msgUri(originalMsgURI);
+ bool fileUrl = StringBeginsWith(msgUri, NS_LITERAL_CSTRING("file:"));
+ int32_t typeIndex = msgUri.Find("type=application/x-message-display");
+ if (typeIndex != kNotFound && typeIndex > 0)
+ {
+ // Strip out type=application/x-message-display because it confuses libmime.
+ msgUri.Cut(typeIndex, sizeof("type=application/x-message-display"));
+ if (fileUrl) // we're dealing with an .eml file msg
+ {
+ // We have now removed the type from the uri. Make sure we don't have
+ // an uri with "&&" now. If we do, remove the second '&'.
+ if (msgUri.CharAt(typeIndex) == '&')
+ msgUri.Cut(typeIndex, 1);
+ // Remove possible trailing '?'.
+ if (msgUri.CharAt(msgUri.Length() - 1) == '?')
+ msgUri.Cut(msgUri.Length() - 1, 1);
+ }
+ else // we're dealing with a message/rfc822 attachment
+ {
+ // nsURLFetcher will check for "realtype=message/rfc822" and will set the
+ // content type to message/rfc822 in the forwarded message.
+ msgUri.Append("&realtype=message/rfc822");
+ }
+ originalMsgURI = msgUri.get();
+ }
+
+ if (compFields)
+ {
+ NS_IF_RELEASE(m_compFields);
+ m_compFields = reinterpret_cast<nsMsgCompFields*>(compFields);
+ NS_ADDREF(m_compFields);
+ }
+ else
+ {
+ m_compFields = new nsMsgCompFields();
+ if (m_compFields)
+ NS_ADDREF(m_compFields);
+ else
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (m_identity && mType != nsIMsgCompType::Draft)
+ {
+ // Setup reply-to field.
+ nsCString replyTo;
+ m_identity->GetReplyTo(replyTo);
+ if (!replyTo.IsEmpty())
+ {
+ nsCString resultStr;
+ RemoveDuplicateAddresses(nsDependentCString(m_compFields->GetReplyTo()),
+ replyTo, resultStr);
+ if (!resultStr.IsEmpty())
+ {
+ replyTo.Append(',');
+ replyTo.Append(resultStr);
+ }
+ m_compFields->SetReplyTo(replyTo.get());
+ }
+
+ // Setup auto-Cc field.
+ bool doCc;
+ m_identity->GetDoCc(&doCc);
+ if (doCc)
+ {
+ nsCString ccList;
+ m_identity->GetDoCcList(ccList);
+
+ nsCString resultStr;
+ RemoveDuplicateAddresses(nsDependentCString(m_compFields->GetCc()),
+ ccList, resultStr);
+ if (!resultStr.IsEmpty())
+ {
+ ccList.Append(',');
+ ccList.Append(resultStr);
+ }
+ m_compFields->SetCc(ccList.get());
+ }
+
+ // Setup auto-Bcc field.
+ bool doBcc;
+ m_identity->GetDoBcc(&doBcc);
+ if (doBcc)
+ {
+ nsCString bccList;
+ m_identity->GetDoBccList(bccList);
+
+ nsCString resultStr;
+ RemoveDuplicateAddresses(nsDependentCString(m_compFields->GetBcc()),
+ bccList, resultStr);
+ if (!resultStr.IsEmpty())
+ {
+ bccList.Append(',');
+ bccList.Append(resultStr);
+ }
+ m_compFields->SetBcc(bccList.get());
+ }
+ }
+
+ if (mType == nsIMsgCompType::Draft)
+ {
+ nsCString curDraftIdURL;
+ rv = m_compFields->GetDraftId(getter_Copies(curDraftIdURL));
+ NS_ASSERTION(NS_SUCCEEDED(rv) && !curDraftIdURL.IsEmpty(), "CreateMessage can't get draft id");
+
+ // Skip if no draft id (probably a new draft msg).
+ if (NS_SUCCEEDED(rv) && !curDraftIdURL.IsEmpty())
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgDBHdr;
+ rv = GetMsgDBHdrFromURI(curDraftIdURL.get(), getter_AddRefs(msgDBHdr));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateMessage can't get msg header DB interface pointer.");
+ if (msgDBHdr)
+ {
+ nsCString queuedDisposition;
+ msgDBHdr->GetStringProperty(QUEUED_DISPOSITION_PROPERTY, getter_Copies(queuedDisposition));
+ // We need to retrieve the original URI from the database so we can
+ // set the disposition flags correctly if the draft is a reply or forwarded message.
+ nsCString originalMsgURIfromDB;
+ msgDBHdr->GetStringProperty(ORIG_URI_PROPERTY, getter_Copies(originalMsgURIfromDB));
+ mOriginalMsgURI = originalMsgURIfromDB;
+ if (!queuedDisposition.IsEmpty())
+ {
+ if (queuedDisposition.Equals("replied"))
+ mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_Replied;
+ else if (queuedDisposition.Equals("forward"))
+ mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_Forwarded;
+ }
+ }
+ }
+ }
+
+ // If we don't have an original message URI, nothing else to do...
+ if (!originalMsgURI || *originalMsgURI == 0)
+ return NS_OK;
+
+ // store the original message URI so we can extract it after we send the message to properly
+ // mark any disposition flags like replied or forwarded on the message.
+ if (mOriginalMsgURI.IsEmpty())
+ mOriginalMsgURI = originalMsgURI;
+
+ nsCOMPtr<nsIPrefBranch> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // "Forward inline" and "Reply with template" processing.
+ // Note the early return at the end of the block.
+ if (type == nsIMsgCompType::ForwardInline ||
+ type == nsIMsgCompType::ReplyWithTemplate)
+ {
+ // Use charset set up in the compose fields by MIME unless we should
+ // use the default charset.
+ bool replyInDefault = false;
+ prefs->GetBoolPref("mailnews.reply_in_default_charset",
+ &replyInDefault);
+ // Use send_default_charset if reply_in_default_charset is on.
+ if (replyInDefault)
+ {
+ nsString str;
+ nsCString charset;
+ NS_GetLocalizedUnicharPreferenceWithDefault(prefs, "mailnews.send_default_charset",
+ EmptyString(), str);
+ if (!str.IsEmpty())
+ {
+ LossyCopyUTF16toASCII(str, charset);
+ m_compFields->SetCharacterSet(charset.get());
+ mAnswerDefaultCharset = true;
+ }
+ }
+
+ // We want to treat this message as a reference too
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = GetMsgDBHdrFromURI(originalMsgURI, getter_AddRefs(msgHdr));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString messageId;
+ msgHdr->GetMessageId(getter_Copies(messageId));
+
+ nsAutoCString reference;
+ // When forwarding we only use the original message for "References:" -
+ // recipients don't have the other messages anyway.
+ // For reply with template we want to preserve all the references.
+ if (type == nsIMsgCompType::ReplyWithTemplate)
+ {
+ uint16_t numReferences = 0;
+ msgHdr->GetNumReferences(&numReferences);
+ for (int32_t i = 0; i < numReferences; i++)
+ {
+ nsAutoCString ref;
+ msgHdr->GetStringReference(i, ref);
+ if (!ref.IsEmpty())
+ {
+ reference.AppendLiteral("<");
+ reference.Append(ref);
+ reference.AppendLiteral("> ");
+ }
+ }
+ reference.Trim(" ", false, true);
+ }
+ msgHdr->GetMessageId(getter_Copies(messageId));
+ reference.AppendLiteral("<");
+ reference.Append(messageId);
+ reference.AppendLiteral(">");
+ m_compFields->SetReferences(reference.get());
+ }
+
+ // Early return for "Forward inline" and "Reply with template" processing.
+ return NS_OK;
+ }
+
+ // All other processing.
+ char *uriList = PL_strdup(originalMsgURI);
+ if (!uriList)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Resulting charset for this message.
+ nsCString charset;
+
+ // Check for the charset of the last displayed message, it
+ // will be used for quoting and as override.
+ nsCString windowCharset;
+ mCharsetOverride = false;
+ mAnswerDefaultCharset = false;
+ GetTopmostMsgWindowCharacterSet(windowCharset, &mCharsetOverride);
+ if (!windowCharset.IsEmpty()) {
+ // Although the charset in which to send the message might change,
+ // the original message will be parsed for quoting using the charset it is
+ // now displayed with.
+ mQuoteCharset = windowCharset;
+
+ if (mCharsetOverride) {
+ // Use override charset.
+ charset = windowCharset;
+ }
+ }
+
+ // Note the following:
+ // LoadDraftOrTemplate() is run in nsMsgComposeService::OpenComposeWindow()
+ // for five compose types: ForwardInline, ReplyWithTemplate (both covered
+ // in the code block above) and Draft, Template and Redirect. For these
+ // compose types, the charset is already correct (incl. MIME-applied override)
+ // unless the default charset should be used.
+
+ bool isFirstPass = true;
+ char *uri = uriList;
+ char *nextUri;
+ do
+ {
+ nextUri = strstr(uri, "://");
+ if (nextUri)
+ {
+ // look for next ://, and then back up to previous ','
+ nextUri = strstr(nextUri + 1, "://");
+ if (nextUri)
+ {
+ *nextUri = '\0';
+ char *saveNextUri = nextUri;
+ nextUri = strrchr(uri, ',');
+ if (nextUri)
+ *nextUri = '\0';
+ *saveNextUri = ':';
+ }
+ }
+
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ if (mOrigMsgHdr)
+ msgHdr = mOrigMsgHdr;
+ else
+ {
+ rv = GetMsgDBHdrFromURI(uri, getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ if (msgHdr)
+ {
+ nsCString decodedCString;
+
+ bool replyInDefault = false;
+ prefs->GetBoolPref("mailnews.reply_in_default_charset",
+ &replyInDefault);
+ // Use send_default_charset if reply_in_default_charset is on.
+ if (replyInDefault)
+ {
+ nsString str;
+ NS_GetLocalizedUnicharPreferenceWithDefault(prefs, "mailnews.send_default_charset",
+ EmptyString(), str);
+ if (!str.IsEmpty()) {
+ LossyCopyUTF16toASCII(str, charset);
+ mAnswerDefaultCharset = true;
+ }
+ }
+
+ // Set the charset we determined, if any, in the comp fields.
+ // For replies, the charset will be set after processing the message
+ // through MIME in QuotingOutputStreamListener::OnStopRequest().
+ if (isFirstPass && !charset.IsEmpty())
+ m_compFields->SetCharacterSet(charset.get());
+
+ nsString subject;
+ rv = msgHdr->GetMime2DecodedSubject(subject);
+ if (NS_FAILED(rv)) return rv;
+
+ // Check if (was: is present in the subject
+ int32_t wasOffset = subject.RFind(NS_LITERAL_STRING(" (was:"));
+ bool strip = true;
+
+ if (wasOffset >= 0) {
+ // Check the number of references, to check if was: should be stripped
+ // First, assume that it should be stripped; the variable will be set to
+ // false later if stripping should not happen.
+ uint16_t numRef;
+ msgHdr->GetNumReferences(&numRef);
+ if (numRef) {
+ // If there are references, look for the first message in the thread
+ // firstly, get the database via the folder
+ nsCOMPtr<nsIMsgFolder> folder;
+ msgHdr->GetFolder(getter_AddRefs(folder));
+ if (folder) {
+ nsCOMPtr<nsIMsgDatabase> db;
+ folder->GetMsgDatabase(getter_AddRefs(db));
+
+ if (db) {
+ nsAutoCString reference;
+ msgHdr->GetStringReference(0, reference);
+
+ nsCOMPtr<nsIMsgDBHdr> refHdr;
+ db->GetMsgHdrForMessageID(reference.get(), getter_AddRefs(refHdr));
+
+ if (refHdr) {
+ nsCString refSubject;
+ rv = refHdr->GetSubject(getter_Copies(refSubject));
+ if (NS_SUCCEEDED(rv)) {
+ if (refSubject.Find(" (was:") >= 0)
+ strip = false;
+ }
+ }
+ }
+ }
+ }
+ else
+ strip = false;
+ }
+
+ if (strip && wasOffset >= 0) {
+ // Strip off the "(was: old subject)" part
+ subject.Assign(Substring(subject, 0, wasOffset));
+ }
+
+ switch (type)
+ {
+ default: break;
+ case nsIMsgCompType::Reply :
+ case nsIMsgCompType::ReplyAll:
+ case nsIMsgCompType::ReplyToList:
+ case nsIMsgCompType::ReplyToGroup:
+ case nsIMsgCompType::ReplyToSender:
+ case nsIMsgCompType::ReplyToSenderAndGroup:
+ {
+ if (!isFirstPass) // safeguard, just in case...
+ {
+ PR_Free(uriList);
+ return rv;
+ }
+ mQuotingToFollow = true;
+
+ subject.Insert(NS_LITERAL_STRING("Re: "), 0);
+ m_compFields->SetSubject(subject);
+
+ // Setup quoting callbacks for later...
+ mWhatHolder = 1;
+ break;
+ }
+ case nsIMsgCompType::ForwardAsAttachment:
+ {
+ // Add the forwarded message in the references, first
+ nsAutoCString messageId;
+ msgHdr->GetMessageId(getter_Copies(messageId));
+ if (isFirstPass)
+ {
+ nsAutoCString reference;
+ reference.Append(NS_LITERAL_CSTRING("<"));
+ reference.Append(messageId);
+ reference.Append(NS_LITERAL_CSTRING(">"));
+ m_compFields->SetReferences(reference.get());
+ }
+ else
+ {
+ nsAutoCString references;
+ m_compFields->GetReferences(getter_Copies(references));
+ references.Append(NS_LITERAL_CSTRING(" <"));
+ references.Append(messageId);
+ references.Append(NS_LITERAL_CSTRING(">"));
+ m_compFields->SetReferences(references.get());
+ }
+
+ uint32_t flags;
+
+ msgHdr->GetFlags(&flags);
+ if (flags & nsMsgMessageFlags::HasRe)
+ subject.Insert(NS_LITERAL_STRING("Re: "), 0);
+
+ // Setup quoting callbacks for later...
+ mQuotingToFollow = false; //We don't need to quote the original message.
+ nsCOMPtr<nsIMsgAttachment> attachment = do_CreateInstance(NS_MSGATTACHMENT_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && attachment)
+ {
+ bool addExtension = true;
+ nsString sanitizedSubj;
+ prefs->GetBoolPref("mail.forward_add_extension", &addExtension);
+
+ // copy subject string to sanitizedSubj, use default if empty
+ if (subject.IsEmpty())
+ {
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> composeBundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties",
+ getter_AddRefs(composeBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ composeBundle->GetStringFromName(u"messageAttachmentSafeName",
+ getter_Copies(sanitizedSubj));
+ }
+ else
+ sanitizedSubj.Assign(subject);
+
+ // set the file size
+ uint32_t messageSize;
+ msgHdr->GetMessageSize(&messageSize);
+ attachment->SetSize(messageSize);
+
+ // change all '.' to '_' see bug #271211
+ MsgReplaceChar(sanitizedSubj, ".", '_');
+ if (addExtension)
+ sanitizedSubj.AppendLiteral(".eml");
+ attachment->SetName(sanitizedSubj);
+ attachment->SetUrl(nsDependentCString(uri));
+ m_compFields->AddAttachment(attachment);
+ }
+
+ if (isFirstPass)
+ {
+ nsCString fwdPrefix;
+ prefs->GetCharPref("mail.forward_subject_prefix", getter_Copies(fwdPrefix));
+ if (!fwdPrefix.IsEmpty())
+ {
+ nsString unicodeFwdPrefix;
+ CopyUTF8toUTF16(fwdPrefix, unicodeFwdPrefix);
+ unicodeFwdPrefix.AppendLiteral(": ");
+ subject.Insert(unicodeFwdPrefix, 0);
+ }
+ else
+ {
+ subject.Insert(NS_LITERAL_STRING("Fwd: "), 0);
+ }
+ m_compFields->SetSubject(subject);
+ }
+ break;
+ }
+ case nsIMsgCompType::Redirect:
+ {
+ // For a redirect, set the Reply-To: header to what was in the original From: header...
+ nsAutoCString author;
+ msgHdr->GetAuthor(getter_Copies(author));
+ m_compFields->SetReplyTo(author.get());
+
+ // ... and empty out the various recipient headers
+ nsAutoString empty;
+ m_compFields->SetTo(empty);
+ m_compFields->SetCc(empty);
+ m_compFields->SetBcc(empty);
+ m_compFields->SetNewsgroups(empty);
+ m_compFields->SetFollowupTo(empty);
+ break;
+ }
+ }
+ }
+ isFirstPass = false;
+ uri = nextUri + 1;
+ }
+ while (nextUri);
+ PR_Free(uriList);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgCompose::GetProgress(nsIMsgProgress **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mProgress;
+ NS_IF_ADDREF(*_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::GetMessageSend(nsIMsgSend **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mMsgSend;
+ NS_IF_ADDREF(*_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::SetMessageSend(nsIMsgSend* aMsgSend)
+{
+ mMsgSend = aMsgSend;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::ClearMessageSend()
+{
+ mMsgSend = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::SetCiteReference(nsString citeReference)
+{
+ mCiteReference = citeReference;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::SetSavedFolderURI(const char *folderURI)
+{
+ m_folderName = folderURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::GetSavedFolderURI(char ** folderURI)
+{
+ NS_ENSURE_ARG_POINTER(folderURI);
+ *folderURI = ToNewCString(m_folderName);
+ return (*folderURI) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP nsMsgCompose::GetOriginalMsgURI(char ** originalMsgURI)
+{
+ NS_ENSURE_ARG_POINTER(originalMsgURI);
+ *originalMsgURI = ToNewCString(mOriginalMsgURI);
+ return (*originalMsgURI) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// THIS IS THE CLASS THAT IS THE STREAM CONSUMER OF THE HTML OUPUT
+// FROM LIBMIME. THIS IS FOR QUOTING
+////////////////////////////////////////////////////////////////////////////////////
+QuotingOutputStreamListener::~QuotingOutputStreamListener()
+{
+ if (mUnicodeConversionBuffer)
+ free(mUnicodeConversionBuffer);
+}
+
+QuotingOutputStreamListener::QuotingOutputStreamListener(const char * originalMsgURI,
+ nsIMsgDBHdr *originalMsgHdr,
+ bool quoteHeaders,
+ bool headersOnly,
+ nsIMsgIdentity *identity,
+ nsIMsgQuote* msgQuote,
+ bool charsetFixed,
+ bool quoteOriginal,
+ const nsACString& htmlToQuote)
+{
+ nsresult rv;
+ mQuoteHeaders = quoteHeaders;
+ mHeadersOnly = headersOnly;
+ mIdentity = identity;
+ mOrigMsgHdr = originalMsgHdr;
+ mUnicodeBufferCharacterLength = 0;
+ mUnicodeConversionBuffer = nullptr;
+ mQuoteOriginal = quoteOriginal;
+ mHtmlToQuote = htmlToQuote;
+ mQuote = msgQuote;
+ mCharsetFixed = charsetFixed;
+
+ if (!mHeadersOnly || !mHtmlToQuote.IsEmpty())
+ {
+ // Get header type, locale and strings from pref.
+ int32_t replyHeaderType;
+ nsAutoString replyHeaderLocale;
+ nsString replyHeaderAuthorWrote;
+ nsString replyHeaderOnDateAuthorWrote;
+ nsString replyHeaderAuthorWroteOnDate;
+ nsString replyHeaderOriginalmessage;
+ GetReplyHeaderInfo(&replyHeaderType,
+ replyHeaderLocale,
+ replyHeaderAuthorWrote,
+ replyHeaderOnDateAuthorWrote,
+ replyHeaderAuthorWroteOnDate,
+ replyHeaderOriginalmessage);
+
+ // For the built message body...
+ if (originalMsgHdr && !quoteHeaders)
+ {
+ // Setup the cite information....
+ nsCString myGetter;
+ if (NS_SUCCEEDED(originalMsgHdr->GetMessageId(getter_Copies(myGetter))))
+ {
+ if (!myGetter.IsEmpty())
+ {
+ nsAutoCString buf;
+ mCiteReference.AssignLiteral("mid:");
+ MsgEscapeURL(myGetter,
+ nsINetUtil::ESCAPE_URL_FILE_BASENAME | nsINetUtil::ESCAPE_URL_FORCED,
+ buf);
+ mCiteReference.Append(NS_ConvertASCIItoUTF16(buf));
+ }
+ }
+
+ bool citingHeader; //Do we have a header needing to cite any info from original message?
+ bool headerDate; //Do we have a header needing to cite date/time from original message?
+ switch (replyHeaderType)
+ {
+ case 0: // No reply header at all (actually the "---- original message ----" string,
+ // which is kinda misleading. TODO: Should there be a "really no header" option?
+ mCitePrefix.Assign(replyHeaderOriginalmessage);
+ citingHeader = false;
+ headerDate = false;
+ break;
+
+ case 2: // Insert both the original author and date in the reply header (date followed by author)
+ mCitePrefix.Assign(replyHeaderOnDateAuthorWrote);
+ citingHeader = true;
+ headerDate = true;
+ break;
+
+ case 3: // Insert both the original author and date in the reply header (author followed by date)
+ mCitePrefix.Assign(replyHeaderAuthorWroteOnDate);
+ citingHeader = true;
+ headerDate = true;
+ break;
+
+ case 4: // TODO bug 107884: implement a more featureful user specified header
+ case 1:
+ default: // Default is to only show the author.
+ mCitePrefix.Assign(replyHeaderAuthorWrote);
+ citingHeader = true;
+ headerDate = false;
+ break;
+ }
+
+ if (citingHeader)
+ {
+ int32_t placeholderIndex = kNotFound;
+
+ if (headerDate)
+ {
+ nsCOMPtr<nsIDateTimeFormat> dateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ PRTime originalMsgDate;
+ rv = originalMsgHdr->GetDate(&originalMsgDate);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsILocale> locale;
+ nsCOMPtr<nsILocaleService> localeService(do_GetService(NS_LOCALESERVICE_CONTRACTID));
+
+ // Format date using "mailnews.reply_header_locale", if empty then use application default locale.
+ if (!replyHeaderLocale.IsEmpty())
+ rv = localeService->NewLocale(replyHeaderLocale, getter_AddRefs(locale));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoString citeDatePart;
+ if ((placeholderIndex = mCitePrefix.Find("#2")) != kNotFound)
+ {
+ rv = dateFormatter->FormatPRTime(locale,
+ kDateFormatShort,
+ kTimeFormatNone,
+ originalMsgDate,
+ citeDatePart);
+ if (NS_SUCCEEDED(rv))
+ mCitePrefix.Replace(placeholderIndex, 2, citeDatePart);
+ }
+ if ((placeholderIndex = mCitePrefix.Find("#3")) != kNotFound)
+ {
+ rv = dateFormatter->FormatPRTime(locale,
+ kDateFormatNone,
+ kTimeFormatNoSeconds,
+ originalMsgDate,
+ citeDatePart);
+ if (NS_SUCCEEDED(rv))
+ mCitePrefix.Replace(placeholderIndex, 2, citeDatePart);
+ }
+ }
+ }
+ }
+ }
+
+ if ((placeholderIndex = mCitePrefix.Find("#1")) != kNotFound)
+ {
+ nsAutoCString author;
+ rv = originalMsgHdr->GetAuthor(getter_Copies(author));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoString citeAuthor;
+ ExtractName(EncodedHeader(author), citeAuthor);
+ mCitePrefix.Replace(placeholderIndex, 2, citeAuthor);
+ }
+ }
+ }
+ }
+
+ // This should not happen, but just in case.
+ if (mCitePrefix.IsEmpty())
+ {
+ mCitePrefix.AppendLiteral("\n\n");
+ mCitePrefix.Append(replyHeaderOriginalmessage);
+ mCitePrefix.AppendLiteral("\n");
+ }
+ }
+}
+
+/**
+ * The formatflowed parameter directs if formatflowed should be used in the conversion.
+ * format=flowed (RFC 2646) is a way to represent flow in a plain text mail, without
+ * disturbing the plain text.
+ */
+nsresult
+QuotingOutputStreamListener::ConvertToPlainText(bool formatflowed,
+ bool delsp,
+ bool formatted,
+ bool disallowBreaks)
+{
+ nsresult rv = ConvertBufToPlainText(mMsgBody, formatflowed,
+ delsp,
+ formatted,
+ disallowBreaks);
+ NS_ENSURE_SUCCESS (rv, rv);
+ return ConvertBufToPlainText(mSignature, formatflowed,
+ delsp,
+ formatted,
+ disallowBreaks);
+}
+
+NS_IMETHODIMP QuotingOutputStreamListener::OnStartRequest(nsIRequest *request, nsISupports * /* ctxt */)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP QuotingOutputStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
+{
+ nsresult rv = NS_OK;
+
+ if (!mHtmlToQuote.IsEmpty())
+ {
+ // If we had a selection in the original message to quote, we can add
+ // it now that we are done ignoring the original body of the message
+ mHeadersOnly = false;
+ rv = AppendToMsgBody(mHtmlToQuote);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIMsgCompose> compose = do_QueryReferent(mWeakComposeObj);
+ NS_ENSURE_TRUE(compose, NS_ERROR_NULL_POINTER);
+
+ MSG_ComposeType type;
+ compose->GetType(&type);
+
+ // Assign cite information if available...
+ if (!mCiteReference.IsEmpty())
+ compose->SetCiteReference(mCiteReference);
+
+ bool overrideReplyTo =
+ mozilla::Preferences::GetBool("mail.override_list_reply_to", true);
+
+ if (mHeaders && (type == nsIMsgCompType::Reply ||
+ type == nsIMsgCompType::ReplyAll ||
+ type == nsIMsgCompType::ReplyToList ||
+ type == nsIMsgCompType::ReplyToSender ||
+ type == nsIMsgCompType::ReplyToGroup ||
+ type == nsIMsgCompType::ReplyToSenderAndGroup) &&
+ mQuoteOriginal)
+ {
+ nsCOMPtr<nsIMsgCompFields> compFields;
+ compose->GetCompFields(getter_AddRefs(compFields));
+ if (compFields)
+ {
+ nsAutoString from;
+ nsAutoString to;
+ nsAutoString cc;
+ nsAutoString bcc;
+ nsAutoString replyTo;
+ nsAutoString mailReplyTo;
+ nsAutoString mailFollowupTo;
+ nsAutoString newgroups;
+ nsAutoString followUpTo;
+ nsAutoString messageId;
+ nsAutoString references;
+ nsAutoString listPost;
+
+ nsCString outCString; // Temp helper string.
+
+ bool needToRemoveDup = false;
+ if (!mMimeConverter)
+ {
+ mMimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ nsCString charset;
+ compFields->GetCharacterSet(getter_Copies(charset));
+
+ if (!mCharsetFixed) {
+ // Get the charset from the channel where MIME left it.
+ if (mQuote) {
+ nsCOMPtr<nsIChannel> quoteChannel;
+ mQuote->GetQuoteChannel(getter_AddRefs(quoteChannel));
+ if (quoteChannel) {
+ quoteChannel->GetContentCharset(charset);
+ if (!charset.IsEmpty()) {
+ rv = fixCharset(charset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ compFields->SetCharacterSet(charset.get());
+ }
+ }
+ }
+ }
+
+ mHeaders->ExtractHeader(HEADER_FROM, true, outCString);
+ ConvertRawBytesToUTF16(outCString, charset.get(), from);
+
+ mHeaders->ExtractHeader(HEADER_TO, true, outCString);
+ ConvertRawBytesToUTF16(outCString, charset.get(), to);
+
+ mHeaders->ExtractHeader(HEADER_CC, true, outCString);
+ ConvertRawBytesToUTF16(outCString, charset.get(), cc);
+
+ mHeaders->ExtractHeader(HEADER_BCC, true, outCString);
+ ConvertRawBytesToUTF16(outCString, charset.get(), bcc);
+
+ mHeaders->ExtractHeader(HEADER_MAIL_FOLLOWUP_TO, true, outCString);
+ ConvertRawBytesToUTF16(outCString, charset.get(), mailFollowupTo);
+
+ mHeaders->ExtractHeader(HEADER_REPLY_TO, false, outCString);
+ ConvertRawBytesToUTF16(outCString, charset.get(), replyTo);
+
+ mHeaders->ExtractHeader(HEADER_MAIL_REPLY_TO, true, outCString);
+ ConvertRawBytesToUTF16(outCString, charset.get(), mailReplyTo);
+
+ mHeaders->ExtractHeader(HEADER_NEWSGROUPS, false, outCString);
+ if (!outCString.IsEmpty())
+ mMimeConverter->DecodeMimeHeader(outCString.get(), charset.get(),
+ false, true, newgroups);
+
+ mHeaders->ExtractHeader(HEADER_FOLLOWUP_TO, false, outCString);
+ if (!outCString.IsEmpty())
+ mMimeConverter->DecodeMimeHeader(outCString.get(), charset.get(),
+ false, true, followUpTo);
+
+ mHeaders->ExtractHeader(HEADER_MESSAGE_ID, false, outCString);
+ if (!outCString.IsEmpty())
+ mMimeConverter->DecodeMimeHeader(outCString.get(), charset.get(),
+ false, true, messageId);
+
+ mHeaders->ExtractHeader(HEADER_REFERENCES, false, outCString);
+ if (!outCString.IsEmpty())
+ mMimeConverter->DecodeMimeHeader(outCString.get(), charset.get(),
+ false, true, references);
+
+ mHeaders->ExtractHeader(HEADER_LIST_POST, true, outCString);
+ if (!outCString.IsEmpty())
+ mMimeConverter->DecodeMimeHeader(outCString.get(), charset.get(),
+ false, true, listPost);
+ if (!listPost.IsEmpty())
+ {
+ int32_t startPos = listPost.Find("<mailto:");
+ int32_t endPos = listPost.FindChar('>', startPos);
+ // Extract the e-mail address.
+ if (endPos > startPos)
+ {
+ const uint32_t mailtoLen = strlen("<mailto:");
+ listPost = Substring(listPost, startPos + mailtoLen, endPos - (startPos + mailtoLen));
+ }
+ }
+
+ nsCString fromEmailAddress;
+ ExtractEmail(EncodedHeader(NS_ConvertUTF16toUTF8(from)), fromEmailAddress);
+
+ nsTArray<nsCString> toEmailAddresses;
+ ExtractEmails(EncodedHeader(NS_ConvertUTF16toUTF8(to)),
+ UTF16ArrayAdapter<>(toEmailAddresses));
+
+ nsTArray<nsCString> ccEmailAddresses;
+ ExtractEmails(EncodedHeader(NS_ConvertUTF16toUTF8(cc)),
+ UTF16ArrayAdapter<>(ccEmailAddresses));
+
+ nsCOMPtr<nsIPrefBranch> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool replyToSelfCheckAll = false;
+ prefs->GetBoolPref("mailnews.reply_to_self_check_all_ident",
+ &replyToSelfCheckAll);
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIArray> identities;
+ nsCString accountKey;
+ mOrigMsgHdr->GetAccountKey(getter_Copies(accountKey));
+ if (replyToSelfCheckAll)
+ {
+ // Check all avaliable identities if the pref was set.
+ accountManager->GetAllIdentities(getter_AddRefs(identities));
+ }
+ else if (!accountKey.IsEmpty())
+ {
+ // Check headers to see which account the message came in from
+ // (only works for pop3).
+ nsCOMPtr<nsIMsgAccount> account;
+ accountManager->GetAccount(accountKey, getter_AddRefs(account));
+
+ if (account)
+ account->GetIdentities(getter_AddRefs(identities));
+ }
+ else
+ {
+ // Check identities only for the server of the folder that the message
+ // is in.
+ nsCOMPtr <nsIMsgFolder> msgFolder;
+ rv = mOrigMsgHdr->GetFolder(getter_AddRefs(msgFolder));
+
+ if (NS_SUCCEEDED(rv) && msgFolder){
+ nsCOMPtr<nsIMsgIncomingServer> nsIMsgIncomingServer;
+ rv = msgFolder->GetServer(getter_AddRefs(nsIMsgIncomingServer));
+
+ if (NS_SUCCEEDED(rv) && nsIMsgIncomingServer)
+ accountManager->GetIdentitiesForServer(nsIMsgIncomingServer, getter_AddRefs(identities));
+ }
+ }
+
+ bool isReplyToSelf = false;
+ nsCOMPtr<nsIMsgIdentity> selfIdentity;
+ if (identities)
+ {
+ // Go through the identities to see if any of them is the author of
+ // the email.
+ nsCOMPtr<nsIMsgIdentity> lookupIdentity;
+
+ uint32_t count = 0;
+ identities->GetLength(&count);
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ lookupIdentity = do_QueryElementAt(identities, i, &rv);
+ if (NS_FAILED(rv))
+ continue;
+
+ selfIdentity = lookupIdentity;
+
+ nsCString curIdentityEmail;
+ lookupIdentity->GetEmail(curIdentityEmail);
+
+ // See if it's a reply to own message, but not a reply between identities.
+ if (curIdentityEmail.Equals(fromEmailAddress))
+ {
+ isReplyToSelf = true;
+ // For a true reply-to-self, none of your identities are normally in
+ // To or Cc. We need to avoid doing a reply-to-self for people that
+ // have multiple identities set and sometimes *uses* the other
+ // identity and sometimes *mails* the other identity.
+ // E.g. husband+wife or own-email+company-role-mail.
+ for (uint32_t j = 0; j < count; j++)
+ {
+ nsCOMPtr<nsIMsgIdentity> lookupIdentity2;
+ rv = identities->QueryElementAt(j, NS_GET_IID(nsIMsgIdentity),
+ getter_AddRefs(lookupIdentity2));
+ if (NS_FAILED(rv))
+ continue;
+
+ nsCString curIdentityEmail2;
+ lookupIdentity2->GetEmail(curIdentityEmail2);
+ if (toEmailAddresses.Contains(curIdentityEmail2))
+ {
+ // However, "From:me To:me" should be treated as
+ // reply-to-self if we have a Bcc. If we don't have a Bcc we
+ // might have the case of a generated mail of the style
+ // "From:me To:me Reply-To:customer". Then we need to to do a
+ // normal reply to the customer.
+ isReplyToSelf = !bcc.IsEmpty(); // true if bcc is set
+ break;
+ }
+ else if (ccEmailAddresses.Contains(curIdentityEmail2))
+ {
+ // If you auto-Cc yourself your email would be in Cc - but we
+ // can't detect why it is in Cc so lets just treat it like a
+ // normal reply.
+ isReplyToSelf = false;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ if (type == nsIMsgCompType::ReplyToSender || type == nsIMsgCompType::Reply)
+ {
+ if (isReplyToSelf)
+ {
+ // Cast to concrete class. We *only* what to change m_identity, not
+ // all the things compose->SetIdentity would do.
+ nsMsgCompose* _compose = static_cast<nsMsgCompose*>(compose.get());
+ _compose->m_identity = selfIdentity;
+ compFields->SetFrom(from);
+ compFields->SetTo(to);
+ compFields->SetReplyTo(replyTo);
+ }
+ else if (!mailReplyTo.IsEmpty())
+ {
+ // handle Mail-Reply-To (http://cr.yp.to/proto/replyto.html)
+ compFields->SetTo(mailReplyTo);
+ needToRemoveDup = true;
+ }
+ else if (!replyTo.IsEmpty())
+ {
+ // default reply behaviour then
+
+ if (overrideReplyTo &&
+ !listPost.IsEmpty() && replyTo.Find(listPost) != kNotFound)
+ {
+ // Reply-To munging in this list post. Reply to From instead,
+ // as the user can choose Reply List if that's what he wants.
+ compFields->SetTo(from);
+ }
+ else
+ {
+ compFields->SetTo(replyTo);
+ }
+ needToRemoveDup = true;
+ }
+ else {
+ compFields->SetTo(from);
+ }
+ }
+ else if (type == nsIMsgCompType::ReplyAll)
+ {
+ if (isReplyToSelf)
+ {
+ // Cast to concrete class. We *only* what to change m_identity, not
+ // all the things compose->SetIdentity would do.
+ nsMsgCompose* _compose = static_cast<nsMsgCompose*>(compose.get());
+ _compose->m_identity = selfIdentity;
+ compFields->SetFrom(from);
+ compFields->SetTo(to);
+ compFields->SetCc(cc);
+ // In case it's a reply to self, but it's not the actual source of the
+ // sent message, then we won't know the Bcc header. So set it only if
+ // it's not empty. If you have auto-bcc and removed the auto-bcc for
+ // the original mail, you will have to do it manually for this reply
+ // too.
+ if (!bcc.IsEmpty())
+ compFields->SetBcc(bcc);
+ compFields->SetReplyTo(replyTo);
+ needToRemoveDup = true;
+ }
+ else if (mailFollowupTo.IsEmpty()) {
+ // default reply-all behaviour then
+
+ nsAutoString allTo;
+ if (!replyTo.IsEmpty())
+ {
+ allTo.Assign(replyTo);
+ needToRemoveDup = true;
+ if (overrideReplyTo &&
+ !listPost.IsEmpty() && replyTo.Find(listPost) != kNotFound)
+ {
+ // Reply-To munging in this list. Add From to recipients, it's the
+ // lesser evil...
+ allTo.AppendLiteral(", ");
+ allTo.Append(from);
+ }
+ }
+ else
+ {
+ allTo.Assign(from);
+ }
+
+ allTo.AppendLiteral(", ");
+ allTo.Append(to);
+ compFields->SetTo(allTo);
+
+ nsAutoString allCc;
+ compFields->GetCc(allCc); // auto-cc
+ if (!allCc.IsEmpty())
+ allCc.AppendLiteral(", ");
+ allCc.Append(cc);
+ compFields->SetCc(allCc);
+
+ needToRemoveDup = true;
+ }
+ else
+ {
+ // Handle Mail-Followup-To (http://cr.yp.to/proto/replyto.html)
+ compFields->SetTo(mailFollowupTo);
+ needToRemoveDup = true; // To remove possible self from To.
+
+ // If Cc is set a this point it's auto-Ccs, so we'll just keep those.
+ }
+ }
+ else if (type == nsIMsgCompType::ReplyToList)
+ {
+ compFields->SetTo(listPost);
+ }
+
+ if (!newgroups.IsEmpty())
+ {
+ if ((type != nsIMsgCompType::Reply) && (type != nsIMsgCompType::ReplyToSender))
+ compFields->SetNewsgroups(newgroups);
+ if (type == nsIMsgCompType::ReplyToGroup)
+ compFields->SetTo(EmptyString());
+ }
+
+ if (!followUpTo.IsEmpty())
+ {
+ // Handle "followup-to: poster" magic keyword here
+ if (followUpTo.EqualsLiteral("poster"))
+ {
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ nsCOMPtr<nsIPrompt> prompt;
+ compose->GetDomWindow(getter_AddRefs(domWindow));
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+ nsCOMPtr<nsPIDOMWindowOuter> composeWindow = nsPIDOMWindowOuter::From(domWindow);
+ if (composeWindow)
+ composeWindow->GetPrompter(getter_AddRefs(prompt));
+ nsMsgDisplayMessageByName(prompt, u"followupToSenderMessage");
+
+ if (!replyTo.IsEmpty())
+ {
+ compFields->SetTo(replyTo);
+ }
+ else
+ {
+ // If reply-to is empty, use the From header to fetch the original
+ // sender's email.
+ compFields->SetTo(from);
+ }
+
+ // Clear the newsgroup: header field, because followup-to: poster
+ // only follows up to the original sender
+ if (!newgroups.IsEmpty())
+ compFields->SetNewsgroups(EmptyString());
+ }
+ else // Process "followup-to: newsgroup-content" here
+ {
+ if (type != nsIMsgCompType::ReplyToSender)
+ compFields->SetNewsgroups(followUpTo);
+ if (type == nsIMsgCompType::Reply)
+ {
+ compFields->SetTo(EmptyString());
+ }
+ }
+ }
+
+ if (!references.IsEmpty())
+ references.Append(char16_t(' '));
+ references += messageId;
+ compFields->SetReferences(NS_LossyConvertUTF16toASCII(references).get());
+
+ nsAutoCString resultStr;
+
+ // Cast interface to concrete class that has direct field getters etc.
+ nsMsgCompFields* _compFields = static_cast<nsMsgCompFields*>(compFields.get());
+
+ // Remove duplicate addresses between To && Cc.
+ if (needToRemoveDup)
+ {
+ nsCString addressesToRemoveFromCc;
+ if (mIdentity)
+ {
+ bool removeMyEmailInCc = true;
+ nsCString myEmail;
+ mIdentity->GetEmail(myEmail);
+
+ // Remove my own address from To, unless it's a reply to self.
+ if (!isReplyToSelf) {
+ RemoveDuplicateAddresses(nsDependentCString(_compFields->GetTo()),
+ myEmail, resultStr);
+ _compFields->SetTo(resultStr.get());
+ }
+ addressesToRemoveFromCc.Assign(_compFields->GetTo());
+
+ // Remove own address from CC unless we want it in there
+ // through the automatic-CC-to-self (see bug 584962). There are
+ // three cases:
+ // - user has no automatic CC
+ // - user has automatic CC but own email is not in it
+ // - user has automatic CC and own email in it
+ // Only in the last case do we want our own email address to stay
+ // in the CC list.
+ bool automaticCc;
+ mIdentity->GetDoCc(&automaticCc);
+ if (automaticCc)
+ {
+ nsCString autoCcList;
+ mIdentity->GetDoCcList(autoCcList);
+ nsTArray<nsCString> autoCcEmailAddresses;
+ ExtractEmails(EncodedHeader(autoCcList),
+ UTF16ArrayAdapter<>(autoCcEmailAddresses));
+ if (autoCcEmailAddresses.Contains(myEmail))
+ {
+ removeMyEmailInCc = false;
+ }
+ }
+
+ if (removeMyEmailInCc)
+ {
+ addressesToRemoveFromCc.AppendLiteral(", ");
+ addressesToRemoveFromCc.Append(myEmail);
+ }
+ }
+ RemoveDuplicateAddresses(nsDependentCString(_compFields->GetCc()),
+ addressesToRemoveFromCc, resultStr);
+ _compFields->SetCc(resultStr.get());
+ if (_compFields->GetBcc())
+ {
+ // Remove addresses already in Cc from Bcc.
+ RemoveDuplicateAddresses(nsDependentCString(_compFields->GetBcc()),
+ nsDependentCString(_compFields->GetCc()),
+ resultStr);
+ if (!resultStr.IsEmpty())
+ {
+ // Remove addresses already in To from Bcc.
+ RemoveDuplicateAddresses(resultStr,
+ nsDependentCString(_compFields->GetTo()),
+ resultStr);
+ }
+ _compFields->SetBcc(resultStr.get());
+ }
+ }
+ }
+ }
+
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+ nsCOMPtr<nsIMsgComposeService> composeService (do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID));
+ composeService->TimeStamp("Done with MIME. Now we're updating the UI elements", false);
+#endif
+
+ if (mQuoteOriginal)
+ compose->NotifyStateListeners(nsIMsgComposeNotificationType::ComposeFieldsReady, NS_OK);
+
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+ composeService->TimeStamp("Addressing widget, window title and focus are now set, time to insert the body", false);
+#endif
+
+ if (! mHeadersOnly)
+ mMsgBody.AppendLiteral("</html>");
+
+ // Now we have an HTML representation of the quoted message.
+ // If we are in plain text mode, we need to convert this to plain
+ // text before we try to insert it into the editor. If we don't, we
+ // just get lots of HTML text in the message...not good.
+ //
+ // XXX not m_composeHTML? /BenB
+ bool composeHTML = true;
+ compose->GetComposeHTML(&composeHTML);
+ if (!composeHTML)
+ {
+ // Downsampling.
+
+ // In plain text quotes we always allow line breaking to not end up with
+ // long lines. The quote is inserted into a span with style
+ // "white-space: pre;" which isn't be wrapped.
+ // Update: Bug 387687 changed this to "white-space: pre-wrap;".
+ // Note that the body of the plain text message is wrapped since it uses
+ // "white-space: pre-wrap; width: 72ch;".
+ // Look at it in the DOM Inspector to see it.
+ //
+ // If we're using format flowed, we need to pass it so the encoder
+ // can add a space at the end.
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ bool flowed = false;
+ if (pPrefBranch) {
+ pPrefBranch->GetBoolPref("mailnews.send_plaintext_flowed", &flowed);
+ }
+
+ rv = ConvertToPlainText(flowed,
+ false, // delsp makes no sense in this context
+ true, // formatted
+ false); // allow line breaks
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ compose->ProcessSignature(mIdentity, true, &mSignature);
+
+ nsCOMPtr<nsIEditor> editor;
+ if (NS_SUCCEEDED(compose->GetEditor(getter_AddRefs(editor))) && editor)
+ {
+ if (mQuoteOriginal)
+ compose->ConvertAndLoadComposeWindow(mCitePrefix,
+ mMsgBody, mSignature,
+ true, composeHTML);
+ else
+ InsertToCompose(editor, composeHTML);
+ }
+
+ if (mQuoteOriginal)
+ compose->NotifyStateListeners(nsIMsgComposeNotificationType::ComposeBodyReady, NS_OK);
+ return rv;
+}
+
+NS_IMETHODIMP QuotingOutputStreamListener::OnDataAvailable(nsIRequest *request,
+ nsISupports *ctxt, nsIInputStream *inStr,
+ uint64_t sourceOffset, uint32_t count)
+{
+ nsresult rv = NS_OK;
+ NS_ENSURE_ARG(inStr);
+
+ if (mHeadersOnly)
+ return rv;
+
+ char *newBuf = (char *)PR_Malloc(count + 1);
+ if (!newBuf)
+ return NS_ERROR_FAILURE;
+
+ uint32_t numWritten = 0;
+ rv = inStr->Read(newBuf, count, &numWritten);
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK)
+ rv = NS_OK;
+ newBuf[numWritten] = '\0';
+ if (NS_SUCCEEDED(rv) && numWritten > 0)
+ {
+ rv = AppendToMsgBody(nsDependentCString(newBuf, numWritten));
+ }
+
+ PR_FREEIF(newBuf);
+ return rv;
+}
+
+NS_IMETHODIMP QuotingOutputStreamListener::AppendToMsgBody(const nsCString &inStr)
+{
+ nsresult rv = NS_OK;
+
+ if (!inStr.IsEmpty())
+ {
+ // Create unicode decoder.
+ if (!mUnicodeDecoder)
+ {
+ nsCOMPtr<nsICharsetConverterManager> ccm =
+ do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = ccm->GetUnicodeDecoderRaw("UTF-8",
+ getter_AddRefs(mUnicodeDecoder));
+ }
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ int32_t unicharLength;
+ int32_t inputLength = inStr.Length();
+ rv = mUnicodeDecoder->GetMaxLength(inStr.get(), inStr.Length(), &unicharLength);
+ if (NS_SUCCEEDED(rv))
+ {
+ // Use this local buffer if possible.
+ const int32_t kLocalBufSize = 4096;
+ char16_t localBuf[kLocalBufSize];
+ char16_t *unichars = localBuf;
+
+ if (unicharLength > kLocalBufSize)
+ {
+ // Otherwise, use the buffer of the class.
+ if (!mUnicodeConversionBuffer ||
+ unicharLength > mUnicodeBufferCharacterLength)
+ {
+ if (mUnicodeConversionBuffer)
+ free(mUnicodeConversionBuffer);
+ mUnicodeConversionBuffer = (char16_t *) moz_xmalloc(unicharLength * sizeof(char16_t));
+ if (!mUnicodeConversionBuffer)
+ {
+ mUnicodeBufferCharacterLength = 0;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ mUnicodeBufferCharacterLength = unicharLength;
+ }
+ unichars = mUnicodeConversionBuffer;
+ }
+
+ int32_t consumedInputLength = 0;
+ int32_t originalInputLength = inputLength;
+ const char *inputBuffer = inStr.get();
+ int32_t convertedOutputLength = 0;
+ int32_t outputBufferLength = unicharLength;
+ char16_t *originalOutputBuffer = unichars;
+ do
+ {
+ rv = mUnicodeDecoder->Convert(inputBuffer, &inputLength, unichars, &unicharLength);
+ if (NS_SUCCEEDED(rv))
+ {
+ convertedOutputLength += unicharLength;
+ break;
+ }
+
+ // if we failed, we consume one byte, replace it with a question mark
+ // and try the conversion again.
+ unichars += unicharLength;
+ *unichars = (char16_t)'?';
+ unichars++;
+ unicharLength++;
+
+ mUnicodeDecoder->Reset();
+
+ inputBuffer += ++inputLength;
+ consumedInputLength += inputLength;
+ inputLength = originalInputLength - consumedInputLength; // update input length to convert
+ convertedOutputLength += unicharLength;
+ unicharLength = outputBufferLength - unicharLength; // update output length
+
+ } while (NS_FAILED(rv) &&
+ (originalInputLength > consumedInputLength) &&
+ (outputBufferLength > convertedOutputLength));
+
+ if (convertedOutputLength > 0)
+ mMsgBody.Append(originalOutputBuffer, convertedOutputLength);
+ }
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+QuotingOutputStreamListener::SetComposeObj(nsIMsgCompose *obj)
+{
+ mWeakComposeObj = do_GetWeakReference(obj);
+ return NS_OK;
+}
+
+nsresult
+QuotingOutputStreamListener::SetMimeHeaders(nsIMimeHeaders * headers)
+{
+ mHeaders = headers;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotingOutputStreamListener::InsertToCompose(nsIEditor *aEditor,
+ bool aHTMLEditor)
+{
+ // First, get the nsIEditor interface for future use
+ nsCOMPtr<nsIDOMNode> nodeInserted;
+
+ TranslateLineEnding(mMsgBody);
+
+ // Now, insert it into the editor...
+ if (aEditor)
+ aEditor->EnableUndo(true);
+
+ nsCOMPtr<nsIMsgCompose> compose = do_QueryReferent(mWeakComposeObj);
+ if (!mMsgBody.IsEmpty() && compose)
+ {
+ compose->SetInsertingQuotedContent(true);
+ if (!mCitePrefix.IsEmpty())
+ {
+ if (!aHTMLEditor)
+ mCitePrefix.AppendLiteral("\n");
+ nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(aEditor));
+ if (textEditor)
+ textEditor->InsertText(mCitePrefix);
+ }
+
+ nsCOMPtr<nsIEditorMailSupport> mailEditor (do_QueryInterface(aEditor));
+ if (mailEditor)
+ {
+ if (aHTMLEditor) {
+ nsAutoString body(mMsgBody);
+ remove_plaintext_tag(body);
+ mailEditor->InsertAsCitedQuotation(body, EmptyString(), true,
+ getter_AddRefs(nodeInserted));
+ } else {
+ mailEditor->InsertAsQuotation(mMsgBody, getter_AddRefs(nodeInserted));
+ }
+ }
+ compose->SetInsertingQuotedContent(false);
+ }
+
+ if (aEditor)
+ {
+ nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(aEditor);
+ if (textEditor)
+ {
+ nsCOMPtr<nsISelection> selection;
+ nsCOMPtr<nsIDOMNode> parent;
+ int32_t offset;
+ nsresult rv;
+
+ // get parent and offset of mailcite
+ rv = GetNodeLocation(nodeInserted, address_of(parent), &offset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get selection
+ aEditor->GetSelection(getter_AddRefs(selection));
+ if (selection)
+ {
+ // place selection after mailcite
+ selection->Collapse(parent, offset+1);
+ // insert a break at current selection
+ textEditor->InsertLineBreak();
+ selection->Collapse(parent, offset+1);
+ }
+ nsCOMPtr<nsISelectionController> selCon;
+ aEditor->GetSelectionController(getter_AddRefs(selCon));
+
+ if (selCon)
+ // After ScrollSelectionIntoView(), the pending notifications might be
+ // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
+ selCon->ScrollSelectionIntoView(
+ nsISelectionController::SELECTION_NORMAL,
+ nsISelectionController::SELECTION_ANCHOR_REGION,
+ true);
+ }
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Returns true if the domain is a match for the given the domain list.
+ * Subdomains are also considered to match.
+ * @param aDomain - the domain name to check
+ * @param aDomainList - a comman separated string of domain names
+ */
+bool IsInDomainList(const nsAString &aDomain, const nsAString &aDomainList)
+{
+ if (aDomain.IsEmpty() || aDomainList.IsEmpty())
+ return false;
+
+ // Check plain text domains.
+ int32_t left = 0;
+ int32_t right = 0;
+ while (right != (int32_t)aDomainList.Length())
+ {
+ right = aDomainList.FindChar(',', left);
+ if (right == kNotFound)
+ right = aDomainList.Length();
+ nsDependentSubstring domain = Substring(aDomainList, left, right);
+
+ if (aDomain.Equals(domain, nsCaseInsensitiveStringComparator()))
+ return true;
+
+ nsAutoString dotDomain;
+ dotDomain.Assign(NS_LITERAL_STRING("."));
+ dotDomain.Append(domain);
+ if (StringEndsWith(aDomain, dotDomain, nsCaseInsensitiveStringComparator()))
+ return true;
+
+ left = right + 1;
+ }
+ return false;
+}
+
+NS_IMPL_ISUPPORTS(QuotingOutputStreamListener,
+ nsIMsgQuotingOutputStreamListener,
+ nsIRequestObserver,
+ nsIStreamListener)
+
+////////////////////////////////////////////////////////////////////////////////////
+// END OF QUOTING LISTENER
+////////////////////////////////////////////////////////////////////////////////////
+
+/* attribute MSG_ComposeType type; */
+NS_IMETHODIMP nsMsgCompose::SetType(MSG_ComposeType aType)
+{
+
+ mType = aType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::GetType(MSG_ComposeType *aType)
+{
+ NS_ENSURE_ARG_POINTER(aType);
+
+ *aType = mType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::QuoteMessage(const char *msgURI)
+{
+ NS_ENSURE_ARG_POINTER(msgURI);
+
+ nsresult rv;
+ mQuotingToFollow = false;
+
+ // Create a mime parser (nsIStreamConverter)!
+ mQuote = do_CreateInstance(NS_MSGQUOTE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ rv = GetMsgDBHdrFromURI(msgURI, getter_AddRefs(msgHdr));
+
+ // Create the consumer output stream.. this will receive all the HTML from libmime
+ mQuoteStreamListener =
+ new QuotingOutputStreamListener(msgURI,
+ msgHdr,
+ false,
+ !mHtmlToQuote.IsEmpty(),
+ m_identity,
+ mQuote,
+ mCharsetOverride || mAnswerDefaultCharset,
+ false,
+ mHtmlToQuote);
+
+ if (!mQuoteStreamListener)
+ return NS_ERROR_FAILURE;
+ NS_ADDREF(mQuoteStreamListener);
+
+ mQuoteStreamListener->SetComposeObj(this);
+
+ rv = mQuote->QuoteMessage(msgURI, false, mQuoteStreamListener,
+ mCharsetOverride ? m_compFields->GetCharacterSet() : "",
+ false, msgHdr);
+ return rv;
+}
+
+nsresult
+nsMsgCompose::QuoteOriginalMessage() // New template
+{
+ nsresult rv;
+
+ mQuotingToFollow = false;
+
+ // Create a mime parser (nsIStreamConverter)!
+ mQuote = do_CreateInstance(NS_MSGQUOTE_CONTRACTID, &rv);
+ if (NS_FAILED(rv) || !mQuote)
+ return NS_ERROR_FAILURE;
+
+ bool bAutoQuote = true;
+ m_identity->GetAutoQuote(&bAutoQuote);
+
+ nsCOMPtr <nsIMsgDBHdr> originalMsgHdr = mOrigMsgHdr;
+ if (!originalMsgHdr)
+ {
+ rv = GetMsgDBHdrFromURI(mOriginalMsgURI.get(), getter_AddRefs(originalMsgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ bool fileUrl = StringBeginsWith(mOriginalMsgURI, NS_LITERAL_CSTRING("file:"));
+ if (fileUrl)
+ {
+ mOriginalMsgURI.Replace(0, 5, NS_LITERAL_CSTRING("mailbox:"));
+ mOriginalMsgURI.AppendLiteral("?number=0");
+ }
+
+ // Create the consumer output stream.. this will receive all the HTML from libmime
+ mQuoteStreamListener =
+ new QuotingOutputStreamListener(mOriginalMsgURI.get(),
+ originalMsgHdr,
+ mWhatHolder != 1,
+ !bAutoQuote || !mHtmlToQuote.IsEmpty(),
+ m_identity,
+ mQuote,
+ mCharsetOverride || mAnswerDefaultCharset,
+ true,
+ mHtmlToQuote);
+
+ if (!mQuoteStreamListener)
+ return NS_ERROR_FAILURE;
+ NS_ADDREF(mQuoteStreamListener);
+
+ mQuoteStreamListener->SetComposeObj(this);
+
+ rv = mQuote->QuoteMessage(mOriginalMsgURI.get(), mWhatHolder != 1, mQuoteStreamListener,
+ mCharsetOverride ? mQuoteCharset.get() : "",
+ !bAutoQuote, originalMsgHdr);
+ return rv;
+}
+
+//CleanUpRecipient will remove un-necessary "<>" when a recipient as an address without name
+void nsMsgCompose::CleanUpRecipients(nsString& recipients)
+{
+ uint16_t i;
+ bool startANewRecipient = true;
+ bool removeBracket = false;
+ nsAutoString newRecipient;
+ char16_t aChar;
+
+ for (i = 0; i < recipients.Length(); i ++)
+ {
+ aChar = recipients[i];
+ switch (aChar)
+ {
+ case '<' :
+ if (startANewRecipient)
+ removeBracket = true;
+ else
+ newRecipient += aChar;
+ startANewRecipient = false;
+ break;
+
+ case '>' :
+ if (removeBracket)
+ removeBracket = false;
+ else
+ newRecipient += aChar;
+ break;
+
+ case ' ' :
+ newRecipient += aChar;
+ break;
+
+ case ',' :
+ newRecipient += aChar;
+ startANewRecipient = true;
+ removeBracket = false;
+ break;
+
+ default :
+ newRecipient += aChar;
+ startANewRecipient = false;
+ break;
+ }
+ }
+ recipients = newRecipient;
+}
+
+NS_IMETHODIMP nsMsgCompose::RememberQueuedDisposition()
+{
+ // need to find the msg hdr in the saved folder and then set a property on
+ // the header that we then look at when we actually send the message.
+ nsresult rv;
+ nsAutoCString dispositionSetting;
+
+ if (mType == nsIMsgCompType::Reply ||
+ mType == nsIMsgCompType::ReplyAll ||
+ mType == nsIMsgCompType::ReplyToList ||
+ mType == nsIMsgCompType::ReplyToGroup ||
+ mType == nsIMsgCompType::ReplyToSender ||
+ mType == nsIMsgCompType::ReplyToSenderAndGroup)
+ {
+ dispositionSetting.AssignLiteral("replied");
+ }
+ else if (mType == nsIMsgCompType::ForwardAsAttachment ||
+ mType == nsIMsgCompType::ForwardInline)
+ {
+ dispositionSetting.AssignLiteral("forwarded");
+ }
+ else if (mType == nsIMsgCompType::Draft)
+ {
+ nsAutoCString curDraftIdURL;
+ rv = m_compFields->GetDraftId(getter_Copies(curDraftIdURL));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!curDraftIdURL.IsEmpty()) {
+ nsCOMPtr <nsIMsgDBHdr> draftHdr;
+ rv = GetMsgDBHdrFromURI(curDraftIdURL.get(), getter_AddRefs(draftHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ draftHdr->GetStringProperty(QUEUED_DISPOSITION_PROPERTY, getter_Copies(dispositionSetting));
+ }
+ }
+
+ nsMsgKey msgKey;
+ if (mMsgSend)
+ {
+ mMsgSend->GetMessageKey(&msgKey);
+ nsAutoCString msgUri(m_folderName);
+ nsCString identityKey;
+
+ m_identity->GetKey(identityKey);
+
+ int32_t insertIndex = StringBeginsWith(msgUri, NS_LITERAL_CSTRING("mailbox")) ? 7 : 4;
+ msgUri.Insert("-message", insertIndex); // "mailbox/imap: -> "mailbox/imap-message:"
+ msgUri.Append('#');
+ msgUri.AppendInt(msgKey);
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ rv = GetMsgDBHdrFromURI(msgUri.get(), getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t pseudoHdrProp = 0;
+ msgHdr->GetUint32Property("pseudoHdr", &pseudoHdrProp);
+ if (pseudoHdrProp)
+ {
+ // Use SetAttributeOnPendingHdr for IMAP pseudo headers, as those
+ // will get deleted (and properties set using SetStringProperty lost.)
+ nsCOMPtr<nsIMsgFolder> folder;
+ rv = msgHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv,rv);
+ nsCOMPtr<nsIMsgDatabase> msgDB;
+ rv = folder->GetMsgDatabase(getter_AddRefs(msgDB));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCString messageId;
+ mMsgSend->GetMessageId(messageId);
+ msgHdr->SetMessageId(messageId.get());
+ if (!mOriginalMsgURI.IsEmpty())
+ {
+ msgDB->SetAttributeOnPendingHdr(msgHdr, ORIG_URI_PROPERTY, mOriginalMsgURI.get());
+ if (!dispositionSetting.IsEmpty())
+ msgDB->SetAttributeOnPendingHdr(msgHdr, QUEUED_DISPOSITION_PROPERTY,
+ dispositionSetting.get());
+ }
+ msgDB->SetAttributeOnPendingHdr(msgHdr, HEADER_X_MOZILLA_IDENTITY_KEY, identityKey.get());
+ }
+ else if (msgHdr)
+ {
+ if (!mOriginalMsgURI.IsEmpty())
+ {
+ msgHdr->SetStringProperty(ORIG_URI_PROPERTY, mOriginalMsgURI.get());
+ if (!dispositionSetting.IsEmpty())
+ msgHdr->SetStringProperty(QUEUED_DISPOSITION_PROPERTY, dispositionSetting.get());
+ }
+ msgHdr->SetStringProperty(HEADER_X_MOZILLA_IDENTITY_KEY, identityKey.get());
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsMsgCompose::ProcessReplyFlags()
+{
+ nsresult rv;
+ // check to see if we were doing a reply or a forward, if we were, set the answered field flag on the message folder
+ // for this URI.
+ if (mType == nsIMsgCompType::Reply ||
+ mType == nsIMsgCompType::ReplyAll ||
+ mType == nsIMsgCompType::ReplyToList ||
+ mType == nsIMsgCompType::ReplyToGroup ||
+ mType == nsIMsgCompType::ReplyToSender ||
+ mType == nsIMsgCompType::ReplyToSenderAndGroup ||
+ mType == nsIMsgCompType::ForwardAsAttachment ||
+ mType == nsIMsgCompType::ForwardInline ||
+ mDraftDisposition != nsIMsgFolder::nsMsgDispositionState_None)
+ {
+ if (!mOriginalMsgURI.IsEmpty())
+ {
+ nsCString msgUri (mOriginalMsgURI);
+ char *newStr = msgUri.BeginWriting();
+ char *uri;
+ while (nullptr != (uri = NS_strtok(",", &newStr)))
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ rv = GetMsgDBHdrFromURI(uri, getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (msgHdr)
+ {
+ // get the folder for the message resource
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ msgHdr->GetFolder(getter_AddRefs(msgFolder));
+ if (msgFolder)
+ {
+ // If it's a draft with disposition, default to replied, otherwise,
+ // check if it's a forward.
+ nsMsgDispositionState dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Replied;
+ if (mDraftDisposition != nsIMsgFolder::nsMsgDispositionState_None)
+ dispositionSetting = mDraftDisposition;
+ else if (mType == nsIMsgCompType::ForwardAsAttachment ||
+ mType == nsIMsgCompType::ForwardInline)
+ dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Forwarded;
+
+ msgFolder->AddMessageDispositionState(msgHdr, dispositionSetting);
+ if (mType != nsIMsgCompType::ForwardAsAttachment)
+ break; // just safeguard
+ }
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgCompose::OnStartSending(const char *aMsgID, uint32_t aMsgSize)
+{
+ nsTObserverArray<nsCOMPtr<nsIMsgSendListener> >::ForwardIterator iter(mExternalSendListeners);
+ nsCOMPtr<nsIMsgSendListener> externalSendListener;
+
+ while (iter.HasMore())
+ {
+ externalSendListener = iter.GetNext();
+ externalSendListener->OnStartSending(aMsgID, aMsgSize);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::OnProgress(const char *aMsgID, uint32_t aProgress, uint32_t aProgressMax)
+{
+ nsTObserverArray<nsCOMPtr<nsIMsgSendListener> >::ForwardIterator iter(mExternalSendListeners);
+ nsCOMPtr<nsIMsgSendListener> externalSendListener;
+
+ while (iter.HasMore())
+ {
+ externalSendListener = iter.GetNext();
+ externalSendListener->OnProgress(aMsgID, aProgress, aProgressMax);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::OnStatus(const char *aMsgID, const char16_t *aMsg)
+{
+ nsTObserverArray<nsCOMPtr<nsIMsgSendListener> >::ForwardIterator iter(mExternalSendListeners);
+ nsCOMPtr<nsIMsgSendListener> externalSendListener;
+
+ while (iter.HasMore())
+ {
+ externalSendListener = iter.GetNext();
+ externalSendListener->OnStatus(aMsgID, aMsg);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::OnStopSending(const char *aMsgID, nsresult aStatus, const char16_t *aMsg,
+ nsIFile *returnFile)
+{
+ nsTObserverArray<nsCOMPtr<nsIMsgSendListener> >::ForwardIterator iter(mExternalSendListeners);
+ nsCOMPtr<nsIMsgSendListener> externalSendListener;
+
+ while (iter.HasMore())
+ {
+ externalSendListener = iter.GetNext();
+ externalSendListener->OnStopSending(aMsgID, aStatus, aMsg, returnFile);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::OnSendNotPerformed(const char *aMsgID, nsresult aStatus)
+{
+ nsTObserverArray<nsCOMPtr<nsIMsgSendListener> >::ForwardIterator iter(mExternalSendListeners);
+ nsCOMPtr<nsIMsgSendListener> externalSendListener;
+
+ while (iter.HasMore())
+ {
+ externalSendListener = iter.GetNext();
+ externalSendListener->OnSendNotPerformed(aMsgID, aStatus);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::OnGetDraftFolderURI(const char *aFolderURI)
+{
+ m_folderName = aFolderURI;
+ nsTObserverArray<nsCOMPtr<nsIMsgSendListener> >::ForwardIterator iter(mExternalSendListeners);
+ nsCOMPtr<nsIMsgSendListener> externalSendListener;
+
+ while (iter.HasMore())
+ {
+ externalSendListener = iter.GetNext();
+ externalSendListener->OnGetDraftFolderURI(aFolderURI);
+ }
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// This is the listener class for both the send operation and the copy operation.
+// We have to create this class to listen for message send completion and deal with
+// failures in both send and copy operations
+////////////////////////////////////////////////////////////////////////////////////
+NS_IMPL_ADDREF(nsMsgComposeSendListener)
+NS_IMPL_RELEASE(nsMsgComposeSendListener)
+
+/*
+NS_IMPL_QUERY_INTERFACE(nsMsgComposeSendListener,
+ nsIMsgComposeSendListener,
+ nsIMsgSendListener,
+ nsIMsgCopyServiceListener,
+ nsIWebProgressListener)
+*/
+NS_INTERFACE_MAP_BEGIN(nsMsgComposeSendListener)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMsgComposeSendListener)
+ NS_INTERFACE_MAP_ENTRY(nsIMsgComposeSendListener)
+ NS_INTERFACE_MAP_ENTRY(nsIMsgSendListener)
+ NS_INTERFACE_MAP_ENTRY(nsIMsgCopyServiceListener)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+NS_INTERFACE_MAP_END
+
+
+nsMsgComposeSendListener::nsMsgComposeSendListener(void)
+{
+ mDeliverMode = 0;
+}
+
+nsMsgComposeSendListener::~nsMsgComposeSendListener(void)
+{
+}
+
+NS_IMETHODIMP nsMsgComposeSendListener::SetMsgCompose(nsIMsgCompose *obj)
+{
+ mWeakComposeObj = do_GetWeakReference(obj);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeSendListener::SetDeliverMode(MSG_DeliverMode deliverMode)
+{
+ mDeliverMode = deliverMode;
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeSendListener::OnStartSending(const char *aMsgID, uint32_t aMsgSize)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgSendListener> composeSendListener = do_QueryReferent(mWeakComposeObj, &rv);
+ if (NS_SUCCEEDED(rv) && composeSendListener)
+ composeSendListener->OnStartSending(aMsgID, aMsgSize);
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeSendListener::OnProgress(const char *aMsgID, uint32_t aProgress, uint32_t aProgressMax)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgSendListener> composeSendListener = do_QueryReferent(mWeakComposeObj, &rv);
+ if (NS_SUCCEEDED(rv) && composeSendListener)
+ composeSendListener->OnProgress(aMsgID, aProgress, aProgressMax);
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeSendListener::OnStatus(const char *aMsgID, const char16_t *aMsg)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgSendListener> composeSendListener = do_QueryReferent(mWeakComposeObj, &rv);
+ if (NS_SUCCEEDED(rv) && composeSendListener)
+ composeSendListener->OnStatus(aMsgID, aMsg);
+ return NS_OK;
+}
+
+nsresult nsMsgComposeSendListener::OnSendNotPerformed(const char *aMsgID, nsresult aStatus)
+{
+ // since OnSendNotPerformed is called in the case where the user aborts the operation
+ // by closing the compose window, we need not do the stuff required
+ // for closing the windows. However we would need to do the other operations as below.
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgCompose> msgCompose = do_QueryReferent(mWeakComposeObj, &rv);
+ if (msgCompose)
+ msgCompose->NotifyStateListeners(nsIMsgComposeNotificationType::ComposeProcessDone, aStatus);
+
+ nsCOMPtr<nsIMsgSendListener> composeSendListener = do_QueryReferent(mWeakComposeObj, &rv);
+ if (NS_SUCCEEDED(rv) && composeSendListener)
+ composeSendListener->OnSendNotPerformed(aMsgID, aStatus);
+
+ return rv;
+}
+
+nsresult nsMsgComposeSendListener::OnStopSending(const char *aMsgID, nsresult aStatus,
+ const char16_t *aMsg, nsIFile *returnFile)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIMsgCompose> msgCompose = do_QueryReferent(mWeakComposeObj, &rv);
+ if (msgCompose)
+ {
+ nsCOMPtr<nsIMsgProgress> progress;
+ msgCompose->GetProgress(getter_AddRefs(progress));
+
+ if (NS_SUCCEEDED(aStatus))
+ {
+ nsCOMPtr<nsIMsgCompFields> compFields;
+ msgCompose->GetCompFields(getter_AddRefs(compFields));
+
+ // only process the reply flags if we successfully sent the message
+ msgCompose->ProcessReplyFlags();
+
+ // See if there is a composer window
+ bool hasDomWindow = true;
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ rv = msgCompose->GetDomWindow(getter_AddRefs(domWindow));
+ if (NS_FAILED(rv) || !domWindow)
+ hasDomWindow = false;
+
+ // Close the window ONLY if we are not going to do a save operation
+ nsAutoString fieldsFCC;
+ if (NS_SUCCEEDED(compFields->GetFcc(fieldsFCC)))
+ {
+ if (!fieldsFCC.IsEmpty())
+ {
+ if (fieldsFCC.LowerCaseEqualsLiteral("nocopy://"))
+ {
+ msgCompose->NotifyStateListeners(nsIMsgComposeNotificationType::ComposeProcessDone, NS_OK);
+ if (progress)
+ {
+ progress->UnregisterListener(this);
+ progress->CloseProgressDialog(false);
+ }
+ if (hasDomWindow)
+ msgCompose->CloseWindow();
+ }
+ }
+ }
+ else
+ {
+ msgCompose->NotifyStateListeners(nsIMsgComposeNotificationType::ComposeProcessDone, NS_OK);
+ if (progress)
+ {
+ progress->UnregisterListener(this);
+ progress->CloseProgressDialog(false);
+ }
+ if (hasDomWindow)
+ msgCompose->CloseWindow(); // if we fail on the simple GetFcc call, close the window to be safe and avoid
+ // windows hanging around to prevent the app from exiting.
+ }
+
+ // Remove the current draft msg when sending draft is done.
+ bool deleteDraft;
+ msgCompose->GetDeleteDraft(&deleteDraft);
+ if (deleteDraft)
+ RemoveCurrentDraftMessage(msgCompose, false);
+ }
+ else
+ {
+ msgCompose->NotifyStateListeners(nsIMsgComposeNotificationType::ComposeProcessDone, aStatus);
+ if (progress)
+ {
+ progress->CloseProgressDialog(true);
+ progress->UnregisterListener(this);
+ }
+ }
+
+ }
+
+ nsCOMPtr<nsIMsgSendListener> composeSendListener = do_QueryReferent(mWeakComposeObj, &rv);
+ if (NS_SUCCEEDED(rv) && composeSendListener)
+ composeSendListener->OnStopSending(aMsgID, aStatus, aMsg, returnFile);
+
+ return rv;
+}
+
+nsresult
+nsMsgComposeSendListener::OnGetDraftFolderURI(const char *aFolderURI)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgSendListener> composeSendListener = do_QueryReferent(mWeakComposeObj, &rv);
+ if (NS_SUCCEEDED(rv) && composeSendListener)
+ composeSendListener->OnGetDraftFolderURI(aFolderURI);
+
+ return NS_OK;
+}
+
+
+nsresult
+nsMsgComposeSendListener::OnStartCopy()
+{
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeSendListener::OnProgress(uint32_t aProgress, uint32_t aProgressMax)
+{
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeSendListener::OnStopCopy(nsresult aStatus)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgCompose> msgCompose = do_QueryReferent(mWeakComposeObj, &rv);
+ if (msgCompose)
+ {
+ if (mDeliverMode == nsIMsgSend::nsMsgQueueForLater ||
+ mDeliverMode == nsIMsgSend::nsMsgDeliverBackground ||
+ mDeliverMode == nsIMsgSend::nsMsgSaveAsDraft)
+ {
+ msgCompose->RememberQueuedDisposition();
+ }
+
+ // Ok, if we are here, we are done with the send/copy operation so
+ // we have to do something with the window....SHOW if failed, Close
+ // if succeeded
+
+ nsCOMPtr<nsIMsgProgress> progress;
+ msgCompose->GetProgress(getter_AddRefs(progress));
+ if (progress)
+ {
+ // Unregister ourself from msg compose progress
+ progress->UnregisterListener(this);
+ progress->CloseProgressDialog(NS_FAILED(aStatus));
+ }
+
+ msgCompose->NotifyStateListeners(nsIMsgComposeNotificationType::ComposeProcessDone, aStatus);
+
+ if (NS_SUCCEEDED(aStatus))
+ {
+ // We should only close the window if we are done. Things like templates
+ // and drafts aren't done so their windows should stay open
+ if (mDeliverMode == nsIMsgSend::nsMsgSaveAsDraft ||
+ mDeliverMode == nsIMsgSend::nsMsgSaveAsTemplate)
+ {
+ msgCompose->NotifyStateListeners(nsIMsgComposeNotificationType::SaveInFolderDone, aStatus);
+ // Remove the current draft msg when saving as draft/template is done.
+ msgCompose->SetDeleteDraft(true);
+ RemoveCurrentDraftMessage(msgCompose, true);
+ }
+ else
+ {
+ // Remove (possible) draft if we're in send later mode
+ if (mDeliverMode == nsIMsgSend::nsMsgQueueForLater ||
+ mDeliverMode == nsIMsgSend::nsMsgDeliverBackground)
+ {
+ msgCompose->SetDeleteDraft(true);
+ RemoveCurrentDraftMessage(msgCompose, true);
+ }
+ msgCompose->CloseWindow();
+ }
+ }
+ msgCompose->ClearMessageSend();
+ }
+
+ return rv;
+}
+
+nsresult
+nsMsgComposeSendListener::GetMsgFolder(nsIMsgCompose *compObj, nsIMsgFolder **msgFolder)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolder> aMsgFolder;
+ nsCString folderUri;
+
+ rv = compObj->GetSavedFolderURI(getter_Copies(folderUri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIRDFService> rdfService (do_GetService("@mozilla.org/rdf/rdf-service;1", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIRDFResource> resource;
+ rv = rdfService->GetResource(folderUri, getter_AddRefs(resource));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aMsgFolder = do_QueryInterface(resource, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_IF_ADDREF(*msgFolder = aMsgFolder);
+ return rv;
+}
+
+nsresult
+nsMsgComposeSendListener::RemoveCurrentDraftMessage(nsIMsgCompose *compObj, bool calledByCopy)
+{
+ nsresult rv;
+ nsCOMPtr <nsIMsgCompFields> compFields = nullptr;
+
+ rv = compObj->GetCompFields(getter_AddRefs(compFields));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get compose fields");
+ if (NS_FAILED(rv) || !compFields)
+ return rv;
+
+ nsCString curDraftIdURL;
+ nsMsgKey newUid = 0;
+ nsCString newDraftIdURL;
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+
+ rv = compFields->GetDraftId(getter_Copies(curDraftIdURL));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get draft id");
+
+ // Skip if no draft id (probably a new draft msg).
+ if (NS_SUCCEEDED(rv) && !curDraftIdURL.IsEmpty())
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgDBHdr;
+ rv = GetMsgDBHdrFromURI(curDraftIdURL.get(), getter_AddRefs(msgDBHdr));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get msg header DB interface pointer.");
+ if (NS_SUCCEEDED(rv) && msgDBHdr)
+ {
+ do { // Break on failure or removal not needed.
+ // Get the folder for the message resource.
+ rv = msgDBHdr->GetFolder(getter_AddRefs(msgFolder));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't get msg folder interface pointer.");
+ if (NS_FAILED(rv) || !msgFolder)
+ break;
+
+ // Only do this if it's a drafts folder.
+ bool isDraft;
+ msgFolder->GetFlag(nsMsgFolderFlags::Drafts, &isDraft);
+ if (!isDraft)
+ break;
+
+ // Only remove if the message is actually in the db. It might have only
+ // been in the use cache.
+ nsMsgKey key;
+ rv = msgDBHdr->GetMessageKey(&key);
+ if (NS_FAILED(rv))
+ break;
+ nsCOMPtr<nsIMsgDatabase> db;
+ msgFolder->GetMsgDatabase(getter_AddRefs(db));
+ if (!db)
+ break;
+ bool containsKey = false;
+ db->ContainsKey(key, &containsKey);
+ if (!containsKey)
+ break;
+
+ // Build the msg array.
+ nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't allocate array.");
+ if (NS_FAILED(rv) || !messageArray)
+ break;
+ rv = messageArray->AppendElement(msgDBHdr, false);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't append msg header to array.");
+ if (NS_FAILED(rv))
+ break;
+
+ // Ready to delete the msg.
+ rv = msgFolder->DeleteMessages(messageArray, nullptr, true, false, nullptr, false /*allowUndo*/);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "RemoveCurrentDraftMessage can't delete message.");
+ } while(false);
+ }
+ else
+ {
+ // If we get here we have the case where the draft folder
+ // is on the server and
+ // it's not currently open (in thread pane), so draft
+ // msgs are saved to the server
+ // but they're not in our local DB. In this case,
+ // GetMsgDBHdrFromURI() will never
+ // find the msg. If the draft folder is a local one
+ // then we'll not get here because
+ // the draft msgs are saved to the local folder and
+ // are in local DB. Make sure the
+ // msg folder is imap. Even if we get here due to
+ // DB errors (worst case), we should
+ // still try to delete msg on the server because
+ // that's where the master copy of the
+ // msgs are stored, if draft folder is on the server.
+ // For local case, since DB is bad
+ // we can't do anything with it anyway so it'll be
+ // noop in this case.
+ rv = GetMsgFolder(compObj, getter_AddRefs(msgFolder));
+ if (NS_SUCCEEDED(rv) && msgFolder)
+ {
+ nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(msgFolder);
+ NS_ASSERTION(imapFolder, "The draft folder MUST be an imap folder in order to mark the msg delete!");
+ if (NS_SUCCEEDED(rv) && imapFolder)
+ {
+ const char * str = PL_strchr(curDraftIdURL.get(), '#');
+ NS_ASSERTION(str, "Failed to get current draft id url");
+ if (str)
+ {
+ nsAutoCString srcStr(str+1);
+ nsresult err;
+ nsMsgKey messageID = srcStr.ToInteger(&err);
+ if (messageID != nsMsgKey_None)
+ {
+ rv = imapFolder->StoreImapFlags(kImapMsgDeletedFlag, true,
+ &messageID, 1, nullptr);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Now get the new uid so that next save will remove the right msg
+ // regardless whether or not the exiting msg can be deleted.
+ if (calledByCopy)
+ {
+ nsCOMPtr<nsIMsgFolder> savedToFolder;
+ nsCOMPtr<nsIMsgSend> msgSend;
+ rv = compObj->GetMessageSend(getter_AddRefs(msgSend));
+ NS_ASSERTION(msgSend, "RemoveCurrentDraftMessage msgSend is null.");
+ if (NS_FAILED(rv) || !msgSend)
+ return rv;
+
+ rv = msgSend->GetMessageKey(&newUid);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Make sure we have a folder interface pointer
+ rv = GetMsgFolder(compObj, getter_AddRefs(savedToFolder));
+
+ // Reset draft (uid) url with the new uid.
+ if (savedToFolder && newUid != nsMsgKey_None)
+ {
+ uint32_t folderFlags;
+ savedToFolder->GetFlags(&folderFlags);
+ if (folderFlags & nsMsgFolderFlags::Drafts)
+ {
+ rv = savedToFolder->GenerateMessageURI(newUid, newDraftIdURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ compFields->SetDraftId(newDraftIdURL.get());
+ }
+ }
+ }
+ return rv;
+}
+
+nsresult
+nsMsgComposeSendListener::SetMessageKey(nsMsgKey aMessageKey)
+{
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeSendListener::GetMessageId(nsACString& messageId)
+{
+ return NS_OK;
+}
+
+/* void onStateChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long aStateFlags, in nsresult aStatus); */
+NS_IMETHODIMP nsMsgComposeSendListener::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aStateFlags, nsresult aStatus)
+{
+ if (aStateFlags == nsIWebProgressListener::STATE_STOP)
+ {
+ nsCOMPtr<nsIMsgCompose> msgCompose = do_QueryReferent(mWeakComposeObj);
+ if (msgCompose)
+ {
+ nsCOMPtr<nsIMsgProgress> progress;
+ msgCompose->GetProgress(getter_AddRefs(progress));
+
+ // Time to stop any pending operation...
+ if (progress)
+ {
+ // Unregister ourself from msg compose progress
+ progress->UnregisterListener(this);
+
+ bool bCanceled = false;
+ progress->GetProcessCanceledByUser(&bCanceled);
+ if (bCanceled)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle(
+ "chrome://messenger/locale/messengercompose/composeMsgs.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsString msg;
+ bundle->GetStringFromName(u"msgCancelling", getter_Copies(msg));
+ progress->OnStatusChange(nullptr, nullptr, NS_OK, msg.get());
+ }
+ }
+
+ nsCOMPtr<nsIMsgSend> msgSend;
+ msgCompose->GetMessageSend(getter_AddRefs(msgSend));
+ if (msgSend)
+ msgSend->Abort();
+ }
+ }
+ return NS_OK;
+}
+
+/* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */
+NS_IMETHODIMP nsMsgComposeSendListener::OnProgressChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, int32_t aCurSelfProgress, int32_t aMaxSelfProgress, int32_t aCurTotalProgress, int32_t aMaxTotalProgress)
+{
+ /* Ignore this call */
+ return NS_OK;
+}
+
+/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location, in unsigned long aFlags); */
+NS_IMETHODIMP nsMsgComposeSendListener::OnLocationChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location, uint32_t aFlags)
+{
+ /* Ignore this call */
+ return NS_OK;
+}
+
+/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
+NS_IMETHODIMP nsMsgComposeSendListener::OnStatusChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsresult aStatus, const char16_t *aMessage)
+{
+ /* Ignore this call */
+ return NS_OK;
+}
+
+/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long state); */
+NS_IMETHODIMP nsMsgComposeSendListener::OnSecurityChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t state)
+{
+ /* Ignore this call */
+ return NS_OK;
+}
+
+nsresult
+nsMsgCompose::ConvertHTMLToText(nsIFile *aSigFile, nsString &aSigData)
+{
+ nsAutoString origBuf;
+
+ nsresult rv = LoadDataFromFile(aSigFile, origBuf);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ ConvertBufToPlainText(origBuf, false, false, true, true);
+ aSigData = origBuf;
+ return NS_OK;
+}
+
+nsresult
+nsMsgCompose::ConvertTextToHTML(nsIFile *aSigFile, nsString &aSigData)
+{
+ nsresult rv;
+ nsAutoString origBuf;
+
+ rv = LoadDataFromFile(aSigFile, origBuf);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Ok, once we are here, we need to escape the data to make sure that
+ // we don't do HTML stuff with plain text sigs.
+ //
+ char16_t *escaped = MsgEscapeHTML2(origBuf.get(), origBuf.Length());
+ if (escaped)
+ {
+ aSigData.Append(escaped);
+ NS_Free(escaped);
+ }
+ else
+ aSigData.Append(origBuf);
+ return NS_OK;
+}
+
+nsresult
+nsMsgCompose::LoadDataFromFile(nsIFile *file, nsString &sigData,
+ bool aAllowUTF8, bool aAllowUTF16)
+{
+ int32_t readSize;
+ uint32_t nGot;
+ char *readBuf;
+ char *ptr;
+
+ bool isDirectory = false;
+ file->IsDirectory(&isDirectory);
+ if (isDirectory) {
+ NS_ERROR("file is a directory");
+ return NS_MSG_ERROR_READING_FILE;
+ }
+
+
+ nsCOMPtr <nsIInputStream> inputFile;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), file);
+ if (NS_FAILED(rv))
+ return NS_MSG_ERROR_READING_FILE;
+
+ int64_t fileSize;
+ file->GetFileSize(&fileSize);
+ readSize = (uint32_t) fileSize;
+
+
+ ptr = readBuf = (char *)PR_Malloc(readSize + 1); if (!readBuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+ memset(readBuf, 0, readSize + 1);
+
+ while (readSize) {
+ inputFile->Read(ptr, readSize, &nGot);
+ if (nGot) {
+ readSize -= nGot;
+ ptr += nGot;
+ }
+ else {
+ readSize = 0;
+ }
+ }
+ inputFile->Close();
+
+ readSize = (uint32_t) fileSize;
+
+ nsAutoCString sigEncoding(nsMsgI18NParseMetaCharset(file));
+ bool removeSigCharset = !sigEncoding.IsEmpty() && m_composeHTML;
+
+ if (sigEncoding.IsEmpty()) {
+ if (aAllowUTF8 && MsgIsUTF8(nsDependentCString(readBuf))) {
+ sigEncoding.Assign("UTF-8");
+ }
+ else if (sigEncoding.IsEmpty() && aAllowUTF16 &&
+ readSize % 2 == 0 && readSize >= 2 &&
+ ((readBuf[0] == char(0xFE) && readBuf[1] == char(0xFF)) ||
+ (readBuf[0] == char(0xFF) && readBuf[1] == char(0xFE)))) {
+ sigEncoding.Assign("UTF-16");
+ }
+ else {
+ //default to platform encoding for plain text files w/o meta charset
+ nsAutoCString textFileCharset;
+ nsMsgI18NTextFileCharset(textFileCharset);
+ sigEncoding.Assign(textFileCharset);
+ }
+ }
+
+ nsAutoCString readStr(readBuf, (int32_t) fileSize);
+ PR_FREEIF(readBuf);
+
+ // XXX: ^^^ could really use nsContentUtils::SlurpFileToString instead!
+
+ if (NS_FAILED(ConvertToUnicode(sigEncoding.get(), readStr, sigData)))
+ CopyASCIItoUTF16(readStr, sigData);
+
+ //remove sig meta charset to allow user charset override during composition
+ if (removeSigCharset)
+ {
+ nsAutoCString metaCharset("charset=");
+ metaCharset.Append(sigEncoding);
+ int32_t pos = sigData.Find(metaCharset.BeginReading(), true);
+ if (pos != kNotFound)
+ sigData.Cut(pos, metaCharset.Length());
+ }
+ return NS_OK;
+}
+
+/**
+ * If the data contains file URLs, convert them to data URLs instead.
+ * This is intended to be used in for signature files, so that we can make sure
+ * images loaded into the editor are available on send.
+ */
+nsresult
+nsMsgCompose::ReplaceFileURLs(nsAutoString &aData)
+{
+ int32_t fPos;
+ int32_t offset = -1;
+ while ((fPos = aData.RFind("file://", true, offset)) != kNotFound) {
+ if (fPos != kNotFound && fPos > 0) {
+ char16_t q = aData.CharAt(fPos - 1);
+ bool quoted = (q == '"' || q == '\'');
+ int32_t end = kNotFound;
+ if (quoted) {
+ end = aData.FindChar(q, fPos);
+ }
+ else {
+ int32_t spacePos = aData.FindChar(' ', fPos);
+ int32_t gtPos = aData.FindChar('>', fPos);
+ if (gtPos != kNotFound && spacePos != kNotFound) {
+ end = (spacePos < gtPos) ? spacePos : gtPos;
+ }
+ else if (gtPos == kNotFound && spacePos != kNotFound) {
+ end = spacePos;
+ }
+ else if (gtPos != kNotFound && spacePos == kNotFound) {
+ end = gtPos;
+ }
+ }
+ if (end == kNotFound) {
+ break;
+ }
+ nsString fileURL;
+ fileURL = Substring(aData, fPos, end - fPos);
+ nsString dataURL;
+ nsresult rv = DataURLForFileURL(fileURL, dataURL);
+ // If this one failed, maybe because the file wasn't found,
+ // continue to process the next one.
+ if (NS_SUCCEEDED(rv)) {
+ aData.Replace(fPos, end - fPos, dataURL);
+ }
+ offset = fPos - 1;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsMsgCompose::DataURLForFileURL(const nsAString &aFileURL, nsAString &aDataURL)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMIMEService> mime = do_GetService("@mozilla.org/mime;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> fileUri;
+ rv = NS_NewURI(getter_AddRefs(fileUri), NS_ConvertUTF16toUTF8(aFileURL).get());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(fileUri, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIFile> file;
+ rv = fileUrl->GetFile(getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString type;
+ rv = mime->GetTypeFromFile(file, type);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString data;
+ rv = nsContentUtils::SlurpFileToString(file, data);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aDataURL.AssignLiteral("data:");
+ AppendUTF8toUTF16(type, aDataURL);
+
+ nsAutoString filename;
+ rv = file->GetLeafName(filename);
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString fn;
+ MsgEscapeURL(NS_ConvertUTF16toUTF8(filename),
+ nsINetUtil::ESCAPE_URL_FILE_BASENAME | nsINetUtil::ESCAPE_URL_FORCED, fn);
+ if (!fn.IsEmpty()) {
+ aDataURL.AppendLiteral(";filename=");
+ aDataURL.Append(NS_ConvertUTF8toUTF16(fn));
+ }
+ }
+
+ aDataURL.AppendLiteral(";base64,");
+ char *result = PL_Base64Encode(data.get(), data.Length(), nullptr);
+ nsDependentCString base64data(result);
+ NS_ENSURE_SUCCESS(rv, rv);
+ AppendUTF8toUTF16(base64data, aDataURL);
+ return NS_OK;
+}
+
+nsresult
+nsMsgCompose::BuildQuotedMessageAndSignature(void)
+{
+ //
+ // This should never happen...if it does, just bail out...
+ //
+ NS_ASSERTION(m_editor, "BuildQuotedMessageAndSignature but no editor!\n");
+ if (!m_editor)
+ return NS_ERROR_FAILURE;
+
+ // We will fire off the quote operation and wait for it to
+ // finish before we actually do anything with Ender...
+ return QuoteOriginalMessage();
+}
+
+//
+// This will process the signature file for the user. This method
+// will always append the results to the mMsgBody member variable.
+//
+nsresult
+nsMsgCompose::ProcessSignature(nsIMsgIdentity *identity, bool aQuoted, nsString *aMsgBody)
+{
+ nsresult rv = NS_OK;
+
+ // Now, we can get sort of fancy. This is the time we need to check
+ // for all sorts of user defined stuff, like signatures and editor
+ // types and the like!
+ //
+ // user_pref(".....sig_file", "y:\\sig.html");
+ // user_pref(".....attach_signature", true);
+ // user_pref(".....htmlSigText", "unicode sig");
+ //
+ // Note: We will have intelligent signature behavior in that we
+ // look at the signature file first...if the extension is .htm or
+ // .html, we assume its HTML, otherwise, we assume it is plain text
+ //
+ // ...and that's not all! What we will also do now is look and see if
+ // the file is an image file. If it is an image file, then we should
+ // insert the correct HTML into the composer to have it work, but if we
+ // are doing plain text compose, we should insert some sort of message
+ // saying "Image Signature Omitted" or something (not done yet).
+ //
+ // If there's a sig pref, it will only be used if there is no sig file defined,
+ // thus if attach_signature is checked, htmlSigText is ignored (bug 324495).
+ // Plain-text signatures may or may not have a trailing line break (bug 428040).
+
+ nsAutoCString sigNativePath;
+ bool attachFile = false;
+ bool useSigFile = false;
+ bool htmlSig = false;
+ bool imageSig = false;
+ nsAutoString sigData;
+ nsAutoString sigOutput;
+ int32_t reply_on_top = 0;
+ bool sig_bottom = true;
+ bool suppressSigSep = false;
+
+ nsCOMPtr<nsIFile> sigFile;
+ if (identity)
+ {
+ if (!CheckIncludeSignaturePrefs(identity))
+ return NS_OK;
+
+ identity->GetReplyOnTop(&reply_on_top);
+ identity->GetSigBottom(&sig_bottom);
+ identity->GetSuppressSigSep(&suppressSigSep);
+
+ rv = identity->GetAttachSignature(&attachFile);
+ if (NS_SUCCEEDED(rv) && attachFile)
+ {
+ rv = identity->GetSignature(getter_AddRefs(sigFile));
+ if (NS_SUCCEEDED(rv) && sigFile) {
+ rv = sigFile->GetNativePath(sigNativePath);
+ if (NS_SUCCEEDED(rv) && !sigNativePath.IsEmpty()) {
+ bool exists = false;
+ sigFile->Exists(&exists);
+ if (exists) {
+ useSigFile = true; // ok, there's a signature file
+
+ // Now, most importantly, we need to figure out what the content type is for
+ // this signature...if we can't, we assume text
+ nsAutoCString sigContentType;
+ nsresult rv2; // don't want to clobber the other rv
+ nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv2));
+ if (NS_SUCCEEDED(rv2)) {
+ rv2 = mimeFinder->GetTypeFromFile(sigFile, sigContentType);
+ if (NS_SUCCEEDED(rv2)) {
+ if (StringBeginsWith(sigContentType, NS_LITERAL_CSTRING("image/"), nsCaseInsensitiveCStringComparator()))
+ imageSig = true;
+ else if (sigContentType.Equals(TEXT_HTML, nsCaseInsensitiveCStringComparator()))
+ htmlSig = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Unless signature to be attached from file, use preference value;
+ // the htmlSigText value is always going to be treated as html if
+ // the htmlSigFormat pref is true, otherwise it is considered text
+ nsAutoString prefSigText;
+ if (identity && !attachFile)
+ identity->GetHtmlSigText(prefSigText);
+ // Now, if they didn't even want to use a signature, we should
+ // just return nicely.
+ //
+ if ((!useSigFile && prefSigText.IsEmpty()) || NS_FAILED(rv))
+ return NS_OK;
+
+ static const char htmlBreak[] = "<br>";
+ static const char dashes[] = "-- ";
+ static const char htmlsigopen[] = "<div class=\"moz-signature\">";
+ static const char htmlsigclose[] = "</div>"; /* XXX: Due to a bug in
+ 4.x' HTML editor, it will not be able to
+ break this HTML sig, if quoted (for the user to
+ interleave a comment). */
+ static const char _preopen[] = "<pre class=\"moz-signature\" cols=%d>";
+ char* preopen;
+ static const char preclose[] = "</pre>";
+
+ int32_t wrapLength = 72; // setup default value in case GetWrapLength failed
+ GetWrapLength(&wrapLength);
+ preopen = PR_smprintf(_preopen, wrapLength);
+ if (!preopen)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ bool paragraphMode =
+ mozilla::Preferences::GetBool("mail.compose.default_to_paragraph", false);
+
+ if (imageSig)
+ {
+ // We have an image signature. If we're using the in HTML composer, we
+ // should put in the appropriate HTML for inclusion, otherwise, do nothing.
+ if (m_composeHTML)
+ {
+ if (!paragraphMode)
+ sigOutput.AppendLiteral(htmlBreak);
+ sigOutput.AppendLiteral(htmlsigopen);
+ if ((mType == nsIMsgCompType::NewsPost || !suppressSigSep) &&
+ (reply_on_top != 1 || sig_bottom || !aQuoted)) {
+ sigOutput.AppendLiteral(dashes);
+ }
+
+ sigOutput.AppendLiteral(htmlBreak);
+ sigOutput.AppendLiteral("<img src='");
+
+ nsCOMPtr<nsIURI> fileURI;
+ nsresult rv = NS_NewFileURI(getter_AddRefs(fileURI), sigFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString fileURL;
+ fileURI->GetSpec(fileURL);
+
+ nsString dataURL;
+ rv = DataURLForFileURL(NS_ConvertUTF8toUTF16(fileURL), dataURL);
+ if (NS_SUCCEEDED(rv)) {
+ sigOutput.Append(dataURL);
+ }
+ sigOutput.AppendLiteral("' border=0>");
+ sigOutput.AppendLiteral(htmlsigclose);
+ }
+ }
+ else if (useSigFile)
+ {
+ // is this a text sig with an HTML editor?
+ if ( (m_composeHTML) && (!htmlSig) ) {
+ ConvertTextToHTML(sigFile, sigData);
+ }
+ // is this a HTML sig with a text window?
+ else if ( (!m_composeHTML) && (htmlSig) ) {
+ ConvertHTMLToText(sigFile, sigData);
+ }
+ else { // We have a match...
+ LoadDataFromFile(sigFile, sigData); // Get the data!
+ ReplaceFileURLs(sigData);
+ }
+ }
+
+ // if we have a prefSigText, append it to sigData.
+ if (!prefSigText.IsEmpty())
+ {
+ // set htmlSig if the pref is supposed to contain HTML code, defaults to false
+ rv = identity->GetHtmlSigFormat(&htmlSig);
+ if (NS_FAILED(rv))
+ htmlSig = false;
+
+ if (!m_composeHTML)
+ {
+ if (htmlSig)
+ ConvertBufToPlainText(prefSigText, false, false, true, true);
+ sigData.Append(prefSigText);
+ }
+ else
+ {
+ if (!htmlSig)
+ {
+ char16_t* escaped = MsgEscapeHTML2(prefSigText.get(), prefSigText.Length());
+ if (escaped)
+ {
+ sigData.Append(escaped);
+ NS_Free(escaped);
+ }
+ else
+ sigData.Append(prefSigText);
+ }
+ else {
+ ReplaceFileURLs(prefSigText);
+ sigData.Append(prefSigText);
+ }
+ }
+ }
+
+ // post-processing for plain-text signatures to ensure we end in CR, LF, or CRLF
+ if (!htmlSig && !m_composeHTML)
+ {
+ int32_t sigLength = sigData.Length();
+ if (sigLength > 0 && !(sigData.CharAt(sigLength - 1) == '\r')
+ && !(sigData.CharAt(sigLength - 1) == '\n'))
+ sigData.AppendLiteral(CRLF);
+ }
+
+ // Now that sigData holds data...if any, append it to the body in a nice
+ // looking manner
+ if (!sigData.IsEmpty())
+ {
+ if (m_composeHTML)
+ {
+ if (!paragraphMode)
+ sigOutput.AppendLiteral(htmlBreak);
+
+ if (htmlSig)
+ sigOutput.AppendLiteral(htmlsigopen);
+ else
+ sigOutput.Append(NS_ConvertASCIItoUTF16(preopen));
+ }
+
+ if ((reply_on_top != 1 || sig_bottom || !aQuoted) &&
+ sigData.Find("\r-- \r", true) < 0 &&
+ sigData.Find("\n-- \n", true) < 0 &&
+ sigData.Find("\n-- \r", true) < 0)
+ {
+ nsDependentSubstring firstFourChars(sigData, 0, 4);
+
+ if ((mType == nsIMsgCompType::NewsPost || !suppressSigSep) &&
+ !(firstFourChars.EqualsLiteral("-- \n") ||
+ firstFourChars.EqualsLiteral("-- \r")))
+ {
+ sigOutput.AppendLiteral(dashes);
+
+ if (!m_composeHTML || !htmlSig)
+ sigOutput.AppendLiteral(CRLF);
+ else if (m_composeHTML)
+ sigOutput.AppendLiteral(htmlBreak);
+ }
+ }
+
+ // add CRLF before signature for plain-text mode if signature comes before quote
+ if (!m_composeHTML && reply_on_top == 1 && !sig_bottom && aQuoted)
+ sigOutput.AppendLiteral(CRLF);
+
+ sigOutput.Append(sigData);
+
+ if (m_composeHTML)
+ {
+ if (htmlSig)
+ sigOutput.AppendLiteral(htmlsigclose);
+ else
+ sigOutput.AppendLiteral(preclose);
+ }
+ }
+
+ aMsgBody->Append(sigOutput);
+ PR_Free(preopen);
+ return NS_OK;
+}
+
+nsresult
+nsMsgCompose::BuildBodyMessageAndSignature()
+{
+ nsresult rv = NS_OK;
+
+ //
+ // This should never happen...if it does, just bail out...
+ //
+ if (!m_editor)
+ return NS_ERROR_FAILURE;
+
+ //
+ // Now, we have the body so we can just blast it into the
+ // composition editor window.
+ //
+ nsAutoString body;
+ m_compFields->GetBody(body);
+
+ /* Some time we want to add a signature and sometime we wont. Let's figure that now...*/
+ bool addSignature;
+ bool isQuoted = false;
+ switch (mType)
+ {
+ case nsIMsgCompType::ForwardInline :
+ addSignature = true;
+ isQuoted = true;
+ break;
+ case nsIMsgCompType::New :
+ case nsIMsgCompType::MailToUrl : /* same as New */
+ case nsIMsgCompType::Reply : /* should not happen! but just in case */
+ case nsIMsgCompType::ReplyAll : /* should not happen! but just in case */
+ case nsIMsgCompType::ReplyToList : /* should not happen! but just in case */
+ case nsIMsgCompType::ForwardAsAttachment : /* should not happen! but just in case */
+ case nsIMsgCompType::NewsPost :
+ case nsIMsgCompType::ReplyToGroup :
+ case nsIMsgCompType::ReplyToSender :
+ case nsIMsgCompType::ReplyToSenderAndGroup :
+ addSignature = true;
+ break;
+
+ case nsIMsgCompType::Draft :
+ case nsIMsgCompType::Template :
+ case nsIMsgCompType::Redirect :
+ addSignature = false;
+ break;
+
+ default :
+ addSignature = false;
+ break;
+ }
+
+ nsAutoString tSignature;
+ if (addSignature)
+ ProcessSignature(m_identity, isQuoted, &tSignature);
+
+ // if type is new, but we have body, this is probably a mapi send, so we need to
+ // replace '\n' with <br> so that the line breaks won't be lost by html.
+ // if mailtourl, do the same.
+ if (m_composeHTML && (mType == nsIMsgCompType::New || mType == nsIMsgCompType::MailToUrl))
+ MsgReplaceSubstring(body, NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("<br>"));
+
+ // Restore flowed text wrapping for Drafts/Templates.
+ // Look for unquoted lines - if we have an unquoted line
+ // that ends in a space, join this line with the next one
+ // by removing the end of line char(s).
+ int32_t wrapping_enabled = 0;
+ GetWrapLength(&wrapping_enabled);
+ if (!m_composeHTML && wrapping_enabled)
+ {
+ bool quote = false;
+ for (uint32_t i = 0; i < body.Length(); i ++)
+ {
+ if (i == 0 || body[i - 1] == '\n') // newline
+ {
+ if (body[i] == '>')
+ {
+ quote = true;
+ continue;
+ }
+ nsString s(Substring(body, i, 10));
+ if (StringBeginsWith(s, NS_LITERAL_STRING("-- \r")) ||
+ StringBeginsWith(s, NS_LITERAL_STRING("-- \n")))
+ {
+ i += 4;
+ continue;
+ }
+ if (StringBeginsWith(s, NS_LITERAL_STRING("- -- \r")) ||
+ StringBeginsWith(s, NS_LITERAL_STRING("- -- \n")))
+ {
+ i += 6;
+ continue;
+ }
+ }
+ if (body[i] == '\n' && i > 1)
+ {
+ if (quote)
+ {
+ quote = false;
+ continue; // skip quoted lines
+ }
+ uint32_t j = i - 1; // look backward for space
+ if (body[j] == '\r')
+ j --;
+ if (body[j] == ' ') // join this line with next one
+ body.Cut(j + 1, i - j); // remove CRLF
+ }
+ }
+ }
+
+ nsString empty;
+ rv = ConvertAndLoadComposeWindow(empty, body, tSignature,
+ false, m_composeHTML);
+
+ return rv;
+}
+
+nsresult nsMsgCompose::NotifyStateListeners(int32_t aNotificationType, nsresult aResult)
+{
+
+ if (aNotificationType == nsIMsgComposeNotificationType::SaveInFolderDone)
+ ResetUrisForEmbeddedObjects();
+
+ nsTObserverArray<nsCOMPtr<nsIMsgComposeStateListener> >::ForwardIterator iter(mStateListeners);
+ nsCOMPtr<nsIMsgComposeStateListener> thisListener;
+
+ while (iter.HasMore())
+ {
+ thisListener = iter.GetNext();
+
+ switch (aNotificationType)
+ {
+ case nsIMsgComposeNotificationType::ComposeFieldsReady:
+ thisListener->NotifyComposeFieldsReady();
+ break;
+
+ case nsIMsgComposeNotificationType::ComposeProcessDone:
+ thisListener->ComposeProcessDone(aResult);
+ break;
+
+ case nsIMsgComposeNotificationType::SaveInFolderDone:
+ thisListener->SaveInFolderDone(m_folderName.get());
+ break;
+
+ case nsIMsgComposeNotificationType::ComposeBodyReady:
+ thisListener->NotifyComposeBodyReady();
+ break;
+
+ default:
+ NS_NOTREACHED("Unknown notification");
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsMsgCompose::AttachmentPrettyName(const nsACString & scheme, const char* charset, nsACString& _retval)
+{
+ nsresult rv;
+
+ if (MsgLowerCaseEqualsLiteral(StringHead(scheme, 5), "file:"))
+ {
+ nsCOMPtr<nsIFile> file;
+ rv = NS_GetFileFromURLSpec(scheme,
+ getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoString leafName;
+ rv = file->GetLeafName(leafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ CopyUTF16toUTF8(leafName, _retval);
+ return rv;
+ }
+
+ // To work around a mysterious bug in VC++ 6.
+ const char* cset = (!charset || !*charset) ? "UTF-8" : charset;
+
+ nsCOMPtr<nsITextToSubURI> textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString retUrl;
+ rv = textToSubURI->UnEscapeURIForUI(nsDependentCString(cset), scheme, retUrl);
+
+ if (NS_SUCCEEDED(rv)) {
+ CopyUTF16toUTF8(retUrl, _retval);
+ } else {
+ _retval.Assign(scheme);
+ }
+ if (MsgLowerCaseEqualsLiteral(StringHead(scheme, 5), "http:"))
+ _retval.Cut(0, 7);
+
+ return NS_OK;
+}
+
+/**
+ * Retrieve address book directories and mailing lists.
+ *
+ * @param aDirUri directory URI
+ * @param allDirectoriesArray retrieved directories and sub-directories
+ * @param allMailListArray retrieved maillists
+ */
+nsresult
+nsMsgCompose::GetABDirAndMailLists(const nsACString& aDirUri,
+ nsCOMArray<nsIAbDirectory> &aDirArray,
+ nsTArray<nsMsgMailList> &aMailListArray)
+{
+ static bool collectedAddressbookFound;
+ if (aDirUri.EqualsLiteral(kMDBDirectoryRoot))
+ collectedAddressbookFound = false;
+
+ nsresult rv;
+ nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbDirectory> directory;
+ rv = abManager->GetDirectory(aDirUri, getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> subDirectories;
+ if (NS_SUCCEEDED(directory->GetChildNodes(getter_AddRefs(subDirectories))) && subDirectories)
+ {
+ nsCOMPtr<nsISupports> item;
+ bool hasMore;
+ while (NS_SUCCEEDED(rv = subDirectories->HasMoreElements(&hasMore)) && hasMore)
+ {
+ if (NS_SUCCEEDED(subDirectories->GetNext(getter_AddRefs(item))))
+ {
+ directory = do_QueryInterface(item, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ bool bIsMailList;
+
+ if (NS_SUCCEEDED(directory->GetIsMailList(&bIsMailList)) && bIsMailList)
+ {
+ aMailListArray.AppendElement(directory);
+ continue;
+ }
+
+ nsCString uri;
+ rv = directory->GetURI(uri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t pos;
+ if (uri.EqualsLiteral(kPersonalAddressbookUri))
+ pos = 0;
+ else
+ {
+ uint32_t count = aDirArray.Count();
+
+ if (uri.EqualsLiteral(kCollectedAddressbookUri))
+ {
+ collectedAddressbookFound = true;
+ pos = count;
+ }
+ else
+ {
+ if (collectedAddressbookFound && count > 1)
+ pos = count - 1;
+ else
+ pos = count;
+ }
+ }
+
+ aDirArray.InsertObjectAt(directory, pos);
+ rv = GetABDirAndMailLists(uri, aDirArray, aMailListArray);
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+/**
+ * Comparator for use with nsTArray::IndexOf to find a recipient.
+ * This comparator will check if an "address" is a mail list or not.
+ */
+struct nsMsgMailListComparator
+{
+ // A mail list will have one of the formats
+ // 1) "mName <mDescription>" when the list has a description
+ // 2) "mName <mName>" when the list lacks description
+ // A recipient is of the form "mName <mEmail>" - for equality the list
+ // name must be the same. The recipient "email" must match the list name for
+ // case 1, and the list description for case 2.
+ bool Equals(const nsMsgMailList &mailList,
+ const nsMsgRecipient &recipient) const {
+ if (!mailList.mName.Equals(recipient.mName,
+ nsCaseInsensitiveStringComparator()))
+ return false;
+ return mailList.mDescription.IsEmpty() ?
+ mailList.mName.Equals(recipient.mEmail, nsCaseInsensitiveStringComparator()) :
+ mailList.mDescription.Equals(recipient.mEmail, nsCaseInsensitiveStringComparator());
+ }
+};
+
+/**
+ * Comparator for use with nsTArray::IndexOf to find a recipient.
+ */
+struct nsMsgRecipientComparator
+{
+ bool Equals(const nsMsgRecipient &recipient,
+ const nsMsgRecipient &recipientToFind) const {
+ if (!recipient.mEmail.Equals(recipientToFind.mEmail,
+ nsCaseInsensitiveStringComparator()))
+ return false;
+
+ if (!recipient.mName.Equals(recipientToFind.mName,
+ nsCaseInsensitiveStringComparator()))
+ return false;
+
+ return true;
+ }
+};
+
+/**
+ * This function recursively resolves a mailing list and returns individual
+ * email addresses. Nested lists are supported. It maintains an array of
+ * already visited mailing lists to avoid endless recursion.
+ *
+ * @param aMailList the list
+ * @param allDirectoriesArray all directories
+ * @param allMailListArray all maillists
+ * @param mailListProcessed maillists processed (to avoid recursive lists)
+ * @param aListMembers list members
+ */
+nsresult
+nsMsgCompose::ResolveMailList(nsIAbDirectory* aMailList,
+ nsCOMArray<nsIAbDirectory> &allDirectoriesArray,
+ nsTArray<nsMsgMailList> &allMailListArray,
+ nsTArray<nsMsgMailList> &mailListProcessed,
+ nsTArray<nsMsgRecipient> &aListMembers)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIMutableArray> mailListAddresses;
+ rv = aMailList->GetAddressLists(getter_AddRefs(mailListAddresses));
+ if (NS_FAILED(rv))
+ return rv;
+
+ uint32_t nbrAddresses = 0;
+ mailListAddresses->GetLength(&nbrAddresses);
+ for (uint32_t i = 0; i < nbrAddresses; i++)
+ {
+ nsCOMPtr<nsIAbCard> existingCard(do_QueryElementAt(mailListAddresses, i, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsMsgRecipient newRecipient;
+
+ rv = existingCard->GetDisplayName(newRecipient.mName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = existingCard->GetPrimaryEmail(newRecipient.mEmail);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (newRecipient.mName.IsEmpty() && newRecipient.mEmail.IsEmpty()) {
+ continue;
+ }
+
+ // First check if it's a mailing list.
+ size_t index = allMailListArray.IndexOf(newRecipient, 0, nsMsgMailListComparator());
+ if (index != allMailListArray.NoIndex && allMailListArray[index].mDirectory)
+ {
+ // Check if maillist processed.
+ if (mailListProcessed.Contains(newRecipient, nsMsgMailListComparator())) {
+ continue;
+ }
+
+ nsCOMPtr<nsIAbDirectory> directory2(allMailListArray[index].mDirectory);
+
+ // Add mailList to mailListProcessed.
+ mailListProcessed.AppendElement(directory2);
+
+ // Resolve mailList members.
+ rv = ResolveMailList(directory2,
+ allDirectoriesArray,
+ allMailListArray,
+ mailListProcessed,
+ aListMembers);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ continue;
+ }
+
+ // Check if recipient is in aListMembers.
+ if (aListMembers.Contains(newRecipient, nsMsgRecipientComparator())) {
+ continue;
+ }
+
+ // Now we need to insert the new address into the list of recipients.
+ newRecipient.mCard = existingCard;
+ newRecipient.mDirectory = aMailList;
+
+ aListMembers.AppendElement(newRecipient);
+ }
+
+ return rv;
+}
+
+/**
+ * Lookup the recipients as specified in the compose fields (To, Cc, Bcc)
+ * in the address books and return an array of individual recipients.
+ * Mailing lists are replaced by the cards they contain, nested and recursive
+ * lists are taken care of, recipients contained in multiple lists are only
+ * added once.
+ *
+ * @param recipientsList (out) recipient array
+ */
+nsresult
+nsMsgCompose::LookupAddressBook(RecipientsArray &recipientsList)
+{
+ nsresult rv = NS_OK;
+
+ // First, build some arrays with the original recipients.
+
+ nsAutoString originalRecipients[MAX_OF_RECIPIENT_ARRAY];
+ m_compFields->GetTo(originalRecipients[0]);
+ m_compFields->GetCc(originalRecipients[1]);
+ m_compFields->GetBcc(originalRecipients[2]);
+
+ for (uint32_t i = 0; i < MAX_OF_RECIPIENT_ARRAY; ++i)
+ {
+ if (originalRecipients[i].IsEmpty())
+ continue;
+
+ rv = m_compFields->SplitRecipientsEx(originalRecipients[i],
+ recipientsList[i]);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Then look them up in the Addressbooks
+ bool stillNeedToSearch = true;
+ nsCOMPtr<nsIAbDirectory> abDirectory;
+ nsCOMPtr<nsIAbCard> existingCard;
+ nsTArray<nsMsgMailList> mailListArray;
+ nsTArray<nsMsgMailList> mailListProcessed;
+
+ nsCOMArray<nsIAbDirectory> addrbookDirArray;
+ rv = GetABDirAndMailLists(NS_LITERAL_CSTRING(kAllDirectoryRoot),
+ addrbookDirArray, mailListArray);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsString dirPath;
+ uint32_t nbrAddressbook = addrbookDirArray.Count();
+
+ for (uint32_t k = 0; k < nbrAddressbook && stillNeedToSearch; ++k)
+ {
+ // Avoid recursive mailing lists.
+ if (abDirectory && (addrbookDirArray[k] == abDirectory))
+ {
+ stillNeedToSearch = false;
+ break;
+ }
+
+ abDirectory = addrbookDirArray[k];
+ if (!abDirectory)
+ continue;
+
+ stillNeedToSearch = false;
+ for (uint32_t i = 0; i < MAX_OF_RECIPIENT_ARRAY; i ++)
+ {
+ mailListProcessed.Clear();
+
+ // Note: We check this each time to allow for length changes.
+ for (uint32_t j = 0; j < recipientsList[i].Length(); j++)
+ {
+ nsMsgRecipient &recipient = recipientsList[i][j];
+ if (!recipient.mDirectory)
+ {
+ // First check if it's a mailing list.
+ size_t index = mailListArray.IndexOf(recipient, 0, nsMsgMailListComparator());
+ if (index != mailListArray.NoIndex && mailListArray[index].mDirectory)
+ {
+ // Check mailList Processed.
+ if (mailListProcessed.Contains(recipient, nsMsgMailListComparator())) {
+ // Remove from recipientsList.
+ recipientsList[i].RemoveElementAt(j--);
+ continue;
+ }
+
+ nsCOMPtr<nsIAbDirectory> directory(mailListArray[index].mDirectory);
+
+ // Add mailList to mailListProcessed.
+ mailListProcessed.AppendElement(directory);
+
+ // Resolve mailList members.
+ nsTArray<nsMsgRecipient> members;
+ rv = ResolveMailList(directory,
+ addrbookDirArray,
+ mailListArray,
+ mailListProcessed,
+ members);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Remove mailList from recipientsList.
+ recipientsList[i].RemoveElementAt(j);
+
+ // Merge members into recipientsList[i].
+ uint32_t pos = 0;
+ for (uint32_t c = 0; c < members.Length(); c++)
+ {
+ nsMsgRecipient &member = members[c];
+ if (!recipientsList[i].Contains(member, nsMsgRecipientComparator())) {
+ recipientsList[i].InsertElementAt(j + pos, member);
+ pos++;
+ }
+ }
+ }
+ else
+ {
+ // Find a card that contains this e-mail address.
+ rv = abDirectory->CardForEmailAddress(NS_ConvertUTF16toUTF8(recipient.mEmail),
+ getter_AddRefs(existingCard));
+ if (NS_SUCCEEDED(rv) && existingCard)
+ {
+ recipient.mCard = existingCard;
+ recipient.mDirectory = abDirectory;
+ }
+ else
+ {
+ stillNeedToSearch = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::ExpandMailingLists()
+{
+ RecipientsArray recipientsList;
+ nsresult rv = LookupAddressBook(recipientsList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Reset the final headers with the expanded mailing lists.
+ nsAutoString recipientsStr;
+
+ for (int i = 0; i < MAX_OF_RECIPIENT_ARRAY; ++i)
+ {
+ uint32_t nbrRecipients = recipientsList[i].Length();
+ if (nbrRecipients == 0)
+ continue;
+ recipientsStr.Truncate();
+
+ // Note: We check this each time to allow for length changes.
+ for (uint32_t j = 0; j < recipientsList[i].Length(); ++j)
+ {
+ nsMsgRecipient &recipient = recipientsList[i][j];
+
+ if (!recipientsStr.IsEmpty())
+ recipientsStr.Append(char16_t(','));
+ nsAutoString address;
+ MakeMimeAddress(recipient.mName, recipient.mEmail, address);
+ recipientsStr.Append(address);
+
+ if (recipient.mCard)
+ {
+ bool readOnly;
+ rv = recipient.mDirectory->GetReadOnly(&readOnly);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Bump the popularity index for this card since we are about to send
+ // e-mail to it.
+ if (!readOnly)
+ {
+ uint32_t popularityIndex = 0;
+ if (NS_FAILED(recipient.mCard->GetPropertyAsUint32(
+ kPopularityIndexProperty, &popularityIndex)))
+ {
+ // TB 2 wrote the popularity value as hex, so if we get here,
+ // then we've probably got a hex value. We'll convert it back
+ // to decimal, as that's the best we can do.
+
+ nsCString hexPopularity;
+ if (NS_SUCCEEDED(recipient.mCard->GetPropertyAsAUTF8String(
+ kPopularityIndexProperty, hexPopularity)))
+ {
+ nsresult errorCode = NS_OK;
+ popularityIndex = hexPopularity.ToInteger(&errorCode, 16);
+ if (NS_FAILED(errorCode))
+ // We failed, just set it to zero.
+ popularityIndex = 0;
+ }
+ else
+ // We couldn't get it as a string either, so just reset to zero.
+ popularityIndex = 0;
+ }
+
+ recipient.mCard->SetPropertyAsUint32(kPopularityIndexProperty,
+ ++popularityIndex);
+ recipient.mDirectory->ModifyCard(recipient.mCard);
+ }
+ }
+ }
+
+ switch (i)
+ {
+ case 0: m_compFields->SetTo(recipientsStr); break;
+ case 1: m_compFields->SetCc(recipientsStr); break;
+ case 2: m_compFields->SetBcc(recipientsStr); break;
+ }
+ }
+
+ return NS_OK;
+}
+
+/**
+ * This function implements the decision logic for delivery format 'Auto-Detect',
+ * including optional 'Auto-Downgrade' behaviour for HTML messages considered
+ * convertible (silent, "lossless" conversion to plain text).
+ * @param aConvertible the result of analysing message body convertibility:
+ * nsIMsgCompConvertible::Plain | Yes | Altering | No
+ * @return nsIMsgCompSendFormat::AskUser | PlainText | HTML | Both
+ */
+NS_IMETHODIMP
+nsMsgCompose::DetermineHTMLAction(int32_t aConvertible, int32_t *result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+ nsresult rv;
+
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // *** Message-centric Auto-Downgrade ***
+ // If the message has practically no HTML formatting,
+ // AND if user accepts auto-downgrading (send options pref),
+ // bypass auto-detection of recipients' preferences and just
+ // send the message as plain text (silent, "lossless" conversion);
+ // which will also avoid asking for newsgroups for this typical scenario.
+ bool autoDowngrade = true;
+ rv = prefBranch->GetBoolPref("mailnews.sendformat.auto_downgrade", &autoDowngrade);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (autoDowngrade && (aConvertible == nsIMsgCompConvertible::Plain))
+ {
+ *result = nsIMsgCompSendFormat::PlainText;
+ return NS_OK;
+ }
+
+ // *** Newsgroups ***
+ // Right now, we don't have logic for newsgroups for intelligent send
+ // preferences. Therefore, bail out early and save us a lot of work if there
+ // are newsgroups.
+
+ nsAutoString newsgroups;
+ m_compFields->GetNewsgroups(newsgroups);
+
+ if (!newsgroups.IsEmpty())
+ {
+ *result = nsIMsgCompSendFormat::AskUser;
+ return NS_OK;
+ }
+
+ // *** Recipient-Centric Auto-Detect ***
+
+ RecipientsArray recipientsList;
+ rv = LookupAddressBook(recipientsList);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Finally return the list of non-HTML recipients if requested and/or rebuilt
+ // the recipient field. Also, check for domain preference when preferFormat
+ // is unknown.
+ nsString plaintextDomains;
+ nsString htmlDomains;
+
+ if (prefBranch)
+ {
+ NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.plaintext_domains",
+ EmptyString(), plaintextDomains);
+ NS_GetUnicharPreferenceWithDefault(prefBranch, "mailnews.html_domains",
+ EmptyString(), htmlDomains);
+ }
+
+ // allHTML and allPlain are summary recipient scopes of format preference
+ // according to address book and send options for recipient-centric Auto-Detect,
+ // used by Auto-Detect to determine the appropriate message delivery format.
+
+ // allHtml: All recipients prefer HTML.
+ bool allHtml = true;
+
+ // allPlain: All recipients prefer Plain Text.
+ bool allPlain = true;
+
+ // Exit the loop early if allHtml and allPlain both decay to false to save us
+ // some work.
+ for (int i = 0; i < MAX_OF_RECIPIENT_ARRAY && (allHtml || allPlain); ++i)
+ {
+ uint32_t nbrRecipients = recipientsList[i].Length();
+ for (uint32_t j = 0; j < nbrRecipients && (allHtml || allPlain); ++j)
+ {
+ nsMsgRecipient &recipient = recipientsList[i][j];
+ uint32_t preferFormat = nsIAbPreferMailFormat::unknown;
+ if (recipient.mCard)
+ {
+ recipient.mCard->GetPropertyAsUint32(kPreferMailFormatProperty,
+ &preferFormat);
+ }
+
+ // if we don't have a prefer format for a recipient, check the domain in
+ // case we have a format defined for it
+ if (preferFormat == nsIAbPreferMailFormat::unknown &&
+ (!plaintextDomains.IsEmpty() || !htmlDomains.IsEmpty()))
+ {
+ int32_t atPos = recipient.mEmail.FindChar('@');
+ if (atPos < 0)
+ continue;
+
+ nsDependentSubstring emailDomain = Substring(recipient.mEmail,
+ atPos + 1);
+ if (IsInDomainList(emailDomain, plaintextDomains))
+ preferFormat = nsIAbPreferMailFormat::plaintext;
+ else if (IsInDomainList(emailDomain, htmlDomains))
+ preferFormat = nsIAbPreferMailFormat::html;
+ }
+
+ // Determine the delivery format preference of this recipient and adjust
+ // the summary recipient scopes of the message accordingly.
+ switch (preferFormat)
+ {
+ case nsIAbPreferMailFormat::html:
+ allPlain = false;
+ break;
+
+ case nsIAbPreferMailFormat::plaintext:
+ allHtml = false;
+ break;
+
+ default: // nsIAbPreferMailFormat::unknown
+ allHtml = false;
+ allPlain = false;
+ break;
+ }
+ }
+ }
+
+ // Here's the final part of recipient-centric Auto-Detect logic where we set
+ // the actual send format (aka delivery format) after analysing recipients'
+ // format preferences above.
+
+ // If all recipients prefer HTML, then return HTML.
+ if (allHtml)
+ {
+ *result = nsIMsgCompSendFormat::HTML;
+ return NS_OK;
+ }
+
+ // If all recipients prefer plaintext, silently strip *all* HTML formatting,
+ // regardless of (non-)convertibility, and send the message as plaintext.
+ // **ToDo: UX-error-prevention, UX-wysiwyg: warn against dataloss potential.**
+ if (allPlain)
+ {
+ *result = nsIMsgCompSendFormat::PlainText;
+ return NS_OK;
+ }
+
+ // Otherwise, check the preference to see what action we should default to.
+ // This pref covers all recipient scopes involving prefers-plain (except allplain)
+ // and prefers-unknown. So we are mixing format conflict resolution options for
+ // prefers-plain with default format setting for prefers-unknown; not ideal.
+ int32_t action = nsIMsgCompSendFormat::AskUser;
+ rv = prefBranch->GetIntPref("mail.default_html_action", &action);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If the action is a known send format, return the value to send in that format.
+ // Otherwise, ask the user.
+ // Note that the preference may default to 0 (Ask), which is not a valid value
+ // for the following enum.
+ if (action == nsIMsgCompSendFormat::PlainText ||
+ action == nsIMsgCompSendFormat::HTML ||
+ action == nsIMsgCompSendFormat::Both)
+ {
+ *result = action;
+ return NS_OK;
+ }
+
+ // At this point, ask the user.
+ *result = nsIMsgCompSendFormat::AskUser;
+ return NS_OK;
+}
+
+/* Decides which tags trigger which convertible mode, i.e. here is the logic
+ for BodyConvertible */
+// Helper function. Parameters are not checked.
+nsresult nsMsgCompose::TagConvertible(nsIDOMElement *node, int32_t *_retval)
+{
+ nsresult rv;
+
+ *_retval = nsIMsgCompConvertible::No;
+
+ uint16_t nodeType;
+ rv = node->GetNodeType(&nodeType);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoString element;
+ rv = node->GetNodeName(element);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIDOMNode> pItem;
+
+ // style attribute on any element can change layout in any way, so that is not convertible.
+ nsAutoString attribValue;
+ if (NS_SUCCEEDED(node->GetAttribute(NS_LITERAL_STRING("style"), attribValue)) &&
+ !attribValue.IsEmpty())
+ {
+ *_retval = nsIMsgCompConvertible::No;
+ return NS_OK;
+ }
+
+ // moz-* classes are used internally by the editor and mail composition
+ // (like moz-cite or moz-signature). Those can be discarded.
+ // But any other ones are unconvertible. Style can be attached to them or any
+ // other context (e.g. in microformats).
+ if (NS_SUCCEEDED(node->GetAttribute(NS_LITERAL_STRING("class"), attribValue)) &&
+ !attribValue.IsEmpty() &&
+ !StringBeginsWith(attribValue, NS_LITERAL_STRING("moz-"), nsCaseInsensitiveStringComparator()))
+ {
+ *_retval = nsIMsgCompConvertible::No;
+ return NS_OK;
+ }
+ // ID attributes can contain attached style/context or be target of links
+ // so we should preserve them.
+ if (NS_SUCCEEDED(node->GetAttribute(NS_LITERAL_STRING("id"), attribValue)) &&
+ !attribValue.IsEmpty())
+ {
+ *_retval = nsIMsgCompConvertible::No;
+ return NS_OK;
+ }
+ if ( // some "simple" elements without "style" attribute
+ element.LowerCaseEqualsLiteral("br") ||
+ element.LowerCaseEqualsLiteral("p") ||
+ element.LowerCaseEqualsLiteral("pre") ||
+ element.LowerCaseEqualsLiteral("tt") ||
+ element.LowerCaseEqualsLiteral("html") ||
+ element.LowerCaseEqualsLiteral("head") ||
+ element.LowerCaseEqualsLiteral("meta") ||
+ element.LowerCaseEqualsLiteral("title")
+ )
+ {
+ *_retval = nsIMsgCompConvertible::Plain;
+ }
+ else if (
+ //element.LowerCaseEqualsLiteral("blockquote") || // see below
+ element.LowerCaseEqualsLiteral("ul") ||
+ element.LowerCaseEqualsLiteral("ol") ||
+ element.LowerCaseEqualsLiteral("li") ||
+ element.LowerCaseEqualsLiteral("dl") ||
+ element.LowerCaseEqualsLiteral("dt") ||
+ element.LowerCaseEqualsLiteral("dd")
+ )
+ {
+ *_retval = nsIMsgCompConvertible::Yes;
+ }
+ else if (
+ //element.LowerCaseEqualsLiteral("a") || // see below
+ element.LowerCaseEqualsLiteral("h1") ||
+ element.LowerCaseEqualsLiteral("h2") ||
+ element.LowerCaseEqualsLiteral("h3") ||
+ element.LowerCaseEqualsLiteral("h4") ||
+ element.LowerCaseEqualsLiteral("h5") ||
+ element.LowerCaseEqualsLiteral("h6") ||
+ element.LowerCaseEqualsLiteral("hr") ||
+ (
+ mConvertStructs
+ &&
+ (
+ element.LowerCaseEqualsLiteral("em") ||
+ element.LowerCaseEqualsLiteral("strong") ||
+ element.LowerCaseEqualsLiteral("code") ||
+ element.LowerCaseEqualsLiteral("b") ||
+ element.LowerCaseEqualsLiteral("i") ||
+ element.LowerCaseEqualsLiteral("u")
+ )
+ )
+ )
+ {
+ *_retval = nsIMsgCompConvertible::Altering;
+ }
+ else if (element.LowerCaseEqualsLiteral("body"))
+ {
+ *_retval = nsIMsgCompConvertible::Plain;
+
+ bool hasAttribute;
+ nsAutoString color;
+ if (NS_SUCCEEDED(node->HasAttribute(NS_LITERAL_STRING("background"), &hasAttribute))
+ && hasAttribute) // There is a background image
+ *_retval = nsIMsgCompConvertible::No;
+ else if (NS_SUCCEEDED(node->HasAttribute(NS_LITERAL_STRING("text"), &hasAttribute)) &&
+ hasAttribute &&
+ NS_SUCCEEDED(node->GetAttribute(NS_LITERAL_STRING("text"), color)) &&
+ !color.EqualsLiteral("#000000")) {
+ *_retval = nsIMsgCompConvertible::Altering;
+ }
+ else if (NS_SUCCEEDED(node->HasAttribute(NS_LITERAL_STRING("bgcolor"), &hasAttribute)) &&
+ hasAttribute &&
+ NS_SUCCEEDED(node->GetAttribute(NS_LITERAL_STRING("bgcolor"), color)) &&
+ !color.LowerCaseEqualsLiteral("#ffffff")) {
+ *_retval = nsIMsgCompConvertible::Altering;
+ }
+ else if (NS_SUCCEEDED(node->HasAttribute(NS_LITERAL_STRING("dir"), &hasAttribute))
+ && hasAttribute) // dir=rtl attributes should not downconvert
+ *_retval = nsIMsgCompConvertible::No;
+
+ //ignore special color setting for link, vlink and alink at this point.
+ }
+ else if (element.LowerCaseEqualsLiteral("blockquote"))
+ {
+ // Skip <blockquote type="cite">
+ *_retval = nsIMsgCompConvertible::Yes;
+
+ if (NS_SUCCEEDED(node->GetAttribute(NS_LITERAL_STRING("type"), attribValue)) &&
+ attribValue.LowerCaseEqualsLiteral("cite"))
+ {
+ *_retval = nsIMsgCompConvertible::Plain;
+ }
+ }
+ else if (
+ element.LowerCaseEqualsLiteral("div") ||
+ element.LowerCaseEqualsLiteral("span") ||
+ element.LowerCaseEqualsLiteral("a")
+ )
+ {
+ /* Do some special checks for these tags. They are inside this |else if|
+ for performance reasons */
+
+ // Maybe, it's an <a> element inserted by another recognizer (e.g. 4.x')
+ if (element.LowerCaseEqualsLiteral("a"))
+ {
+ /* Ignore anchor tag, if the URI is the same as the text
+ (as inserted by recognizers) */
+ *_retval = nsIMsgCompConvertible::Altering;
+
+ nsAutoString hrefValue;
+ bool hasChild;
+ if (NS_SUCCEEDED(node->GetAttribute(NS_LITERAL_STRING("href"), hrefValue)) &&
+ NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild)
+ {
+ nsCOMPtr<nsIDOMNodeList> children;
+ if (NS_SUCCEEDED(node->GetChildNodes(getter_AddRefs(children))) &&
+ children &&
+ NS_SUCCEEDED(children->Item(0, getter_AddRefs(pItem))) &&
+ pItem)
+ {
+ nsAutoString textValue;
+ if (NS_SUCCEEDED(pItem->GetNodeValue(textValue)) &&
+ textValue == hrefValue)
+ *_retval = nsIMsgCompConvertible::Plain;
+ }
+ }
+ }
+
+ // Lastly, test, if it is just a "simple" <div> or <span>
+ else if (
+ element.LowerCaseEqualsLiteral("div") ||
+ element.LowerCaseEqualsLiteral("span")
+ )
+ {
+ *_retval = nsIMsgCompConvertible::Plain;
+ }
+ }
+
+ return rv;
+}
+
+nsresult nsMsgCompose::_NodeTreeConvertible(nsIDOMElement *node, int32_t *_retval)
+{
+ NS_ENSURE_TRUE(node && _retval, NS_ERROR_NULL_POINTER);
+
+ nsresult rv;
+ int32_t result;
+
+ // Check this node
+ rv = TagConvertible(node, &result);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Walk tree recursively to check the children
+ bool hasChild;
+ if (NS_SUCCEEDED(node->HasChildNodes(&hasChild)) && hasChild)
+ {
+ nsCOMPtr<nsIDOMNodeList> children;
+ if (NS_SUCCEEDED(node->GetChildNodes(getter_AddRefs(children)))
+ && children)
+ {
+ uint32_t nbrOfElements;
+ rv = children->GetLength(&nbrOfElements);
+ for (uint32_t i = 0; NS_SUCCEEDED(rv) && i < nbrOfElements; i++)
+ {
+ nsCOMPtr<nsIDOMNode> pItem;
+ if (NS_SUCCEEDED(children->Item(i, getter_AddRefs(pItem)))
+ && pItem)
+ {
+ // We assume all nodes that are not elements are convertible,
+ // so only test elements.
+ nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(pItem);
+ if (domElement) {
+ int32_t curresult;
+ rv = _NodeTreeConvertible(domElement, &curresult);
+
+ if (NS_SUCCEEDED(rv) && curresult > result)
+ result = curresult;
+ }
+ }
+ }
+ }
+ }
+
+ *_retval = result;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::BodyConvertible(int32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ NS_ENSURE_STATE(m_editor);
+
+ nsCOMPtr<nsIDOMDocument> rootDocument;
+ nsresult rv = m_editor->GetDocument(getter_AddRefs(rootDocument));
+ if (NS_FAILED(rv) || !rootDocument)
+ return rv;
+
+ // get the top level element, which contains <html>
+ nsCOMPtr<nsIDOMElement> rootElement;
+ rv = rootDocument->GetDocumentElement(getter_AddRefs(rootElement));
+ if (NS_FAILED(rv) || !rootElement)
+ return rv;
+
+ return _NodeTreeConvertible(rootElement, _retval);
+}
+
+NS_IMETHODIMP
+nsMsgCompose::GetIdentity(nsIMsgIdentity **aIdentity)
+{
+ NS_ENSURE_ARG_POINTER(aIdentity);
+ NS_IF_ADDREF(*aIdentity = m_identity);
+ return NS_OK;
+}
+
+/**
+ * Position above the quote, that is either <blockquote> or
+ * <div class="moz-cite-prefix"> or <div class="moz-forward-container">
+ * in an inline-forwarded message.
+ */
+nsresult
+nsMsgCompose::MoveToAboveQuote(void)
+{
+ nsCOMPtr<nsIDOMElement> rootElement;
+ nsresult rv = m_editor->GetRootElement(getter_AddRefs(rootElement));
+ if (NS_FAILED(rv) || !rootElement) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIDOMNode> node;
+ nsAutoString attributeName;
+ nsAutoString attributeValue;
+ nsAutoString tagLocalName;
+ attributeName.AssignLiteral("class");
+
+ rv = rootElement->GetFirstChild(getter_AddRefs(node));
+ while (NS_SUCCEEDED(rv) && node) {
+ nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
+ if (element) {
+ // First check for <blockquote>. This will most likely not trigger
+ // since well-behaved quotes are preceded by a cite prefix.
+ node->GetLocalName(tagLocalName);
+ if (tagLocalName.EqualsLiteral("blockquote")) {
+ break;
+ }
+
+ // Get the class value.
+ element->GetAttribute(attributeName, attributeValue);
+
+ // Now check for the cite prefix, so an element with
+ // class="moz-cite-prefix".
+ if (attributeValue.Find("moz-cite-prefix", true) != kNotFound) {
+ break;
+ }
+
+ // Next check for forwarded content.
+ // The forwarded part is inside an element with
+ // class="moz-forward-container".
+ if (attributeValue.Find("moz-forward-container", true) != kNotFound) {
+ break;
+ }
+ }
+
+ rv = node->GetNextSibling(getter_AddRefs(node));
+ if (NS_FAILED(rv) || !node) {
+ // No further siblings found, so we didn't find what we were looking for.
+ rv = NS_OK;
+ node = nullptr;
+ break;
+ }
+ }
+
+ // Now position. If no quote was found, we position to the very front.
+ int32_t offset = 0;
+ if (node) {
+ rv = GetChildOffset(node, rootElement, offset);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ nsCOMPtr<nsISelection> selection;
+ m_editor->GetSelection(getter_AddRefs(selection));
+ if (selection)
+ rv = selection->Collapse(rootElement, offset);
+
+ return rv;
+}
+
+/**
+ * nsEditor::BeginningOfDocument() will position to the beginning of the document
+ * before the first editable element. It will position into a container.
+ * We need to be at the very front.
+ */
+nsresult
+nsMsgCompose::MoveToBeginningOfDocument(void)
+{
+ nsCOMPtr<nsIDOMElement> rootElement;
+ nsresult rv = m_editor->GetRootElement(getter_AddRefs(rootElement));
+ if (NS_FAILED(rv) || !rootElement) {
+ return rv;
+ }
+
+ nsCOMPtr<nsISelection> selection;
+ m_editor->GetSelection(getter_AddRefs(selection));
+ if (selection)
+ rv = selection->Collapse(rootElement, 0);
+
+ return rv;
+}
+
+/**
+ * M-C's nsEditor::EndOfDocument() will position to the end of the document
+ * but it will position into a container. We really need to position
+ * after the last container so we don't accidentally position into a
+ * <blockquote>. That's why we use our own function.
+ */
+nsresult
+nsMsgCompose::MoveToEndOfDocument(void)
+{
+ int32_t offset;
+ nsCOMPtr<nsIDOMElement> rootElement;
+ nsCOMPtr<nsIDOMNode> lastNode;
+ nsresult rv = m_editor->GetRootElement(getter_AddRefs(rootElement));
+ if (NS_FAILED(rv) || !rootElement) {
+ return rv;
+ }
+
+ rv = rootElement->GetLastChild(getter_AddRefs(lastNode));
+ if (NS_FAILED(rv) || !lastNode) {
+ return rv;
+ }
+
+ rv = GetChildOffset(lastNode, rootElement, offset);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCOMPtr<nsISelection> selection;
+ m_editor->GetSelection(getter_AddRefs(selection));
+ if (selection)
+ rv = selection->Collapse(rootElement, offset + 1);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgCompose::SetIdentity(nsIMsgIdentity *aIdentity)
+{
+ NS_ENSURE_ARG_POINTER(aIdentity);
+
+ m_identity = aIdentity;
+
+ nsresult rv;
+
+ if (! m_editor)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIDOMElement> rootElement;
+ rv = m_editor->GetRootElement(getter_AddRefs(rootElement));
+ if (NS_FAILED(rv) || !rootElement)
+ return rv;
+
+ //First look for the current signature, if we have one
+ nsCOMPtr<nsIDOMNode> lastNode;
+ nsCOMPtr<nsIDOMNode> node;
+ nsCOMPtr<nsIDOMNode> tempNode;
+ nsAutoString tagLocalName;
+
+ rv = rootElement->GetLastChild(getter_AddRefs(lastNode));
+ if (NS_SUCCEEDED(rv) && lastNode)
+ {
+ node = lastNode;
+ // In html, the signature is inside an element with
+ // class="moz-signature"
+ bool signatureFound = false;
+ nsAutoString attributeName;
+ attributeName.AssignLiteral("class");
+
+ do
+ {
+ nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
+ if (element)
+ {
+ nsAutoString attributeValue;
+
+ rv = element->GetAttribute(attributeName, attributeValue);
+
+ if (attributeValue.Find("moz-signature", true) != kNotFound) {
+ signatureFound = true;
+ break;
+ }
+ }
+ } while (!signatureFound &&
+ node &&
+ NS_SUCCEEDED(node->GetPreviousSibling(getter_AddRefs(node))));
+
+ if (signatureFound)
+ {
+ m_editor->BeginTransaction();
+ node->GetPreviousSibling(getter_AddRefs(tempNode));
+ rv = m_editor->DeleteNode(node);
+ if (NS_FAILED(rv))
+ {
+ m_editor->EndTransaction();
+ return rv;
+ }
+
+ // Also, remove the <br> right before the signature.
+ if (tempNode)
+ {
+ tempNode->GetLocalName(tagLocalName);
+ if (tagLocalName.EqualsLiteral("br"))
+ m_editor->DeleteNode(tempNode);
+ }
+ m_editor->EndTransaction();
+ }
+ }
+
+ if (!CheckIncludeSignaturePrefs(aIdentity))
+ return NS_OK;
+
+ // Then add the new one if needed
+ nsAutoString aSignature;
+
+ // No delimiter needed if not a compose window
+ bool isQuoted;
+ switch (mType)
+ {
+ case nsIMsgCompType::New :
+ case nsIMsgCompType::NewsPost :
+ case nsIMsgCompType::MailToUrl :
+ case nsIMsgCompType::ForwardAsAttachment :
+ isQuoted = false;
+ break;
+ default :
+ isQuoted = true;
+ break;
+ }
+
+ ProcessSignature(aIdentity, isQuoted, &aSignature);
+
+ if (!aSignature.IsEmpty())
+ {
+ TranslateLineEnding(aSignature);
+
+ m_editor->BeginTransaction();
+ int32_t reply_on_top = 0;
+ bool sig_bottom = true;
+ aIdentity->GetReplyOnTop(&reply_on_top);
+ aIdentity->GetSigBottom(&sig_bottom);
+ bool sigOnTop = (reply_on_top == 1 && !sig_bottom);
+ if (sigOnTop && isQuoted) {
+ rv = MoveToAboveQuote();
+ } else {
+ // Note: New messages aren't quoted so we always move to the end.
+ rv = MoveToEndOfDocument();
+ }
+
+ if (NS_SUCCEEDED(rv)) {
+ if (m_composeHTML) {
+ nsCOMPtr<nsIHTMLEditor> htmlEditor (do_QueryInterface(m_editor));
+ rv = htmlEditor->InsertHTML(aSignature);
+ } else {
+ nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(m_editor));
+ rv = textEditor->InsertLineBreak();
+ InsertDivWrappedTextAtSelection(aSignature, NS_LITERAL_STRING("moz-signature"));
+ }
+ }
+ m_editor->EndTransaction();
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgCompose::CheckCharsetConversion(nsIMsgIdentity *identity, char **fallbackCharset, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(identity);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ // Kept around for legacy reasons. This method is supposed to check that the
+ // headers can be converted to the appropriate charset, but we don't support
+ // encoding headers to non-UTF-8, so this is now moot.
+ if (fallbackCharset)
+ *fallbackCharset = nullptr;
+ *_retval = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgCompose::GetDeliverMode(MSG_DeliverMode* aDeliverMode)
+{
+ NS_ENSURE_ARG_POINTER(aDeliverMode);
+ *aDeliverMode = mDeliverMode;
+ return NS_OK;
+}
+
+nsMsgMailList::nsMsgMailList(nsIAbDirectory* directory) :
+ mDirectory(directory)
+{
+ mDirectory->GetDirName(mName);
+ mDirectory->GetDescription(mDescription);
+
+ if (mDescription.IsEmpty())
+ mDescription = mName;
+
+ mDirectory = directory;
+}
diff --git a/mailnews/compose/src/nsMsgCompose.h b/mailnews/compose/src/nsMsgCompose.h
new file mode 100644
index 000000000..19609228f
--- /dev/null
+++ b/mailnews/compose/src/nsMsgCompose.h
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsMsgCompose_H_
+#define _nsMsgCompose_H_
+
+#include "nsIMsgCompose.h"
+#include "nsCOMArray.h"
+#include "nsTObserverArray.h"
+#include "nsWeakReference.h"
+#include "nsMsgCompFields.h"
+#include "nsIOutputStream.h"
+#include "nsIMsgQuote.h"
+#include "nsIMsgCopyServiceListener.h"
+#include "nsIBaseWindow.h"
+#include "nsIAbDirectory.h"
+#include "nsIWebProgressListener.h"
+#include "nsIMimeConverter.h"
+#include "nsIUnicodeDecoder.h"
+#include "nsIMsgFolder.h"
+#include "nsIDOMNode.h"
+#include "mozIDOMWindow.h"
+
+// Forward declares
+class QuotingOutputStreamListener;
+class nsMsgComposeSendListener;
+class nsIEditorMailSupport;
+class nsIRDFService;
+class nsIArray;
+struct nsMsgMailList;
+
+class nsMsgCompose : public nsIMsgCompose, public nsSupportsWeakReference
+{
+ public:
+
+ nsMsgCompose();
+
+ /* this macro defines QueryInterface, AddRef and Release for this class */
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /*** nsIMsgCompose pure virtual functions */
+ NS_DECL_NSIMSGCOMPOSE
+
+ /* nsIMsgSendListener interface */
+ NS_DECL_NSIMSGSENDLISTENER
+
+protected:
+ virtual ~nsMsgCompose();
+
+ // Deal with quoting issues...
+ nsresult QuoteOriginalMessage(); // New template
+ nsresult SetQuotingToFollow(bool aVal);
+ nsresult ConvertHTMLToText(nsIFile *aSigFile, nsString &aSigData);
+ nsresult ConvertTextToHTML(nsIFile *aSigFile, nsString &aSigData);
+ bool IsEmbeddedObjectSafe(const char * originalScheme,
+ const char * originalHost,
+ const char * originalPath,
+ nsIDOMNode * object);
+ nsresult ResetUrisForEmbeddedObjects();
+ nsresult TagEmbeddedObjects(nsIEditorMailSupport *aMailEditor);
+
+ nsCString mQuoteCharset;
+ nsCString mOriginalMsgURI; // used so we can mark message disposition flags after we send the message
+
+ int32_t mWhatHolder;
+
+ nsresult LoadDataFromFile(nsIFile *file,
+ nsString &sigData,
+ bool aAllowUTF8 = true,
+ bool aAllowUTF16 = true);
+
+ bool CheckIncludeSignaturePrefs(nsIMsgIdentity *identity);
+ //m_folderName to store the value of the saved drafts folder.
+ nsCString m_folderName;
+ void InsertDivWrappedTextAtSelection(const nsAString &aText,
+ const nsAString &classStr);
+
+ protected:
+ nsresult CreateMessage(const char * originalMsgURI, MSG_ComposeType type, nsIMsgCompFields* compFields);
+ void CleanUpRecipients(nsString& recipients);
+ nsresult GetABDirAndMailLists(const nsACString& aDirUri,
+ nsCOMArray<nsIAbDirectory>& aDirArray,
+ nsTArray<nsMsgMailList>& aMailListArray);
+ nsresult ResolveMailList(nsIAbDirectory* aMailList,
+ nsCOMArray<nsIAbDirectory>& allDirectoriesArray,
+ nsTArray<nsMsgMailList>& allMailListArray,
+ nsTArray<nsMsgMailList>& mailListResolved,
+ nsTArray<nsMsgRecipient>& aListMembers);
+ nsresult TagConvertible(nsIDOMElement *node, int32_t *_retval);
+ nsresult _NodeTreeConvertible(nsIDOMElement *node, int32_t *_retval);
+ nsresult MoveToAboveQuote(void);
+ nsresult MoveToBeginningOfDocument(void);
+ nsresult MoveToEndOfDocument(void);
+ nsresult ReplaceFileURLs(nsAutoString &sigData);
+ nsresult DataURLForFileURL(const nsAString &aFileURL, nsAString &aDataURL);
+
+// 3 = To, Cc, Bcc
+#define MAX_OF_RECIPIENT_ARRAY 3
+ typedef nsTArray<nsMsgRecipient> RecipientsArray[MAX_OF_RECIPIENT_ARRAY];
+ /**
+ * This method parses the compose fields and associates email addresses with
+ * the relevant cards from the address books.
+ */
+ nsresult LookupAddressBook(RecipientsArray &recipientList);
+ bool IsLastWindow();
+
+ // Helper function. Parameters are not checked.
+ bool mConvertStructs; // for TagConvertible
+
+ nsCOMPtr<nsIEditor> m_editor;
+ mozIDOMWindowProxy *m_window;
+ nsCOMPtr<nsIDocShell> mDocShell;
+ nsCOMPtr<nsIBaseWindow> m_baseWindow;
+ nsMsgCompFields *m_compFields;
+ nsCOMPtr<nsIMsgIdentity> m_identity;
+ bool m_composeHTML;
+ QuotingOutputStreamListener *mQuoteStreamListener;
+ nsCOMPtr<nsIOutputStream> mBaseStream;
+
+ nsCOMPtr<nsIMsgSend> mMsgSend; // for composition back end
+ nsCOMPtr<nsIMsgProgress> mProgress; // use by the back end to report progress to the front end
+
+ // Deal with quoting issues...
+ nsString mCiteReference;
+ nsCOMPtr<nsIMsgQuote> mQuote;
+ bool mQuotingToFollow; // Quoting indicator
+ MSG_ComposeType mType; // Message type
+ bool mCharsetOverride;
+ bool mAnswerDefaultCharset;
+ bool mDeleteDraft;
+ nsMsgDispositionState mDraftDisposition;
+ nsCOMPtr <nsIMsgDBHdr> mOrigMsgHdr;
+
+ nsCString mSmtpPassword;
+ nsCString mHtmlToQuote;
+
+ nsTObserverArray<nsCOMPtr<nsIMsgComposeStateListener> > mStateListeners;
+ nsTObserverArray<nsCOMPtr<nsIMsgSendListener> > mExternalSendListeners;
+
+ bool mInsertingQuotedContent;
+ MSG_DeliverMode mDeliverMode; // nsIMsgCompDeliverMode long.
+
+ friend class QuotingOutputStreamListener;
+ friend class nsMsgComposeSendListener;
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+// THIS IS THE CLASS THAT IS THE STREAM Listener OF THE HTML OUPUT
+// FROM LIBMIME. THIS IS FOR QUOTING
+////////////////////////////////////////////////////////////////////////////////////
+class QuotingOutputStreamListener : public nsIMsgQuotingOutputStreamListener
+{
+public:
+ QuotingOutputStreamListener(const char *originalMsgURI,
+ nsIMsgDBHdr *origMsgHdr,
+ bool quoteHeaders,
+ bool headersOnly,
+ nsIMsgIdentity *identity,
+ nsIMsgQuote* msgQuote,
+ bool charsetFixed,
+ bool quoteOriginal,
+ const nsACString& htmlToQuote);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIMSGQUOTINGOUTPUTSTREAMLISTENER
+
+ NS_IMETHOD SetComposeObj(nsIMsgCompose *obj);
+ NS_IMETHOD ConvertToPlainText(bool formatflowed,
+ bool delsp,
+ bool formatted,
+ bool disallowBreaks);
+ NS_IMETHOD InsertToCompose(nsIEditor *aEditor, bool aHTMLEditor);
+ NS_IMETHOD AppendToMsgBody(const nsCString &inStr);
+
+private:
+ virtual ~QuotingOutputStreamListener();
+ nsWeakPtr mWeakComposeObj;
+ nsString mMsgBody;
+ nsString mCitePrefix;
+ nsString mSignature;
+ bool mQuoteHeaders;
+ bool mHeadersOnly;
+ bool mCharsetFixed;
+ nsCOMPtr<nsIMsgQuote> mQuote;
+ nsCOMPtr<nsIMimeHeaders> mHeaders;
+ nsCOMPtr<nsIMsgIdentity> mIdentity;
+ nsCOMPtr<nsIMsgDBHdr> mOrigMsgHdr;
+ nsString mCiteReference;
+ nsCOMPtr<nsIMimeConverter> mMimeConverter;
+ nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
+ int32_t mUnicodeBufferCharacterLength;
+ char16_t* mUnicodeConversionBuffer;
+ bool mQuoteOriginal;
+ nsCString mHtmlToQuote;
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+// This is the listener class for the send operation. We have to create this class
+// to listen for message send completion and eventually notify the caller
+////////////////////////////////////////////////////////////////////////////////////
+class nsMsgComposeSendListener : public nsIMsgComposeSendListener, public nsIMsgSendListener, public nsIMsgCopyServiceListener, public nsIWebProgressListener
+{
+public:
+ nsMsgComposeSendListener(void);
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsIMsgComposeSendListener interface
+ NS_DECL_NSIMSGCOMPOSESENDLISTENER
+
+ // nsIMsgSendListener interface
+ NS_DECL_NSIMSGSENDLISTENER
+
+ // nsIMsgCopyServiceListener interface
+ NS_DECL_NSIMSGCOPYSERVICELISTENER
+
+ // nsIWebProgressListener interface
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+ nsresult RemoveCurrentDraftMessage(nsIMsgCompose *compObj, bool calledByCopy);
+ nsresult GetMsgFolder(nsIMsgCompose *compObj, nsIMsgFolder **msgFolder);
+
+private:
+ virtual ~nsMsgComposeSendListener();
+ nsWeakPtr mWeakComposeObj;
+ MSG_DeliverMode mDeliverMode;
+};
+
+/******************************************************************************
+ * nsMsgMailList
+ ******************************************************************************/
+struct nsMsgMailList
+{
+ explicit nsMsgMailList(nsIAbDirectory* directory);
+
+ nsString mName;
+ nsString mDescription;
+ nsCOMPtr<nsIAbDirectory> mDirectory;
+};
+
+#endif /* _nsMsgCompose_H_ */
diff --git a/mailnews/compose/src/nsMsgComposeContentHandler.cpp b/mailnews/compose/src/nsMsgComposeContentHandler.cpp
new file mode 100644
index 000000000..cc52bcb67
--- /dev/null
+++ b/mailnews/compose/src/nsMsgComposeContentHandler.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgComposeContentHandler.h"
+#include "nsMsgComposeService.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgCompCID.h"
+#include "nsIChannel.h"
+#include "nsIURI.h"
+#include "plstr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsPIDOMWindow.h"
+#include "mozIDOMWindow.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsNetUtil.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsIMsgAccountManager.h"
+
+static NS_DEFINE_CID(kMsgComposeServiceCID, NS_MSGCOMPOSESERVICE_CID);
+
+nsMsgComposeContentHandler::nsMsgComposeContentHandler()
+{
+}
+
+// The following macro actually implement addref, release and query interface
+// for our component.
+NS_IMPL_ISUPPORTS(nsMsgComposeContentHandler, nsIContentHandler)
+
+nsMsgComposeContentHandler::~nsMsgComposeContentHandler()
+{
+}
+
+// Try to get an appropriate nsIMsgIdentity by going through the window, getting
+// the document's URI, then the corresponding nsIMsgDBHdr. Then find the server
+// associated with that header and get the first identity for it.
+nsresult nsMsgComposeContentHandler::GetBestIdentity(
+ nsIInterfaceRequestor* aWindowContext, nsIMsgIdentity **aIdentity)
+{
+ nsresult rv;
+
+ nsCOMPtr<mozIDOMWindowProxy> domWindow = do_GetInterface(aWindowContext);
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+ nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(domWindow);
+
+ nsAutoString documentURIString;
+ rv = window->GetDoc()->GetDocumentURI(documentURIString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> documentURI;
+ rv = NS_NewURI(getter_AddRefs(documentURI), documentURIString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgMessageUrl> msgURI = do_QueryInterface(documentURI);
+ if (!msgURI)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = msgURI->GetMessageHeader(getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> folder;
+ rv = msgHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // nsIMsgDBHdrs from .eml messages have a null folder, so bail out if that's
+ // the case.
+ if (!folder)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = folder->GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(
+ NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = accountManager->GetFirstIdentityForServer(server, aIdentity);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgComposeContentHandler::HandleContent(const char * aContentType,
+ nsIInterfaceRequestor* aWindowContext, nsIRequest *request)
+{
+ nsresult rv = NS_OK;
+ if (!request)
+ return NS_ERROR_NULL_POINTER;
+
+ // First of all, get the content type and make sure it is a content type we
+ // know how to handle!
+ if (PL_strcasecmp(aContentType, "application/x-mailto") == 0) {
+ nsCOMPtr<nsIMsgIdentity> identity;
+
+ if (aWindowContext)
+ GetBestIdentity(aWindowContext, getter_AddRefs(identity));
+
+ nsCOMPtr<nsIURI> aUri;
+ nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
+ if(!aChannel) return NS_ERROR_FAILURE;
+
+ rv = aChannel->GetURI(getter_AddRefs(aUri));
+ if (aUri)
+ {
+ nsCOMPtr<nsIMsgComposeService> composeService =
+ do_GetService(kMsgComposeServiceCID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = composeService->OpenComposeWindowWithURI(nullptr, aUri, identity);
+ }
+ } else {
+ // The content-type was not application/x-mailto...
+ return NS_ERROR_WONT_HANDLE_CONTENT;
+ }
+
+ return rv;
+}
diff --git a/mailnews/compose/src/nsMsgComposeContentHandler.h b/mailnews/compose/src/nsMsgComposeContentHandler.h
new file mode 100644
index 000000000..360a83608
--- /dev/null
+++ b/mailnews/compose/src/nsMsgComposeContentHandler.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIContentHandler.h"
+#include "nsIMsgIdentity.h"
+
+class nsMsgComposeContentHandler : public nsIContentHandler
+{
+public:
+ nsMsgComposeContentHandler();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTENTHANDLER
+private:
+ virtual ~nsMsgComposeContentHandler();
+ nsresult GetBestIdentity(nsIInterfaceRequestor* aWindowContext,
+ nsIMsgIdentity **identity);
+};
diff --git a/mailnews/compose/src/nsMsgComposeParams.cpp b/mailnews/compose/src/nsMsgComposeParams.cpp
new file mode 100644
index 000000000..46f964cee
--- /dev/null
+++ b/mailnews/compose/src/nsMsgComposeParams.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgComposeParams.h"
+
+nsMsgComposeParams::nsMsgComposeParams() :
+ mType(nsIMsgCompType::New),
+ mFormat(nsIMsgCompFormat::Default),
+ mBodyIsLink(false)
+{
+}
+
+/* the following macro actually implement addref, release and query interface for our component. */
+NS_IMPL_ISUPPORTS(nsMsgComposeParams, nsIMsgComposeParams)
+
+nsMsgComposeParams::~nsMsgComposeParams()
+{
+}
+
+/* attribute MSG_ComposeType type; */
+NS_IMETHODIMP nsMsgComposeParams::GetType(MSG_ComposeType *aType)
+{
+ NS_ENSURE_ARG_POINTER(aType);
+
+ *aType = mType;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeParams::SetType(MSG_ComposeType aType)
+{
+ mType = aType;
+ return NS_OK;
+}
+
+/* attribute MSG_ComposeFormat format; */
+NS_IMETHODIMP nsMsgComposeParams::GetFormat(MSG_ComposeFormat *aFormat)
+{
+ NS_ENSURE_ARG_POINTER(aFormat);
+
+ *aFormat = mFormat;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeParams::SetFormat(MSG_ComposeFormat aFormat)
+{
+ mFormat = aFormat;
+ return NS_OK;
+}
+
+/* attribute string originalMsgURI; */
+NS_IMETHODIMP nsMsgComposeParams::GetOriginalMsgURI(char * *aOriginalMsgURI)
+{
+ NS_ENSURE_ARG_POINTER(aOriginalMsgURI);
+
+ *aOriginalMsgURI = ToNewCString(mOriginalMsgUri);
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeParams::SetOriginalMsgURI(const char * aOriginalMsgURI)
+{
+ mOriginalMsgUri = aOriginalMsgURI;
+ return NS_OK;
+}
+
+/* attribute nsIMsgIdentity identity; */
+NS_IMETHODIMP nsMsgComposeParams::GetIdentity(nsIMsgIdentity * *aIdentity)
+{
+ NS_ENSURE_ARG_POINTER(aIdentity);
+ NS_IF_ADDREF(*aIdentity = mIdentity);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeParams::SetIdentity(nsIMsgIdentity * aIdentity)
+{
+ mIdentity = aIdentity;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeParams::SetOrigMsgHdr(nsIMsgDBHdr *aMsgHdr)
+{
+ mOrigMsgHdr = aMsgHdr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeParams::GetOrigMsgHdr(nsIMsgDBHdr * *aMsgHdr)
+{
+ NS_ENSURE_ARG_POINTER(aMsgHdr);
+ NS_IF_ADDREF(*aMsgHdr = mOrigMsgHdr);
+ return NS_OK;
+}
+
+/* attribute ACString htmlToQuote; */
+NS_IMETHODIMP nsMsgComposeParams::GetHtmlToQuote(nsACString& aHtmlToQuote)
+{
+ aHtmlToQuote = mHtmlToQuote;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeParams::SetHtmlToQuote(const nsACString& aHtmlToQuote)
+{
+ mHtmlToQuote = aHtmlToQuote;
+ return NS_OK;
+}
+
+/* attribute nsIMsgCompFields composeFields; */
+NS_IMETHODIMP nsMsgComposeParams::GetComposeFields(nsIMsgCompFields * *aComposeFields)
+{
+ NS_ENSURE_ARG_POINTER(aComposeFields);
+
+ if (mComposeFields)
+ {
+ *aComposeFields = mComposeFields;
+ NS_ADDREF(*aComposeFields);
+ }
+ else
+ *aComposeFields = nullptr;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeParams::SetComposeFields(nsIMsgCompFields * aComposeFields)
+{
+ mComposeFields = aComposeFields;
+ return NS_OK;
+}
+
+/* attribute boolean bodyIsLink; */
+NS_IMETHODIMP nsMsgComposeParams::GetBodyIsLink(bool *aBodyIsLink)
+{
+ NS_ENSURE_ARG_POINTER(aBodyIsLink);
+
+ *aBodyIsLink = mBodyIsLink;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeParams::SetBodyIsLink(bool aBodyIsLink)
+{
+ mBodyIsLink = aBodyIsLink;
+ return NS_OK;
+}
+
+/* attribute nsIMsgSendLisneter sendListener; */
+NS_IMETHODIMP nsMsgComposeParams::GetSendListener(nsIMsgSendListener * *aSendListener)
+{
+ NS_ENSURE_ARG_POINTER(aSendListener);
+
+ if (mSendListener)
+ {
+ *aSendListener = mSendListener;
+ NS_ADDREF(*aSendListener);
+ }
+ else
+ *aSendListener = nullptr;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeParams::SetSendListener(nsIMsgSendListener * aSendListener)
+{
+ mSendListener = aSendListener;
+ return NS_OK;
+}
+
+/* attribute string smtpPassword; */
+NS_IMETHODIMP nsMsgComposeParams::GetSmtpPassword(char * *aSmtpPassword)
+{
+ NS_ENSURE_ARG_POINTER(aSmtpPassword);
+
+ *aSmtpPassword = ToNewCString(mSMTPPassword);
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeParams::SetSmtpPassword(const char * aSmtpPassword)
+{
+ mSMTPPassword = aSmtpPassword;
+ return NS_OK;
+}
+
diff --git a/mailnews/compose/src/nsMsgComposeParams.h b/mailnews/compose/src/nsMsgComposeParams.h
new file mode 100644
index 000000000..00eaaa335
--- /dev/null
+++ b/mailnews/compose/src/nsMsgComposeParams.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMsgComposeParams.h"
+#include "nsStringGlue.h"
+#include "nsIMsgHdr.h"
+#include "nsCOMPtr.h"
+class nsMsgComposeParams : public nsIMsgComposeParams
+{
+public:
+ nsMsgComposeParams();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGCOMPOSEPARAMS
+
+private:
+ virtual ~nsMsgComposeParams();
+ MSG_ComposeType mType;
+ MSG_ComposeFormat mFormat;
+ nsCString mOriginalMsgUri;
+ nsCOMPtr<nsIMsgIdentity> mIdentity;
+ nsCOMPtr<nsIMsgCompFields> mComposeFields;
+ bool mBodyIsLink;
+ nsCOMPtr<nsIMsgSendListener> mSendListener;
+ nsCString mSMTPPassword;
+ nsCOMPtr<nsIMsgDBHdr> mOrigMsgHdr;
+ nsCString mHtmlToQuote;
+};
diff --git a/mailnews/compose/src/nsMsgComposeProgressParams.cpp b/mailnews/compose/src/nsMsgComposeProgressParams.cpp
new file mode 100644
index 000000000..23c5507c5
--- /dev/null
+++ b/mailnews/compose/src/nsMsgComposeProgressParams.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgComposeProgressParams.h"
+#include "nsServiceManagerUtils.h"
+
+NS_IMPL_ISUPPORTS(nsMsgComposeProgressParams, nsIMsgComposeProgressParams)
+
+nsMsgComposeProgressParams::nsMsgComposeProgressParams() :
+ m_deliveryMode(nsIMsgCompDeliverMode::Now)
+{
+}
+
+nsMsgComposeProgressParams::~nsMsgComposeProgressParams()
+{
+}
+
+/* attribute wstring subject; */
+NS_IMETHODIMP nsMsgComposeProgressParams::GetSubject(char16_t * *aSubject)
+{
+ NS_ENSURE_ARG(aSubject);
+
+ *aSubject = ToNewUnicode(m_subject);
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeProgressParams::SetSubject(const char16_t * aSubject)
+{
+ m_subject = aSubject;
+ return NS_OK;
+}
+
+/* attribute MSG_DeliverMode deliveryMode; */
+NS_IMETHODIMP nsMsgComposeProgressParams::GetDeliveryMode(MSG_DeliverMode *aDeliveryMode)
+{
+ NS_ENSURE_ARG(aDeliveryMode);
+
+ *aDeliveryMode = m_deliveryMode;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeProgressParams::SetDeliveryMode(MSG_DeliverMode aDeliveryMode)
+{
+ m_deliveryMode = aDeliveryMode;
+ return NS_OK;
+}
diff --git a/mailnews/compose/src/nsMsgComposeProgressParams.h b/mailnews/compose/src/nsMsgComposeProgressParams.h
new file mode 100644
index 000000000..6d9598f16
--- /dev/null
+++ b/mailnews/compose/src/nsMsgComposeProgressParams.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIMsgComposeProgressParams.h"
+
+class nsMsgComposeProgressParams : public nsIMsgComposeProgressParams
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGCOMPOSEPROGRESSPARAMS
+
+ nsMsgComposeProgressParams();
+
+private:
+ virtual ~nsMsgComposeProgressParams();
+ nsString m_subject;
+ MSG_DeliverMode m_deliveryMode;
+};
diff --git a/mailnews/compose/src/nsMsgComposeService.cpp b/mailnews/compose/src/nsMsgComposeService.cpp
new file mode 100644
index 000000000..944f7dbe3
--- /dev/null
+++ b/mailnews/compose/src/nsMsgComposeService.cpp
@@ -0,0 +1,1479 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgComposeService.h"
+#include "nsMsgCompCID.h"
+#include "nsIMsgSend.h"
+#include "nsIServiceManager.h"
+#include "nsIObserverService.h"
+#include "nsIMsgIdentity.h"
+#include "nsISmtpUrl.h"
+#include "nsIURI.h"
+#include "nsMsgI18N.h"
+#include "nsIMsgComposeParams.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIWindowWatcher.h"
+#include "mozIDOMWindow.h"
+#include "nsIContentViewer.h"
+#include "nsIMsgWindow.h"
+#include "nsIDocShell.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMHTMLDocument.h"
+#include "nsIDOMElement.h"
+#include "nsIXULWindow.h"
+#include "nsIWindowMediator.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIBaseWindow.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsMsgBaseCID.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMimeMiscStatus.h"
+#include "nsIStreamConverter.h"
+#include "nsMsgMimeCID.h"
+#include "nsToolkitCompsCID.h"
+#include "nsNetUtil.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIMsgDatabase.h"
+#include "nsIDocumentEncoder.h"
+#include "nsContentCID.h"
+#include "nsISelection.h"
+#include "nsUTF8Utils.h"
+#include "nsILineBreaker.h"
+#include "nsLWBrkCIID.h"
+#include "mozilla/Services.h"
+#include "mimemoz2.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+#include "mozilla/Logging.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgMessageService.h"
+#include "nsMsgUtils.h"
+#endif
+
+#include "nsICommandLine.h"
+#include "nsIAppStartup.h"
+#include "nsMsgUtils.h"
+#include "nsIPrincipal.h"
+
+#ifdef XP_WIN32
+#include <windows.h>
+#include <shellapi.h>
+#include "nsIWidget.h"
+#endif
+
+#define DEFAULT_CHROME "chrome://messenger/content/messengercompose/messengercompose.xul"
+
+#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION "mailnews.reply_quoting_selection"
+#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION_MULTI_WORD "mailnews.reply_quoting_selection.multi_word"
+#define PREF_MAILNEWS_REPLY_QUOTING_SELECTION_ONLY_IF "mailnews.reply_quoting_selection.only_if_chars"
+
+#define MAIL_ROOT_PREF "mail."
+#define MAILNEWS_ROOT_PREF "mailnews."
+#define HTMLDOMAINUPDATE_VERSION_PREF_NAME "global_html_domains.version"
+#define HTMLDOMAINUPDATE_DOMAINLIST_PREF_NAME "global_html_domains"
+#define USER_CURRENT_HTMLDOMAINLIST_PREF_NAME "html_domains"
+#define USER_CURRENT_PLAINTEXTDOMAINLIST_PREF_NAME "plaintext_domains"
+#define DOMAIN_DELIMITER ','
+
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+static PRLogModuleInfo *MsgComposeLogModule = nullptr;
+
+static uint32_t GetMessageSizeFromURI(const char * originalMsgURI)
+{
+ uint32_t msgSize = 0;
+
+ if (originalMsgURI && *originalMsgURI)
+ {
+ nsCOMPtr <nsIMsgDBHdr> originalMsgHdr;
+ GetMsgDBHdrFromURI(originalMsgURI, getter_AddRefs(originalMsgHdr));
+ if (originalMsgHdr)
+ originalMsgHdr->GetMessageSize(&msgSize);
+ }
+
+ return msgSize;
+}
+#endif
+
+nsMsgComposeService::nsMsgComposeService()
+{
+
+// Defaulting the value of mLogComposePerformance to FALSE to prevent logging.
+ mLogComposePerformance = false;
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+ if (!MsgComposeLogModule)
+ MsgComposeLogModule = PR_NewLogModule("msgcompose");
+
+ mStartTime = PR_IntervalNow();
+ mPreviousTime = mStartTime;
+#endif
+
+}
+
+NS_IMPL_ISUPPORTS(nsMsgComposeService,
+ nsIMsgComposeService,
+ ICOMMANDLINEHANDLER,
+ nsISupportsWeakReference)
+
+nsMsgComposeService::~nsMsgComposeService()
+{
+ mOpenComposeWindows.Clear();
+}
+
+nsresult nsMsgComposeService::Init()
+{
+ nsresult rv = NS_OK;
+
+ Reset();
+
+ AddGlobalHtmlDomains();
+ // Since the compose service should only be initialized once, we can
+ // be pretty sure there aren't any existing compose windows open.
+ MsgCleanupTempFiles("nsmail", "tmp");
+ MsgCleanupTempFiles("nsemail", "html");
+ MsgCleanupTempFiles("nscopy", "tmp");
+ return rv;
+}
+
+void nsMsgComposeService::Reset()
+{
+ mOpenComposeWindows.Clear();
+
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefs)
+ prefs->GetBoolPref("mailnews.logComposePerformance", &mLogComposePerformance);
+}
+
+// Function to open a message compose window and pass an nsIMsgComposeParams
+// parameter to it.
+NS_IMETHODIMP
+nsMsgComposeService::OpenComposeWindowWithParams(const char *chrome,
+ nsIMsgComposeParams *params)
+{
+ NS_ENSURE_ARG_POINTER(params);
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+ if(mLogComposePerformance)
+ {
+ TimeStamp("Start opening the window", true);
+ }
+#endif
+
+ nsresult rv;
+
+ NS_ENSURE_ARG_POINTER(params);
+
+ //Use default identity if no identity has been specified
+ nsCOMPtr<nsIMsgIdentity> identity;
+ params->GetIdentity(getter_AddRefs(identity));
+ if (!identity)
+ {
+ GetDefaultIdentity(getter_AddRefs(identity));
+ params->SetIdentity(identity);
+ }
+
+ // Create a new window.
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ if (!wwatch)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsISupportsInterfacePointer> msgParamsWrapper =
+ do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ msgParamsWrapper->SetData(params);
+ msgParamsWrapper->SetDataIID(&NS_GET_IID(nsIMsgComposeParams));
+
+ nsCOMPtr<mozIDOMWindowProxy> newWindow;
+ rv = wwatch->OpenWindow(0, chrome && *chrome ? chrome : DEFAULT_CHROME,
+ "_blank", "all,chrome,dialog=no,status,toolbar", msgParamsWrapper,
+ getter_AddRefs(newWindow));
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::DetermineComposeHTML(nsIMsgIdentity *aIdentity, MSG_ComposeFormat aFormat, bool *aComposeHTML)
+{
+ NS_ENSURE_ARG_POINTER(aComposeHTML);
+
+ *aComposeHTML = true;
+ switch (aFormat)
+ {
+ case nsIMsgCompFormat::HTML:
+ *aComposeHTML = true;
+ break;
+ case nsIMsgCompFormat::PlainText:
+ *aComposeHTML = false;
+ break;
+
+ default:
+ nsCOMPtr<nsIMsgIdentity> identity = aIdentity;
+ if (!identity)
+ GetDefaultIdentity(getter_AddRefs(identity));
+
+ if (identity)
+ {
+ identity->GetComposeHtml(aComposeHTML);
+ if (aFormat == nsIMsgCompFormat::OppositeOfDefault)
+ *aComposeHTML = !*aComposeHTML;
+ }
+ else
+ {
+ // default identity not found. Use the mail.html_compose pref to determine
+ // message compose type (HTML or PlainText).
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefs)
+ {
+ nsresult rv;
+ bool useHTMLCompose;
+ rv = prefs->GetBoolPref(MAIL_ROOT_PREF "html_compose", &useHTMLCompose);
+ if (NS_SUCCEEDED(rv))
+ *aComposeHTML = useHTMLCompose;
+ }
+ }
+ break;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeService::GetOrigWindowSelection(MSG_ComposeType type, nsIMsgWindow *aMsgWindow, nsACString& aSelHTML)
+{
+ nsresult rv;
+
+ // Good hygiene
+ aSelHTML.Truncate();
+
+ // Get the pref to see if we even should do reply quoting selection
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool replyQuotingSelection;
+ rv = prefs->GetBoolPref(PREF_MAILNEWS_REPLY_QUOTING_SELECTION, &replyQuotingSelection);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!replyQuotingSelection)
+ return NS_ERROR_ABORT;
+
+ // Now delve down in to the message to get the HTML representation of the selection
+ nsCOMPtr<nsIDocShell> rootDocShell;
+ rv = aMsgWindow->GetRootDocShell(getter_AddRefs(rootDocShell));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocShellTreeItem> childAsItem;
+ rv = rootDocShell->FindChildWithName(NS_LITERAL_STRING("messagepane"),
+ true, false, nullptr, nullptr, getter_AddRefs(childAsItem));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(childAsItem, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<mozIDOMWindowProxy> domWindow(do_GetInterface(childAsItem));
+ NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
+ nsCOMPtr<nsPIDOMWindowOuter> privateWindow = nsPIDOMWindowOuter::From(domWindow);
+ nsCOMPtr<nsISelection> sel = privateWindow->GetSelection();
+ NS_ENSURE_TRUE(sel, NS_ERROR_FAILURE);
+
+ bool requireMultipleWords = true;
+ nsAutoCString charsOnlyIf;
+ prefs->GetBoolPref(PREF_MAILNEWS_REPLY_QUOTING_SELECTION_MULTI_WORD, &requireMultipleWords);
+ prefs->GetCharPref(PREF_MAILNEWS_REPLY_QUOTING_SELECTION_ONLY_IF, getter_Copies(charsOnlyIf));
+ if (sel && (requireMultipleWords || !charsOnlyIf.IsEmpty()))
+ {
+ nsAutoString selPlain;
+ rv = sel->ToString(selPlain);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If "mailnews.reply_quoting_selection.multi_word" is on, then there must be at least
+ // two words selected in order to quote just the selected text
+ if (requireMultipleWords)
+ {
+ if (selPlain.IsEmpty())
+ return NS_ERROR_ABORT;
+
+ nsCOMPtr<nsILineBreaker> lineBreaker = do_GetService(NS_LBRK_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ const uint32_t length = selPlain.Length();
+ const char16_t* unicodeStr = selPlain.get();
+ int32_t endWordPos = lineBreaker->Next(unicodeStr, length, 0);
+
+ // If there's not even one word, then there's not multiple words
+ if (endWordPos == NS_LINEBREAKER_NEED_MORE_TEXT)
+ return NS_ERROR_ABORT;
+
+ // If after the first word is only space, then there's not multiple words
+ const char16_t* end;
+ for (end = unicodeStr + endWordPos; NS_IsSpace(*end); end++)
+ ;
+ if (!*end)
+ return NS_ERROR_ABORT;
+ }
+ }
+
+ if (!charsOnlyIf.IsEmpty())
+ {
+ if (MsgFindCharInSet(selPlain, charsOnlyIf.get()) < 0)
+ return NS_ERROR_ABORT;
+ }
+ }
+
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMDocument> domDocument;
+ rv = contentViewer->GetDOMDocument(getter_AddRefs(domDocument));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocumentEncoder> docEncoder(do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = docEncoder->Init(domDocument, NS_LITERAL_STRING("text/html"), 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = docEncoder->SetSelection(sel);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString selHTML;
+ rv = docEncoder->EncodeToString(selHTML);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now remove <span class="moz-txt-citetags">&gt; </span>.
+ nsAutoCString html(NS_ConvertUTF16toUTF8(selHTML).get());
+ int32_t spanInd = html.Find("<span class=\"moz-txt-citetags\">");
+ while (spanInd != kNotFound) {
+ nsAutoCString right0(Substring(html, spanInd));
+ int32_t endInd = right0.Find("</span>");
+ if (endInd == kNotFound)
+ break; // oops, where is the closing tag gone?
+ nsAutoCString right1(Substring(html, spanInd + endInd + 7));
+ html.SetLength(spanInd);
+ html.Append(right1);
+ spanInd = html.Find("<span class=\"moz-txt-citetags\">");
+ }
+
+ aSelHTML.Assign(html);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::OpenComposeWindow(const char *msgComposeWindowURL, nsIMsgDBHdr *origMsgHdr, const char *originalMsgURI,
+ MSG_ComposeType type, MSG_ComposeFormat format, nsIMsgIdentity * aIdentity, nsIMsgWindow *aMsgWindow)
+{
+ nsresult rv;
+
+ // Check for any reply type that wants to ignore the quote.
+ bool ignoreQuote = false;
+ if (type >= nsIMsgCompType::ReplyIgnoreQuote) {
+ type -= nsIMsgCompType::ReplyIgnoreQuote;
+ ignoreQuote = true;
+ }
+
+ nsCOMPtr<nsIMsgIdentity> identity = aIdentity;
+ if (!identity)
+ GetDefaultIdentity(getter_AddRefs(identity));
+
+ /* Actually, the only way to implement forward inline is to simulate a template message.
+ Maybe one day when we will have more time we can change that
+ */
+ if (type == nsIMsgCompType::ForwardInline || type == nsIMsgCompType::Draft || type == nsIMsgCompType::Template
+ || type == nsIMsgCompType::ReplyWithTemplate || type == nsIMsgCompType::Redirect)
+ {
+ nsAutoCString uriToOpen(originalMsgURI);
+ uriToOpen += (uriToOpen.FindChar('?') == kNotFound) ? '?' : '&';
+ uriToOpen.Append("fetchCompleteMessage=true");
+ if (type == nsIMsgCompType::Redirect)
+ uriToOpen.Append("&redirect=true");
+
+ return LoadDraftOrTemplate(uriToOpen, type == nsIMsgCompType::ForwardInline || type == nsIMsgCompType::Draft ?
+ nsMimeOutput::nsMimeMessageDraftOrTemplate : nsMimeOutput::nsMimeMessageEditorTemplate,
+ identity, originalMsgURI, origMsgHdr, type == nsIMsgCompType::ForwardInline,
+ format == nsIMsgCompFormat::OppositeOfDefault, aMsgWindow);
+ }
+
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && pMsgComposeParams)
+ {
+ nsCOMPtr<nsIMsgCompFields> pMsgCompFields (do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && pMsgCompFields)
+ {
+ pMsgComposeParams->SetType(type);
+ pMsgComposeParams->SetFormat(format);
+ pMsgComposeParams->SetIdentity(identity);
+
+ // When doing a reply (except with a template) see if there's a selection that we should quote
+ if (!ignoreQuote &&
+ (type == nsIMsgCompType::Reply ||
+ type == nsIMsgCompType::ReplyAll ||
+ type == nsIMsgCompType::ReplyToSender ||
+ type == nsIMsgCompType::ReplyToGroup ||
+ type == nsIMsgCompType::ReplyToSenderAndGroup ||
+ type == nsIMsgCompType::ReplyToList))
+ {
+ nsAutoCString selHTML;
+ if (NS_SUCCEEDED(GetOrigWindowSelection(type, aMsgWindow, selHTML)))
+ pMsgComposeParams->SetHtmlToQuote(selHTML);
+ }
+
+ if (originalMsgURI && *originalMsgURI)
+ {
+ if (type == nsIMsgCompType::NewsPost)
+ {
+ nsAutoCString newsURI(originalMsgURI);
+ nsAutoCString group;
+ nsAutoCString host;
+
+ int32_t slashpos = newsURI.RFindChar('/');
+ if (slashpos > 0 )
+ {
+ // uri is "[s]news://host[:port]/group"
+ host = StringHead(newsURI, slashpos);
+ group = Substring(newsURI, slashpos + 1);
+
+ }
+ else
+ group = originalMsgURI;
+
+ nsAutoCString unescapedName;
+ MsgUnescapeString(group,
+ nsINetUtil::ESCAPE_URL_FILE_BASENAME | nsINetUtil::ESCAPE_URL_FORCED,
+ unescapedName);
+ pMsgCompFields->SetNewsgroups(NS_ConvertUTF8toUTF16(unescapedName));
+ pMsgCompFields->SetNewspostUrl(host.get());
+ }
+ else
+ {
+ pMsgComposeParams->SetOriginalMsgURI(originalMsgURI);
+ pMsgComposeParams->SetOrigMsgHdr(origMsgHdr);
+ }
+ }
+
+ pMsgComposeParams->SetComposeFields(pMsgCompFields);
+
+ if(mLogComposePerformance)
+ {
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+ // ducarroz, properly fix this in the case of new message (not a reply)
+ if (type != nsIMsgCompType::NewsPost) {
+ char buff[256];
+ sprintf(buff, "Start opening the window, message size = %d", GetMessageSizeFromURI(originalMsgURI));
+ TimeStamp(buff, true);
+ }
+#endif
+ }//end if(mLogComposePerformance)
+
+ rv = OpenComposeWindowWithParams(msgComposeWindowURL, pMsgComposeParams);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgComposeService::GetParamsForMailto(nsIURI * aURI, nsIMsgComposeParams ** aParams)
+{
+ nsresult rv = NS_OK;
+ if (aURI)
+ {
+ nsCOMPtr<nsIMailtoUrl> aMailtoUrl;
+ rv = aURI->QueryInterface(NS_GET_IID(nsIMailtoUrl), getter_AddRefs(aMailtoUrl));
+ if (NS_SUCCEEDED(rv))
+ {
+ MSG_ComposeFormat requestedComposeFormat = nsIMsgCompFormat::Default;
+ nsCString toPart;
+ nsCString ccPart;
+ nsCString bccPart;
+ nsCString subjectPart;
+ nsCString bodyPart;
+ nsCString newsgroup;
+ nsCString refPart;
+ nsCString HTMLBodyPart;
+
+ aMailtoUrl->GetMessageContents(toPart, ccPart, bccPart, subjectPart,
+ bodyPart, HTMLBodyPart, refPart,
+ newsgroup, &requestedComposeFormat);
+
+ nsAutoString sanitizedBody;
+
+ bool composeHTMLFormat;
+ DetermineComposeHTML(NULL, requestedComposeFormat, &composeHTMLFormat);
+
+ // If there was an 'html-body' param, finding it will have requested
+ // HTML format in GetMessageContents, so we try to use it first. If it's
+ // empty, but we are composing in HTML because of the user's prefs, the
+ // 'body' param needs to be escaped, since it's supposed to be plain
+ // text, but it then doesn't need to sanitized.
+ nsString rawBody;
+ if (HTMLBodyPart.IsEmpty())
+ {
+ if (composeHTMLFormat)
+ {
+ char *escaped = MsgEscapeHTML(bodyPart.get());
+ if (!escaped)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ CopyUTF8toUTF16(nsDependentCString(escaped), sanitizedBody);
+ free(escaped);
+ }
+ else
+ CopyUTF8toUTF16(bodyPart, rawBody);
+ }
+ else
+ CopyUTF8toUTF16(HTMLBodyPart, rawBody);
+
+ if (!rawBody.IsEmpty() && composeHTMLFormat)
+ {
+ //For security reason, we must sanitize the message body before accepting any html...
+
+ rv = HTMLSanitize(rawBody, sanitizedBody); // from mimemoz2.h
+
+ if (NS_FAILED(rv))
+ {
+ // Something went horribly wrong with parsing for html format
+ // in the body. Set composeHTMLFormat to false so we show the
+ // plain text mail compose.
+ composeHTMLFormat = false;
+ }
+ }
+
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && pMsgComposeParams)
+ {
+ pMsgComposeParams->SetType(nsIMsgCompType::MailToUrl);
+ pMsgComposeParams->SetFormat(composeHTMLFormat ? nsIMsgCompFormat::HTML : nsIMsgCompFormat::PlainText);
+
+
+ nsCOMPtr<nsIMsgCompFields> pMsgCompFields (do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv));
+ if (pMsgCompFields)
+ {
+ //ugghh more conversion work!!!!
+ pMsgCompFields->SetTo(NS_ConvertUTF8toUTF16(toPart));
+ pMsgCompFields->SetCc(NS_ConvertUTF8toUTF16(ccPart));
+ pMsgCompFields->SetBcc(NS_ConvertUTF8toUTF16(bccPart));
+ pMsgCompFields->SetNewsgroups(NS_ConvertUTF8toUTF16(newsgroup));
+ pMsgCompFields->SetReferences(refPart.get());
+ pMsgCompFields->SetSubject(NS_ConvertUTF8toUTF16(subjectPart));
+ pMsgCompFields->SetBody(composeHTMLFormat ? sanitizedBody : rawBody);
+ pMsgComposeParams->SetComposeFields(pMsgCompFields);
+
+ NS_ADDREF(*aParams = pMsgComposeParams);
+ return NS_OK;
+ }
+ } // if we created msg compose params....
+ } // if we had a mailto url
+ } // if we had a url...
+
+ // if we got here we must have encountered an error
+ *aParams = nullptr;
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsMsgComposeService::OpenComposeWindowWithURI(const char * aMsgComposeWindowURL, nsIURI * aURI, nsIMsgIdentity *identity)
+{
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams;
+ nsresult rv = GetParamsForMailto(aURI, getter_AddRefs(pMsgComposeParams));
+ if (NS_SUCCEEDED(rv)) {
+ pMsgComposeParams->SetIdentity(identity);
+ rv = OpenComposeWindowWithParams(aMsgComposeWindowURL, pMsgComposeParams);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgComposeService::InitCompose(nsIMsgComposeParams *aParams,
+ mozIDOMWindowProxy *aWindow,
+ nsIDocShell *aDocShell,
+ nsIMsgCompose **_retval)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgCompose> msgCompose =
+ do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = msgCompose->Initialize(aParams, aWindow, aDocShell);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ NS_IF_ADDREF(*_retval = msgCompose);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::GetDefaultIdentity(nsIMsgIdentity **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAccount> defaultAccount;
+ rv = accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return defaultAccount->GetDefaultIdentity(_retval);
+}
+
+/* readonly attribute boolean logComposePerformance; */
+NS_IMETHODIMP nsMsgComposeService::GetLogComposePerformance(bool *aLogComposePerformance)
+{
+ *aLogComposePerformance = mLogComposePerformance;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeService::TimeStamp(const char * label, bool resetTime)
+{
+ if (!mLogComposePerformance)
+ return NS_OK;
+
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+
+ PRIntervalTime now;
+
+ if (resetTime)
+ {
+ MOZ_LOG(MsgComposeLogModule, mozilla::LogLevel::Info, ("\n[process]: [totalTime][deltaTime]\n--------------------\n"));
+
+ mStartTime = PR_IntervalNow();
+ mPreviousTime = mStartTime;
+ now = mStartTime;
+ }
+ else
+ now = PR_IntervalNow();
+
+ PRIntervalTime totalTime = PR_IntervalToMilliseconds(now - mStartTime);
+ PRIntervalTime deltaTime = PR_IntervalToMilliseconds(now - mPreviousTime);
+
+ MOZ_LOG(MsgComposeLogModule, mozilla::LogLevel::Info, ("[%3.2f][%3.2f] - %s\n",
+((double)totalTime/1000.0) + 0.005, ((double)deltaTime/1000.0) + 0.005, label));
+
+ mPreviousTime = now;
+#endif
+ return NS_OK;
+}
+
+class nsMsgTemplateReplyHelper final: public nsIStreamListener,
+ public nsIUrlListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIURLLISTENER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIREQUESTOBSERVER
+
+ nsMsgTemplateReplyHelper();
+
+ nsCOMPtr<nsIMsgDBHdr> mHdrToReplyTo;
+ nsCOMPtr<nsIMsgDBHdr> mTemplateHdr;
+ nsCOMPtr<nsIMsgWindow> mMsgWindow;
+ nsCOMPtr<nsIMsgIdentity> mIdentity;
+ nsCString mTemplateBody;
+ bool mInMsgBody;
+ char mLastBlockChars[3];
+
+private:
+ ~nsMsgTemplateReplyHelper();
+};
+
+NS_IMPL_ISUPPORTS(nsMsgTemplateReplyHelper,
+ nsIStreamListener,
+ nsIRequestObserver,
+ nsIUrlListener)
+
+nsMsgTemplateReplyHelper::nsMsgTemplateReplyHelper()
+{
+ mInMsgBody = false;
+ memset(mLastBlockChars, 0, sizeof(mLastBlockChars));
+}
+
+nsMsgTemplateReplyHelper::~nsMsgTemplateReplyHelper()
+{
+}
+
+
+NS_IMETHODIMP nsMsgTemplateReplyHelper::OnStartRunningUrl(nsIURI *aUrl)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgTemplateReplyHelper::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
+{
+ NS_ENSURE_SUCCESS(aExitCode, aExitCode);
+ nsresult rv;
+ nsCOMPtr<nsPIDOMWindowOuter> parentWindow;
+ if (mMsgWindow)
+ {
+ nsCOMPtr<nsIDocShell> docShell;
+ rv = mMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
+ NS_ENSURE_SUCCESS(rv, rv);
+ parentWindow = do_GetInterface(docShell);
+ NS_ENSURE_TRUE(parentWindow, NS_ERROR_FAILURE);
+ }
+
+ // create the compose params object
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv));
+ if (NS_FAILED(rv) || (!pMsgComposeParams) ) return rv ;
+ nsCOMPtr<nsIMsgCompFields> compFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv) ;
+
+ nsCString replyTo;
+ mHdrToReplyTo->GetStringProperty("replyTo", getter_Copies(replyTo));
+ if (replyTo.IsEmpty())
+ mHdrToReplyTo->GetAuthor(getter_Copies(replyTo));
+ compFields->SetTo(NS_ConvertUTF8toUTF16(replyTo));
+
+ nsString body;
+ nsString templateSubject, replySubject;
+
+ mHdrToReplyTo->GetMime2DecodedSubject(replySubject);
+ mTemplateHdr->GetMime2DecodedSubject(templateSubject);
+ nsString subject(NS_LITERAL_STRING("Auto: ")); // RFC 3834 3.1.5.
+ subject.Append(templateSubject);
+ if (!replySubject.IsEmpty())
+ {
+ subject.Append(NS_LITERAL_STRING(" (was: "));
+ subject.Append(replySubject);
+ subject.Append(NS_LITERAL_STRING(")"));
+ }
+
+ compFields->SetSubject(subject);
+ compFields->SetRawHeader("Auto-Submitted", NS_LITERAL_CSTRING("auto-replied"), nullptr);
+
+ nsCString charset;
+ rv = mTemplateHdr->GetCharset(getter_Copies(charset));
+ NS_ENSURE_SUCCESS(rv, rv);
+ compFields->SetCharacterSet(charset.get());
+ rv = nsMsgI18NConvertToUnicode(charset.get(), mTemplateBody, body);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "couldn't convert templ body to unicode");
+ compFields->SetBody(body);
+
+ nsCString msgUri;
+ nsCOMPtr <nsIMsgFolder> folder;
+ mHdrToReplyTo->GetFolder(getter_AddRefs(folder));
+ folder->GetUriForMsg(mHdrToReplyTo, msgUri);
+ // populate the compose params
+ pMsgComposeParams->SetType(nsIMsgCompType::ReplyWithTemplate);
+ pMsgComposeParams->SetFormat(nsIMsgCompFormat::Default);
+ pMsgComposeParams->SetIdentity(mIdentity);
+ pMsgComposeParams->SetComposeFields(compFields);
+ pMsgComposeParams->SetOriginalMsgURI(msgUri.get());
+
+ // create the nsIMsgCompose object to send the object
+ nsCOMPtr<nsIMsgCompose> pMsgCompose (do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /** initialize nsIMsgCompose, Send the message, wait for send completion response **/
+
+ rv = pMsgCompose->Initialize(pMsgComposeParams, parentWindow, nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ return pMsgCompose->SendMsg(nsIMsgSend::nsMsgDeliverNow, mIdentity, nullptr, nullptr, nullptr) ;
+}
+
+NS_IMETHODIMP
+nsMsgTemplateReplyHelper::OnStartRequest(nsIRequest* request, nsISupports* aSupport)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgTemplateReplyHelper::OnStopRequest(nsIRequest* request, nsISupports* aSupport,
+ nsresult status)
+{
+ if (NS_SUCCEEDED(status))
+ {
+ // now we've got the message body in mTemplateBody -
+ // need to set body in compose params and send the reply.
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgTemplateReplyHelper::OnDataAvailable(nsIRequest* request,
+ nsISupports* aSupport,
+ nsIInputStream* inStream,
+ uint64_t srcOffset,
+ uint32_t count)
+{
+ nsresult rv = NS_OK;
+
+ char readBuf[1024];
+
+ uint64_t available;
+ uint32_t readCount;
+ uint32_t maxReadCount = sizeof(readBuf) - 1;
+
+ rv = inStream->Available(&available);
+ while (NS_SUCCEEDED(rv) && available > 0)
+ {
+ uint32_t bodyOffset = 0, readOffset = 0;
+ if (!mInMsgBody && mLastBlockChars[0])
+ {
+ memcpy(readBuf, mLastBlockChars, 3);
+ readOffset = 3;
+ maxReadCount -= 3;
+ }
+ if (maxReadCount > available)
+ maxReadCount = (uint32_t)available;
+ memset(readBuf, 0, sizeof(readBuf));
+ rv = inStream->Read(readBuf + readOffset, maxReadCount, &readCount);
+ available -= readCount;
+ readCount += readOffset;
+ // we're mainly interested in the msg body, so we need to
+ // find the header/body delimiter of a blank line. A blank line
+ // looks like <CR><CR>, <LF><LF>, or <CRLF><CRLF>
+ if (!mInMsgBody)
+ {
+ for (uint32_t charIndex = 0; charIndex < readCount && !bodyOffset; charIndex++)
+ {
+ if (readBuf[charIndex] == '\r' || readBuf[charIndex] == '\n')
+ {
+ if (charIndex + 1 < readCount)
+ {
+ if (readBuf[charIndex] == readBuf[charIndex + 1])
+ {
+ // got header+body separator
+ bodyOffset = charIndex + 2;
+ break;
+ }
+ else if ((charIndex + 3 < readCount) && !strncmp(readBuf + charIndex, "\r\n\r\n", 4))
+ {
+ bodyOffset = charIndex + 4;
+ break;
+ }
+ }
+ }
+ }
+ mInMsgBody = bodyOffset != 0;
+ if (!mInMsgBody && readCount > 3) // still in msg hdrs
+ strncpy(mLastBlockChars, readBuf + readCount - 3, 3);
+ }
+ mTemplateBody.Append(readBuf + bodyOffset);
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsMsgComposeService::ReplyWithTemplate(nsIMsgDBHdr *aMsgHdr, const char *templateUri,
+ nsIMsgWindow *aMsgWindow, nsIMsgIncomingServer *aServer)
+{
+ // To reply with template, we need the message body of the template.
+ // I think we're going to need to stream the template message to ourselves,
+ // and construct the body, and call setBody on the compFields.
+ nsresult rv;
+ nsCOMPtr <nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAccount> account;
+ rv = accountManager->FindAccountForServer(aServer, getter_AddRefs(account));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIArray> identities;
+ rv = account->GetIdentities(getter_AddRefs(identities));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString recipients;
+ aMsgHdr->GetRecipients(getter_Copies(recipients));
+
+ nsAutoCString ccList;
+ aMsgHdr->GetCcList(getter_Copies(ccList));
+
+ // Go through the identities to see to whom this was addressed.
+ // In case we get no match, this is likely a list/bulk/bcc/spam mail and we
+ // shouldn't reply. RFC 3834 2.
+ nsCOMPtr<nsIMsgIdentity> identity; // identity to reply from
+ uint32_t count = 0;
+ identities->GetLength(&count);
+ for (uint32_t i = 0; i < count; i++)
+ {
+ nsCOMPtr<nsIMsgIdentity> anIdentity(do_QueryElementAt(identities, i, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString identityEmail;
+ anIdentity->GetEmail(identityEmail);
+
+ if (recipients.Find(identityEmail, CaseInsensitiveCompare) != kNotFound ||
+ ccList.Find(identityEmail, CaseInsensitiveCompare) != kNotFound)
+ {
+ identity = anIdentity;
+ break;
+ }
+ }
+ if (!identity) // Found no match -> don't reply.
+ return NS_ERROR_ABORT;
+
+ RefPtr<nsMsgTemplateReplyHelper> helper = new nsMsgTemplateReplyHelper;
+
+ helper->mHdrToReplyTo = aMsgHdr;
+ helper->mMsgWindow = aMsgWindow;
+ helper->mIdentity = identity;
+
+ nsAutoCString replyTo;
+ aMsgHdr->GetStringProperty("replyTo", getter_Copies(replyTo));
+ if (replyTo.IsEmpty())
+ aMsgHdr->GetAuthor(getter_Copies(replyTo));
+ if (replyTo.IsEmpty())
+ return NS_ERROR_FAILURE; // nowhere to send the reply
+
+ nsCOMPtr <nsIMsgFolder> templateFolder;
+ nsCOMPtr <nsIMsgDatabase> templateDB;
+ nsCString templateMsgHdrUri;
+ const char * query = PL_strstr(templateUri, "?messageId=");
+ if (!query)
+ return NS_ERROR_FAILURE;
+
+ nsAutoCString folderUri(Substring(templateUri, query));
+ rv = GetExistingFolder(folderUri, getter_AddRefs(templateFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = templateFolder->GetMsgDatabase(getter_AddRefs(templateDB));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const char *subject = PL_strstr(templateUri, "&subject=");
+ if (subject)
+ {
+ const char *subjectEnd = subject + strlen(subject);
+ nsAutoCString messageId(Substring(query + 11, subject));
+ nsAutoCString subjectString(Substring(subject + 9, subjectEnd));
+ templateDB->GetMsgHdrForMessageID(messageId.get(), getter_AddRefs(helper->mTemplateHdr));
+ if (helper->mTemplateHdr)
+ templateFolder->GetUriForMsg(helper->mTemplateHdr, templateMsgHdrUri);
+ // to use the subject, we'd need to expose a method to find a message by subject,
+ // or painfully iterate through messages...We'll try to make the message-id
+ // not change when saving a template first.
+ }
+ if (templateMsgHdrUri.IsEmpty())
+ {
+ // ### probably want to return a specific error and
+ // have the calling code disable the filter.
+ NS_ASSERTION(false, "failed to get msg hdr");
+ return NS_ERROR_FAILURE;
+ }
+ // we need to convert the template uri, which is of the form
+ // <folder uri>?messageId=<messageId>&subject=<subject>
+ nsCOMPtr <nsIMsgMessageService> msgService;
+ rv = GetMessageServiceFromURI(templateMsgHdrUri, getter_AddRefs(msgService));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> listenerSupports;
+ helper->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(listenerSupports));
+
+ nsCOMPtr<nsIURI> dummyNull;
+ rv = msgService->StreamMessage(templateMsgHdrUri.get(), listenerSupports,
+ aMsgWindow, helper,
+ false, // convert data
+ EmptyCString(), false, getter_AddRefs(dummyNull));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> folder;
+ aMsgHdr->GetFolder(getter_AddRefs(folder));
+ if (!folder)
+ return NS_ERROR_NULL_POINTER;
+
+ // We're sending a new message. Conceptually it's a reply though, so mark the
+ // original message as replied.
+ return folder->AddMessageDispositionState(aMsgHdr, nsIMsgFolder::nsMsgDispositionState_Replied);
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::ForwardMessage(const nsAString &forwardTo,
+ nsIMsgDBHdr *aMsgHdr,
+ nsIMsgWindow *aMsgWindow,
+ nsIMsgIncomingServer *aServer,
+ uint32_t aForwardType)
+{
+ NS_ENSURE_ARG_POINTER(aMsgHdr);
+
+ nsresult rv;
+ if (aForwardType == nsIMsgComposeService::kForwardAsDefault)
+ {
+ int32_t forwardPref = 0;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ prefBranch->GetIntPref("mail.forward_message_mode", &forwardPref);
+ // 0=default as attachment 2=forward as inline with attachments,
+ // (obsolete 4.x value)1=forward as quoted (mapped to 2 in mozilla)
+ aForwardType = forwardPref == 0 ? nsIMsgComposeService::kForwardAsAttachment :
+ nsIMsgComposeService::kForwardInline;
+ }
+ nsCString msgUri;
+
+ nsCOMPtr<nsIMsgFolder> folder;
+ aMsgHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_TRUE(folder, NS_ERROR_NULL_POINTER);
+
+ folder->GetUriForMsg(aMsgHdr, msgUri);
+
+ nsAutoCString uriToOpen(msgUri);
+ uriToOpen += (uriToOpen.FindChar('?') == kNotFound) ? '?' : '&';
+ uriToOpen.Append("fetchCompleteMessage=true");
+
+ // get the MsgIdentity for the above key using AccountManager
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService (NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAccount> account;
+ nsCOMPtr<nsIMsgIdentity> identity;
+
+ rv = accountManager->FindAccountForServer(aServer, getter_AddRefs(account));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = account->GetDefaultIdentity(getter_AddRefs(identity));
+ // Use default identity if no identity has been found on this account
+ if (NS_FAILED(rv) || !identity)
+ {
+ rv = GetDefaultIdentity(getter_AddRefs(identity));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (aForwardType == nsIMsgComposeService::kForwardInline)
+ return RunMessageThroughMimeDraft(uriToOpen,
+ nsMimeOutput::nsMimeMessageDraftOrTemplate,
+ identity,
+ uriToOpen.get(), aMsgHdr,
+ true, forwardTo,
+ false, aMsgWindow);
+
+ nsCOMPtr<mozIDOMWindowProxy> parentWindow;
+ if (aMsgWindow)
+ {
+ nsCOMPtr<nsIDocShell> docShell;
+ rv = aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
+ NS_ENSURE_SUCCESS(rv, rv);
+ parentWindow = do_GetInterface(docShell);
+ NS_ENSURE_TRUE(parentWindow, NS_ERROR_FAILURE);
+ }
+ // create the compose params object
+ nsCOMPtr<nsIMsgComposeParams> pMsgComposeParams (do_CreateInstance(NS_MSGCOMPOSEPARAMS_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgCompFields> compFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv);
+
+ compFields->SetTo(forwardTo);
+ // populate the compose params
+ pMsgComposeParams->SetType(nsIMsgCompType::ForwardAsAttachment);
+ pMsgComposeParams->SetFormat(nsIMsgCompFormat::Default);
+ pMsgComposeParams->SetIdentity(identity);
+ pMsgComposeParams->SetComposeFields(compFields);
+ pMsgComposeParams->SetOriginalMsgURI(uriToOpen.get());
+ // create the nsIMsgCompose object to send the object
+ nsCOMPtr<nsIMsgCompose> pMsgCompose (do_CreateInstance(NS_MSGCOMPOSE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ /** initialize nsIMsgCompose, Send the message, wait for send completion response **/
+ rv = pMsgCompose->Initialize(pMsgComposeParams, parentWindow, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = pMsgCompose->SendMsg(nsIMsgSend::nsMsgDeliverNow, identity, nullptr, nullptr, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // nsMsgCompose::ProcessReplyFlags usually takes care of marking messages
+ // as forwarded. ProcessReplyFlags is normally called from
+ // nsMsgComposeSendListener::OnStopSending but for this case the msgCompose
+ // object is not set so ProcessReplyFlags won't get called.
+ // Therefore, let's just mark it here instead.
+ return folder->AddMessageDispositionState(aMsgHdr, nsIMsgFolder::nsMsgDispositionState_Forwarded);
+}
+
+nsresult nsMsgComposeService::AddGlobalHtmlDomains()
+{
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ rv = prefs->GetBranch(MAILNEWS_ROOT_PREF, getter_AddRefs(prefBranch));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIPrefBranch> defaultsPrefBranch;
+ rv = prefs->GetDefaultBranch(MAILNEWS_ROOT_PREF, getter_AddRefs(defaultsPrefBranch));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ /**
+ * Check to see if we need to add any global domains.
+ * If so, make sure the following prefs are added to mailnews.js
+ *
+ * 1. pref("mailnews.global_html_domains.version", version number);
+ * This pref registers the current version in the user prefs file. A default value is stored
+ * in mailnews file. Depending the changes we plan to make we can move the default version number.
+ * Comparing version number from user's prefs file and the default one from mailnews.js, we
+ * can effect ppropriate changes.
+ *
+ * 2. pref("mailnews.global_html_domains", <comma separated domain list>);
+ * This pref contains the list of html domains that ISP can add to make that user's contain all
+ * of these under the HTML domains in the Mail&NewsGrpus|Send Format under global preferences.
+ */
+ int32_t htmlDomainListCurrentVersion, htmlDomainListDefaultVersion;
+ rv = prefBranch->GetIntPref(HTMLDOMAINUPDATE_VERSION_PREF_NAME, &htmlDomainListCurrentVersion);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = defaultsPrefBranch->GetIntPref(HTMLDOMAINUPDATE_VERSION_PREF_NAME, &htmlDomainListDefaultVersion);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Update the list as needed
+ if (htmlDomainListCurrentVersion <= htmlDomainListDefaultVersion) {
+ // Get list of global domains need to be added
+ nsCString globalHtmlDomainList;
+ rv = prefBranch->GetCharPref(HTMLDOMAINUPDATE_DOMAINLIST_PREF_NAME, getter_Copies(globalHtmlDomainList));
+
+ if (NS_SUCCEEDED(rv) && !globalHtmlDomainList.IsEmpty()) {
+ nsTArray<nsCString> domainArray;
+
+ // Get user's current HTML domain set for send format
+ nsCString currentHtmlDomainList;
+ rv = prefBranch->GetCharPref(USER_CURRENT_HTMLDOMAINLIST_PREF_NAME, getter_Copies(currentHtmlDomainList));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsAutoCString newHtmlDomainList(currentHtmlDomainList);
+ // Get the current html domain list into new list var
+ ParseString(currentHtmlDomainList, DOMAIN_DELIMITER, domainArray);
+
+ // Get user's current Plaintext domain set for send format
+ nsCString currentPlaintextDomainList;
+ rv = prefBranch->GetCharPref(USER_CURRENT_PLAINTEXTDOMAINLIST_PREF_NAME, getter_Copies(currentPlaintextDomainList));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Get the current plaintext domain list into new list var
+ ParseString(currentPlaintextDomainList, DOMAIN_DELIMITER, domainArray);
+
+ size_t i = domainArray.Length();
+ if (i > 0) {
+ // Append each domain in the preconfigured html domain list
+ globalHtmlDomainList.StripWhitespace();
+ ParseString(globalHtmlDomainList, DOMAIN_DELIMITER, domainArray);
+
+ // Now add each domain that does not already appear in
+ // the user's current html or plaintext domain lists
+ for (; i < domainArray.Length(); i++) {
+ if (domainArray.IndexOf(domainArray[i]) == i) {
+ if (!newHtmlDomainList.IsEmpty())
+ newHtmlDomainList += DOMAIN_DELIMITER;
+ newHtmlDomainList += domainArray[i];
+ }
+ }
+ }
+ else
+ {
+ // User has no domains listed either in html or plain text category.
+ // Assign the global list to be the user's current html domain list
+ newHtmlDomainList = globalHtmlDomainList;
+ }
+
+ // Set user's html domain pref with the updated list
+ rv = prefBranch->SetCharPref(USER_CURRENT_HTMLDOMAINLIST_PREF_NAME, newHtmlDomainList.get());
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Increase the version to avoid running the update code unless needed (based on default version)
+ rv = prefBranch->SetIntPref(HTMLDOMAINUPDATE_VERSION_PREF_NAME, htmlDomainListCurrentVersion + 1);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::RegisterComposeDocShell(nsIDocShell *aDocShell,
+ nsIMsgCompose *aComposeObject)
+{
+ NS_ENSURE_ARG_POINTER(aDocShell);
+ NS_ENSURE_ARG_POINTER(aComposeObject);
+
+ nsresult rv;
+
+ // add the msg compose / dom window mapping to our hash table
+ nsCOMPtr<nsIWeakReference> weakDocShell = do_GetWeakReference(aDocShell, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ nsCOMPtr<nsIWeakReference> weakMsgComposePtr = do_GetWeakReference(aComposeObject);
+ NS_ENSURE_SUCCESS(rv,rv);
+ mOpenComposeWindows.Put(weakDocShell, weakMsgComposePtr);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::UnregisterComposeDocShell(nsIDocShell *aDocShell)
+{
+ NS_ENSURE_ARG_POINTER(aDocShell);
+
+ nsresult rv;
+ nsCOMPtr<nsIWeakReference> weakDocShell = do_GetWeakReference(aDocShell, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ mOpenComposeWindows.Remove(weakDocShell);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::GetMsgComposeForDocShell(nsIDocShell *aDocShell,
+ nsIMsgCompose **aComposeObject)
+{
+ NS_ENSURE_ARG_POINTER(aDocShell);
+ NS_ENSURE_ARG_POINTER(aComposeObject);
+
+ if (!mOpenComposeWindows.Count())
+ return NS_ERROR_FAILURE;
+
+ // get the weak reference for our dom window
+ nsresult rv;
+ nsCOMPtr<nsIWeakReference> weakDocShell = do_GetWeakReference(aDocShell, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIWeakReference> weakMsgComposePtr;
+
+ if (!mOpenComposeWindows.Get(weakDocShell,
+ getter_AddRefs(weakMsgComposePtr)))
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIMsgCompose> msgCompose = do_QueryReferent(weakMsgComposePtr, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*aComposeObject = msgCompose);
+ return rv;
+}
+
+/**
+ * LoadDraftOrTemplate
+ * Helper routine used to run msgURI through libmime in order to fetch the contents for a
+ * draft or template.
+ */
+nsresult
+nsMsgComposeService::LoadDraftOrTemplate(const nsACString& aMsgURI, nsMimeOutputType aOutType,
+ nsIMsgIdentity * aIdentity, const char * aOriginalMsgURI,
+ nsIMsgDBHdr * aOrigMsgHdr,
+ bool aForwardInline,
+ bool overrideComposeFormat,
+ nsIMsgWindow *aMsgWindow)
+{
+ return RunMessageThroughMimeDraft(aMsgURI, aOutType, aIdentity,
+ aOriginalMsgURI, aOrigMsgHdr,
+ aForwardInline, EmptyString(),
+ overrideComposeFormat, aMsgWindow);
+}
+
+/**
+ * Run the aMsgURI message through libmime. We set various attributes of the
+ * nsIMimeStreamConverter so mimedrft.cpp will know what to do with the message
+ * when its done streaming. Usually that will be opening a compose window
+ * with the contents of the message, but if forwardTo is non-empty, mimedrft.cpp
+ * will forward the contents directly.
+ *
+ * @param aMsgURI URI to stream, which is the msgUri + any extra terms, e.g.,
+ * "redirect=true".
+ * @param aOutType nsMimeOutput::nsMimeMessageDraftOrTemplate or
+ * nsMimeOutput::nsMimeMessageEditorTemplate
+ * @param aIdentity identity to use for the new message
+ * @param aOriginalMsgURI msgURI w/o any extra terms
+ * @param aOrigMsgHdr nsIMsgDBHdr corresponding to aOriginalMsgURI
+ * @param aForwardInline true if doing a forward inline
+ * @param aForwardTo e-mail address to forward msg to. This is used for
+ * forward inline message filter actions.
+ * @param aOverrideComposeFormat True if the user had shift key down when
+ doing a command that opens the compose window,
+ * which means we switch the compose window used
+ * from the default.
+ * @param aMsgWindow msgWindow to pass into DisplayMessage.
+ */
+nsresult
+nsMsgComposeService::RunMessageThroughMimeDraft(
+ const nsACString& aMsgURI, nsMimeOutputType aOutType,
+ nsIMsgIdentity * aIdentity, const char * aOriginalMsgURI,
+ nsIMsgDBHdr * aOrigMsgHdr,
+ bool aForwardInline,
+ const nsAString &aForwardTo,
+ bool aOverrideComposeFormat,
+ nsIMsgWindow *aMsgWindow)
+{
+ nsCOMPtr <nsIMsgMessageService> messageService;
+ nsresult rv = GetMessageServiceFromURI(aMsgURI, getter_AddRefs(messageService));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create a mime parser (nsIMimeStreamConverter)to do the conversion.
+ nsCOMPtr<nsIMimeStreamConverter> mimeConverter =
+ do_CreateInstance(NS_MAILNEWS_MIME_STREAM_CONVERTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mimeConverter->SetMimeOutputType(aOutType); // Set the type of output for libmime
+ mimeConverter->SetForwardInline(aForwardInline);
+ if (!aForwardTo.IsEmpty())
+ {
+ mimeConverter->SetForwardInlineFilter(true);
+ mimeConverter->SetForwardToAddress(aForwardTo);
+ }
+ mimeConverter->SetOverrideComposeFormat(aOverrideComposeFormat);
+ mimeConverter->SetIdentity(aIdentity);
+ mimeConverter->SetOriginalMsgURI(aOriginalMsgURI);
+ mimeConverter->SetOrigMsgHdr(aOrigMsgHdr);
+
+ nsCOMPtr<nsIURI> url;
+ bool fileUrl = StringBeginsWith(aMsgURI, NS_LITERAL_CSTRING("file:"));
+ nsCString mailboxUri(aMsgURI);
+ if (fileUrl)
+ {
+ // We loaded a .eml file from a file: url. Construct equivalent mailbox url.
+ mailboxUri.Replace(0, 5, NS_LITERAL_CSTRING("mailbox:"));
+ mailboxUri.Append(NS_LITERAL_CSTRING("&number=0"));
+ // Need this to prevent nsMsgCompose::TagEmbeddedObjects from setting
+ // inline images as moz-do-not-send.
+ mimeConverter->SetOriginalMsgURI(mailboxUri.get());
+ }
+ if (fileUrl || PromiseFlatCString(aMsgURI).Find("&type=application/x-message-display") >= 0)
+ rv = NS_NewURI(getter_AddRefs(url), mailboxUri);
+ else
+ rv = messageService->GetUrlForUri(PromiseFlatCString(aMsgURI).get(), getter_AddRefs(url), aMsgWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // ignore errors here - it's not fatal, and in the case of mailbox messages,
+ // we're always passing in an invalid spec...
+ (void )url->SetSpec(mailboxUri);
+
+ // if we are forwarding a message and that message used a charset over ride
+ // then use that over ride charset instead of the charset specified in the message
+ nsCString mailCharset;
+ if (aMsgWindow)
+ {
+ bool charsetOverride;
+ if (NS_SUCCEEDED(aMsgWindow->GetCharsetOverride(&charsetOverride)) && charsetOverride)
+ {
+ if (NS_SUCCEEDED(aMsgWindow->GetMailCharacterSet(mailCharset)))
+ {
+ nsCOMPtr<nsIMsgI18NUrl> i18nUrl(do_QueryInterface(url));
+ if (i18nUrl)
+ (void) i18nUrl->SetCharsetOverRide(mailCharset.get());
+ }
+ }
+ }
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateInstance of nullprincipal failed");
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
+ url,
+ nullptr,
+ nullPrincipal,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "NS_NewChannel failed.");
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIStreamConverter> converter = do_QueryInterface(mimeConverter);
+ rv = converter->AsyncConvertData(nullptr, nullptr, nullptr, channel);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now, just plug the two together and get the hell out of the way!
+ nsCOMPtr<nsIStreamListener> streamListener = do_QueryInterface(mimeConverter);
+ nsCOMPtr<nsIURI> dummyNull;
+ return messageService->DisplayMessage(PromiseFlatCString(aMsgURI).get(), streamListener,
+ aMsgWindow, nullptr, mailCharset.get(),
+ getter_AddRefs(dummyNull));
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::Handle(nsICommandLine* aCmdLine)
+{
+ NS_ENSURE_ARG_POINTER(aCmdLine);
+
+ nsresult rv;
+ int32_t found, end, count;
+ nsAutoString uristr;
+ bool composeShouldHandle = true;
+
+ rv = aCmdLine->FindFlag(NS_LITERAL_STRING("compose"), false, &found);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifndef MOZ_SUITE
+ // MAC OS X passes in -url mailto:mscott@mozilla.org into the command line
+ // instead of -compose.
+ if (found == -1)
+ {
+ rv = aCmdLine->FindFlag(NS_LITERAL_STRING("url"), false, &found);
+ // we don't want to consume the argument for -url unless we're sure it is a mailto url and we'll
+ // figure that out shortly.
+ composeShouldHandle = false;
+ }
+#endif
+
+ if (found == -1)
+ return NS_OK;
+
+ end = found;
+
+ rv = aCmdLine->GetLength(&count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (count > found + 1) {
+ aCmdLine->GetArgument(found + 1, uristr);
+ if (StringBeginsWith(uristr, NS_LITERAL_STRING("mailto:")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("preselectid=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("to=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("cc=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("bcc=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("newsgroups=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("subject=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("format=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("body=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("attachment=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("message=")) ||
+ StringBeginsWith(uristr, NS_LITERAL_STRING("from="))) {
+ composeShouldHandle = true; // the -url argument looks like mailto
+ end++;
+ // mailto: URIs are frequently passed with spaces in them. They should be
+ // escaped with %20, but we hack around broken clients. See bug 231032.
+ while (end + 1 < count) {
+ nsAutoString curarg;
+ aCmdLine->GetArgument(end + 1, curarg);
+ if (curarg.First() == '-')
+ break;
+
+ uristr.Append(' ');
+ uristr.Append(curarg);
+ ++end;
+ }
+ }
+ else {
+ uristr.Truncate();
+ }
+ }
+ if (composeShouldHandle)
+ {
+ aCmdLine->RemoveArguments(found, end);
+
+ nsCOMPtr<nsIWindowWatcher> wwatch (do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ NS_ENSURE_TRUE(wwatch, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsISupportsString> arg(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+ if (arg)
+ arg->SetData(uristr);
+
+ nsCOMPtr<mozIDOMWindowProxy> opened;
+ wwatch->OpenWindow(nullptr, DEFAULT_CHROME, "_blank",
+ "chrome,dialog=no,all", arg, getter_AddRefs(opened));
+
+ aCmdLine->SetPreventDefault(true);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeService::GetHelpInfo(nsACString& aResult)
+{
+ aResult.AssignLiteral(
+ " -compose [ <options> ] Compose a mail or news message. Options are specified\n"
+ " as string \"option='value,...',option=value,...\" and\n"
+ " include: from, to, cc, bcc, newsgroups, subject, body,\n"
+ " message (file), attachment (file), format (html | text).\n"
+ " Example: \"to=john@example.com,subject='Dinner tonight?'\"\n");
+ return NS_OK;
+}
diff --git a/mailnews/compose/src/nsMsgComposeService.h b/mailnews/compose/src/nsMsgComposeService.h
new file mode 100644
index 000000000..6ba029050
--- /dev/null
+++ b/mailnews/compose/src/nsMsgComposeService.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define MSGCOMP_TRACE_PERFORMANCE 1
+
+#include "nsIMsgComposeService.h"
+#include "nsCOMPtr.h"
+#include "mozIDOMWindow.h"
+#include "nsIXULWindow.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+#include "nsIMimeStreamConverter.h"
+#include "nsInterfaceHashtable.h"
+
+#include "nsICommandLineHandler.h"
+#define ICOMMANDLINEHANDLER nsICommandLineHandler
+
+class nsMsgComposeService :
+ public nsIMsgComposeService,
+ public ICOMMANDLINEHANDLER,
+ public nsSupportsWeakReference
+{
+public:
+ nsMsgComposeService();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGCOMPOSESERVICE
+ NS_DECL_NSICOMMANDLINEHANDLER
+
+ nsresult Init();
+ void Reset();
+ void DeleteCachedWindows();
+ nsresult AddGlobalHtmlDomains();
+
+private:
+ virtual ~nsMsgComposeService();
+ bool mLogComposePerformance;
+
+ nsresult LoadDraftOrTemplate(const nsACString& aMsgURI, nsMimeOutputType aOutType,
+ nsIMsgIdentity * aIdentity, const char * aOriginalMsgURI,
+ nsIMsgDBHdr * aOrigMsgHdr, bool aForwardInline,
+ bool overrideComposeFormat,
+ nsIMsgWindow *aMsgWindow);
+
+ nsresult RunMessageThroughMimeDraft(const nsACString& aMsgURI,
+ nsMimeOutputType aOutType,
+ nsIMsgIdentity * aIdentity,
+ const char * aOriginalMsgURI,
+ nsIMsgDBHdr * aOrigMsgHdr,
+ bool aForwardInline,
+ const nsAString &forwardTo,
+ bool overrideComposeFormat,
+ nsIMsgWindow *aMsgWindow);
+
+ // hash table mapping dom windows to nsIMsgCompose objects
+ nsInterfaceHashtable<nsISupportsHashKey, nsIWeakReference> mOpenComposeWindows;
+
+ // When doing a reply and the settings are enabled, get the HTML of the selected text
+ // in the original message window so that it can be quoted instead of the entire message.
+ nsresult GetOrigWindowSelection(MSG_ComposeType type, nsIMsgWindow *aMsgWindow, nsACString& aSelHTML);
+
+#ifdef MSGCOMP_TRACE_PERFORMANCE
+ PRIntervalTime mStartTime;
+ PRIntervalTime mPreviousTime;
+#endif
+};
diff --git a/mailnews/compose/src/nsMsgCopy.cpp b/mailnews/compose/src/nsMsgCopy.cpp
new file mode 100644
index 000000000..5c441b0c1
--- /dev/null
+++ b/mailnews/compose/src/nsMsgCopy.cpp
@@ -0,0 +1,553 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsMsgCopy.h"
+
+#include "nsCOMPtr.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgFolderFlags.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsIMsgProtocolInfo.h"
+#include "nsISupports.h"
+#include "nsIRDFService.h"
+#include "nsIRDFResource.h"
+#include "nsRDFCID.h"
+#include "nsIURL.h"
+#include "nsNetCID.h"
+#include "nsMsgCompUtils.h"
+#include "prcmon.h"
+#include "nsIMsgImapMailFolder.h"
+#include "nsThreadUtils.h"
+#include "nsIMsgWindow.h"
+#include "nsIMsgProgress.h"
+#include "nsComposeStrings.h"
+#include "prmem.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+#include "nsArrayUtils.h"
+
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+
+////////////////////////////////////////////////////////////////////////////////////
+// This is the listener class for the copy operation. We have to create this class
+// to listen for message copy completion and eventually notify the caller
+////////////////////////////////////////////////////////////////////////////////////
+NS_IMPL_ISUPPORTS(CopyListener, nsIMsgCopyServiceListener)
+
+CopyListener::CopyListener(void)
+{
+ mCopyInProgress = false;
+}
+
+CopyListener::~CopyListener(void)
+{
+}
+
+nsresult
+CopyListener::OnStartCopy()
+{
+#ifdef NS_DEBUG
+ printf("CopyListener::OnStartCopy()\n");
+#endif
+
+ if (mComposeAndSend)
+ mComposeAndSend->NotifyListenerOnStartCopy();
+ return NS_OK;
+}
+
+nsresult
+CopyListener::OnProgress(uint32_t aProgress, uint32_t aProgressMax)
+{
+#ifdef NS_DEBUG
+ printf("CopyListener::OnProgress() %d of %d\n", aProgress, aProgressMax);
+#endif
+
+ if (mComposeAndSend)
+ mComposeAndSend->NotifyListenerOnProgressCopy(aProgress, aProgressMax);
+
+ return NS_OK;
+}
+
+nsresult
+CopyListener::SetMessageKey(nsMsgKey aMessageKey)
+{
+ if (mComposeAndSend)
+ mComposeAndSend->SetMessageKey(aMessageKey);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CopyListener::GetMessageId(nsACString& aMessageId)
+{
+ if (mComposeAndSend)
+ mComposeAndSend->GetMessageId(aMessageId);
+ return NS_OK;
+}
+
+nsresult
+CopyListener::OnStopCopy(nsresult aStatus)
+{
+ if (NS_SUCCEEDED(aStatus))
+ {
+#ifdef NS_DEBUG
+ printf("CopyListener: SUCCESSFUL ON THE COPY OPERATION!\n");
+#endif
+ }
+ else
+ {
+#ifdef NS_DEBUG
+ printf("CopyListener: COPY OPERATION FAILED!\n");
+#endif
+ }
+
+ if (mCopyInProgress)
+ {
+ PR_CEnterMonitor(this);
+ PR_CNotifyAll(this);
+ mCopyInProgress = false;
+ PR_CExitMonitor(this);
+ }
+ if (mComposeAndSend)
+ mComposeAndSend->NotifyListenerOnStopCopy(aStatus);
+
+ return NS_OK;
+}
+
+nsresult
+CopyListener::SetMsgComposeAndSendObject(nsIMsgSend *obj)
+{
+ if (obj)
+ mComposeAndSend = obj;
+
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// END END END END END END END END END END END END END END END
+// This is the listener class for the copy operation. We have to create this class
+// to listen for message copy completion and eventually notify the caller
+////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS(nsMsgCopy, nsIUrlListener)
+
+nsMsgCopy::nsMsgCopy()
+{
+ mFile = nullptr;
+ mMode = nsIMsgSend::nsMsgDeliverNow;
+ mSavePref = nullptr;
+}
+
+nsMsgCopy::~nsMsgCopy()
+{
+ PR_Free(mSavePref);
+}
+
+nsresult
+nsMsgCopy::StartCopyOperation(nsIMsgIdentity *aUserIdentity,
+ nsIFile *aFile,
+ nsMsgDeliverMode aMode,
+ nsIMsgSend *aMsgSendObj,
+ const char *aSavePref,
+ nsIMsgDBHdr *aMsgToReplace)
+{
+ nsCOMPtr<nsIMsgFolder> dstFolder;
+ bool isDraft = false;
+ bool waitForUrl = false;
+ nsresult rv;
+
+ if (!aMsgSendObj)
+ return NS_ERROR_INVALID_ARG;
+
+ // Store away the server location...
+ if (aSavePref)
+ mSavePref = PL_strdup(aSavePref);
+
+ //
+ // Vars for implementation...
+ //
+
+ // QueueForLater (Outbox)
+ if (aMode == nsIMsgSend::nsMsgQueueForLater ||
+ aMode == nsIMsgSend::nsMsgDeliverBackground)
+ {
+ rv = GetUnsentMessagesFolder(aUserIdentity, getter_AddRefs(dstFolder), &waitForUrl);
+ isDraft = false;
+ if (!dstFolder || NS_FAILED(rv)) {
+ return NS_MSG_UNABLE_TO_SEND_LATER;
+ }
+ }
+ else if (aMode == nsIMsgSend::nsMsgSaveAsDraft) // SaveAsDraft (Drafts)
+ {
+ rv = GetDraftsFolder(aUserIdentity, getter_AddRefs(dstFolder), &waitForUrl);
+ isDraft = true;
+ if (!dstFolder || NS_FAILED(rv))
+ return NS_MSG_UNABLE_TO_SAVE_DRAFT;
+ }
+ else if (aMode == nsIMsgSend::nsMsgSaveAsTemplate) // SaveAsTemplate (Templates)
+ {
+ rv = GetTemplatesFolder(aUserIdentity, getter_AddRefs(dstFolder), &waitForUrl);
+ isDraft = false;
+ if (!dstFolder || NS_FAILED(rv))
+ return NS_MSG_UNABLE_TO_SAVE_TEMPLATE;
+ }
+ else // SaveInSentFolder (Sent) - nsMsgDeliverNow or nsMsgSendUnsent
+ {
+ rv = GetSentFolder(aUserIdentity, getter_AddRefs(dstFolder), &waitForUrl);
+ isDraft = false;
+ if (!dstFolder || NS_FAILED(rv))
+ return NS_MSG_COULDNT_OPEN_FCC_FOLDER;
+ }
+
+ nsCOMPtr <nsIMsgWindow> msgWindow;
+
+ if (aMsgSendObj)
+ {
+ nsCOMPtr <nsIMsgProgress> progress;
+ aMsgSendObj->GetProgress(getter_AddRefs(progress));
+ if (progress)
+ progress->GetMsgWindow(getter_AddRefs(msgWindow));
+ }
+
+ mMode = aMode;
+ mFile = aFile;
+ mDstFolder = dstFolder;
+ mMsgToReplace = aMsgToReplace;
+ mIsDraft = isDraft;
+ mMsgSendObj = aMsgSendObj;
+ if (!waitForUrl)
+ {
+ // cache info needed for DoCopy and call DoCopy when OnStopUrl is called.
+ rv = DoCopy(aFile, dstFolder, aMsgToReplace, isDraft, msgWindow, aMsgSendObj);
+ // N.B. "this" may be deleted when this call returns.
+ }
+ return rv;
+}
+
+nsresult
+nsMsgCopy::DoCopy(nsIFile *aDiskFile, nsIMsgFolder *dstFolder,
+ nsIMsgDBHdr *aMsgToReplace, bool aIsDraft,
+ nsIMsgWindow *msgWindow,
+ nsIMsgSend *aMsgSendObj)
+{
+ nsresult rv = NS_OK;
+
+ // Check sanity
+ if ((!aDiskFile) || (!dstFolder))
+ return NS_ERROR_INVALID_ARG;
+
+ //Call copyservice with dstFolder, disk file, and txnManager
+ if(NS_SUCCEEDED(rv))
+ {
+ RefPtr<CopyListener> copyListener = new CopyListener();
+ if (!copyListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ copyListener->SetMsgComposeAndSendObject(aMsgSendObj);
+ nsCOMPtr<nsIThread> thread;
+
+ if (aIsDraft)
+ {
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
+ do_QueryInterface(dstFolder);
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ bool shutdownInProgress = false;
+ rv = accountManager->GetShutdownInProgress(&shutdownInProgress);
+
+ if (NS_SUCCEEDED(rv) && shutdownInProgress && imapFolder)
+ {
+ // set the following only when we were in the middle of shutdown
+ // process
+ copyListener->mCopyInProgress = true;
+ thread = do_GetCurrentThread();
+ }
+ }
+ nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = copyService->CopyFileMessage(aDiskFile, dstFolder, aMsgToReplace,
+ aIsDraft,
+ aIsDraft ? 0 : nsMsgMessageFlags::Read,
+ EmptyCString(), copyListener, msgWindow);
+ // copyListener->mCopyInProgress can only be set when we are in the
+ // middle of the shutdown process
+ while (copyListener->mCopyInProgress)
+ {
+ PR_CEnterMonitor(copyListener);
+ PR_CWait(copyListener, PR_MicrosecondsToInterval(1000UL));
+ PR_CExitMonitor(copyListener);
+ if (thread)
+ NS_ProcessPendingEvents(thread);
+ }
+ }
+
+ return rv;
+}
+
+// nsIUrlListener methods
+NS_IMETHODIMP
+nsMsgCopy::OnStartRunningUrl(nsIURI * aUrl)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgCopy::OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode)
+{
+ nsresult rv = aExitCode;
+ if (NS_SUCCEEDED(aExitCode))
+ {
+ rv = DoCopy(mFile, mDstFolder, mMsgToReplace, mIsDraft, nullptr, mMsgSendObj);
+ }
+ return rv;
+}
+
+nsresult
+nsMsgCopy::GetUnsentMessagesFolder(nsIMsgIdentity *userIdentity, nsIMsgFolder **folder, bool *waitForUrl)
+{
+ nsresult ret = LocateMessageFolder(userIdentity, nsIMsgSend::nsMsgQueueForLater, mSavePref, folder);
+ if (*folder)
+ (*folder)->SetFlag(nsMsgFolderFlags::Queue);
+ CreateIfMissing(folder, waitForUrl);
+ return ret;
+}
+
+nsresult
+nsMsgCopy::GetDraftsFolder(nsIMsgIdentity *userIdentity, nsIMsgFolder **folder, bool *waitForUrl)
+{
+ nsresult ret = LocateMessageFolder(userIdentity, nsIMsgSend::nsMsgSaveAsDraft, mSavePref, folder);
+ if (*folder)
+ (*folder)->SetFlag(nsMsgFolderFlags::Drafts);
+ CreateIfMissing(folder, waitForUrl);
+ return ret;
+}
+
+nsresult
+nsMsgCopy::GetTemplatesFolder(nsIMsgIdentity *userIdentity, nsIMsgFolder **folder, bool *waitForUrl)
+{
+ nsresult ret = LocateMessageFolder(userIdentity, nsIMsgSend::nsMsgSaveAsTemplate, mSavePref, folder);
+ if (*folder)
+ (*folder)->SetFlag(nsMsgFolderFlags::Templates);
+ CreateIfMissing(folder, waitForUrl);
+ return ret;
+}
+
+nsresult
+nsMsgCopy::GetSentFolder(nsIMsgIdentity *userIdentity, nsIMsgFolder **folder, bool *waitForUrl)
+{
+ nsresult ret = LocateMessageFolder(userIdentity, nsIMsgSend::nsMsgDeliverNow, mSavePref, folder);
+ if (*folder)
+ {
+ // If mSavePref is the same as the identity's fcc folder, set the sent flag.
+ nsCString identityFccUri;
+ userIdentity->GetFccFolder(identityFccUri);
+ if (identityFccUri.Equals(mSavePref))
+ (*folder)->SetFlag(nsMsgFolderFlags::SentMail);
+ }
+ CreateIfMissing(folder, waitForUrl);
+ return ret;
+}
+
+nsresult
+nsMsgCopy::CreateIfMissing(nsIMsgFolder **folder, bool *waitForUrl)
+{
+ nsresult rv = NS_OK;
+ if (folder && *folder)
+ {
+ nsCOMPtr<nsIMsgFolder> parent;
+ (*folder)->GetParent(getter_AddRefs(parent));
+ if (!parent)
+ {
+ nsCOMPtr<nsIFile> folderPath;
+ // for local folders, path is to the berkeley mailbox.
+ // for imap folders, path needs to have .msf appended to the name
+ (*folder)->GetFilePath(getter_AddRefs(folderPath));
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = (*folder)->GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgProtocolInfo> protocolInfo;
+ rv = server->GetProtocolInfo(getter_AddRefs(protocolInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isAsyncFolder;
+ rv = protocolInfo->GetFoldersCreatedAsync(&isAsyncFolder);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if we can't get the path from the folder, then try to create the storage.
+ // for imap, it doesn't matter if the .msf file exists - it still might not
+ // exist on the server, so we should try to create it
+ bool exists = false;
+ if (!isAsyncFolder && folderPath)
+ folderPath->Exists(&exists);
+ if (!exists)
+ {
+ (*folder)->CreateStorageIfMissing(this);
+ if (isAsyncFolder)
+ *waitForUrl = true;
+
+ rv = NS_OK;
+ }
+ }
+ }
+ return rv;
+}
+////////////////////////////////////////////////////////////////////////////////////
+// Utility Functions for MsgFolders
+////////////////////////////////////////////////////////////////////////////////////
+nsresult
+LocateMessageFolder(nsIMsgIdentity *userIdentity,
+ nsMsgDeliverMode aFolderType,
+ const char *aFolderURI,
+ nsIMsgFolder **msgFolder)
+{
+ nsresult rv = NS_OK;
+
+ if (!msgFolder) return NS_ERROR_NULL_POINTER;
+ *msgFolder = nullptr;
+
+ if (!aFolderURI || !*aFolderURI)
+ return NS_ERROR_INVALID_ARG;
+
+ // as long as it doesn't start with anyfolder://
+ if (PL_strncasecmp(ANY_SERVER, aFolderURI, strlen(aFolderURI)) != 0)
+ {
+ nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ // get the corresponding RDF resource
+ // RDF will create the folder resource if it doesn't already exist
+ nsCOMPtr<nsIRDFResource> resource;
+ rv = rdf->GetResource(nsDependentCString(aFolderURI), getter_AddRefs(resource));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr <nsIMsgFolder> folderResource;
+ folderResource = do_QueryInterface(resource, &rv);
+ if (NS_SUCCEEDED(rv) && folderResource)
+ {
+ // don't check validity of folder - caller will handle creating it
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ //make sure that folder hierarchy is built so that legitimate parent-child relationship is established
+ rv = folderResource->GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv,rv);
+ return server->GetMsgFolderFromURI(folderResource, nsDependentCString(aFolderURI), msgFolder);
+ }
+ else
+ {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ else
+ {
+ uint32_t cnt = 0;
+ uint32_t i;
+
+ if (!userIdentity)
+ return NS_ERROR_INVALID_ARG;
+
+ // get the account manager
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ // If any folder will do, go look for one.
+ nsCOMPtr<nsIArray> retval;
+ accountManager->GetServersForIdentity(userIdentity, getter_AddRefs(retval));
+ if (!retval) return NS_ERROR_FAILURE;
+
+ // Ok, we have to look through the servers and try to find the server that
+ // has a valid folder of the type that interests us...
+ rv = retval->GetLength(&cnt);
+ if (NS_FAILED(rv)) return rv;
+
+ for (i=0; i<cnt; i++) {
+ // Now that we have the server...we need to get the named message folder
+ nsCOMPtr<nsIMsgIncomingServer> inServer;
+
+ inServer = do_QueryElementAt(retval, i, &rv);
+ if(NS_FAILED(rv) || (!inServer))
+ continue;
+
+ //
+ // If aFolderURI is passed in, then the user has chosen a specific
+ // mail folder to save the message, but if it is null, just find the
+ // first one and make that work. The folder is specified as a URI, like
+ // the following:
+ //
+ // mailbox://nobody@Local Folders/Sent
+ // imap://rhp@nsmail-2/Drafts
+ // newsgroup://news.mozilla.org/netscape.test
+ //
+ nsCString serverURI;
+ rv = inServer->GetServerURI(serverURI);
+ if (NS_FAILED(rv) || serverURI.IsEmpty())
+ continue;
+
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ rv = inServer->GetRootFolder(getter_AddRefs(rootFolder));
+
+ if(NS_FAILED(rv) || (!rootFolder))
+ continue;
+
+ // use the defaults by getting the folder by flags
+ if (aFolderType == nsIMsgSend::nsMsgQueueForLater ||
+ aFolderType == nsIMsgSend::nsMsgDeliverBackground)
+ {
+ // QueueForLater (Outbox)
+ rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Queue, msgFolder);
+ }
+ else if (aFolderType == nsIMsgSend::nsMsgSaveAsDraft) // SaveAsDraft (Drafts)
+ {
+ rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Drafts, msgFolder);
+ }
+ else if (aFolderType == nsIMsgSend::nsMsgSaveAsTemplate) // SaveAsTemplate (Templates)
+ {
+ rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Templates, msgFolder);
+ }
+ else // SaveInSentFolder (Sent) - nsMsgDeliverNow or nsMsgSendUnsent
+ {
+ rootFolder->GetFolderWithFlags(nsMsgFolderFlags::SentMail, msgFolder);
+ }
+
+ if (*msgFolder)
+ {
+ return NS_OK;
+ }
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+//
+// Figure out if a folder is local or not and return a boolean to
+// say so.
+//
+nsresult
+MessageFolderIsLocal(nsIMsgIdentity *userIdentity,
+ nsMsgDeliverMode aFolderType,
+ const char *aFolderURI,
+ bool *aResult)
+{
+ nsresult rv;
+
+ if (!aFolderURI) return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr <nsIURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = url->SetSpec(nsDependentCString(aFolderURI));
+ if (NS_FAILED(rv)) return rv;
+
+ /* mailbox:/ means its local (on disk) */
+ rv = url->SchemeIs("mailbox", aResult);
+ if (NS_FAILED(rv)) return rv;
+ return NS_OK;
+}
+
diff --git a/mailnews/compose/src/nsMsgCopy.h b/mailnews/compose/src/nsMsgCopy.h
new file mode 100644
index 000000000..7b559d754
--- /dev/null
+++ b/mailnews/compose/src/nsMsgCopy.h
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsMsgCopy_H_
+#define _nsMsgCopy_H_
+
+#include "mozilla/Attributes.h"
+#include "nscore.h"
+#include "nsIFile.h"
+#include "nsMsgSend.h"
+#include "nsIMsgFolder.h"
+#include "nsITransactionManager.h"
+#include "nsIMsgCopyServiceListener.h"
+#include "nsIMsgCopyService.h"
+
+// {0874C3B5-317D-11d3-8EFB-00A024A7D144}
+#define NS_IMSGCOPY_IID \
+{ 0x874c3b5, 0x317d, 0x11d3, \
+{ 0x8e, 0xfb, 0x0, 0xa0, 0x24, 0xa7, 0xd1, 0x44 } };
+
+// Forward declarations...
+class nsMsgCopy;
+
+////////////////////////////////////////////////////////////////////////////////////
+// This is the listener class for the copy operation. We have to create this class
+// to listen for message copy completion and eventually notify the caller
+////////////////////////////////////////////////////////////////////////////////////
+class CopyListener : public nsIMsgCopyServiceListener
+{
+public:
+ CopyListener(void);
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD OnStartCopy() override;
+
+ NS_IMETHOD OnProgress(uint32_t aProgress, uint32_t aProgressMax) override;
+
+ NS_IMETHOD SetMessageKey(nsMsgKey aMessageKey) override;
+
+ NS_IMETHOD GetMessageId(nsACString& aMessageId) override;
+
+ NS_IMETHOD OnStopCopy(nsresult aStatus) override;
+
+ NS_IMETHOD SetMsgComposeAndSendObject(nsIMsgSend *obj);
+
+ bool mCopyInProgress;
+
+private:
+ virtual ~CopyListener();
+ nsCOMPtr<nsIMsgSend> mComposeAndSend;
+};
+
+//
+// This is a class that deals with processing remote attachments. It implements
+// an nsIStreamListener interface to deal with incoming data
+//
+class nsMsgCopy : public nsIUrlListener
+{
+public:
+ nsMsgCopy();
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIURLLISTENER
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Object methods...
+ //////////////////////////////////////////////////////////////////////
+ //
+ nsresult StartCopyOperation(nsIMsgIdentity *aUserIdentity,
+ nsIFile *aFile,
+ nsMsgDeliverMode aMode,
+ nsIMsgSend *aMsgSendObj,
+ const char *aSavePref,
+ nsIMsgDBHdr *aMsgToReplace);
+
+ nsresult DoCopy(nsIFile *aDiskFile, nsIMsgFolder *dstFolder,
+ nsIMsgDBHdr *aMsgToReplace, bool aIsDraft,
+ nsIMsgWindow *msgWindow,
+ nsIMsgSend *aMsgSendObj);
+
+ nsresult GetUnsentMessagesFolder(nsIMsgIdentity *userIdentity, nsIMsgFolder **msgFolder, bool *waitForUrl);
+ nsresult GetDraftsFolder(nsIMsgIdentity *userIdentity, nsIMsgFolder **msgFolder, bool *waitForUrl);
+ nsresult GetTemplatesFolder(nsIMsgIdentity *userIdentity, nsIMsgFolder **msgFolder, bool *waitForUrl);
+ nsresult GetSentFolder(nsIMsgIdentity *userIdentity, nsIMsgFolder **msgFolder, bool *waitForUrl);
+ nsresult CreateIfMissing(nsIMsgFolder **folder, bool *waitForUrl);
+
+
+ //
+ // Vars for implementation...
+ //
+ nsIFile *mFile; // the file we are sending...
+ nsMsgDeliverMode mMode;
+ nsCOMPtr<nsIMsgFolder> mDstFolder;
+ nsCOMPtr<nsIMsgDBHdr> mMsgToReplace;
+ bool mIsDraft;
+ nsCOMPtr<nsIMsgSend> mMsgSendObj;
+ char *mSavePref;
+
+private:
+ virtual ~nsMsgCopy();
+};
+
+// Useful function for the back end...
+nsresult LocateMessageFolder(nsIMsgIdentity *userIdentity,
+ nsMsgDeliverMode aFolderType,
+ const char *aSaveURI,
+ nsIMsgFolder **msgFolder);
+
+nsresult MessageFolderIsLocal(nsIMsgIdentity *userIdentity,
+ nsMsgDeliverMode aFolderType,
+ const char *aSaveURI,
+ bool *aResult);
+
+#endif /* _nsMsgCopy_H_ */
diff --git a/mailnews/compose/src/nsMsgPrompts.cpp b/mailnews/compose/src/nsMsgPrompts.cpp
new file mode 100644
index 000000000..473469f16
--- /dev/null
+++ b/mailnews/compose/src/nsMsgPrompts.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsMsgPrompts.h"
+
+#include "nsMsgCopy.h"
+#include "nsIPrompt.h"
+#include "nsIWindowWatcher.h"
+#include "nsMsgCompCID.h"
+#include "nsComposeStrings.h"
+#include "nsIStringBundle.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMsgUtils.h"
+#include "mozilla/Services.h"
+
+nsresult
+nsMsgGetMessageByName(const char16_t* aName, nsString& aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle(
+ "chrome://messenger/locale/messengercompose/composeMsgs.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return bundle->GetStringFromName(aName, getter_Copies(aResult));
+}
+
+static nsresult
+nsMsgBuildMessageByName(const char16_t *aName, nsIFile *aFile, nsString& aResult)
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString path;
+ aFile->GetPath(path);
+
+ const char16_t *params[1] = {path.get()};
+ return bundle->FormatStringFromName(aName, params, 1, getter_Copies(aResult));
+}
+
+nsresult
+nsMsgBuildMessageWithFile(nsIFile *aFile, nsString& aResult)
+{
+ return nsMsgBuildMessageByName(u"unableToOpenFile", aFile, aResult);
+}
+
+nsresult
+nsMsgBuildMessageWithTmpFile(nsIFile *aFile, nsString& aResult)
+{
+ return nsMsgBuildMessageByName(u"unableToOpenTmpFile", aFile, aResult);
+}
+
+nsresult
+nsMsgDisplayMessageByName(nsIPrompt *aPrompt, const char16_t* aName, const char16_t *windowTitle)
+{
+ nsString msg;
+ nsMsgGetMessageByName(aName, msg);
+ return nsMsgDisplayMessageByString(aPrompt, msg.get(), windowTitle);
+}
+
+nsresult
+nsMsgDisplayMessageByString(nsIPrompt * aPrompt, const char16_t * msg, const char16_t * windowTitle)
+{
+ NS_ENSURE_ARG_POINTER(msg);
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIPrompt> prompt = aPrompt;
+
+ if (!prompt)
+ {
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ if (wwatch)
+ wwatch->GetNewPrompter(0, getter_AddRefs(prompt));
+ }
+
+ if (prompt)
+ rv = prompt->Alert(windowTitle, msg);
+
+ return rv;
+}
+
+nsresult
+nsMsgAskBooleanQuestionByString(nsIPrompt * aPrompt, const char16_t * msg, bool *answer, const char16_t * windowTitle)
+{
+ NS_ENSURE_TRUE(msg && *msg, NS_ERROR_INVALID_ARG);
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIPrompt> dialog = aPrompt;
+
+ if (!dialog)
+ {
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ if (wwatch)
+ wwatch->GetNewPrompter(0, getter_AddRefs(dialog));
+ }
+
+ if (dialog) {
+ rv = dialog->Confirm(windowTitle, msg, answer);
+ }
+
+ return rv;
+}
diff --git a/mailnews/compose/src/nsMsgPrompts.h b/mailnews/compose/src/nsMsgPrompts.h
new file mode 100644
index 000000000..9aed5252e
--- /dev/null
+++ b/mailnews/compose/src/nsMsgPrompts.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsMsgPrompts_H_
+#define _nsMsgPrompts_H_
+
+#include "nscore.h"
+#include "nsError.h"
+#include "nsStringGlue.h"
+
+class nsIPrompt;
+
+nsresult nsMsgGetMessageByName(const char16_t* aName, nsString& aResult);
+nsresult nsMsgBuildMessageWithFile(nsIFile * aFile, nsString& aResult);
+nsresult nsMsgBuildMessageWithTmpFile(nsIFile * aFile, nsString& aResult);
+nsresult nsMsgDisplayMessageByName(nsIPrompt *aPrompt, const char16_t *aName, const char16_t *windowTitle = nullptr);
+nsresult nsMsgDisplayMessageByString(nsIPrompt * aPrompt, const char16_t * msg, const char16_t * windowTitle = nullptr);
+nsresult nsMsgAskBooleanQuestionByString(nsIPrompt * aPrompt, const char16_t * msg, bool *answer, const char16_t * windowTitle = nullptr);
+
+#endif /* _nsMsgPrompts_H_ */
diff --git a/mailnews/compose/src/nsMsgQuote.cpp b/mailnews/compose/src/nsMsgQuote.cpp
new file mode 100644
index 000000000..ce8e1b5d0
--- /dev/null
+++ b/mailnews/compose/src/nsMsgQuote.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIURL.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIServiceManager.h"
+#include "nsIStreamListener.h"
+#include "nsIStreamConverter.h"
+#include "nsIStreamConverterService.h"
+#include "nsIMimeStreamConverter.h"
+#include "nsMimeTypes.h"
+#include "nsICharsetConverterManager.h"
+#include "prprf.h"
+#include "nsMsgQuote.h"
+#include "nsMsgCompUtils.h"
+#include "nsIMsgMessageService.h"
+#include "nsMsgUtils.h"
+#include "nsNetUtil.h"
+#include "nsMsgMimeCID.h"
+#include "nsMsgCompCID.h"
+#include "nsMsgCompose.h"
+#include "nsMsgMailNewsUrl.h"
+#include "mozilla/Services.h"
+#include "nsIScriptSecurityManager.h"
+
+NS_IMPL_ISUPPORTS(nsMsgQuoteListener, nsIMsgQuoteListener,
+ nsIMimeStreamConverterListener)
+
+nsMsgQuoteListener::nsMsgQuoteListener()
+{
+}
+
+nsMsgQuoteListener::~nsMsgQuoteListener()
+{
+}
+
+NS_IMETHODIMP nsMsgQuoteListener::SetMsgQuote(nsIMsgQuote * msgQuote)
+{
+ mMsgQuote = do_GetWeakReference(msgQuote);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgQuoteListener::GetMsgQuote(nsIMsgQuote ** aMsgQuote)
+{
+ nsresult rv = NS_OK;
+ if (aMsgQuote)
+ {
+ nsCOMPtr<nsIMsgQuote> msgQuote = do_QueryReferent(mMsgQuote);
+ *aMsgQuote = msgQuote;
+ NS_IF_ADDREF(*aMsgQuote);
+ }
+ else
+ rv = NS_ERROR_NULL_POINTER;
+
+ return rv;
+}
+
+nsresult nsMsgQuoteListener::OnHeadersReady(nsIMimeHeaders * headers)
+{
+ nsCOMPtr<nsIMsgQuotingOutputStreamListener> quotingOutputStreamListener;
+ nsCOMPtr<nsIMsgQuote> msgQuote = do_QueryReferent(mMsgQuote);
+
+ if (msgQuote)
+ msgQuote->GetStreamListener(getter_AddRefs(quotingOutputStreamListener));
+
+ if (quotingOutputStreamListener)
+ quotingOutputStreamListener->SetMimeHeaders(headers);
+ return NS_OK;
+}
+
+//
+// Implementation...
+//
+nsMsgQuote::nsMsgQuote()
+{
+ mQuoteHeaders = false;
+ mQuoteListener = nullptr;
+}
+
+nsMsgQuote::~nsMsgQuote()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsMsgQuote, nsIMsgQuote, nsISupportsWeakReference)
+
+NS_IMETHODIMP nsMsgQuote::GetStreamListener(nsIMsgQuotingOutputStreamListener ** aStreamListener)
+{
+ nsresult rv = NS_OK;
+ if (aStreamListener)
+ {
+ *aStreamListener = mStreamListener;
+ NS_IF_ADDREF(*aStreamListener);
+ }
+ else
+ rv = NS_ERROR_NULL_POINTER;
+
+ return rv;
+}
+
+nsresult
+nsMsgQuote::QuoteMessage(const char *msgURI, bool quoteHeaders,
+ nsIMsgQuotingOutputStreamListener * aQuoteMsgStreamListener,
+ const char * aMsgCharSet, bool headersOnly,
+ nsIMsgDBHdr *aMsgHdr)
+{
+ nsresult rv;
+ if (!msgURI)
+ return NS_ERROR_INVALID_ARG;
+
+ mQuoteHeaders = quoteHeaders;
+ mStreamListener = aQuoteMsgStreamListener;
+
+ nsAutoCString msgUri(msgURI);
+ bool fileUrl = !strncmp(msgURI, "file:", 5);
+ bool forwardedMessage = PL_strstr(msgURI, "&realtype=message/rfc822") != nullptr;
+ nsCOMPtr<nsIURI> aURL;
+ if (fileUrl)
+ {
+ msgUri.Replace(0, 5, NS_LITERAL_CSTRING("mailbox:"));
+ msgUri.AppendLiteral("?number=0");
+ rv = NS_NewURI(getter_AddRefs(aURL), msgUri);
+ nsCOMPtr<nsIMsgMessageUrl> mailUrl(do_QueryInterface(aURL));
+ if (mailUrl)
+ mailUrl->SetMessageHeader(aMsgHdr);
+ }
+ else if (forwardedMessage)
+ rv = NS_NewURI(getter_AddRefs(aURL), msgURI);
+ else
+ {
+ nsCOMPtr <nsIMsgMessageService> msgService;
+ rv = GetMessageServiceFromURI(nsDependentCString(msgURI), getter_AddRefs(msgService));
+ if (NS_FAILED(rv)) return rv;
+ rv = msgService->GetUrlForUri(msgURI, getter_AddRefs(aURL), nullptr);
+ }
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr <nsIURL> mailNewsUrl = do_QueryInterface(aURL, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsAutoCString queryPart;
+ rv = mailNewsUrl->GetQuery(queryPart);
+ if (!queryPart.IsEmpty())
+ queryPart.Append('&');
+
+ if (headersOnly) /* We don't need to quote the message body but we still need to extract the headers */
+ queryPart.Append("header=only");
+ else if (quoteHeaders)
+ queryPart.Append("header=quote");
+ else
+ queryPart.Append("header=quotebody");
+ rv = mailNewsUrl->SetQuery(queryPart);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // if we were given a non empty charset, then use it
+ if (aMsgCharSet && *aMsgCharSet)
+ {
+ nsCOMPtr<nsIMsgI18NUrl> i18nUrl (do_QueryInterface(aURL));
+ if (i18nUrl)
+ i18nUrl->SetCharsetOverRide(aMsgCharSet);
+ }
+
+ mQuoteListener = do_CreateInstance(NS_MSGQUOTELISTENER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ mQuoteListener->SetMsgQuote(this);
+
+ // funky magic go get the isupports for this class which inherits from multiple interfaces.
+ nsISupports * supports;
+ QueryInterface(NS_GET_IID(nsISupports), (void **) &supports);
+ nsCOMPtr<nsISupports> quoteSupport = supports;
+ NS_IF_RELEASE(supports);
+
+ // now we want to create a necko channel for this url and we want to open it
+ nsCOMPtr<nsIScriptSecurityManager> secMan(
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIPrincipal> systemPrincipal;
+ rv = secMan->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ mQuoteChannel = nullptr;
+ nsCOMPtr<nsIIOService> netService = mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(netService, NS_ERROR_UNEXPECTED);
+ rv = netService->NewChannelFromURI2(aURL,
+ nullptr,
+ systemPrincipal,
+ nullptr,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER,
+ getter_AddRefs(mQuoteChannel));
+
+ if (NS_FAILED(rv)) return rv;
+ nsCOMPtr<nsISupports> ctxt = do_QueryInterface(aURL);
+
+ nsCOMPtr<nsIStreamConverterService> streamConverterService =
+ do_GetService("@mozilla.org/streamConverters;1", &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIStreamListener> convertedListener;
+ rv = streamConverterService->AsyncConvertData("message/rfc822",
+ "application/vnd.mozilla.xul+xml",
+ mStreamListener,
+ quoteSupport,
+ getter_AddRefs(convertedListener));
+ if (NS_FAILED(rv)) return rv;
+
+ // now try to open the channel passing in our display consumer as the listener
+ rv = mQuoteChannel->AsyncOpen(convertedListener, ctxt);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgQuote::GetQuoteListener(nsIMimeStreamConverterListener** aQuoteListener)
+{
+ if (!aQuoteListener || !mQuoteListener)
+ return NS_ERROR_NULL_POINTER;
+ *aQuoteListener = mQuoteListener;
+ NS_ADDREF(*aQuoteListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgQuote::GetQuoteChannel(nsIChannel** aQuoteChannel)
+{
+ if (!aQuoteChannel || !mQuoteChannel)
+ return NS_ERROR_NULL_POINTER;
+ *aQuoteChannel = mQuoteChannel;
+ NS_ADDREF(*aQuoteChannel);
+ return NS_OK;
+}
diff --git a/mailnews/compose/src/nsMsgQuote.h b/mailnews/compose/src/nsMsgQuote.h
new file mode 100644
index 000000000..2151828ef
--- /dev/null
+++ b/mailnews/compose/src/nsMsgQuote.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef __nsMsgQuote_h__
+#define __nsMsgQuote_h__
+
+#include "nsIMsgQuote.h"
+#include "nsIMsgMessageService.h"
+#include "nsIStreamListener.h"
+#include "nsIMimeStreamConverter.h"
+#include "nsIChannel.h"
+#include "nsCOMPtr.h"
+#include "nsWeakReference.h"
+
+class nsMsgQuote;
+
+class nsMsgQuoteListener: public nsIMsgQuoteListener
+{
+public:
+ nsMsgQuoteListener();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsIMimeStreamConverterListener support
+ NS_DECL_NSIMIMESTREAMCONVERTERLISTENER
+ NS_DECL_NSIMSGQUOTELISTENER
+
+private:
+ virtual ~nsMsgQuoteListener();
+ nsWeakPtr mMsgQuote;
+};
+
+class nsMsgQuote: public nsIMsgQuote, public nsSupportsWeakReference {
+public:
+ nsMsgQuote();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIMSGQUOTE
+
+private:
+ virtual ~nsMsgQuote();
+ //
+ // Implementation data...
+ //
+ nsCOMPtr<nsIMsgQuotingOutputStreamListener> mStreamListener;
+ bool mQuoteHeaders;
+ nsCOMPtr<nsIMsgQuoteListener> mQuoteListener;
+ nsCOMPtr<nsIChannel> mQuoteChannel;
+};
+
+#endif /* __nsMsgQuote_h__ */
diff --git a/mailnews/compose/src/nsMsgSend.cpp b/mailnews/compose/src/nsMsgSend.cpp
new file mode 100644
index 000000000..c0f74e11c
--- /dev/null
+++ b/mailnews/compose/src/nsMsgSend.cpp
@@ -0,0 +1,5218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsMsgSend.h"
+#include "prmem.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMsgSendPart.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgNewsCID.h"
+#include "nsISmtpService.h" // for actually sending the message...
+#include "nsINntpService.h" // for actually posting the message...
+#include "nsIMsgMailSession.h"
+#include "nsIMsgIdentity.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsMsgCompUtils.h"
+#include "nsMsgI18N.h"
+#include "nsICharsetConverterManager.h"
+#include "nsIMsgSendListener.h"
+#include "nsIMsgCopyServiceListener.h"
+#include "nsIFile.h"
+#include "nsIURL.h"
+#include "nsNetUtil.h"
+#include "nsIFileURL.h"
+#include "nsMsgCopy.h"
+#include "nsUnicharUtils.h"
+#include "nsMsgPrompts.h"
+#include "nsIDOMHTMLBodyElement.h"
+#include "nsIDOMHTMLImageElement.h"
+#include "nsIDOMHTMLLinkElement.h"
+#include "nsIDOMHTMLAnchorElement.h"
+#include "nsCExternalHandlerService.h"
+#include "nsIMIMEService.h"
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsMsgCompCID.h"
+#include "nsIAbAddressCollector.h"
+#include "nsAbBaseCID.h"
+#include "nsCOMPtr.h"
+#include "mozITXTToHTMLConv.h"
+#include "nsIMsgStatusFeedback.h"
+#include "nsIMsgWindow.h"
+#include "nsTextFormatter.h"
+#include "nsIPrompt.h"
+#include "nsMailHeaders.h"
+#include "nsIDocShell.h"
+#include "nsMimeTypes.h"
+#include "nsISmtpUrl.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIEditorMailSupport.h"
+#include "nsIDocumentEncoder.h" // for editor output flags
+#include "nsILoadGroup.h"
+#include "nsMsgSendReport.h"
+#include "nsNetCID.h"
+#include "nsError.h"
+#include "nsMsgUtils.h"
+#include "nsIMsgMdnGenerator.h"
+#include "nsISmtpServer.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "nsIMsgAccountManager.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIAbCard.h"
+#include "nsIMsgAttachment.h"
+#include "nsIMsgProgress.h"
+#include "nsIMsgMessageService.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgFolder.h"
+#include "nsComposeStrings.h"
+#include "nsStringGlue.h"
+#include "nsMsgUtils.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+#include "mozilla/Services.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/mailnews/MimeEncoder.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "nsIMutableArray.h"
+#include "nsIMsgFilterService.h"
+#include "nsIMsgProtocolInfo.h"
+#include "mozIDOMWindow.h"
+#include "mozilla/Preferences.h"
+
+using namespace mozilla;
+using namespace mozilla::mailnews;
+
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+
+#define PREF_MAIL_SEND_STRUCT "mail.send_struct"
+#define PREF_MAIL_STRICTLY_MIME "mail.strictly_mime"
+#define PREF_MAIL_MESSAGE_WARNING_SIZE "mailnews.message_warning_size"
+#define PREF_MAIL_COLLECT_EMAIL_ADDRESS_OUTGOING "mail.collect_email_address_outgoing"
+
+#define ATTR_MOZ_DO_NOT_SEND "moz-do-not-send"
+
+enum { kDefaultMode = (PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE) };
+
+static bool mime_use_quoted_printable_p = false;
+
+//
+// Ugh, we need to do this currently to access this boolean.
+//
+bool
+UseQuotedPrintable(void)
+{
+ return mime_use_quoted_printable_p;
+}
+
+/* This function will parse a list of email addresses and groups and just
+ * return a list of email addresses (recipient)
+ *
+ * The input could be:
+ * [recipient | group] *[,recipient | group]
+ *
+ * The group syntax is:
+ * group-name:[recipient *[,recipient]];
+ *
+ * the output will be:
+ * recipient *[, recipient]
+ *
+ * As the result will always be equal or smaller than the input string,
+ * the extraction will be made in place. Don't need to create a new buffer.
+ */
+static nsresult StripOutGroupNames(char * addresses)
+{
+ char aChar;
+ char * readPtr = addresses; // current read position
+ char * writePtr = addresses; // current write position
+ char * previousSeparator = addresses; // remember last time we wrote a recipient separator
+ char * endPtr = addresses + PL_strlen(addresses);
+
+ bool quoted = false; // indicate if we are between double quote
+ bool group = false; // indicate if we found a group prefix
+ bool atFound = false; // indicate if we found an @ in the current recipient. group name should not have an @
+
+ while (readPtr < endPtr)
+ {
+ aChar = *readPtr;
+ readPtr ++;
+ switch(aChar)
+ {
+ case '\\':
+ if (*readPtr == '"') //ignore escaped quote
+ readPtr ++;
+ continue;
+
+ case '"':
+ quoted = !quoted;
+ break;
+
+ case '@':
+ if (!quoted)
+ atFound = true;
+ break;
+
+ case ':':
+ if (!quoted && !atFound)
+ {
+ // ok, we found a group name
+ // let's backup the write cursor to remove the group name
+ writePtr = previousSeparator + 1;
+ group = true;
+ continue;
+ }
+ break;
+
+ case ';':
+ if (quoted || !group)
+ break;
+ else
+ group = false;
+ //end of the group, act like a recipient separator now...
+ /* NO BREAK */
+ MOZ_FALLTHROUGH;
+ case ',':
+ if (!quoted)
+ {
+ atFound = false;
+ //let check if we already have a comma separator in the output string
+ if (writePtr > addresses && *(writePtr - 1) == ',')
+ writePtr --;
+ *writePtr = ',';
+ previousSeparator = writePtr;
+ writePtr ++;
+ continue;
+ }
+ break;
+ }
+ *writePtr = aChar;
+ writePtr ++;
+ }
+
+ if (writePtr > addresses && *(writePtr - 1) == ',')
+ writePtr --;
+ *writePtr = '\0';
+
+ return NS_OK;
+}
+
+
+// This private class just provides us an external URL listener, with callback functionality.
+
+class MsgDeliveryListener : public nsIUrlListener
+{
+public:
+ MsgDeliveryListener(nsIMsgSend *aMsgSend, bool inIsNewsDelivery);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIURLLISTENER
+
+private:
+ virtual ~MsgDeliveryListener();
+ nsCOMPtr<nsIMsgSend> mMsgSend;
+ bool mIsNewsDelivery;
+};
+
+NS_IMPL_ISUPPORTS(MsgDeliveryListener, nsIUrlListener)
+
+MsgDeliveryListener::MsgDeliveryListener(nsIMsgSend *aMsgSend, bool inIsNewsDelivery)
+{
+ mMsgSend = aMsgSend;
+ mIsNewsDelivery = inIsNewsDelivery;
+}
+
+MsgDeliveryListener::~MsgDeliveryListener()
+{
+}
+
+NS_IMETHODIMP MsgDeliveryListener::OnStartRunningUrl(nsIURI *url)
+{
+ if (mMsgSend)
+ mMsgSend->NotifyListenerOnStartSending(nullptr, 0);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP MsgDeliveryListener::OnStopRunningUrl(nsIURI *url, nsresult aExitCode)
+{
+ if (url)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(url);
+ if (mailUrl)
+ mailUrl->UnRegisterListener(this);
+ }
+
+ // Let mMsgSend sort out the OnStopSending notification - it knows more about
+ // the messages than we do.
+ if (mMsgSend)
+ mMsgSend->SendDeliveryCallback(url, mIsNewsDelivery, aExitCode);
+
+ return NS_OK;
+}
+
+
+/* the following macro actually implement addref, release and query interface for our component. */
+NS_IMPL_ISUPPORTS(nsMsgComposeAndSend, nsIMsgSend, nsIMsgOperationListener,
+ nsISupportsWeakReference)
+
+nsMsgComposeAndSend::nsMsgComposeAndSend() :
+ m_messageKey(nsMsgKey_None)
+{
+ mGUINotificationEnabled = true;
+ mAbortInProcess = false;
+ mMultipartRelatedAttachmentCount = -1;
+ mSendMailAlso = false;
+
+ m_dont_deliver_p = false;
+ m_deliver_mode = nsMsgDeliverNow;
+
+ m_pre_snarfed_attachments_p = false;
+ m_digest_p = false;
+ m_be_synchronous_p = false;
+ m_attachment1_type = 0;
+ m_attachment1_encoding = 0;
+ m_attachment1_body = 0;
+ m_attachment1_body_length = 0;
+ m_attachment_count = 0;
+ m_attachment_pending_count = 0;
+ m_status = NS_OK;
+ m_plaintext = nullptr;
+ m_related_part = nullptr;
+ m_related_body_part = nullptr;
+ mOriginalHTMLBody = nullptr;
+
+ mNeedToPerformSecondFCC = false;
+ mPerformingSecondFCC = false;
+
+ mPreloadedAttachmentCount = 0;
+ mRemoteAttachmentCount = 0;
+ mCompFieldLocalAttachments = 0;
+ mCompFieldRemoteAttachments = 0;
+ mMessageWarningSize = 0;
+
+ mSendReport = new nsMsgSendReport();
+}
+
+nsMsgComposeAndSend::~nsMsgComposeAndSend()
+{
+ PR_Free(m_attachment1_type);
+ PR_Free(m_attachment1_encoding);
+ PR_Free(m_attachment1_body);
+ PR_Free(mOriginalHTMLBody);
+
+ if (m_plaintext)
+ {
+ if (m_plaintext->mTmpFile)
+ m_plaintext->mTmpFile->Remove(false);
+
+ m_plaintext = nullptr;
+ }
+
+ if (mHTMLFile)
+ mHTMLFile->Remove(false);
+
+ if (mCopyFile)
+ mCopyFile->Remove(false);
+
+ if (mCopyFile2)
+ mCopyFile2->Remove(false);
+
+ if (mTempFile && !mReturnFile)
+ mTempFile->Remove(false);
+
+ m_attachments.Clear();
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetDefaultPrompt(nsIPrompt ** aPrompt)
+{
+ NS_ENSURE_ARG(aPrompt);
+ *aPrompt = nullptr;
+
+ nsresult rv = NS_OK;
+
+ if (mParentWindow)
+ {
+ rv = mParentWindow->GetPrompter(aPrompt);
+ if (NS_SUCCEEDED(rv) && *aPrompt)
+ return NS_OK;
+ }
+
+ /* If we cannot find a prompter, try the mail3Pane window */
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsCOMPtr <nsIMsgMailSession> mailSession (do_GetService(NS_MSGMAILSESSION_CONTRACTID));
+ if (mailSession)
+ {
+ mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+ if (msgWindow)
+ rv = msgWindow->GetPromptDialog(aPrompt);
+ }
+
+ return rv;
+}
+
+nsresult nsMsgComposeAndSend::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks)
+{
+// TODO: stop using mail3pane window!
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIMsgMailSession> mailSession(do_GetService(NS_MSGMAILSESSION_CONTRACTID));
+ mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+ if (msgWindow) {
+ nsCOMPtr<nsIDocShell> docShell;
+ msgWindow->GetRootDocShell(getter_AddRefs(docShell));
+ nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(docShell));
+ nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
+ msgWindow->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
+ if (notificationCallbacks) {
+ nsCOMPtr<nsIInterfaceRequestor> aggregrateIR;
+ MsgNewInterfaceRequestorAggregation(notificationCallbacks, ir, getter_AddRefs(aggregrateIR));
+ ir = aggregrateIR;
+ }
+ if (ir) {
+ NS_ADDREF(*aCallbacks = ir);
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+
+static char *mime_mailto_stream_read_buffer = 0;
+static char *mime_mailto_stream_write_buffer = 0;
+
+
+char * mime_get_stream_write_buffer(void)
+{
+ if (!mime_mailto_stream_write_buffer)
+ mime_mailto_stream_write_buffer = (char *) PR_Malloc(MIME_BUFFER_SIZE);
+ return mime_mailto_stream_write_buffer;
+}
+
+static bool isEmpty(const char* aString)
+{
+ return (!aString) || (!*aString);
+}
+
+void nsMsgComposeAndSend::GenerateMessageId()
+{
+ if (isEmpty(mCompFields->GetMessageId()))
+ {
+ if (isEmpty(mCompFields->GetTo()) &&
+ isEmpty(mCompFields->GetCc()) &&
+ isEmpty(mCompFields->GetBcc()) &&
+ !isEmpty(mCompFields->GetNewsgroups()))
+ {
+ bool generateNewsMessageId = false;
+ mUserIdentity->GetBoolAttribute("generate_news_message_id", &generateNewsMessageId);
+ if (!generateNewsMessageId)
+ return;
+ }
+
+ char* msgID = msg_generate_message_id(mUserIdentity);
+ mCompFields->SetMessageId(msgID);
+ PR_Free(msgID);
+ }
+}
+
+// Don't I18N this line...this is per the spec!
+#define MIME_MULTIPART_BLURB "This is a multi-part message in MIME format."
+
+/* 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.
+ */
+NS_IMETHODIMP
+nsMsgComposeAndSend::GatherMimeAttachments()
+{
+ bool shouldDeleteDeliveryState = true;
+ nsresult status;
+ uint32_t i;
+ PRFileDesc *in_file = 0;
+ char *buffer = 0;
+ nsString msg;
+ bool body_is_us_ascii = true;
+ bool isUsingQP = false;
+
+ nsMsgSendPart* toppart = nullptr; // The very top most container of the message
+ // that we are going to send.
+
+ nsMsgSendPart* mainbody = nullptr; // The leaf node that contains the text of the
+ // message we're going to contain.
+
+ nsMsgSendPart* maincontainer = nullptr; // 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.
+
+ nsMsgSendPart* plainpart = nullptr; // 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.)
+
+ uint32_t multipartRelatedCount = GetMultipartRelatedCount(); // The number of related part we will have to generate
+
+ nsCOMPtr<nsIPrompt> promptObject; // only used if we have to show an alert here....
+ GetDefaultPrompt(getter_AddRefs(promptObject));
+
+ char *hdrs = 0;
+ bool maincontainerISrelatedpart = false;
+ const char * toppart_type = nullptr;
+
+ status = m_status;
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ if (!m_attachment1_type) {
+ m_attachment1_type = PL_strdup(TEXT_PLAIN);
+ if (!m_attachment1_type)
+ goto FAILMEM;
+ }
+
+ nsresult rv;
+
+ // 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 == nullptr &&
+ (mCompFields->GetForcePlainText() ||
+ mCompFields->GetUseMultipartAlternative()) &&
+ m_attachment1_body && PL_strcmp(m_attachment1_type, TEXT_HTML) == 0)
+ {
+ //
+ // If we get here, we have an HTML body, but we really need to send
+ // a text/plain message, so we will write the HTML out to a disk file,
+ // fire off another URL request for this local disk file and that will
+ // take care of the conversion...
+ //
+ rv = nsMsgCreateTempFile("nsemail.html", getter_AddRefs(mHTMLFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIOutputStream> tempfile;
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(tempfile), mHTMLFile, -1, 00600);
+ if (NS_FAILED(rv))
+ {
+ if (mSendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithTmpFile(mHTMLFile, error_msg);
+ mSendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ status = NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
+ goto FAIL;
+ }
+
+ if (mOriginalHTMLBody)
+ {
+ uint32_t origLen = strlen(mOriginalHTMLBody);
+ uint32_t n;
+ nsresult rv = tempfile->Write(mOriginalHTMLBody, origLen, &n);
+ if (NS_FAILED(rv) || n != origLen)
+ {
+ status = NS_MSG_ERROR_WRITING_FILE;
+ goto FAIL;
+ }
+ }
+
+ if (NS_FAILED(tempfile->Flush()))
+ {
+ status = NS_MSG_ERROR_WRITING_FILE;
+ goto FAIL;
+ }
+
+ tempfile->Close();
+
+ m_plaintext = new nsMsgAttachmentHandler;
+ if (!m_plaintext)
+ goto FAILMEM;
+ m_plaintext->SetMimeDeliveryState(this);
+ m_plaintext->m_bogus_attachment = true;
+
+ nsAutoCString tempURL;
+ rv = NS_GetURLSpecFromFile(mHTMLFile, tempURL);
+ if (NS_FAILED(rv) || NS_FAILED(nsMsgNewURL(getter_AddRefs(m_plaintext->mURL), tempURL.get())))
+ {
+ m_plaintext = nullptr;
+ goto FAILMEM;
+ }
+
+ m_plaintext->m_type = TEXT_HTML;
+ m_plaintext->m_charset = mCompFields->GetCharacterSet();
+ m_plaintext->m_desiredType = TEXT_PLAIN;
+ m_attachment_pending_count ++;
+ status = m_plaintext->SnarfAttachment(mCompFields);
+ if (NS_FAILED(status))
+ goto FAIL;
+ if (m_attachment_pending_count > 0)
+ return NS_OK;
+ }
+
+ /* Kludge to avoid having to allocate memory on the toy computers... */
+ buffer = mime_get_stream_write_buffer();
+ if (! buffer)
+ goto FAILMEM;
+
+ NS_ASSERTION (m_attachment_pending_count == 0, "m_attachment_pending_count != 0");
+
+ mComposeBundle->GetStringFromName(u"assemblingMessage",
+ getter_Copies(msg));
+ SetStatusMessage( msg );
+
+ /* First, open the message file.
+ */
+ rv = nsMsgCreateTempFile("nsemail.eml", getter_AddRefs(mTempFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mOutputFile), mTempFile, -1, 00600);
+ if (NS_FAILED(rv))
+ {
+ status = NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
+ if (mSendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithTmpFile(mTempFile, error_msg);
+ mSendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ goto FAIL;
+ }
+
+ // generate a message id, if necessary
+ GenerateMessageId( );
+
+ mainbody = new nsMsgSendPart(this, mCompFields->GetCharacterSet());
+ if (!mainbody)
+ goto FAILMEM;
+
+ mainbody->SetMainPart(true);
+ mainbody->SetType(m_attachment1_type ? m_attachment1_type : TEXT_PLAIN);
+
+ NS_ASSERTION(mainbody->GetBuffer() == nullptr, "not-null buffer");
+ status = mainbody->SetBuffer(m_attachment1_body ? m_attachment1_body : "");
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ // Determine the encoding of the main message body before we free it.
+ PR_FREEIF(m_attachment1_encoding);
+ if (m_attachment1_body)
+ mCompFields->GetBodyIsAsciiOnly(&body_is_us_ascii);
+
+ if (!mCompFields->GetForceMsgEncoding() && (body_is_us_ascii ||
+ nsMsgI18Nstateful_charset(mCompFields->GetCharacterSet()))) {
+ m_attachment1_encoding = PL_strdup (ENCODING_7BIT);
+ } else if (mime_use_quoted_printable_p) {
+ m_attachment1_encoding = PL_strdup (ENCODING_QUOTED_PRINTABLE);
+ isUsingQP = true;
+ } else {
+ m_attachment1_encoding = PL_strdup (ENCODING_8BIT);
+ }
+
+ // Make sure the lines don't get to long.
+ if (m_attachment1_body) {
+ uint32_t max_column = 0;
+ uint32_t cur_column = 0;
+ for (char* c = m_attachment1_body; *c; c++) {
+ if (*c == '\n') {
+ if (cur_column > max_column)
+ max_column = cur_column;
+ cur_column = 0;
+ } else if (*c != '\r') {
+ cur_column++;
+ }
+ }
+ // Check one last time for the last line.
+ if (cur_column > max_column)
+ max_column = cur_column;
+ if (max_column > LINELENGTH_ENCODING_THRESHOLD && !isUsingQP) {
+ // To encode "long lines" use a CTE that will transmit shorter lines.
+ // Switch to base64 if we are not already using "quoted printable".
+ PR_FREEIF(m_attachment1_encoding);
+ m_attachment1_encoding = PL_strdup (ENCODING_BASE64);
+ }
+ }
+ PR_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.
+ delete mainbody;
+
+ // No matter what, maincontainer points to the outermost related part.
+ maincontainer = m_related_part;
+ maincontainerISrelatedpart = true;
+
+ mainbody = m_related_part->GetChild(0);
+ 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. Shove it in.
+ //
+ plainpart = new nsMsgSendPart(this, mCompFields->GetCharacterSet());
+ if (!plainpart)
+ goto FAILMEM;
+ status = plainpart->SetType(TEXT_PLAIN);
+ if (NS_FAILED(status))
+ goto FAIL;
+ status = plainpart->SetFile(m_plaintext->mTmpFile);
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ m_plaintext->mMainBody = true;
+
+ // Determine Content-Transfer-Encoding for the attachments.
+ m_plaintext->PickEncoding(mCompFields->GetCharacterSet(), this);
+ const char *charset = mCompFields->GetCharacterSet();
+ hdrs = mime_generate_attachment_headers(m_plaintext->m_type.get(),
+ nullptr,
+ m_plaintext->m_encoding.get(),
+ m_plaintext->m_description.get(),
+ m_plaintext->m_xMacType.get(),
+ m_plaintext->m_xMacCreator.get(),
+ nullptr, 0,
+ m_digest_p,
+ m_plaintext,
+ charset,
+ charset,
+ body_is_us_ascii,
+ nullptr,
+ true);
+ if (!hdrs)
+ goto FAILMEM;
+ status = plainpart->SetOtherHeaders(hdrs);
+ PR_Free(hdrs);
+ hdrs = nullptr;
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ if (mCompFields->GetUseMultipartAlternative())
+ {
+ nsMsgSendPart* htmlpart = maincontainer;
+ maincontainer = new nsMsgSendPart(this);
+ if (!maincontainer)
+ goto FAILMEM;
+
+ // Setup the maincontainer stuff...
+ status = maincontainer->SetType(MULTIPART_ALTERNATIVE);
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ status = maincontainer->AddChild(plainpart);
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ status = maincontainer->AddChild(htmlpart);
+ if (NS_FAILED(status))
+ 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 nsMsgSendPart, then reshuffle the parts. Sigh.)
+ if (m_plaintext->m_encoding.LowerCaseEqualsLiteral(ENCODING_QUOTED_PRINTABLE))
+ {
+ plainpart->SetEncoder(MimeEncoder::GetQPEncoder(
+ mime_encoder_output_fn, this));
+ }
+ else if (m_plaintext->m_encoding.LowerCaseEqualsLiteral(ENCODING_BASE64))
+ {
+ plainpart->SetEncoder(MimeEncoder::GetBase64Encoder(
+ mime_encoder_output_fn, this));
+ }
+ }
+ else
+ {
+ delete maincontainer;
+ if (maincontainerISrelatedpart)
+ m_related_part = nullptr; // in that case, m_related_part == maincontainer which we have just deleted!
+ maincontainer = plainpart;
+ mainbody = maincontainer;
+ PR_FREEIF(m_attachment1_type);
+ m_attachment1_type = PL_strdup(TEXT_PLAIN);
+ if (!m_attachment1_type)
+ goto FAILMEM;
+
+ // Override attachment1_encoding here. We do this blindly since we are
+ // sending plaintext only at this point.
+ PR_FREEIF(m_attachment1_encoding);
+ m_attachment1_encoding = ToNewCString(m_plaintext->m_encoding);
+ }
+ }
+
+ // check if we need to encapsulate the message in a multipart/mixed or multipart/digest
+ if (m_attachment_count > multipartRelatedCount)
+ {
+ toppart = new nsMsgSendPart(this);
+ if (!toppart)
+ goto FAILMEM;
+
+ status = toppart->SetType(m_digest_p ? MULTIPART_DIGEST : MULTIPART_MIXED);
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ status = toppart->AddChild(maincontainer);
+ if (NS_FAILED(status))
+ goto FAIL;
+ }
+ else
+ toppart = maincontainer;
+
+ // Is the top part a multipart container?
+ // can't use m_attachment_count because it's not reliable for that
+ // instead use type of main part. See bug #174396
+ toppart_type = toppart->GetType(); // GetType return directly the member variable, don't free it!
+ if (!m_crypto_closure && toppart_type && !PL_strncasecmp(toppart_type, "multipart/", 10))
+ {
+ status = toppart->SetBuffer(MIME_MULTIPART_BLURB);
+ if (NS_FAILED(status))
+ goto FAIL;
+ }
+
+ {
+ nsCOMPtr<msgIWritableStructuredHeaders> outputHeaders =
+ do_CreateInstance(NS_ISTRUCTUREDHEADERS_CONTRACTID);
+ status = mime_generate_headers(mCompFields, m_deliver_mode, outputHeaders);
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ // Convert the blocks of headers into a single string for emission.
+ nsAutoCString headers;
+ outputHeaders->BuildMimeText(headers);
+
+ // 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.
+ if (plainpart && plainpart == toppart)
+ status = toppart->AppendOtherHeaders(headers.get());
+ else
+ status = toppart->SetOtherHeaders(headers.get());
+ }
+
+ if (NS_FAILED(status))
+ 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.
+
+ // Set up encoder for the first part (message body.)
+ //
+ NS_ASSERTION(!m_attachment1_encoder, "not-null m_attachment1_encoder");
+ if (!PL_strcasecmp(m_attachment1_encoding, ENCODING_BASE64))
+ {
+ m_attachment1_encoder = MimeEncoder::GetBase64Encoder(
+ mime_encoder_output_fn, this);
+ }
+ else if (!PL_strcasecmp(m_attachment1_encoding, ENCODING_QUOTED_PRINTABLE))
+ {
+ m_attachment1_encoder = MimeEncoder::GetQPEncoder(mime_encoder_output_fn,
+ this);
+ }
+
+ // 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))
+ {
+ const char *charset = mCompFields->GetCharacterSet();
+ hdrs = mime_generate_attachment_headers (m_attachment1_type,
+ nullptr,
+ m_attachment1_encoding,
+ 0, 0, 0, 0, 0,
+ m_digest_p,
+ nullptr, /* no "ma"! */
+ charset,
+ charset,
+ mCompFields->GetBodyIsAsciiOnly(),
+ nullptr,
+ true);
+ if (!hdrs)
+ goto FAILMEM;
+ status = mainbody->AppendOtherHeaders(hdrs);
+ if (NS_FAILED(status))
+ goto FAIL;
+ }
+
+ PR_FREEIF(hdrs);
+
+ mainbody->SetEncoder(m_attachment1_encoder.forget());
+
+ //
+ // Now we need to process attachments and slot them in the
+ // correct hierarchy.
+ //
+ if (m_attachment_count > 0)
+ {
+ // Kludge to avoid having to allocate memory on the toy computers...
+ if (! mime_mailto_stream_read_buffer)
+ mime_mailto_stream_read_buffer = (char *) PR_Malloc (MIME_BUFFER_SIZE);
+ buffer = mime_mailto_stream_read_buffer;
+ if (! buffer)
+ goto FAILMEM;
+
+ // Gather all of the attachments for this message that are NOT
+ // part of an enclosed MHTML message!
+ for (i = 0; i < m_attachment_count; i++)
+ {
+ nsMsgAttachmentHandler *ma = m_attachments[i];
+ if (!ma->mMHTMLPart)
+ PreProcessPart(ma, toppart);
+ }
+
+ //
+ // If we have a m_related_part as a container for children, then we have to
+ // tack on these children for the part
+ //
+ if (m_related_part)
+ {
+ for (i = 0; i < m_attachment_count; i++)
+ {
+ //
+ // look for earlier part with the same content id. If we find it,
+ // need to remember the mapping between our node index and the
+ // part num of the earlier part.
+ int32_t nodeIndex = m_attachments[i]->mNodeIndex;
+ if (nodeIndex != -1)
+ {
+ for (uint32_t j = 0; j < i; j++)
+ {
+ if (m_attachments[j]->mNodeIndex != -1 &&
+ m_attachments[j]->m_contentId.Equals(m_attachments[i]->m_contentId))
+ m_partNumbers[nodeIndex] = m_partNumbers[m_attachments[j]->mNodeIndex];
+ }
+ }
+ // rhp: This is here because we could get here after saying OK
+ // to a lot of prompts about not being able to fetch this part!
+ //
+ if (m_attachments[i]->mPartUserOmissionOverride)
+ continue;
+
+ // Now, we need to add this part to the m_related_part member so the
+ // message will be generated correctly.
+ if (m_attachments[i]->mMHTMLPart)
+ PreProcessPart(m_attachments[i], m_related_part);
+ }
+ }
+
+ }
+
+ // Tell the user we are creating the message...
+ mComposeBundle->GetStringFromName(u"creatingMailMessage",
+ getter_Copies(msg));
+ SetStatusMessage( msg );
+
+ // OK, now actually write the structure we've carefully built up.
+ status = toppart->Write();
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ /* Close down encryption stream */
+ if (m_crypto_closure)
+ {
+ status = m_crypto_closure->FinishCryptoEncapsulation(false, mSendReport);
+ m_crypto_closure = nullptr;
+ if (NS_FAILED(status)) goto FAIL;
+ }
+
+ if (mOutputFile)
+ {
+ if (NS_FAILED(mOutputFile->Flush()))
+ {
+ status = NS_MSG_ERROR_WRITING_FILE;
+ goto FAIL;
+ }
+
+ mOutputFile->Close();
+ mOutputFile = nullptr;
+
+ // mTempFile is stale because we wrote to it. Get another copy to refresh.
+ nsCOMPtr<nsIFile> tempFileCopy;
+ mTempFile->Clone(getter_AddRefs(tempFileCopy));
+ mTempFile = tempFileCopy;
+ tempFileCopy = nullptr;
+ /* If we don't do this check...ZERO length files can be sent */
+ int64_t fileSize;
+ rv = mTempFile->GetFileSize(&fileSize);
+ if (NS_FAILED(rv) || fileSize == 0)
+ {
+ status = NS_MSG_ERROR_WRITING_FILE;
+ goto FAIL;
+ }
+ }
+
+ mComposeBundle->GetStringFromName(u"assemblingMessageDone",
+ getter_Copies(msg));
+ SetStatusMessage(msg);
+
+ if (m_dont_deliver_p && mListener)
+ {
+ //
+ // Need to ditch the file spec here so that we don't delete the
+ // file, since in this case, the caller wants the file
+ //
+ mReturnFile = mTempFile;
+ mTempFile = nullptr;
+ if (!mReturnFile)
+ NotifyListenerOnStopSending(nullptr, NS_ERROR_OUT_OF_MEMORY, nullptr, nullptr);
+ else
+ {
+ NotifyListenerOnStopSending(nullptr, NS_OK, nullptr, mReturnFile);
+ }
+ }
+ else
+ {
+ status = DeliverMessage();
+ if (NS_SUCCEEDED(status))
+ shouldDeleteDeliveryState = false;
+ }
+ goto FAIL;
+
+FAILMEM:
+ status = NS_ERROR_OUT_OF_MEMORY;
+
+FAIL:
+ if (toppart)
+ delete toppart;
+ toppart = nullptr;
+ mainbody = nullptr;
+ maincontainer = nullptr;
+
+ if (in_file)
+ {
+ PR_Close (in_file);
+ in_file = nullptr;
+ }
+
+ if (shouldDeleteDeliveryState)
+ {
+ if (NS_FAILED(status))
+ {
+ m_status = status;
+ nsresult ignoreMe;
+ Fail(status, nullptr, &ignoreMe);
+ }
+ }
+
+ return status;
+}
+
+int32_t
+nsMsgComposeAndSend::PreProcessPart(nsMsgAttachmentHandler *ma,
+ nsMsgSendPart *toppart) // The very top most container of the message
+{
+ nsresult status;
+ char *hdrs = 0;
+ nsMsgSendPart *part = nullptr;
+
+ // If this was one of those dead parts from a quoted web page,
+ // then just return safely.
+ //
+ if (ma->m_bogus_attachment)
+ return 0;
+
+ // If at this point we *still* don't have a content-type, then
+ // we're never going to get one.
+ if (ma->m_type.IsEmpty())
+ ma->m_type = UNKNOWN_CONTENT_TYPE;
+
+ ma->PickEncoding(mCompFields->GetCharacterSet(), this);
+ ma->PickCharset();
+
+ part = new nsMsgSendPart(this);
+ if (!part)
+ return 0;
+ status = toppart->AddChild(part);
+ // Remember the part number if it has a node index.
+ if (ma->mNodeIndex != -1)
+ m_partNumbers[ma->mNodeIndex] = part->m_partNum;
+
+ if (NS_FAILED(status))
+ return 0;
+ status = part->SetType(ma->m_type.get());
+ if (NS_FAILED(status))
+ return 0;
+
+ if (ma->mSendViaCloud)
+ ma->m_encoding = ENCODING_7BIT;
+
+ nsCString turl;
+ if (!ma->mURL)
+ {
+ if (!ma->m_uri.IsEmpty())
+ turl = ma->m_uri;
+ }
+ else {
+ status = ma->mURL->GetSpec(turl);
+ if (NS_FAILED(status))
+ return 0;
+ }
+
+ nsCString type(ma->m_type);
+ nsCString realName(ma->m_realName);
+
+ // for cloud attachments, make the part an html part with no name,
+ // so we don't show it as an attachment.
+ if (ma->mSendViaCloud)
+ {
+ type.Assign("application/octet-stream");
+ realName.Truncate();
+ }
+ hdrs = mime_generate_attachment_headers (type.get(),
+ ma->m_typeParam.get(),
+ ma->m_encoding.get(),
+ ma->m_description.get(),
+ ma->m_xMacType.get(),
+ ma->m_xMacCreator.get(),
+ realName.get(),
+ turl.get(),
+ m_digest_p,
+ ma,
+ ma->m_charset.get(), // rhp - this needs
+ // to be the charset
+ // we determine from
+ // the file or none
+ // at all!
+ mCompFields->GetCharacterSet(),
+ false, // bodyIsAsciiOnly to false
+ // for attachments
+ ma->m_contentId.get(),
+ false);
+ if (!hdrs)
+ return 0;
+
+ status = part->SetOtherHeaders(hdrs);
+ PR_FREEIF(hdrs);
+ if (ma->mSendViaCloud)
+ {
+ nsCString urlSpec;
+ status = ma->mURL->GetSpec(urlSpec);
+ if (NS_FAILED(status))
+ return 0;
+
+ // Need to add some headers so that libmime can restore the cloud info
+ // when loading a draft message.
+ nsCString draftInfo(HEADER_X_MOZILLA_CLOUD_PART": cloudFile; url=");
+ draftInfo.Append(ma->mCloudUrl.get());
+ // don't leak user file paths or account keys to recipients.
+ if (m_deliver_mode == nsMsgSaveAsDraft)
+ {
+ draftInfo.Append("; provider=");
+ draftInfo.Append(ma->mCloudProviderKey.get());
+ draftInfo.Append("; file=");
+ draftInfo.Append(urlSpec.get());
+ }
+ draftInfo.Append("; name=");
+ draftInfo.Append(ma->m_realName.get());
+ draftInfo.Append(CRLF);
+ part->AppendOtherHeaders(draftInfo.get());
+ part->SetType("application/octet-stream");
+ part->SetBuffer("");
+ }
+ if (NS_FAILED(status))
+ return 0;
+ status = part->SetFile(ma->mTmpFile);
+ if (NS_FAILED(status))
+ return 0;
+ if (ma->m_encoder)
+ {
+ part->SetEncoder(ma->m_encoder.forget());
+ }
+
+ ma->m_current_column = 0;
+
+ if (ma->m_type.LowerCaseEqualsLiteral(MESSAGE_RFC822) ||
+ ma->m_type.LowerCaseEqualsLiteral(MESSAGE_NEWS)) {
+ part->SetStripSensitiveHeaders(true);
+ }
+
+ return 1;
+}
+
+# define FROB(X) \
+ if (X && *X) \
+ { \
+ if (*recipients) PL_strcat(recipients, ","); \
+ PL_strcat(recipients, X); \
+ }
+
+nsresult nsMsgComposeAndSend::BeginCryptoEncapsulation ()
+{
+ // Try to create a secure compose object. If we can create it, then query to see
+ // if we need to use it for this send transaction.
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgComposeSecure> secureCompose;
+ secureCompose = do_CreateInstance(NS_MSGCOMPOSESECURE_CONTRACTID, &rv);
+ // it's not an error scenario of there is secure compose
+ if (NS_FAILED(rv))
+ return NS_OK;
+
+ if (secureCompose)
+ {
+ bool requiresEncryptionWork = false;
+ secureCompose->RequiresCryptoEncapsulation(mUserIdentity, mCompFields, &requiresEncryptionWork);
+ if (requiresEncryptionWork)
+ {
+ m_crypto_closure = secureCompose;
+ // bah i'd like to move the following blurb into the implementation of BeginCryptoEncapsulation; however
+ // the apis for nsIMsgComposeField just aren't rich enough. It requires the implementor to jump through way
+ // too many string conversions....
+ char * recipients = (char *)
+ PR_MALLOC((mCompFields->GetTo() ? strlen(mCompFields->GetTo()) : 0) +
+ (mCompFields->GetCc() ? strlen(mCompFields->GetCc()) : 0) +
+ (mCompFields->GetBcc() ? strlen(mCompFields->GetBcc()) : 0) +
+ (mCompFields->GetNewsgroups() ? strlen(mCompFields->GetNewsgroups()) : 0) + 20);
+ if (!recipients) return NS_ERROR_OUT_OF_MEMORY;
+
+ *recipients = 0;
+
+ FROB(mCompFields->GetTo())
+ FROB(mCompFields->GetCc())
+ FROB(mCompFields->GetBcc())
+ FROB(mCompFields->GetNewsgroups())
+
+ // end section of code I'd like to move to the implementor.....
+ rv = m_crypto_closure->BeginCryptoEncapsulation(mOutputFile,
+ recipients,
+ mCompFields,
+ mUserIdentity,
+ mSendReport,
+ (m_deliver_mode == nsMsgSaveAsDraft));
+
+ PR_FREEIF(recipients);
+ }
+
+ }
+
+ return rv;
+}
+
+nsresult
+mime_write_message_body(nsIMsgSend *state, const char *buf, uint32_t size)
+{
+ NS_ENSURE_ARG_POINTER(state);
+
+ nsCOMPtr<nsIOutputStream> output;
+ nsCOMPtr<nsIMsgComposeSecure> crypto_closure;
+
+ state->GetOutputStream(getter_AddRefs(output));
+ if (!output)
+ return NS_MSG_ERROR_WRITING_FILE;
+
+ state->GetCryptoclosure(getter_AddRefs(crypto_closure));
+ if (crypto_closure)
+ {
+ // Copy to new null-terminated string so JS glue doesn't crash when
+ // MimeCryptoWriteBlock() is implemented in JS.
+ nsCString bufWithNull;
+ bufWithNull.Assign(buf, size);
+ return crypto_closure->MimeCryptoWriteBlock(bufWithNull.get(), size);
+ }
+
+ uint32_t n;
+ nsresult rv = output->Write(buf, size, &n);
+ if (NS_FAILED(rv) || n != size)
+ {
+ return NS_MSG_ERROR_WRITING_FILE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+mime_encoder_output_fn(const char *buf, int32_t size, void *closure)
+{
+ nsMsgComposeAndSend *state = (nsMsgComposeAndSend *) closure;
+ return mime_write_message_body (state, (char *) buf, (uint32_t)size);
+}
+
+nsresult
+nsMsgComposeAndSend::GetEmbeddedObjectInfo(nsIDOMNode *node, nsMsgAttachmentData *attachment, bool *acceptObject)
+{
+ NS_ENSURE_ARG_POINTER(node);
+ NS_ENSURE_ARG_POINTER(attachment);
+ NS_ENSURE_ARG_POINTER(acceptObject);
+
+ // GetEmbeddedObjectInfo will determine if we need to attach the source of the
+ // embedded object with the message. The decision is made automatically unless
+ // the attribute moz-do-not-send has been set to true or false.
+ nsresult rv = NS_OK;
+
+ // Reset this structure to null!
+ *acceptObject = false;
+
+ // We're only interested in body, image, link and anchors which are all
+ // elements.
+ nsCOMPtr<nsIDOMElement> domElement = do_QueryInterface(node);
+ if (!domElement)
+ return NS_OK;
+
+ bool isImage = false;
+ nsAutoString mozDoNotSendAttr;
+ domElement->GetAttribute(NS_LITERAL_STRING(ATTR_MOZ_DO_NOT_SEND), mozDoNotSendAttr);
+
+ // Only empty or moz-do-not-send="false" may be accepted later.
+ if (!(mozDoNotSendAttr.IsEmpty() || mozDoNotSendAttr.LowerCaseEqualsLiteral("false")))
+ return NS_OK;
+
+ // Now, we know the types of objects this node can be, so we will do
+ // our query interface here and see what we come up with
+ nsCOMPtr<nsIDOMHTMLBodyElement> body = (do_QueryInterface(node));
+ // XXX convert to use nsIImageLoadingContent?
+ nsCOMPtr<nsIDOMHTMLImageElement> image = (do_QueryInterface(node));
+ nsCOMPtr<nsIDOMHTMLLinkElement> link = (do_QueryInterface(node));
+ nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = (do_QueryInterface(node));
+
+ // First, try to see if the body as a background image
+ if (body)
+ {
+ nsAutoString tUrl;
+ if (NS_SUCCEEDED(body->GetBackground(tUrl)))
+ {
+ nsAutoCString turlC;
+ CopyUTF16toUTF8(tUrl, turlC);
+ if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), turlC.get())))
+ return NS_OK;
+ }
+ isImage = true;
+ }
+ else if (image) // Is this an image?
+ {
+ nsString tUrl;
+ nsString tName;
+ nsString tDesc;
+
+ // Create the URI
+ if (NS_FAILED(image->GetSrc(tUrl)))
+ return NS_ERROR_FAILURE;
+ if (tUrl.IsEmpty())
+ return NS_OK;
+
+ nsAutoCString turlC;
+ CopyUTF16toUTF8(tUrl, turlC);
+ if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), turlC.get())))
+ {
+ // Well, the first time failed...which means we probably didn't get
+ // the full path name...
+ //
+ nsIDOMDocument *ownerDocument = nullptr;
+ node->GetOwnerDocument(&ownerDocument);
+ if (ownerDocument)
+ {
+ nsIDocument *doc = nullptr;
+ if (NS_FAILED(ownerDocument->QueryInterface(NS_GET_IID(nsIDocument),(void**)&doc)) || !doc)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsAutoCString spec;
+ nsIURI *uri = doc->GetDocumentURI();
+
+ if (!uri)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = uri->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Ok, now get the path to the root doc and tack on the name we
+ // got from the GetSrc() call....
+ NS_ConvertUTF8toUTF16 workURL(spec);
+
+ int32_t loc = workURL.RFindChar('/');
+ if (loc >= 0)
+ workURL.SetLength(loc+1);
+ workURL.Append(tUrl);
+ NS_ConvertUTF16toUTF8 workurlC(workURL);
+ if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), workurlC.get())))
+ return NS_OK; // Continue and send it without this image.
+ }
+ }
+ isImage = true;
+
+ rv = image->GetName(tName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ LossyCopyUTF16toASCII(tName, attachment->m_realName);
+ rv = image->GetLongDesc(tDesc);
+ NS_ENSURE_SUCCESS(rv, rv);
+ attachment->m_description = NS_LossyConvertUTF16toASCII(tDesc); // XXX i18n
+ }
+ else if (link) // Is this a link?
+ {
+ nsString tUrl;
+
+ // Create the URI
+ rv = link->GetHref(tUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (tUrl.IsEmpty())
+ return NS_OK;
+ nsAutoCString turlC;
+ CopyUTF16toUTF8(tUrl, turlC);
+ if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), turlC.get())))
+ return NS_OK;
+ }
+ else if (anchor)
+ {
+ nsString tUrl;
+ nsString tName;
+
+ // Create the URI
+ rv = anchor->GetHref(tUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (tUrl.IsEmpty())
+ return NS_OK;
+ nsAutoCString turlC;
+ CopyUTF16toUTF8(tUrl, turlC);
+ // This can fail since the URL might not be recognised, for example:
+ // <a href="skype:some-name?call" title="Skype">Some Name</a>
+ if (NS_FAILED(nsMsgNewURL(getter_AddRefs(attachment->m_url), turlC.get())))
+ return NS_OK;
+ rv = anchor->GetName(tName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ LossyCopyUTF16toASCII(tName, attachment->m_realName);
+ }
+ else
+ {
+ // If we get here, we got something we didn't expect!
+ // Just try to continue and send it without this thing.
+ return NS_OK;
+ }
+
+ // Before going further, check what scheme we're dealing with. Files need to
+ // be converted to data URLs during composition. "Attaching" means
+ // sending as a cid: part instead of original URL.
+ bool isHttp =
+ (NS_SUCCEEDED(attachment->m_url->SchemeIs("http", &isHttp)) && isHttp) ||
+ (NS_SUCCEEDED(attachment->m_url->SchemeIs("https", &isHttp)) && isHttp);
+ // Attach (= create cid: part) http resources if moz-do-not-send is set to
+ // "false". Special processing for images: We attach if the preference says so.
+ // Note that moz-do-not-send="true" is already processed above so the preference
+ // doesn't override this.
+ if (isHttp)
+ {
+ *acceptObject =
+ (isImage && Preferences::GetBool("mail.compose.attach_http_images", false)) ||
+ mozDoNotSendAttr.LowerCaseEqualsLiteral("false");
+ return NS_OK;
+ }
+
+ bool isData =
+ (NS_SUCCEEDED(attachment->m_url->SchemeIs("data", &isData)) && isData);
+ bool isNews =
+ (NS_SUCCEEDED(attachment->m_url->SchemeIs("news", &isNews)) && isNews) ||
+ (NS_SUCCEEDED(attachment->m_url->SchemeIs("snews", &isNews)) && isNews) ||
+ (NS_SUCCEEDED(attachment->m_url->SchemeIs("nntp", &isNews)) && isNews);
+ // Attach (= create cid: part) data resources if moz-do-not-send is not
+ // specified or set to "false".
+ if (isData || isNews)
+ {
+ *acceptObject = mozDoNotSendAttr.IsEmpty() ||
+ mozDoNotSendAttr.LowerCaseEqualsLiteral("false");
+ return NS_OK;
+ }
+
+ return NS_OK;
+}
+
+
+uint32_t
+nsMsgComposeAndSend::GetMultipartRelatedCount(bool forceToBeCalculated /*=false*/)
+{
+ nsresult rv = NS_OK;
+ uint32_t count;
+
+ if (mMultipartRelatedAttachmentCount != -1 && !forceToBeCalculated)
+ return (uint32_t)mMultipartRelatedAttachmentCount;
+
+ //First time here, let's calculate the correct number of related part we need to generate
+ mMultipartRelatedAttachmentCount = 0;
+ if (mEditor)
+ {
+ nsCOMPtr<nsIEditorMailSupport> mailEditor (do_QueryInterface(mEditor));
+ if (!mailEditor)
+ return 0;
+
+ rv = mailEditor->GetEmbeddedObjects(getter_AddRefs(mEmbeddedObjectList));
+ if (NS_FAILED(rv))
+ return 0;
+ }
+ if (!mEmbeddedObjectList)
+ return 0;
+
+ if (NS_SUCCEEDED(mEmbeddedObjectList->GetLength(&count)))
+ {
+ if (count > 0)
+ {
+ // preallocate space for part numbers
+ m_partNumbers.SetLength(count);
+ // Let parse the list to count the number of valid objects. BTW, we can remove the others from the list
+ RefPtr<nsMsgAttachmentData> attachment(new nsMsgAttachmentData);
+
+ int32_t i;
+ nsCOMPtr<nsIDOMNode> node;
+
+ for (i = count - 1, count = 0; i >= 0; i --)
+ {
+ // Reset this structure to null!
+
+ // now we need to get the element in the array and do the magic
+ // to process this element.
+ //
+ node = do_QueryElementAt(mEmbeddedObjectList, i, &rv);
+ bool acceptObject = false;
+ if (node)
+ {
+ rv = GetEmbeddedObjectInfo(node, attachment, &acceptObject);
+ }
+ else // outlook import case
+ {
+ nsCOMPtr<nsIMsgEmbeddedImageData> imageData =
+ do_QueryElementAt(mEmbeddedObjectList, i, &rv);
+ if (!imageData)
+ continue;
+ acceptObject = true;
+ }
+ if (NS_SUCCEEDED(rv) && acceptObject)
+ count ++;
+ }
+ }
+ mMultipartRelatedAttachmentCount = (int32_t)count;
+ return count;
+ }
+ else
+ return 0;
+}
+
+nsresult
+nsMsgComposeAndSend::GetBodyFromEditor()
+{
+ //
+ // Now we have to fix up and get the HTML from the editor. After we
+ // get the HTML data, we need to store it in the m_attachment_1_body
+ // member variable after doing the necessary charset conversion.
+ //
+
+ //
+ // Query the editor, get the body of HTML!
+ //
+ uint32_t flags = nsIDocumentEncoder::OutputFormatted |
+ nsIDocumentEncoder::OutputNoFormattingInPre |
+ nsIDocumentEncoder::OutputDisallowLineBreaking;
+ nsAutoString bodyStr;
+ char16_t* bodyText = nullptr;
+ nsresult rv;
+ char16_t *origHTMLBody = nullptr;
+
+ // Ok, get the body...the DOM should have been whacked with
+ // Content ID's already
+ if (mEditor)
+ mEditor->OutputToString(NS_LITERAL_STRING(TEXT_HTML), flags, bodyStr);
+ else
+ bodyStr = NS_ConvertASCIItoUTF16(m_attachment1_body);
+
+ // If we really didn't get a body, just return NS_OK
+ if (bodyStr.IsEmpty())
+ return NS_OK;
+ bodyText = ToNewUnicode(bodyStr);
+ if (!bodyText)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // If we are forcing this to be plain text, we should not be
+ // doing this conversion.
+ bool doConversion = true;
+
+ if ( (mCompFields) && mCompFields->GetForcePlainText() )
+ doConversion = false;
+
+ if (doConversion)
+ {
+ nsCOMPtr<mozITXTToHTMLConv> conv = do_CreateInstance(MOZ_TXTTOHTMLCONV_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ uint32_t whattodo = mozITXTToHTMLConv::kURLs;
+ bool enable_structs = false;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (pPrefBranch)
+ {
+ rv = pPrefBranch->GetBoolPref(PREF_MAIL_SEND_STRUCT, &enable_structs);
+ if (enable_structs)
+ whattodo = whattodo | mozITXTToHTMLConv::kStructPhrase;
+ }
+
+ char16_t* wresult;
+ rv = conv->ScanHTML(bodyText, whattodo, &wresult);
+ if (NS_SUCCEEDED(rv))
+ {
+ // Save the original body for possible attachment as plain text
+ // We should have what the user typed in stored in mOriginalHTMLBody
+ origHTMLBody = bodyText;
+ bodyText = wresult;
+ }
+ }
+ }
+
+ nsCString attachment1_body;
+
+ // Convert body to mail charset
+ nsCString outCString;
+ const char *aCharset = mCompFields->GetCharacterSet();
+
+ if (aCharset && *aCharset)
+ {
+ rv = nsMsgI18NConvertFromUnicode(aCharset, nsDependentString(bodyText), outCString, false, true);
+ bool isAsciiOnly = NS_IsAscii(outCString.get()) &&
+ !nsMsgI18Nstateful_charset(mCompFields->GetCharacterSet());
+ if (mCompFields->GetForceMsgEncoding())
+ isAsciiOnly = false;
+ mCompFields->SetBodyIsAsciiOnly(isAsciiOnly);
+
+ // If the body contains characters outside the current mail charset,
+ // convert to UTF-8.
+ if (NS_ERROR_UENC_NOMAPPING == rv)
+ {
+ bool needToCheckCharset;
+ mCompFields->GetNeedToCheckCharset(&needToCheckCharset);
+ if (needToCheckCharset)
+ {
+ // Just use UTF-8 and be done with it
+ // unless disable_fallback_to_utf8 is set for this charset.
+ bool disableFallback = false;
+ nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (prefBranch)
+ {
+ nsCString prefName("mailnews.disable_fallback_to_utf8.");
+ prefName.Append(aCharset);
+ prefBranch->GetBoolPref(prefName.get(), &disableFallback);
+ }
+ if (!disableFallback)
+ {
+ CopyUTF16toUTF8(nsDependentString(bodyText), outCString);
+ mCompFields->SetCharacterSet("UTF-8");
+ }
+ }
+ }
+
+ if (NS_SUCCEEDED(rv))
+ attachment1_body = outCString;
+
+ // If we have an origHTMLBody that is not null, this means that it is
+ // different than the bodyText because of formatting conversions. Because of
+ // this we need to do the charset conversion on this part separately
+ if (origHTMLBody)
+ {
+ nsCString newBody;
+ rv = nsMsgI18NConvertFromUnicode(aCharset,
+ nsDependentString(origHTMLBody), newBody, false, true);
+ if (NS_SUCCEEDED(rv))
+ {
+ mOriginalHTMLBody = ToNewCString(newBody);
+ }
+ }
+ else {
+ mOriginalHTMLBody = ToNewCString(attachment1_body);
+ }
+
+ NS_Free(bodyText); //Don't need it anymore
+ }
+ else
+ return NS_ERROR_FAILURE;
+
+ rv = SnarfAndCopyBody(attachment1_body, TEXT_HTML);
+
+ return rv;
+}
+
+//
+// This is the routine that does the magic of generating the body and the
+// attachments for the multipart/related email message.
+//
+typedef struct
+{
+ nsIDOMNode *node;
+ char *url;
+} domSaveStruct;
+
+nsresult
+nsMsgComposeAndSend::ProcessMultipartRelated(int32_t *aMailboxCount, int32_t *aNewsCount)
+{
+ uint32_t multipartCount = GetMultipartRelatedCount();
+ nsresult rv = NS_OK;
+ uint32_t i;
+ int32_t j = -1;
+ uint32_t k;
+ int32_t duplicateOf;
+ domSaveStruct *domSaveArray = nullptr;
+
+ if (!mEmbeddedObjectList)
+ return NS_ERROR_MIME_MPART_ATTACHMENT_ERROR;
+
+ RefPtr<nsMsgAttachmentData> attachment(new nsMsgAttachmentData);
+ int32_t locCount = -1;
+
+ if (multipartCount > 0)
+ {
+ domSaveArray = (domSaveStruct *)PR_MALLOC(sizeof(domSaveStruct) * multipartCount);
+ if (!domSaveArray)
+ return NS_ERROR_MIME_MPART_ATTACHMENT_ERROR;
+ memset(domSaveArray, 0, sizeof(domSaveStruct) * multipartCount);
+ }
+
+ nsCOMPtr<nsIDOMNode> node;
+ for (i = mPreloadedAttachmentCount; i < (mPreloadedAttachmentCount + multipartCount);)
+ {
+ // Ok, now we need to get the element in the array and do the magic
+ // to process this element.
+ //
+
+ locCount++;
+ mEmbeddedObjectList->QueryElementAt(locCount, NS_GET_IID(nsIDOMNode), getter_AddRefs(node));
+ if (node)
+ {
+ bool acceptObject = false;
+ rv = GetEmbeddedObjectInfo(node, attachment, &acceptObject);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_MIME_MPART_ATTACHMENT_ERROR);
+ if (!acceptObject)
+ continue;
+ nsString nodeValue;
+ node->GetNodeValue(nodeValue);
+ LossyCopyUTF16toASCII(nodeValue, m_attachments[i]->m_contentId);
+ }
+ else
+ {
+ nsCOMPtr<nsIMsgEmbeddedImageData> imageData = do_QueryElementAt(mEmbeddedObjectList, locCount, &rv);
+ if (!imageData)
+ return NS_ERROR_MIME_MPART_ATTACHMENT_ERROR;
+ imageData->GetUri(getter_AddRefs(attachment->m_url));
+ if (!attachment->m_url)
+ return NS_ERROR_MIME_MPART_ATTACHMENT_ERROR;
+ imageData->GetCid(m_attachments[i]->m_contentId);
+ imageData->GetName(attachment->m_realName);
+ }
+
+
+ // MUST set this to get placed in the correct part of the message
+ m_attachments[i]->mMHTMLPart = true;
+
+ m_attachments[i]->mDeleteFile = true;
+ m_attachments[i]->m_done = false;
+ m_attachments[i]->SetMimeDeliveryState(this);
+ m_attachments[i]->mNodeIndex = locCount;
+
+ j++;
+ domSaveArray[j].node = node;
+
+ // check if we have alreay attached this object, don't need to attach it twice
+ duplicateOf = -1;
+ for (k = mPreloadedAttachmentCount; k < i; k++)
+ {
+ bool isEqual = false;
+ NS_ASSERTION(attachment->m_url, "null attachment url!");
+ if (attachment->m_url)
+ (void)attachment->m_url->Equals(m_attachments[k]->mURL, &isEqual);
+ if (isEqual)
+ {
+ duplicateOf = k;
+ break;
+ }
+ }
+
+ if (duplicateOf == -1)
+ {
+ //
+ // Now we have to get all of the interesting information from
+ // the nsIDOMNode we have in hand...
+ m_attachments[i]->mURL = attachment->m_url;
+
+ m_attachments[i]->m_overrideType = attachment->m_realType;
+ m_attachments[i]->m_overrideEncoding = attachment->m_realEncoding;
+ m_attachments[i]->m_desiredType = attachment->m_desiredType;
+ m_attachments[i]->m_description = attachment->m_description;
+ m_attachments[i]->m_realName = attachment->m_realName;
+ m_attachments[i]->m_xMacType = attachment->m_xMacType;
+ m_attachments[i]->m_xMacCreator = attachment->m_xMacCreator;
+
+ m_attachments[i]->m_charset = mCompFields->GetCharacterSet();
+ m_attachments[i]->m_encoding = ENCODING_7BIT;
+
+ if (m_attachments[i]->mURL)
+ msg_pick_real_name(m_attachments[i], nullptr, mCompFields->GetCharacterSet());
+
+ if (m_attachments[i]->m_contentId.IsEmpty())
+ {
+ //
+ // Next, generate a content id for use with this part
+ //
+ nsCString email;
+ mUserIdentity->GetEmail(email);
+ m_attachments[i]->m_contentId = mime_gen_content_id(locCount+1, email.get());
+ }
+
+ //
+ // Start counting the attachments which are going to come from mail folders
+ // and from NNTP servers.
+ //
+ if (m_attachments[i]->mURL)
+ {
+ nsIURI *uri = m_attachments[i]->mURL;
+ bool match = false;
+ if ((NS_SUCCEEDED(uri->SchemeIs("mailbox", &match)) && match) ||
+ (NS_SUCCEEDED(uri->SchemeIs("imap", &match)) && match))
+ (*aMailboxCount)++;
+ else if ((NS_SUCCEEDED(uri->SchemeIs("news", &match)) && match) ||
+ (NS_SUCCEEDED(uri->SchemeIs("snews", &match)) && match))
+ (*aNewsCount)++;
+ else
+ {
+ // Additional account types need a mechanism to report that they are
+ // message protocols. If there is an nsIMsgProtocolInfo component
+ // registered for this scheme, we'll consider it a mailbox
+ // attachment.
+ nsAutoCString contractID;
+ contractID.Assign(
+ NS_LITERAL_CSTRING("@mozilla.org/messenger/protocol/info;1"));
+ nsAutoCString scheme;
+ uri->GetScheme(scheme);
+ contractID.Append(scheme);
+ nsCOMPtr<nsIMsgProtocolInfo> msgProtocolInfo =
+ do_CreateInstance(contractID.get());
+ if (msgProtocolInfo)
+ (*aMailboxCount)++;
+ }
+
+ }
+ }
+ else
+ {
+ m_attachments[i]->m_contentId = m_attachments[duplicateOf]->m_contentId;
+ m_attachments[i]->SetMimeDeliveryState(nullptr);
+ }
+
+ //
+ // Ok, while we are here, we should whack the DOM with the generated
+ // Content-ID for this object. This will be necessary for generating
+ // the HTML we need.
+ //
+ nsString domURL;
+ if (!m_attachments[duplicateOf == -1 ? i : duplicateOf]->m_contentId.IsEmpty())
+ {
+ nsString newSpec(NS_LITERAL_STRING("cid:"));
+ newSpec.AppendASCII(m_attachments[duplicateOf == -1 ? i : duplicateOf]->m_contentId.get());
+
+ // Now, we know the types of objects this node can be, so we will do
+ // our query interface here and see what we come up with
+ nsCOMPtr<nsIDOMHTMLBodyElement> body = (do_QueryInterface(domSaveArray[j].node));
+ nsCOMPtr<nsIDOMHTMLImageElement> image = (do_QueryInterface(domSaveArray[j].node));
+ nsCOMPtr<nsIDOMHTMLLinkElement> link = (do_QueryInterface(domSaveArray[j].node));
+ nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = (do_QueryInterface(domSaveArray[j].node));
+
+ if (anchor)
+ {
+ anchor->GetHref(domURL);
+ anchor->SetHref(newSpec);
+ }
+ else if (link)
+ {
+ link->GetHref(domURL);
+ link->SetHref(newSpec);
+ }
+ else if (image)
+ {
+ image->GetSrc(domURL);
+ image->SetSrc(newSpec);
+ }
+ else if (body)
+ {
+ body->GetBackground(domURL);
+ body->SetBackground(newSpec);
+ }
+
+ if (!domURL.IsEmpty())
+ domSaveArray[j].url = ToNewCString(NS_LossyConvertUTF16toASCII(domURL));
+ }
+ i++;
+ }
+
+ rv = GetBodyFromEditor();
+
+ //
+ // Ok, now we need to un-whack the DOM or we have a screwed up document on
+ // Send failure.
+ //
+ for (i = 0; i < multipartCount; i++)
+ {
+ if ( (!domSaveArray[i].node) || (!domSaveArray[i].url) )
+ continue;
+
+ // Now, we know the types of objects this node can be, so we will do
+ // our query interface here and see what we come up with
+ nsCOMPtr<nsIDOMHTMLBodyElement> body = (do_QueryInterface(domSaveArray[i].node));
+ nsCOMPtr<nsIDOMHTMLImageElement> image = (do_QueryInterface(domSaveArray[i].node));
+ nsCOMPtr<nsIDOMHTMLLinkElement> link = (do_QueryInterface(domSaveArray[i].node));
+ nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = (do_QueryInterface(domSaveArray[i].node));
+
+ // STRING USE WARNING: hoisting the following conversion might save code-space, since it happens along every path
+
+ if (anchor)
+ anchor->SetHref(NS_ConvertASCIItoUTF16(domSaveArray[i].url));
+ else if (link)
+ link->SetHref(NS_ConvertASCIItoUTF16(domSaveArray[i].url));
+ else if (image)
+ image->SetSrc(NS_ConvertASCIItoUTF16(domSaveArray[i].url));
+ else if (body)
+ body->SetBackground(NS_ConvertASCIItoUTF16(domSaveArray[i].url));
+
+ free(domSaveArray[i].url);
+ }
+
+ PR_FREEIF(domSaveArray);
+
+ //
+ // Now, we have to create that first child node for the multipart
+ // message that holds the body as well as the attachment handler
+ // for this body part.
+ //
+ // If we ONLY have multipart objects, then we don't need the container
+ // for the multipart section...
+ //
+ m_related_part = new nsMsgSendPart(this);
+ if (!m_related_part)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ m_related_part->SetMimeDeliveryState(this);
+ m_related_part->SetType(MULTIPART_RELATED);
+ // We are now going to use the m_related_part as a way to store the
+ // MHTML message for this email.
+ //
+ m_related_body_part = new nsMsgSendPart(this);
+ if (!m_related_body_part)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Set the body contents...
+ m_related_body_part->SetBuffer(m_attachment1_body);
+ m_related_body_part->SetType(m_attachment1_type);
+
+ m_related_part->AddChild(m_related_body_part);
+
+ return rv;
+}
+
+nsresult
+nsMsgComposeAndSend::CountCompFieldAttachments()
+{
+ //Reset the counters
+ mCompFieldLocalAttachments = 0;
+ mCompFieldRemoteAttachments = 0;
+
+ //Get the attachments array
+ nsCOMPtr<nsISimpleEnumerator> attachments;
+ mCompFields->GetAttachments(getter_AddRefs(attachments));
+ if (!attachments)
+ return NS_OK;
+
+ nsresult rv;
+
+ // Parse the attachments array
+ bool moreAttachments;
+ nsCString url;
+ nsCOMPtr<nsISupports> element;
+ while (NS_SUCCEEDED(attachments->HasMoreElements(&moreAttachments)) && moreAttachments) {
+ rv = attachments->GetNext(getter_AddRefs(element));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAttachment> attachment = do_QueryInterface(element, &rv);
+ if (NS_SUCCEEDED(rv) && attachment)
+ {
+ attachment->GetUrl(url);
+ if (!url.IsEmpty())
+ {
+ // Check to see if this is a file URL, if so, don't retrieve
+ // like a remote URL...
+ if (nsMsgIsLocalFile(url.get()))
+ mCompFieldLocalAttachments++;
+ else // This is a remote URL...
+ mCompFieldRemoteAttachments++;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+//
+// Since we are at the head of the list, we start from ZERO.
+//
+nsresult
+nsMsgComposeAndSend::AddCompFieldLocalAttachments()
+{
+ // If none, just return...
+ if (mCompFieldLocalAttachments <= 0)
+ return NS_OK;
+
+ //Get the attachments array
+ nsCOMPtr<nsISimpleEnumerator> attachments;
+ mCompFields->GetAttachments(getter_AddRefs(attachments));
+ if (!attachments)
+ return NS_OK;
+
+ uint32_t newLoc = 0;
+ nsresult rv;
+ nsCString url;
+
+ //Parse the attachments array
+ bool moreAttachments;
+ nsCOMPtr<nsISupports> element;
+ while (NS_SUCCEEDED(attachments->HasMoreElements(&moreAttachments)) && moreAttachments) {
+ rv = attachments->GetNext(getter_AddRefs(element));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAttachment> attachment = do_QueryInterface(element, &rv);
+ if (NS_SUCCEEDED(rv) && attachment)
+ {
+ bool sendViaCloud = false;
+ attachment->GetSendViaCloud(&sendViaCloud);
+ m_attachments[newLoc]->mSendViaCloud = sendViaCloud;
+ attachment->GetUrl(url);
+ if (!url.IsEmpty())
+ {
+ bool sendViaCloud;
+ attachment->GetSendViaCloud(&sendViaCloud);
+ if (sendViaCloud)
+ {
+ nsCString cloudProviderKey;
+ // We'd like to output a part for the attachment, just an html part
+ // with information about how to download the attachment.
+ // m_attachments[newLoc]->m_done = true;
+ attachment->GetHtmlAnnotation(m_attachments[newLoc]->mHtmlAnnotation);
+ m_attachments[newLoc]->m_type.AssignLiteral("text/html");
+ attachment->GetCloudProviderKey(m_attachments[newLoc]->mCloudProviderKey);
+ attachment->GetContentLocation(m_attachments[newLoc]->mCloudUrl);
+ }
+ // Just look for local file:// attachments and do the right thing.
+ if (nsMsgIsLocalFile(url.get()))
+ {
+ //
+ // Now we have to setup the m_attachments entry for the file://
+ // URL that is passed in...
+ //
+ m_attachments[newLoc]->mDeleteFile = false;
+
+ nsMsgNewURL(getter_AddRefs(m_attachments[newLoc]->mURL), url.get());
+
+ if (m_attachments[newLoc]->mTmpFile)
+ {
+ if (m_attachments[newLoc]->mDeleteFile)
+ m_attachments[newLoc]->mTmpFile->Remove(false);
+ m_attachments[newLoc]->mTmpFile =nullptr;
+ }
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr <nsIURI> uri;
+ rv = ioService->NewURI(url, nullptr, nullptr, getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIFileURL> fileURL = do_QueryInterface(uri);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIFile> fileURLFile;
+ fileURL->GetFile(getter_AddRefs(fileURLFile));
+ m_attachments[newLoc]->mTmpFile = do_QueryInterface(fileURLFile);
+ m_attachments[newLoc]->mDeleteFile = false;
+ if (m_attachments[newLoc]->mURL)
+ {
+ nsAutoString proposedName;
+ attachment->GetName(proposedName);
+ msg_pick_real_name(m_attachments[newLoc], proposedName.get(), mCompFields->GetCharacterSet());
+ }
+
+ // Now, most importantly, we need to figure out what the content type is for
+ // this attachment...If we can't, then just make it application/octet-stream
+
+ #ifdef MAC_OSX
+ //Mac always need to snarf the file to figure out how to send it, maybe we need to use apple double...
+ // unless caller has already set the content type, in which case, trust them.
+ bool mustSnarfAttachment = true;
+ #else
+ bool mustSnarfAttachment = false;
+ #endif
+ if (sendViaCloud)
+ mustSnarfAttachment = false;
+
+ attachment->GetContentType(getter_Copies(m_attachments[newLoc]->m_type));
+ if (m_attachments[newLoc]->m_type.IsEmpty())
+ {
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && mimeFinder)
+ {
+ nsCOMPtr<nsIURL> fileUrl(do_CreateInstance(NS_STANDARDURL_CONTRACTID));
+ if (fileUrl)
+ {
+ nsAutoCString fileExt;
+ //First try using the real file name
+ rv = fileUrl->SetFileName(m_attachments[newLoc]->m_realName);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = fileUrl->GetFileExtension(fileExt);
+ if (NS_SUCCEEDED(rv) && !fileExt.IsEmpty()) {
+ nsAutoCString type;
+ mimeFinder->GetTypeFromExtension(fileExt, type);
+ #ifndef XP_MACOSX
+ if (!type.Equals("multipart/appledouble")) // can't do apple double on non-macs
+ #endif
+ m_attachments[newLoc]->m_type = type;
+ }
+ }
+
+ //Then try using the url if we still haven't figured out the content type
+ if (m_attachments[newLoc]->m_type.IsEmpty())
+ {
+ rv = fileUrl->SetSpec(url);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = fileUrl->GetFileExtension(fileExt);
+ if (NS_SUCCEEDED(rv) && !fileExt.IsEmpty()) {
+ nsAutoCString type;
+ mimeFinder->GetTypeFromExtension(fileExt, type);
+ #ifndef XP_MACOSX
+ if (!type.Equals("multipart/appledouble")) // can't do apple double on non-macs
+ #endif
+ m_attachments[newLoc]->m_type = type;
+ // rtf and vcs files may look like text to sniffers,
+ // but they're not human readable.
+ if (type.IsEmpty() && !fileExt.IsEmpty() &&
+ (MsgLowerCaseEqualsLiteral(fileExt, "rtf") ||
+ MsgLowerCaseEqualsLiteral(fileExt, "vcs")))
+ m_attachments[newLoc]->m_type = APPLICATION_OCTET_STREAM;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ attachment->GetContentTypeParam(getter_Copies(m_attachments[newLoc]->m_typeParam));
+ mustSnarfAttachment = false;
+ }
+
+ //We need to snarf the file to figure out how to send it only if we don't have a content type...
+ if (mustSnarfAttachment || m_attachments[newLoc]->m_type.IsEmpty())
+ {
+ m_attachments[newLoc]->m_done = false;
+ m_attachments[newLoc]->SetMimeDeliveryState(this);
+ }
+ else
+ {
+ m_attachments[newLoc]->m_done = true;
+ m_attachments[newLoc]->SetMimeDeliveryState(nullptr);
+ }
+ // For local files, if they are HTML docs and we don't have a charset, we should
+ // sniff the file and see if we can figure it out.
+ if (!m_attachments[newLoc]->m_type.IsEmpty())
+ {
+ if (m_attachments[newLoc]->m_type.LowerCaseEqualsLiteral(TEXT_HTML))
+ {
+ char *tmpCharset = (char *)nsMsgI18NParseMetaCharset(m_attachments[newLoc]->mTmpFile);
+ if (tmpCharset[0] != '\0')
+ m_attachments[newLoc]->m_charset = tmpCharset;
+ }
+ }
+
+ attachment->GetMacType(getter_Copies(m_attachments[newLoc]->m_xMacType));
+ attachment->GetMacCreator(getter_Copies(m_attachments[newLoc]->m_xMacCreator));
+
+ ++newLoc;
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeAndSend::AddCompFieldRemoteAttachments(uint32_t aStartLocation,
+ int32_t *aMailboxCount,
+ int32_t *aNewsCount)
+{
+ // If none, just return...
+ if (mCompFieldRemoteAttachments <= 0)
+ return NS_OK;
+
+ //Get the attachments array
+ nsCOMPtr<nsISimpleEnumerator> attachments;
+ mCompFields->GetAttachments(getter_AddRefs(attachments));
+ if (!attachments)
+ return NS_OK;
+
+ uint32_t newLoc = aStartLocation;
+
+ nsresult rv;
+ bool moreAttachments;
+ nsCString url;
+ nsCOMPtr<nsISupports> element;
+ while (NS_SUCCEEDED(attachments->HasMoreElements(&moreAttachments)) && moreAttachments) {
+ rv = attachments->GetNext(getter_AddRefs(element));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAttachment> attachment = do_QueryInterface(element, &rv);
+ if (NS_SUCCEEDED(rv) && attachment)
+ {
+ attachment->GetUrl(url);
+ if (!url.IsEmpty())
+ {
+ // Just look for files that are NOT local file attachments and do
+ // the right thing.
+ if (! nsMsgIsLocalFile(url.get()))
+ {
+ // Check for message attachment, see nsMsgMailNewsUrl::GetIsMessageUri.
+ nsCOMPtr<nsIURI> nsiuri = do_CreateInstance(NS_STANDARDURL_CONTRACTID);
+ NS_ENSURE_STATE(nsiuri);
+ nsiuri->SetSpec(url);
+ nsAutoCString scheme;
+ nsiuri->GetScheme(scheme);
+ bool isAMessageAttachment =
+ StringEndsWith(scheme, NS_LITERAL_CSTRING("-message"));
+
+ m_attachments[newLoc]->mDeleteFile = true;
+ m_attachments[newLoc]->m_done = false;
+ m_attachments[newLoc]->SetMimeDeliveryState(this);
+
+ if (!isAMessageAttachment)
+ nsMsgNewURL(getter_AddRefs(m_attachments[newLoc]->mURL), url.get());
+
+ m_attachments[newLoc]->m_encoding = ENCODING_7BIT;
+
+ attachment->GetMacType(getter_Copies(m_attachments[newLoc]->m_xMacType));
+ attachment->GetMacCreator(getter_Copies(m_attachments[newLoc]->m_xMacCreator));
+
+ /* Count up attachments which are going to come from mail folders
+ and from NNTP servers. */
+ bool do_add_attachment = false;
+ if (isAMessageAttachment)
+ {
+ do_add_attachment = true;
+ if (!PL_strncasecmp(url.get(), "news-message://", 15))
+ (*aNewsCount)++;
+ else
+ (*aMailboxCount)++;
+
+ m_attachments[newLoc]->m_uri = url;
+ m_attachments[newLoc]->mURL = nullptr;
+ }
+ else
+ do_add_attachment = (nullptr != m_attachments[newLoc]->mURL);
+ m_attachments[newLoc]->mSendViaCloud = false;
+ if (do_add_attachment)
+ {
+ nsAutoString proposedName;
+ attachment->GetName(proposedName);
+ msg_pick_real_name(m_attachments[newLoc], proposedName.get(), mCompFields->GetCharacterSet());
+ ++newLoc;
+ }
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeAndSend::HackAttachments(nsIArray *attachments,
+ nsIArray *preloadedAttachments)
+{
+ //
+ // First, count the total number of attachments we are going to process
+ // for this operation! This is a little more complicated than you might
+ // think because we have a few ways to specify attachments. Via the nsMsgAttachmentData
+ // as well as the composition fields.
+ //
+ CountCompFieldAttachments();
+
+ // Count the preloaded attachments!
+ mPreloadedAttachmentCount = 0;
+
+ // For now, manually add the local attachments in the comp field!
+ mPreloadedAttachmentCount += mCompFieldLocalAttachments;
+ uint32_t numAttachments = 0, numPreloadedAttachments = 0;
+ if (attachments)
+ attachments->GetLength(&numAttachments);
+ if (preloadedAttachments)
+ preloadedAttachments->GetLength(&numPreloadedAttachments);
+ mPreloadedAttachmentCount += numPreloadedAttachments;
+
+ // Count the attachments we have to go retrieve! Keep in mind, that these
+ // will be APPENDED to the current list of URL's that we have gathered if
+ // this is a multpart/related send operation
+ mRemoteAttachmentCount = GetMultipartRelatedCount();
+
+ // For now, manually add the remote attachments in the comp field!
+ mRemoteAttachmentCount += mCompFieldRemoteAttachments;
+
+ mRemoteAttachmentCount += numAttachments;
+
+ m_attachment_count = mPreloadedAttachmentCount + mRemoteAttachmentCount;
+
+ uint32_t i; // counter for location in attachment array...
+ // Now create the array of attachment handlers...
+ for (i = 0; i < m_attachment_count; i++) {
+ RefPtr<nsMsgAttachmentHandler> handler = new nsMsgAttachmentHandler;
+ m_attachments.AppendElement(handler);
+ }
+
+ //
+ // First, we need to attach the files that are defined in the comp fields...
+ if (NS_FAILED(AddCompFieldLocalAttachments()))
+ return NS_ERROR_INVALID_ARG;
+
+ // Now handle the preloaded attachments...
+ if (numPreloadedAttachments > 0)
+ {
+ // These are attachments which have already been downloaded to tmp files.
+ // We merely need to point the internal attachment data at those tmp
+ // files.
+ m_pre_snarfed_attachments_p = true;
+
+ for (i = mCompFieldLocalAttachments; i < mPreloadedAttachmentCount; i++)
+ {
+ nsCOMPtr<nsIMsgAttachedFile> attachedFile = do_QueryElementAt(preloadedAttachments, i);
+ if (!attachedFile)
+ continue;
+
+ /* These attachments are already "snarfed". */
+ m_attachments[i]->mDeleteFile = false;
+ m_attachments[i]->SetMimeDeliveryState(nullptr);
+ m_attachments[i]->m_done = true;
+
+ attachedFile->GetOrigUrl(getter_AddRefs(m_attachments[i]->mURL));
+
+ attachedFile->GetType(m_attachments[i]->m_type);
+
+ // Set it to the compose fields for a default...
+ m_attachments[i]->m_charset = mCompFields->GetCharacterSet();
+
+ // If we still don't have a content type, we should really try sniff one out!
+ if (m_attachments[i]->m_type.IsEmpty())
+ m_attachments[i]->PickEncoding(mCompFields->GetCharacterSet(), this);
+
+ // For local files, if they are HTML docs and we don't have a charset, we should
+ // sniff the file and see if we can figure it out.
+ if (!m_attachments[i]->m_type.IsEmpty())
+ {
+ nsCOMPtr<nsIFile> tmpFile;
+ attachedFile->GetTmpFile(getter_AddRefs(tmpFile));
+ if (m_attachments[i]->m_type.LowerCaseEqualsLiteral(TEXT_HTML) && tmpFile)
+ {
+ char *tmpCharset = (char *)nsMsgI18NParseMetaCharset(tmpFile);
+ if (tmpCharset[0] != '\0')
+ m_attachments[i]->m_charset = tmpCharset;
+ }
+ }
+
+ attachedFile->GetDescription(m_attachments[i]->m_description);
+ attachedFile->GetRealName(m_attachments[i]->m_realName);
+ attachedFile->GetXMacType(m_attachments[i]->m_xMacType);
+ attachedFile->GetXMacCreator(m_attachments[i]->m_xMacCreator);
+ attachedFile->GetEncoding(m_attachments[i]->m_encoding);
+
+ if (m_attachments[i]->mTmpFile)
+ {
+ if (m_attachments[i]->mDeleteFile)
+ m_attachments[i]->mTmpFile->Remove(false);
+ m_attachments[i]->mTmpFile = nullptr;
+ }
+ attachedFile->GetTmpFile(getter_AddRefs(m_attachments[i]->mTmpFile));
+
+ attachedFile->GetSize(&m_attachments[i]->m_size);
+ attachedFile->GetUnprintableCount(&m_attachments[i]->m_unprintable_count);
+ attachedFile->GetHighbitCount(&m_attachments[i]->m_highbit_count);
+ attachedFile->GetCtlCount(&m_attachments[i]->m_ctl_count);
+ attachedFile->GetNullCount(&m_attachments[i]->m_null_count);
+ attachedFile->GetMaxLineLength(&m_attachments[i]->m_max_column);
+
+ /* If the attachment has an encoding, and it's not one of
+ the "null" encodings, then keep it. */
+ if (!m_attachments[i]->m_encoding.IsEmpty() &&
+ !m_attachments[i]->m_encoding.LowerCaseEqualsLiteral(ENCODING_7BIT) &&
+ !m_attachments[i]->m_encoding.LowerCaseEqualsLiteral(ENCODING_8BIT) &&
+ !m_attachments[i]->m_encoding.LowerCaseEqualsLiteral(ENCODING_BINARY))
+ m_attachments[i]->m_already_encoded_p = true;
+
+ if (m_attachments[i]->mURL)
+ msg_pick_real_name(m_attachments[i], nullptr, mCompFields->GetCharacterSet());
+ }
+ }
+
+ // First, handle the multipart related attachments if any...
+ //
+ int32_t mailbox_count = 0, news_count = 0;
+ int32_t multipartRelatedCount = GetMultipartRelatedCount();
+
+ if (multipartRelatedCount > 0)
+ {
+ nsresult rv = ProcessMultipartRelated(&mailbox_count, &news_count);
+ if (NS_FAILED(rv))
+ {
+ // The destructor will take care of the m_attachment array
+ return rv;
+ }
+ }
+
+ //
+ // Now add the comp field remote attachments...
+ //
+ if (NS_FAILED( AddCompFieldRemoteAttachments( (mPreloadedAttachmentCount + multipartRelatedCount),
+ &mailbox_count, &news_count) ))
+ return NS_ERROR_INVALID_ARG;
+
+ //
+ // Now deal remote attachments and attach multipart/related attachments (url's and such..)
+ // first!
+ //
+ if (attachments)
+ {
+ int32_t locCount = -1;
+
+ for (i = (mPreloadedAttachmentCount + GetMultipartRelatedCount() + mCompFieldRemoteAttachments); i < m_attachment_count; i++)
+ {
+ locCount++;
+ nsCOMPtr<nsIMsgAttachmentData> attachment(do_QueryElementAt(attachments, i));
+ if (!attachment)
+ continue;
+ m_attachments[i]->mDeleteFile = true;
+ m_attachments[i]->m_done = false;
+ m_attachments[i]->SetMimeDeliveryState(this);
+
+ attachment->GetUrl(getter_AddRefs(m_attachments[i]->mURL));
+
+ attachment->GetRealType(m_attachments[i]->m_overrideType);
+ m_attachments[i]->m_charset = mCompFields->GetCharacterSet();
+ attachment->GetRealEncoding(m_attachments[i]->m_overrideEncoding);
+ attachment->GetDesiredType(m_attachments[i]->m_desiredType);
+ attachment->GetDescription(m_attachments[i]->m_description);
+ attachment->GetRealName(m_attachments[i]->m_realName);
+ attachment->GetXMacType(m_attachments[i]->m_xMacType);
+ attachment->GetXMacCreator(m_attachments[i]->m_xMacCreator);
+ m_attachments[i]->m_encoding = ENCODING_7BIT;
+
+ // real name is set in the case of vcard so don't change it. XXX STILL NEEDED?
+ // m_attachments[i]->m_real_name = 0;
+
+ /* Count up attachments which are going to come from mail folders
+ and from NNTP servers. */
+ if (m_attachments[i]->mURL)
+ {
+ nsIURI *uri = m_attachments[i]->mURL;
+ bool match = false;
+ if ((NS_SUCCEEDED(uri->SchemeIs("mailbox", &match)) && match) ||
+ (NS_SUCCEEDED(uri->SchemeIs("imap", &match)) && match))
+ mailbox_count++;
+ else if ((NS_SUCCEEDED(uri->SchemeIs("news", &match)) && match) ||
+ (NS_SUCCEEDED(uri->SchemeIs("snews", &match)) && match))
+ news_count++;
+ else
+ {
+ // Additional account types need a mechanism to report that they are
+ // message protocols. If there is an nsIMsgProtocolInfo component
+ // registered for this scheme, we'll consider it a mailbox
+ // attachment.
+ nsAutoCString contractID;
+ contractID.Assign(
+ NS_LITERAL_CSTRING("@mozilla.org/messenger/protocol/info;1"));
+ nsAutoCString scheme;
+ uri->GetScheme(scheme);
+ contractID.Append(scheme);
+ nsCOMPtr<nsIMsgProtocolInfo> msgProtocolInfo =
+ do_CreateInstance(contractID.get());
+ if (msgProtocolInfo)
+ mailbox_count++;
+ }
+ if (uri)
+ msg_pick_real_name(m_attachments[i], nullptr, mCompFields->GetCharacterSet());
+ }
+ }
+ }
+
+ bool needToCallGatherMimeAttachments = true;
+
+ if (m_attachment_count > 0)
+ {
+ // 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).
+
+ for (i = 0; i < m_attachment_count; i++)
+ {
+ if (m_attachments[i]->m_done || m_attachments[i]->mSendViaCloud)
+ {
+ m_attachment_pending_count--;
+ continue;
+ }
+
+ //
+ // IF we get here and the URL is NULL, just dec the pending count and move on!!!
+ //
+ if ( (!m_attachments[i]->mURL) && (!m_attachments[i]->m_uri.Length()) )
+ {
+ m_attachments[i]->m_bogus_attachment = true;
+ m_attachments[i]->m_done = true;
+ m_attachments[i]->SetMimeDeliveryState(nullptr);
+ m_attachment_pending_count--;
+ continue;
+ }
+
+ //
+ // This only returns a failure code if NET_GetURL was not called
+ // (and thus no exit routine was or will be called.)
+ //
+
+ // Display some feedback to user...
+ nsString msg;
+ nsAutoString attachmentFileName;
+ NS_ConvertUTF8toUTF16 params(m_attachments[i]->m_realName);
+ const char16_t *formatParams[1];
+ if (!params.IsEmpty()) {
+ formatParams[0] = params.get();
+ } else if (m_attachments[i]->mURL) {
+ nsCString asciiSpec;
+ m_attachments[i]->mURL->GetAsciiSpec(asciiSpec);
+ attachmentFileName.AssignASCII(asciiSpec.get());
+ formatParams[0] = attachmentFileName.get();
+ }
+ mComposeBundle->FormatStringFromName(u"gatheringAttachment",
+ formatParams, 1, getter_Copies(msg));
+
+ if (!msg.IsEmpty())
+ {
+ SetStatusMessage(msg);
+ }
+
+ /* As SnarfAttachment will call GatherMimeAttachments when it will be done (this is an async process),
+ we need to avoid to call it ourself.
+ */
+ needToCallGatherMimeAttachments = false;
+
+ nsresult status = m_attachments[i]->SnarfAttachment(mCompFields);
+ if (NS_FAILED(status))
+ {
+ nsString errorMsg;
+ nsresult rv = ConvertToUnicode(nsMsgI18NFileSystemCharset(), m_attachments[i]->m_realName, attachmentFileName);
+ if (attachmentFileName.IsEmpty() && m_attachments[i]->mURL) {
+ nsCString asciiSpec;
+ m_attachments[i]->mURL->GetAsciiSpec(asciiSpec);
+ attachmentFileName.AssignASCII(asciiSpec.get());
+ rv = NS_OK;
+ }
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIStringBundle> bundle;
+ const char16_t *params[] = { attachmentFileName.get() };
+ mComposeBundle->FormatStringFromName(u"errorAttachingFile",
+ params, 1,
+ getter_Copies(errorMsg));
+ mSendReport->SetMessage(nsIMsgSendReport::process_Current, errorMsg.get(), false);
+ mSendReport->SetError(nsIMsgSendReport::process_Current,
+ NS_MSG_ERROR_ATTACHING_FILE,
+ false);
+ }
+ return NS_MSG_ERROR_ATTACHING_FILE;
+ }
+ if (m_be_synchronous_p)
+ break;
+ }
+ }
+
+ // If no attachments - finish now (this will call the done_callback).
+ if (needToCallGatherMimeAttachments)
+ return GatherMimeAttachments();
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeAndSend::InitCompositionFields(nsMsgCompFields *fields,
+ const nsACString &aOriginalMsgURI,
+ MSG_ComposeType aType)
+{
+ nsresult rv = NS_OK;
+ const char *pStr = nullptr;
+
+ mCompFields = new nsMsgCompFields();
+ if (!mCompFields)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ const char *cset = fields->GetCharacterSet();
+ // Make sure charset is sane...
+ if (!cset || !*cset)
+ {
+ mCompFields->SetCharacterSet("UTF-8");
+ }
+ else
+ {
+ mCompFields->SetCharacterSet(fields->GetCharacterSet());
+ }
+
+ // Now, we will look for a URI defined as the default FCC pref. If this is set,
+ // then SetFcc will use this value. The FCC field is a URI for the server that
+ // will hold the "Sent" folder...the
+ //
+ // First, look at what was passed in via the "fields" structure...if that was
+ // set then use it, otherwise, fall back to what is set in the prefs...
+ //
+ // But even before that, pay attention to the new OVERRIDE pref that will cancel
+ // any and all copy operations!
+ //
+ bool doFcc = true;
+ rv = mUserIdentity->GetDoFcc(&doFcc);
+ if (!doFcc)
+ {
+ // If the identity pref "fcc" is set to false, then we will not do
+ // any FCC operation!
+ mCompFields->SetFcc("");
+ }
+ else
+ {
+ bool useDefaultFCC = true;
+ const char *fieldsFCC = fields->GetFcc();
+ if (fieldsFCC && *fieldsFCC)
+ {
+ if (PL_strcasecmp(fieldsFCC, "nocopy://") == 0)
+ {
+ useDefaultFCC = false;
+ mCompFields->SetFcc("");
+ }
+ else
+ {
+ nsCOMPtr<nsIMsgFolder> folder;
+ GetExistingFolder(nsDependentCString(fieldsFCC), getter_AddRefs(folder));
+ if (folder)
+ {
+ useDefaultFCC = false;
+ mCompFields->SetFcc(mime_fix_header(fieldsFCC));
+ }
+ }
+ }
+
+ // We use default FCC setting if it's not set or was set to an invalid folder.
+ if (useDefaultFCC)
+ {
+ // Only check whether the user wants the message in the original message
+ // folder if the msgcomptype is some kind of a reply.
+ if (!aOriginalMsgURI.IsEmpty() && (
+ aType == nsIMsgCompType::Reply ||
+ aType == nsIMsgCompType::ReplyAll ||
+ aType == nsIMsgCompType::ReplyToGroup ||
+ aType == nsIMsgCompType::ReplyToSender ||
+ aType == nsIMsgCompType::ReplyToSenderAndGroup ||
+ aType == nsIMsgCompType::ReplyWithTemplate )
+ )
+ {
+ nsCOMPtr <nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ rv = GetMsgDBHdrFromURI(PromiseFlatCString(aOriginalMsgURI).get(),
+ getter_AddRefs(msgHdr));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr <nsIMsgFolder> folder;
+ msgHdr->GetFolder(getter_AddRefs(folder));
+ if (NS_SUCCEEDED(rv))
+ {
+ bool canFileMessages;
+ rv = folder->GetCanFileMessages(&canFileMessages);
+ if (NS_SUCCEEDED(rv) && canFileMessages)
+ {
+ nsCOMPtr <nsIMsgIncomingServer> incomingServer;
+ rv = folder->GetServer(getter_AddRefs(incomingServer));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCString incomingServerType;
+ rv = incomingServer->GetCharValue("type", incomingServerType);
+ // Exclude RSS accounts, as they falsely report
+ // 'canFileMessages' = true
+ if (NS_SUCCEEDED(rv) && !incomingServerType.Equals("rss"))
+ {
+ bool fccReplyFollowsParent;
+ rv = mUserIdentity->GetFccReplyFollowsParent(
+ &fccReplyFollowsParent);
+ if (NS_SUCCEEDED(rv) && fccReplyFollowsParent)
+ {
+ nsCString folderURI;
+ rv = folder->GetURI(folderURI);
+ if (NS_SUCCEEDED(rv))
+ {
+ mCompFields->SetFcc(folderURI.get());
+ useDefaultFCC = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (useDefaultFCC)
+ {
+ nsCString uri;
+ GetFolderURIFromUserPrefs(nsMsgDeliverNow, mUserIdentity, uri);
+ mCompFields->SetFcc(MsgLowerCaseEqualsLiteral(uri, "nocopy://") ? "" : uri.get());
+ }
+ }
+ }
+
+ //
+ // Deal with an additional FCC operation for this email.
+ //
+ const char *fieldsFCC2 = fields->GetFcc2();
+ if ( (fieldsFCC2) && (*fieldsFCC2) )
+ {
+ if (PL_strcasecmp(fieldsFCC2, "nocopy://") == 0)
+ {
+ mCompFields->SetFcc2("");
+ mNeedToPerformSecondFCC = false;
+ }
+ else
+ {
+ mCompFields->SetFcc2(fieldsFCC2);
+ mNeedToPerformSecondFCC = true;
+ }
+ }
+
+ // Copy the main bodies of headers over.
+ rv = mCompFields->AddAllHeaders(fields);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> srcAttachments;
+ fields->GetAttachments(getter_AddRefs(srcAttachments));
+ if (srcAttachments)
+ {
+ bool moreAttachments;
+ nsCOMPtr<nsISupports> element;
+ while (NS_SUCCEEDED(srcAttachments->HasMoreElements(&moreAttachments)) && moreAttachments) {
+ rv = srcAttachments->GetNext(getter_AddRefs(element));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgAttachment> attachment = do_QueryInterface(element, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCompFields->AddAttachment(attachment);
+ }
+ }
+
+ AddDefaultCustomHeaders();
+
+ AddMailFollowupToHeader();
+ AddMailReplyToHeader();
+
+ if (aType == nsIMsgCompType::ForwardInline ||
+ aType == nsIMsgCompType::ForwardAsAttachment)
+ AddXForwardedMessageIdHeader();
+
+ pStr = fields->GetPriority();
+ if (pStr)
+ mCompFields->SetPriority((char *) pStr);
+
+ mCompFields->SetAttachVCard(fields->GetAttachVCard());
+ mCompFields->SetForcePlainText(fields->GetForcePlainText());
+ mCompFields->SetUseMultipartAlternative(fields->GetUseMultipartAlternative());
+ int32_t receiptType = nsIMsgMdnGenerator::eDntType;
+ fields->GetReceiptHeaderType(&receiptType);
+
+ mCompFields->SetReturnReceipt(fields->GetReturnReceipt());
+ mCompFields->SetAttachmentReminder(fields->GetAttachmentReminder());
+ mCompFields->SetDeliveryFormat(fields->GetDeliveryFormat());
+ mCompFields->SetContentLanguage(fields->GetContentLanguage());
+ mCompFields->SetReceiptHeaderType(receiptType);
+
+ mCompFields->SetDSN(fields->GetDSN());
+
+ mCompFields->SetBodyIsAsciiOnly(fields->GetBodyIsAsciiOnly());
+ mCompFields->SetForceMsgEncoding(fields->GetForceMsgEncoding());
+
+ nsCOMPtr<nsISupports> secInfo;
+ fields->GetSecurityInfo(getter_AddRefs(secInfo));
+
+ mCompFields->SetSecurityInfo(secInfo);
+
+ bool needToCheckCharset;
+ fields->GetNeedToCheckCharset(&needToCheckCharset);
+ mCompFields->SetNeedToCheckCharset(needToCheckCharset);
+
+ if ( m_deliver_mode != nsMsgSaveAsDraft && m_deliver_mode != nsMsgSaveAsTemplate )
+ {
+ // Check the fields for legitimacy...
+ return mime_sanity_check_fields (
+ mCompFields->GetFrom(), mCompFields->GetReplyTo(),
+ mCompFields->GetTo(), mCompFields->GetCc(),
+ mCompFields->GetBcc(), mCompFields->GetFcc(),
+ mCompFields->GetNewsgroups(), mCompFields->GetFollowupTo(),
+ mCompFields->GetSubject(), mCompFields->GetReferences(),
+ mCompFields->GetOrganization(), "");
+ }
+ return NS_OK;
+}
+
+// Add default headers to outgoing messages see Bug #61520
+// mail.identity.<id#>.headers pref is a comma separated value of pref names
+// containging headers to add headers are stored in
+// mail.identity.<id#>.header.<header name> grab all the headers, mime encode
+// them and add them to the other custom headers.
+nsresult
+nsMsgComposeAndSend::AddDefaultCustomHeaders() {
+ nsCString headersList;
+ // get names of prefs containing headers to add
+ nsresult rv = mUserIdentity->GetCharAttribute("headers", headersList);
+ if (NS_SUCCEEDED(rv) && !headersList.IsEmpty()) {
+ int32_t start = 0;
+ int32_t end = 0;
+ int32_t len = 0;
+ while (end != -1) {
+ end = headersList.FindChar(',', start);
+ if (end == -1) {
+ len = headersList.Length() - start;
+ } else {
+ len = end - start;
+ }
+ // grab the name of the current header pref
+ nsAutoCString headerName("header.");
+ headerName.Append(Substring(headersList, start, len));
+ start = end + 1;
+
+ nsCString headerVal;
+ rv = mUserIdentity->GetCharAttribute(headerName.get(), headerVal);
+ if (NS_SUCCEEDED(rv)) {
+ int32_t colonIdx = headerVal.FindChar(':');
+ if (colonIdx > 0) { // check that the header is *most likely* valid.
+ nsCString name(Substring(headerVal, 0, colonIdx));
+ mCompFields->SetRawHeader(name.get(),
+ Substring(headerVal, colonIdx + 1), nullptr);
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+// Add Mail-Followup-To header
+// See bug #204339 and http://cr.yp.to/proto/replyto.html for details
+nsresult
+nsMsgComposeAndSend::AddMailFollowupToHeader() {
+ nsresult rv;
+
+ // If there's already a Mail-Followup-To header, don't need to do anything.
+ nsAutoCString mftHeader;
+ mCompFields->GetRawHeader(HEADER_MAIL_FOLLOWUP_TO, mftHeader);
+ if (!mftHeader.IsEmpty())
+ {
+ return NS_OK;
+ }
+
+ // Get list of subscribed mailing lists
+ nsAutoCString mailing_lists;
+ rv = mUserIdentity->GetCharAttribute("subscribed_mailing_lists", mailing_lists);
+ // Stop here if this list is missing or empty
+ if (NS_FAILED(rv) || mailing_lists.IsEmpty())
+ return NS_OK;
+
+ // Get a list of all recipients excluding bcc
+ nsDependentCString to(mCompFields->GetTo());
+ nsDependentCString cc(mCompFields->GetCc());
+ nsAutoCString recipients;
+
+ if (to.IsEmpty() && cc.IsEmpty())
+ // We have bcc recipients only, so we don't add the Mail-Followup-To header
+ return NS_OK;
+
+ if (!to.IsEmpty() && cc.IsEmpty())
+ recipients = to;
+ else if (to.IsEmpty() && !cc.IsEmpty())
+ recipients = cc;
+ else
+ {
+ recipients.Assign(to);
+ recipients.AppendLiteral(", ");
+ recipients.Append(cc);
+ }
+
+ // Remove duplicate addresses in recipients
+ nsAutoCString recipients_no_dups;
+ RemoveDuplicateAddresses(recipients, EmptyCString(), recipients_no_dups);
+
+ // Remove subscribed mailing lists from recipients...
+ nsAutoCString recipients_without_mailing_lists;
+ RemoveDuplicateAddresses(recipients_no_dups, mailing_lists,
+ recipients_without_mailing_lists);
+
+ // ... If the result is equal to the input, we don't write to a subscribed
+ // mailing list and therefore we don't add Mail-Followup-To
+ if (recipients_no_dups == recipients_without_mailing_lists)
+ return NS_OK;
+
+ // Set Mail-Followup-To
+ return mCompFields->SetRawHeader(HEADER_MAIL_FOLLOWUP_TO, recipients,
+ mCompFields->GetCharacterSet());
+}
+
+// Add Mail-Reply-To header
+// See bug #204339 and http://cr.yp.to/proto/replyto.html for details
+nsresult
+nsMsgComposeAndSend::AddMailReplyToHeader() {
+ nsresult rv;
+
+ // If there's already a Mail-Reply-To header, don't need to do anything.
+ nsAutoCString mrtHeader;
+ mCompFields->GetRawHeader(HEADER_MAIL_REPLY_TO, mrtHeader);
+ if (!mrtHeader.IsEmpty())
+ return NS_OK;
+
+ // Get list of reply-to mangling mailing lists
+ nsAutoCString mailing_lists;
+ rv = mUserIdentity->GetCharAttribute("replyto_mangling_mailing_lists", mailing_lists);
+ // Stop here if this list is missing or empty
+ if (NS_FAILED(rv) || mailing_lists.IsEmpty())
+ return NS_OK;
+
+ // MRT will be set if the recipients of the message contains at least one
+ // of the addresses in mailing_lists or if mailing_lists has '*' as first
+ // character. The latter case gives the user an easy way to always set
+ // the MRT header. Notice that this behaviour wouldn't make sense for MFT
+ // in AddMailFollowupToHeader() above.
+
+ if (mailing_lists[0] != '*') {
+ // Get a list of all recipients excluding bcc
+ nsDependentCString to(mCompFields->GetTo());
+ nsDependentCString cc(mCompFields->GetCc());
+ nsAutoCString recipients;
+
+ if (to.IsEmpty() && cc.IsEmpty())
+ // We have bcc recipients only, so we don't add the Mail-Reply-To header
+ return NS_OK;
+
+ if (!to.IsEmpty() && cc.IsEmpty())
+ recipients = to;
+ else if (to.IsEmpty() && !cc.IsEmpty())
+ recipients = cc;
+ else
+ {
+ recipients.Assign(to);
+ recipients.AppendLiteral(", ");
+ recipients.Append(cc);
+ }
+
+ // Remove duplicate addresses in recipients
+ nsAutoCString recipients_no_dups;
+ RemoveDuplicateAddresses(recipients, EmptyCString(), recipients_no_dups);
+
+ // Remove reply-to mangling mailing lists from recipients...
+ nsAutoCString recipients_without_mailing_lists;
+ RemoveDuplicateAddresses(recipients_no_dups, mailing_lists,
+ recipients_without_mailing_lists);
+
+ // ... If the result is equal to the input, none of the recipients
+ // occure in the MRT addresses and therefore we stop here.
+ if (recipients_no_dups == recipients_without_mailing_lists)
+ return NS_OK;
+ }
+
+ // Set Mail-Reply-To
+ nsAutoCString replyTo, mailReplyTo;
+ replyTo = mCompFields->GetReplyTo();
+ if (replyTo.IsEmpty())
+ mailReplyTo = mCompFields->GetFrom();
+ else
+ mailReplyTo = replyTo;
+
+ mCompFields->SetRawHeader(HEADER_MAIL_REPLY_TO, mailReplyTo,
+ mCompFields->GetCharacterSet());
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeAndSend::AddXForwardedMessageIdHeader() {
+ return mCompFields->SetRawHeader("X-Forwarded-Message-Id",
+ nsDependentCString(mCompFields->GetReferences()), nullptr);
+}
+
+nsresult
+nsMsgComposeAndSend::SnarfAndCopyBody(const nsACString &attachment1_body,
+ const char *attachment1_type)
+{
+ //
+ // If we are here, then just process the body from what was
+ // passed in the attachment1_body field.
+ //
+ // strip out whitespaces from the end of body ONLY.
+ nsAutoCString body(attachment1_body);
+ body.Trim(" ", false, true);
+
+ if (body.Length() > 0)
+ {
+ m_attachment1_body = ToNewCString(body);
+ if (!m_attachment1_body) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ m_attachment1_body_length = body.Length();
+ }
+
+ PR_FREEIF(m_attachment1_type);
+ m_attachment1_type = PL_strdup (attachment1_type);
+ PR_FREEIF(m_attachment1_encoding);
+ m_attachment1_encoding = PL_strdup (ENCODING_8BIT);
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeAndSend::Init(
+ nsIMsgIdentity *aUserIdentity,
+ const char *aAccountKey,
+ nsMsgCompFields *fields,
+ nsIFile *sendFile,
+ bool digest_p,
+ bool dont_deliver_p,
+ nsMsgDeliverMode mode,
+ nsIMsgDBHdr *msgToReplace,
+ const char *attachment1_type,
+ const nsACString &attachment1_body,
+ nsIArray *attachments,
+ nsIArray *preloaded_attachments,
+ const char *password,
+ const nsACString &aOriginalMsgURI,
+ MSG_ComposeType aType)
+{
+ nsresult rv = NS_OK;
+
+ //Let make sure we retreive the correct number of related parts. It may have changed since last time
+ GetMultipartRelatedCount(true);
+
+ nsString msg;
+ if (!mComposeBundle)
+ {
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(mComposeBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Tell the user we are assembling the message...
+ mComposeBundle->GetStringFromName(u"assemblingMailInformation", getter_Copies(msg));
+ SetStatusMessage(msg);
+ if (mSendReport)
+ mSendReport->SetCurrentProcess(nsIMsgSendReport::process_BuildMessage);
+
+ //
+ // The Init() method should initialize a send operation for full
+ // blown create and send operations as well as just the "send a file"
+ // operations.
+ //
+ m_dont_deliver_p = dont_deliver_p;
+ m_deliver_mode = mode;
+ mMsgToReplace = msgToReplace;
+
+ mUserIdentity = aUserIdentity;
+ mAccountKey = aAccountKey;
+ NS_ASSERTION(mUserIdentity, "Got null identity!\n");
+ if (!mUserIdentity) return NS_ERROR_UNEXPECTED;
+
+ //
+ // First sanity check the composition fields parameter and
+ // see if we should continue
+ //
+ if (!fields)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ m_digest_p = digest_p;
+
+ //
+ // Needed for mime encoding!
+ //
+ bool strictly_mime = true;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (pPrefBranch)
+ {
+ rv = pPrefBranch->GetBoolPref(PREF_MAIL_STRICTLY_MIME, &strictly_mime);
+ rv = pPrefBranch->GetIntPref(PREF_MAIL_MESSAGE_WARNING_SIZE, (int32_t *) &mMessageWarningSize);
+ }
+
+ nsCOMPtr<nsIMsgComposeSecure> secureCompose
+ = do_CreateInstance(NS_MSGCOMPOSESECURE_CONTRACTID, &rv);
+ // It's not an error scenario if there is no secure compose.
+ // The S/MIME extension may be unavailable.
+ if (NS_SUCCEEDED(rv) && secureCompose)
+ {
+ bool requiresEncryptionWork = false;
+ rv = secureCompose->RequiresCryptoEncapsulation(aUserIdentity, fields,
+ &requiresEncryptionWork);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (requiresEncryptionWork)
+ {
+ strictly_mime = true;
+ // RFC2633 3.1.3 doesn't require multipart/signed entities to have
+ // transfer encoding applied for ascii, but do it anyway to make sure
+ // the content (e.g. line endings) isn't mangled along the way.
+ fields->SetForceMsgEncoding(true);
+ }
+ }
+
+ nsMsgMIMESetConformToStandard(strictly_mime);
+ mime_use_quoted_printable_p = strictly_mime;
+
+ rv = InitCompositionFields(fields, aOriginalMsgURI, aType);
+ if (NS_FAILED(rv))
+ return rv;
+
+ //
+ // At this point, if we are only creating this object to do
+ // send operations on externally created RFC822 disk files,
+ // make sure we have setup the appropriate nsIFile and
+ // move on with life.
+ //
+ //
+ // First check to see if we are doing a send operation on an external file
+ // or creating the file itself.
+ //
+ if (sendFile)
+ {
+ mTempFile = sendFile;
+ return NS_OK;
+ }
+
+ // Ok, now watch me pull a rabbit out of my hat....what we need
+ // to do here is figure out what the body will be. If this is a
+ // MHTML request, then we need to do some processing of the document
+ // and figure out what we need to package along with this message
+ // to send. See ProcessMultipartRelated() for further details.
+ //
+
+ //
+ // If we don't have an editor, then we won't be doing multipart related processing
+ // for the body, so make a copy of the one passed in.
+ //
+ if (!mEditor)
+ {
+ SnarfAndCopyBody(attachment1_body, attachment1_type);
+ mOriginalHTMLBody = ToNewCString(attachment1_body);
+ }
+ else if (GetMultipartRelatedCount() == 0) // Only do this if there are not embedded objects
+ {
+ rv = GetBodyFromEditor();
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ mSmtpPassword = password;
+
+ return HackAttachments(attachments, preloaded_attachments);
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::SendDeliveryCallback(nsIURI *aUrl, bool inIsNewsDelivery, nsresult aExitCode)
+{
+ if (inIsNewsDelivery)
+ {
+ if (NS_FAILED(aExitCode))
+ if (aExitCode != NS_ERROR_ABORT && !NS_IS_MSG_ERROR(aExitCode))
+ aExitCode = NS_ERROR_POST_FAILED;
+
+ DeliverAsNewsExit(aUrl, aExitCode);
+ }
+ else
+ {
+ if (NS_FAILED(aExitCode))
+ {
+#ifdef __GNUC__
+// Temporary workaroung until bug 783526 is fixed.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#endif
+ switch (aExitCode)
+ {
+ case NS_ERROR_UNKNOWN_HOST:
+ case NS_ERROR_UNKNOWN_PROXY_HOST:
+ aExitCode = NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_SERVER;
+ break;
+ case NS_ERROR_CONNECTION_REFUSED:
+ case NS_ERROR_PROXY_CONNECTION_REFUSED:
+ aExitCode = NS_ERROR_SMTP_SEND_FAILED_REFUSED;
+ break;
+ case NS_ERROR_NET_INTERRUPT:
+ aExitCode = NS_ERROR_SMTP_SEND_FAILED_INTERRUPTED;
+ break;
+ case NS_ERROR_NET_TIMEOUT:
+ case NS_ERROR_NET_RESET:
+ aExitCode = NS_ERROR_SMTP_SEND_FAILED_TIMEOUT;
+ break;
+ case NS_ERROR_SMTP_PASSWORD_UNDEFINED:
+ // nothing to do, just keep the code
+ break;
+ default:
+ if (aExitCode != NS_ERROR_ABORT && !NS_IS_MSG_ERROR(aExitCode))
+ aExitCode = NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_REASON;
+ break;
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+ DeliverAsMailExit(aUrl, aExitCode);
+ }
+
+ return aExitCode;
+}
+
+nsresult
+nsMsgComposeAndSend::DeliverMessage()
+{
+ if (mSendProgress)
+ {
+ bool canceled = false;
+ mSendProgress->GetProcessCanceledByUser(&canceled);
+ if (canceled)
+ return NS_ERROR_ABORT;
+ }
+
+ bool mail_p = ((mCompFields->GetTo() && *mCompFields->GetTo()) ||
+ (mCompFields->GetCc() && *mCompFields->GetCc()) ||
+ (mCompFields->GetBcc() && *mCompFields->GetBcc()));
+ bool news_p = mCompFields->GetNewsgroups() && *(mCompFields->GetNewsgroups());
+ NS_ASSERTION(!( m_deliver_mode != nsMsgSaveAsDraft && m_deliver_mode != nsMsgSaveAsTemplate) || (mail_p || news_p), "message without destination");
+ if (m_deliver_mode == nsMsgQueueForLater ||
+ m_deliver_mode == nsMsgDeliverBackground ||
+ m_deliver_mode == nsMsgSaveAsDraft ||
+ m_deliver_mode == nsMsgSaveAsTemplate)
+ return SendToMagicFolder(m_deliver_mode);
+
+ //
+ // Ok, we are about to send the file that we have built up...but what
+ // if this is a mongo email...we should have a way to warn the user that
+ // they are about to do something they may not want to do.
+ //
+ int64_t fileSize;
+ nsresult rv = mTempFile->GetFileSize(&fileSize);
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ if ((mMessageWarningSize > 0) && (fileSize > mMessageWarningSize) && (mGUINotificationEnabled))
+ {
+ bool abortTheSend = false;
+ nsString msg;
+ nsAutoString formattedFileSize;
+ FormatFileSize(fileSize, true, formattedFileSize);
+ const char16_t* params[] = { formattedFileSize.get() };
+ mComposeBundle->FormatStringFromName(u"largeMessageSendWarning",
+ params, 1, getter_Copies(msg));
+
+ if (!msg.IsEmpty())
+ {
+ nsCOMPtr<nsIPrompt> prompt;
+ GetDefaultPrompt(getter_AddRefs(prompt));
+ nsMsgAskBooleanQuestionByString(prompt, msg.get(), &abortTheSend);
+ if (!abortTheSend)
+ {
+ nsresult ignoreMe;
+ Fail(NS_ERROR_BUT_DONT_SHOW_ALERT, msg.get(), &ignoreMe);
+ return NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ if (news_p)
+ {
+ if (mail_p)
+ mSendMailAlso = true;
+
+ return DeliverFileAsNews(); /* will call DeliverFileAsMail if it needs to */
+ }
+ else if (mail_p)
+ return DeliverFileAsMail();
+ else
+ return NS_ERROR_UNEXPECTED;
+ return NS_OK;
+}
+
+
+nsresult
+nsMsgComposeAndSend::DeliverFileAsMail()
+{
+ char *buf, *buf2;
+ buf = (char *) PR_Malloc ((mCompFields->GetTo() ? PL_strlen (mCompFields->GetTo()) + 10 : 0) +
+ (mCompFields->GetCc() ? PL_strlen (mCompFields->GetCc()) + 10 : 0) +
+ (mCompFields->GetBcc() ? PL_strlen (mCompFields->GetBcc()) + 10 : 0) +
+ 10);
+
+ if (mSendReport)
+ mSendReport->SetCurrentProcess(nsIMsgSendReport::process_SMTP);
+
+ nsCOMPtr<nsIPrompt> promptObject;
+ GetDefaultPrompt(getter_AddRefs(promptObject));
+
+ if (!buf)
+ {
+ nsresult ignoreMe;
+ Fail(NS_ERROR_OUT_OF_MEMORY, nullptr, &ignoreMe);
+ NotifyListenerOnStopSending(nullptr, NS_ERROR_OUT_OF_MEMORY, nullptr, nullptr);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ bool collectOutgoingAddresses = true;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (pPrefBranch)
+ pPrefBranch->GetBoolPref(PREF_MAIL_COLLECT_EMAIL_ADDRESS_OUTGOING, &collectOutgoingAddresses);
+
+ nsCOMPtr<nsIAbAddressCollector> addressCollector =
+ do_GetService(NS_ABADDRESSCOLLECTOR_CONTRACTID);
+
+ bool collectAddresses = (collectOutgoingAddresses && addressCollector);
+ uint32_t sendFormat = nsIAbPreferMailFormat::unknown;
+
+ // this code is not ready yet
+ // see bug #44494 for more details
+ // so for now, just pass in nsIAbPreferMailFormat::unknown
+ // which will have no effect on the "prefers" attribute in the ab
+#if 0
+ bool forcePlainText = mCompFields->GetForcePlainText();
+ bool useMultipartAlternative = mCompFields->GetUseMultipartAlternative();
+ // see GenericSendMessage() in MsgComposeCommands.js for the reverse logic
+ // if we choose to send both (html and plain) remember html.
+ if (forcePlainText && !useMultipartAlternative)
+ {
+ // for now, don't remember the "plaintext" decision.
+ // we could get in here because while sending html mail
+ // the body was "convertible", but that doesn't mean
+ // we intended to force plain text here.
+ // so for now, use "unknown" which will have no effect on the
+ // "prefers" attribute in the ab.
+ // see bug #245520 for more details
+ // sendFormat = nsIAbPreferMailFormat::plaintext;
+ sendFormat = nsIAbPreferMailFormat::unknown;
+ }
+ else if (!forcePlainText)
+ sendFormat = nsIAbPreferMailFormat::html;
+ else
+ NS_ERROR("unknown send format, should not happen");
+#endif
+
+ PL_strcpy (buf, "");
+ buf2 = buf + PL_strlen (buf);
+ if (mCompFields->GetTo() && *mCompFields->GetTo())
+ {
+ PL_strcat (buf2, mCompFields->GetTo());
+ if (addressCollector)
+ addressCollector->CollectAddress(nsCString(mCompFields->GetTo()),
+ collectAddresses /* create card if one doesn't exist */, sendFormat);
+ }
+ if (mCompFields->GetCc() && *mCompFields->GetCc()) {
+ if (*buf2) PL_strcat (buf2, ",");
+ PL_strcat (buf2, mCompFields->GetCc());
+ if (addressCollector)
+ addressCollector->CollectAddress(nsCString(mCompFields->GetCc()),
+ collectAddresses /* create card if one doesn't exist */, sendFormat);
+ }
+ if (mCompFields->GetBcc() && *mCompFields->GetBcc()) {
+ if (*buf2) PL_strcat (buf2, ",");
+ PL_strcat (buf2, mCompFields->GetBcc());
+ if (addressCollector)
+ addressCollector->CollectAddress(nsCString(mCompFields->GetBcc()),
+ collectAddresses /* create card if one doesn't exist */, sendFormat);
+ }
+
+ // We need undo groups to keep only the addresses
+ nsresult rv = StripOutGroupNames(buf);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Ok, now MIME II encode this to prevent 8bit problems...
+ char *convbuf = nsMsgI18NEncodeMimePartIIStr(buf, true,
+ mCompFields->GetCharacterSet(), 0, nsMsgMIMEGetConformToStandard());
+ if (convbuf)
+ {
+ // MIME-PartII conversion
+ PR_FREEIF(buf);
+ buf = convbuf;
+ }
+
+ nsCString escaped_buf;
+ MsgEscapeString(nsDependentCString(buf), nsINetUtil::ESCAPE_URL_PATH, escaped_buf);
+
+ if (!escaped_buf.IsEmpty())
+ {
+ NS_Free(buf);
+ buf = ToNewCString(escaped_buf);
+ }
+
+ nsCOMPtr<nsISmtpService> smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && smtpService)
+ {
+ MsgDeliveryListener *deliveryListener = new MsgDeliveryListener(this, false);
+ if (!deliveryListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // we used to get the prompt from the compose window and we'd pass that in
+ // to the smtp protocol as the prompt to use. But when you send a message,
+ // we dismiss the compose window.....so you are parenting off of a window that
+ // isn't there. To have it work correctly I think we want the alert dialogs to be modal
+ // to the top most mail window...after all, that's where we are going to be sending status
+ // update information too....
+
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ GetNotificationCallbacks(getter_AddRefs(callbacks));
+
+ // Tell the user we are sending the message!
+ nsString msg;
+ mComposeBundle->GetStringFromName(u"sendingMessage", getter_Copies(msg));
+ SetStatusMessage(msg);
+ nsCOMPtr<nsIMsgStatusFeedback> msgStatus (do_QueryInterface(mSendProgress));
+ // if the sendProgress isn't set, let's use the member variable.
+ if (!msgStatus)
+ msgStatus = do_QueryInterface(mStatusFeedback);
+
+ nsCOMPtr<nsIURI> runningUrl;
+ rv = smtpService->SendMailMessage(mTempFile, buf, mUserIdentity,
+ mSmtpPassword.get(), deliveryListener, msgStatus,
+ callbacks, mCompFields->GetDSN(),
+ getter_AddRefs(runningUrl),
+ getter_AddRefs(mRunningRequest));
+ // set envid on the returned URL
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsISmtpUrl> smtpUrl(do_QueryInterface(runningUrl, &rv));
+ if (NS_SUCCEEDED(rv))
+ smtpUrl->SetDsnEnvid(nsDependentCString(mCompFields->GetMessageId()));
+ }
+ }
+
+ PR_FREEIF(buf); // free the buf because we are done with it....
+ return rv;
+}
+
+nsresult
+nsMsgComposeAndSend::DeliverFileAsNews()
+{
+ nsresult rv = NS_OK;
+ if (!(mCompFields->GetNewsgroups()))
+ return rv;
+
+ if (mSendReport)
+ mSendReport->SetCurrentProcess(nsIMsgSendReport::process_NNTP);
+
+ nsCOMPtr<nsIPrompt> promptObject;
+ GetDefaultPrompt(getter_AddRefs(promptObject));
+
+ nsCOMPtr<nsINntpService> nntpService(do_GetService(NS_NNTPSERVICE_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv) && nntpService)
+ {
+ MsgDeliveryListener *deliveryListener = new MsgDeliveryListener(this, true);
+ if (!deliveryListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Tell the user we are posting the message!
+ nsString msg;
+ mComposeBundle->GetStringFromName(u"postingMessage",
+ getter_Copies(msg));
+ SetStatusMessage(msg);
+
+ nsCOMPtr <nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // JFD TODO: we should use GetDefaultPrompt instead
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+ // see bug #163139
+ // we might not have a msg window if only the compose window is open.
+ if(NS_FAILED(rv))
+ msgWindow = nullptr;
+
+ rv = nntpService->PostMessage(mTempFile, mCompFields->GetNewsgroups(), mAccountKey.get(),
+ deliveryListener, msgWindow, nullptr);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::Fail(nsresult aFailureCode, const char16_t *aErrorMsg,
+ nsresult *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = aFailureCode;
+
+ if (NS_FAILED(aFailureCode))
+ {
+ nsCOMPtr<nsIPrompt> prompt;
+ GetDefaultPrompt(getter_AddRefs(prompt));
+
+ if (mSendReport)
+ {
+ int32_t process;
+ if (NS_SUCCEEDED(mSendReport->GetCurrentProcess(&process)) && process == nsIMsgSendReport::process_Current)
+ {
+ // currentProcess isn't set yet, so we need another value.
+ mSendReport->SetCurrentProcess(nsIMsgSendReport::process_BuildMessage);
+ }
+ mSendReport->SetError(nsIMsgSendReport::process_Current, aFailureCode, false);
+ mSendReport->SetMessage(nsIMsgSendReport::process_Current, aErrorMsg, false);
+ mSendReport->DisplayReport(prompt, true, true, aResult);
+ }
+ else
+ {
+ if (aFailureCode != NS_ERROR_BUT_DONT_SHOW_ALERT)
+ nsMsgDisplayMessageByName(prompt, u"sendFailed");
+ }
+ }
+
+ if (NS_SUCCEEDED(m_status))
+ m_status = NS_ERROR_BUT_DONT_SHOW_ALERT;
+
+ //Stop any pending process...
+ Abort();
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgComposeAndSend::FormatStringWithSMTPHostNameByName(const char16_t* aMsgName, char16_t **aString)
+{
+ NS_ENSURE_ARG(aString);
+
+ nsresult rv;
+ nsCOMPtr<nsISmtpService> smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Get the smtp hostname and format the string.
+ nsCString smtpHostName;
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = smtpService->GetServerByIdentity(mUserIdentity, getter_AddRefs(smtpServer));
+ if (NS_SUCCEEDED(rv))
+ smtpServer->GetHostname(smtpHostName);
+
+ nsAutoString hostStr;
+ CopyASCIItoUTF16(smtpHostName, hostStr);
+ const char16_t *params[] = { hostStr.get() };
+ if (NS_SUCCEEDED(rv))
+ mComposeBundle->FormatStringFromName(aMsgName, params, 1, aString);
+ return rv;
+}
+
+void
+nsMsgComposeAndSend::DoDeliveryExitProcessing(nsIURI * aUri, nsresult aExitCode, bool aCheckForMail)
+{
+ // If we fail on the news delivery, no sense in going on so just notify
+ // the user and exit.
+ if (NS_FAILED(aExitCode))
+ {
+ const char16_t* exitString = errorStringNameForErrorCode(aExitCode);
+ nsString eMsg;
+ if (aExitCode == NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_SERVER ||
+ aExitCode == NS_ERROR_SMTP_SEND_FAILED_UNKNOWN_REASON ||
+ aExitCode == NS_ERROR_SMTP_SEND_FAILED_REFUSED ||
+ aExitCode == NS_ERROR_SMTP_SEND_FAILED_INTERRUPTED ||
+ aExitCode == NS_ERROR_SMTP_SEND_FAILED_TIMEOUT ||
+ aExitCode == NS_ERROR_SMTP_PASSWORD_UNDEFINED ||
+ aExitCode == NS_ERROR_SMTP_AUTH_FAILURE ||
+ aExitCode == NS_ERROR_SMTP_AUTH_GSSAPI ||
+ aExitCode == NS_ERROR_SMTP_AUTH_MECH_NOT_SUPPORTED ||
+ aExitCode == NS_ERROR_SMTP_AUTH_NOT_SUPPORTED ||
+ aExitCode == NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_NO_SSL ||
+ aExitCode == NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_SSL ||
+ aExitCode == NS_ERROR_SMTP_AUTH_CHANGE_PLAIN_TO_ENCRYPT ||
+ aExitCode == NS_ERROR_STARTTLS_FAILED_EHLO_STARTTLS) {
+ FormatStringWithSMTPHostNameByName(exitString, getter_Copies(eMsg));
+ } else {
+ mComposeBundle->GetStringFromName(exitString, getter_Copies(eMsg));
+ }
+
+ Fail(aExitCode, eMsg.get(), &aExitCode);
+ NotifyListenerOnStopSending(nullptr, aExitCode, nullptr, nullptr);
+ return;
+ }
+
+ if (aCheckForMail)
+ {
+ if ((mCompFields->GetTo() && *mCompFields->GetTo()) ||
+ (mCompFields->GetCc() && *mCompFields->GetCc()) ||
+ (mCompFields->GetBcc() && *mCompFields->GetBcc()))
+ {
+ // If we're sending this news message to mail as well, start it now.
+ // Completion and further errors will be handled there.
+ DeliverFileAsMail();
+ return;
+ }
+ }
+
+ //
+ // Tell the listeners that we are done with the sending operation...
+ //
+ NotifyListenerOnStopSending(mCompFields->GetMessageId(),
+ aExitCode,
+ nullptr,
+ nullptr);
+
+ // If we hit here, we are done with delivery!
+ //
+ // Just call the DoFCC() method and if that fails, then we should just
+ // cleanup and get out. If DoFCC "succeeds", then all that means is the
+ // async copy operation has been started and we will be notified later
+ // when it is done. DON'T cleanup until the copy is complete and don't
+ // notify the listeners with OnStop() until we are done.
+ //
+ // For now, we don't need to do anything here, but the code will stay this
+ // way until later...
+ //
+
+ DoFcc();
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::DeliverAsMailExit(nsIURI *aUrl, nsresult aExitCode)
+{
+ DoDeliveryExitProcessing(aUrl, aExitCode, false);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::DeliverAsNewsExit(nsIURI *aUrl, nsresult aExitCode)
+{
+ DoDeliveryExitProcessing(aUrl, aExitCode, mSendMailAlso);
+ return NS_OK;
+}
+
+bool nsMsgComposeAndSend::CanSaveMessagesToFolder(const char *folderURL)
+{
+ nsresult rv;
+ nsCOMPtr<nsIRDFService> rdf(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv));
+ if (NS_FAILED(rv))
+ return false;
+
+ nsCOMPtr<nsIRDFResource> resource;
+ rv = rdf->GetResource(nsDependentCString(folderURL), getter_AddRefs(resource));
+ if (NS_FAILED(rv))
+ return false;
+
+ nsCOMPtr <nsIMsgFolder> thisFolder;
+ thisFolder = do_QueryInterface(resource, &rv);
+ if (NS_FAILED(rv) || !thisFolder)
+ return false;
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = thisFolder->GetServer(getter_AddRefs(server));
+ if (NS_FAILED(rv) || !server)
+ return false;
+
+ // See if we are allowed to save/file msgs to this folder.
+ bool canSave;
+ rv = server->GetCanFileMessagesOnServer(&canSave);
+ return canSave;
+}
+
+//
+// Now, start the appropriate copy operation.
+//
+nsresult
+nsMsgComposeAndSend::DoFcc()
+{
+ //
+ // Just cleanup and return success if we're not allowed to save msgs to FCC folder.
+ //
+ const char* fcc = mCompFields->GetFcc();
+ if (!fcc || !*fcc || !CanSaveMessagesToFolder(fcc))
+ {
+
+ // It is the caller's responsibility to say we've stopped sending, so just
+ // let the listeners know we're not doing a copy.
+ NotifyListenerOnStopCopy(NS_OK); // For closure of compose window...
+ return NS_OK;
+ }
+
+ if (mSendReport)
+ mSendReport->SetCurrentProcess(nsIMsgSendReport::process_Copy);
+
+ //
+ // If we are here, then we need to save off the FCC file to save and
+ // start the copy operation. MimeDoFCC() will take care of all of this
+ // for us.
+ //
+ nsresult rv = MimeDoFCC(mTempFile,
+ nsMsgDeliverNow,
+ mCompFields->GetBcc(),
+ mCompFields->GetFcc(),
+ mCompFields->GetNewspostUrl());
+ if (NS_FAILED(rv))
+ {
+ //
+ // If we hit here, the copy operation FAILED and we should at least tell the
+ // user that it did fail but the send operation has already succeeded.
+ //
+ NotifyListenerOnStopCopy(rv);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::NotifyListenerOnStartSending(const char *aMsgID, uint32_t aMsgSize)
+{
+ if (mListener)
+ mListener->OnStartSending(aMsgID, aMsgSize);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::NotifyListenerOnProgress(const char *aMsgID, uint32_t aProgress, uint32_t aProgressMax)
+{
+ if (mListener)
+ mListener->OnProgress(aMsgID, aProgress, aProgressMax);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::NotifyListenerOnStatus(const char *aMsgID, const char16_t *aMsg)
+{
+ if (mListener)
+ mListener->OnStatus(aMsgID, aMsg);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::NotifyListenerOnStopSending(const char *aMsgID, nsresult aStatus, const char16_t *aMsg,
+ nsIFile *returnFile)
+{
+ if (mListener != nullptr)
+ mListener->OnStopSending(aMsgID, aStatus, aMsg, returnFile);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::NotifyListenerOnStartCopy()
+{
+ nsCOMPtr<nsIMsgCopyServiceListener> copyListener;
+
+ if (mListener)
+ {
+ copyListener = do_QueryInterface(mListener);
+ if (copyListener)
+ copyListener->OnStartCopy();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::NotifyListenerOnProgressCopy(uint32_t aProgress,
+ uint32_t aProgressMax)
+{
+ nsCOMPtr<nsIMsgCopyServiceListener> copyListener;
+
+ if (mListener)
+ {
+ copyListener = do_QueryInterface(mListener);
+ if (copyListener)
+ copyListener->OnProgress(aProgress, aProgressMax);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::SetMessageKey(nsMsgKey aMessageKey)
+{
+ m_messageKey = aMessageKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetMessageKey(nsMsgKey *aMessageKey)
+{
+ *aMessageKey = m_messageKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetFolderUri(nsACString &aFolderUri)
+{
+ aFolderUri = m_folderName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetPartForDomIndex(int32_t aDomIndex, nsACString &aPartNum)
+{
+ aPartNum = m_partNumbers.SafeElementAt(aDomIndex, EmptyCString());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetMessageId(nsACString& aMessageId)
+{
+ nsresult rv = NS_OK;
+ if (mCompFields)
+ aMessageId = mCompFields->GetMessageId();
+ else
+ rv = NS_ERROR_NULL_POINTER;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::NotifyListenerOnStopCopy(nsresult aStatus)
+{
+ // This is one per copy so make sure we clean this up first.
+ mCopyObj = nullptr;
+
+ // Set a status message...
+ nsString msg;
+ if (NS_SUCCEEDED(aStatus))
+ mComposeBundle->GetStringFromName(u"copyMessageComplete", getter_Copies(msg));
+ else
+ mComposeBundle->GetStringFromName(u"copyMessageFailed", getter_Copies(msg));
+
+ SetStatusMessage(msg);
+ nsCOMPtr<nsIPrompt> prompt;
+ GetDefaultPrompt(getter_AddRefs(prompt));
+
+ if (NS_FAILED(aStatus))
+ {
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString msg;
+ const char16_t *formatStrings[] = { mSavedToFolderName.get() };
+
+ rv = bundle->FormatStringFromName(u"errorSavingMsg",
+ formatStrings, 1,
+ getter_Copies(msg));
+ if (NS_SUCCEEDED(rv))
+ {
+ bool retry = false;
+ nsMsgAskBooleanQuestionByString(prompt, msg.get(), &retry, nullptr);
+ if (retry)
+ {
+ mSendProgress = nullptr; // this was cancelled, so we need to clear it.
+ return SendToMagicFolder(m_deliver_mode);
+ }
+ }
+
+ // We failed, and the user decided not to retry. So we're just going to
+ // fail out. However, give Fail a success code so that it doesn't prompt
+ // the user a second time as they already know about the failure.
+ Fail(NS_OK, nullptr, &aStatus);
+ }
+
+ if (NS_SUCCEEDED(aStatus) &&
+ !mPerformingSecondFCC && m_messageKey != nsMsgKey_None &&
+ (m_deliver_mode == nsMsgDeliverNow || m_deliver_mode == nsMsgSendUnsent))
+ {
+ nsresult rv = FilterSentMessage();
+ if (NS_FAILED(rv))
+ OnStopOperation(rv);
+ return rv;
+ }
+
+ return MaybePerformSecondFCC(aStatus);
+}
+
+nsresult
+nsMsgComposeAndSend::FilterSentMessage()
+{
+ if (mSendReport)
+ mSendReport->SetCurrentProcess(nsIMsgSendReport::process_Filter);
+
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = GetExistingFolder(m_folderName, getter_AddRefs(folder));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = folder->GetMessageHeader(m_messageKey, getter_AddRefs(msgHdr));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMutableArray> msgArray(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgFilterService> filterSvc = do_GetService(NS_MSGFILTERSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = msgArray->AppendElement(msgHdr, false);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ if (mSendProgress)
+ mSendProgress->GetMsgWindow(getter_AddRefs(msgWindow));
+
+ return filterSvc->ApplyFilters(nsMsgFilterType::PostOutgoing, msgArray, folder, msgWindow, this);
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::OnStopOperation(nsresult aStatus)
+{
+ // Set a status message...
+ nsString msg;
+ if (NS_SUCCEEDED(aStatus))
+ mComposeBundle->GetStringFromName(u"filterMessageComplete", getter_Copies(msg));
+ else
+ mComposeBundle->GetStringFromName(u"filterMessageFailed", getter_Copies(msg));
+
+ SetStatusMessage(msg);
+
+ if (NS_FAILED(aStatus))
+ {
+ nsresult rv = mComposeBundle->GetStringFromName(u"errorFilteringMsg", getter_Copies(msg));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIPrompt> prompt;
+ GetDefaultPrompt(getter_AddRefs(prompt));
+ nsMsgDisplayMessageByString(prompt, msg.get(), nullptr);
+ }
+
+ // We failed, however, give Fail a success code so that it doesn't prompt
+ // the user a second time as they already know about the failure.
+ Fail(NS_OK, nullptr, &aStatus);
+ }
+
+ return MaybePerformSecondFCC(aStatus);
+}
+
+nsresult
+nsMsgComposeAndSend::MaybePerformSecondFCC(nsresult aStatus)
+{
+ // Ok, now to support a second copy operation, we need to figure
+ // out which copy request just finished. If the user has requested
+ // a second copy operation, then we need to fire that off, but if they
+ // just wanted a single copy operation, we can tell everyone we are done
+ // and move on with life. Only do the second copy if the first one worked.
+ //
+ if ( NS_SUCCEEDED(aStatus) && (mNeedToPerformSecondFCC) )
+ {
+ if (mSendReport)
+ mSendReport->SetCurrentProcess(nsIMsgSendReport::process_FCC);
+
+ mNeedToPerformSecondFCC = false;
+ mPerformingSecondFCC = true;
+
+ const char *fcc2 = mCompFields->GetFcc2();
+ if (fcc2 && *fcc2)
+ {
+ nsresult rv = MimeDoFCC(mTempFile,
+ nsMsgDeliverNow,
+ mCompFields->GetBcc(),
+ fcc2,
+ mCompFields->GetNewspostUrl());
+ if (NS_FAILED(rv))
+ Fail(rv, nullptr, &aStatus);
+ else
+ return NS_OK;
+ }
+ }
+
+ // If we are here, its real cleanup time!
+ if (mListener)
+ {
+ nsCOMPtr<nsIMsgCopyServiceListener> copyListener =
+ do_QueryInterface(mListener);
+ if (copyListener)
+ copyListener->OnStopCopy(aStatus);
+ }
+
+ return aStatus;
+}
+
+/* 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'
+ and `attachment1_body'. These may all be 0 if all attachments are
+ provided externally.
+
+ Subsequent attachments are provided as URLs to load, described in the
+ nsMsgAttachmentData 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...)
+ */
+NS_IMETHODIMP
+nsMsgComposeAndSend::CreateAndSendMessage(
+ nsIEditor *aEditor,
+ nsIMsgIdentity *aUserIdentity,
+ const char *aAccountKey,
+ nsIMsgCompFields *fields,
+ bool digest_p,
+ bool dont_deliver_p,
+ nsMsgDeliverMode mode,
+ nsIMsgDBHdr *msgToReplace,
+ const char *attachment1_type,
+ const nsACString &attachment1_body,
+ nsIArray *attachments,
+ nsIArray *preloaded_attachments,
+ mozIDOMWindowProxy *parentWindow,
+ nsIMsgProgress *progress,
+ nsIMsgSendListener *aListener,
+ const char *password,
+ const nsACString &aOriginalMsgURI,
+ MSG_ComposeType aType
+ )
+{
+ nsresult rv;
+ /* First thing to do is to reset the send errors report */
+ mSendReport->Reset();
+ mSendReport->SetDeliveryMode(mode);
+
+ mParentWindow = do_QueryInterface(parentWindow);
+ mSendProgress = progress;
+ mListener = aListener;
+
+ // Set the editor for MHTML operations if necessary
+ if (aEditor)
+ mEditor = aEditor;
+
+ rv = Init(aUserIdentity, aAccountKey, (nsMsgCompFields *)fields, nullptr,
+ digest_p, dont_deliver_p, mode, msgToReplace,
+ attachment1_type, attachment1_body,
+ attachments, preloaded_attachments,
+ password, aOriginalMsgURI, aType);
+
+ if (NS_FAILED(rv) && mSendReport)
+ mSendReport->SetError(nsIMsgSendReport::process_Current, rv, false);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::CreateRFC822Message(
+ nsIMsgIdentity *aUserIdentity,
+ nsIMsgCompFields *aFields,
+ const char *aMsgType,
+ const nsACString &aMsgBody,
+ bool aIsDraft,
+ nsIArray *aAttachments,
+ nsIArray *aEmbeddedObjects,
+ nsIMsgSendListener *aListener
+ )
+{
+ nsresult rv;
+ nsMsgDeliverMode mode = aIsDraft ? nsIMsgSend::nsMsgSaveAsDraft :
+ nsIMsgSend::nsMsgDeliverNow;
+
+ /* First thing to do is to reset the send errors report */
+ mSendReport->Reset();
+ mSendReport->SetDeliveryMode(mode);
+
+ mParentWindow = nullptr;
+ mSendProgress = nullptr;
+ mListener = aListener;
+ mEmbeddedObjectList = aEmbeddedObjects;
+
+ rv = Init(aUserIdentity, nullptr, (nsMsgCompFields *)aFields, nullptr,
+ false, true, mode, nullptr,
+ aMsgType,
+ aMsgBody,
+ nullptr, aAttachments,
+ nullptr, EmptyCString(), nsIMsgCompType::New);
+
+ if (NS_FAILED(rv) && mSendReport)
+ mSendReport->SetError(nsIMsgSendReport::process_Current, rv, false);
+
+ return rv;
+}
+
+nsresult
+nsMsgComposeAndSend::SendMessageFile(
+ nsIMsgIdentity *aUserIndentity,
+ const char *aAccountKey,
+ nsIMsgCompFields *fields,
+ nsIFile *sendIFile,
+ bool deleteSendFileOnCompletion,
+ bool digest_p,
+ nsMsgDeliverMode mode,
+ nsIMsgDBHdr *msgToReplace,
+ nsIMsgSendListener *aListener,
+ nsIMsgStatusFeedback *aStatusFeedback,
+ const char *password
+ )
+{
+ NS_ENSURE_ARG_POINTER(fields);
+ NS_ENSURE_ARG_POINTER(sendIFile);
+
+ nsresult rv;
+
+ /* First thing to do is to reset the send errors report */
+ mSendReport->Reset();
+ mSendReport->SetDeliveryMode(mode);
+
+ mStatusFeedback = aStatusFeedback;
+ //
+ // First check to see if the external file we are sending is a valid file.
+ //
+ bool exists;
+ if (NS_FAILED(sendIFile->Exists(&exists)))
+ return NS_ERROR_INVALID_ARG;
+
+ if (!exists)
+ return NS_ERROR_INVALID_ARG;
+
+ // Setup the listener...
+ mListener = aListener;
+
+ // Should we delete the temp file when done?
+ if (!deleteSendFileOnCompletion)
+ mReturnFile = sendIFile;
+
+ rv = Init(aUserIndentity, aAccountKey, (nsMsgCompFields *)fields, sendIFile,
+ digest_p, false, mode, msgToReplace,
+ nullptr, EmptyCString(),
+ nullptr, nullptr,
+ password, EmptyCString(), nsIMsgCompType::New);
+
+ if (NS_SUCCEEDED(rv))
+ rv = DeliverMessage();
+
+ if (NS_FAILED(rv) && mSendReport)
+ mSendReport->SetError(nsIMsgSendReport::process_Current, rv, false);
+
+ return rv;
+}
+
+//
+// Send the message to the magic folder, and runs the completion/failure
+// callback.
+//
+nsresult
+nsMsgComposeAndSend::SendToMagicFolder(nsMsgDeliverMode mode)
+{
+ nsresult rv = MimeDoFCC(mTempFile,
+ mode,
+ mCompFields->GetBcc(),
+ mCompFields->GetFcc(),
+ mCompFields->GetNewspostUrl());
+ //
+ // The caller of MimeDoFCC needs to deal with failure.
+ //
+ if (NS_FAILED(rv))
+ rv = NotifyListenerOnStopCopy(rv);
+
+ return rv;
+}
+
+char*
+nsMsgGetEnvelopeLine(void)
+{
+ static char result[75] = "";
+ PRExplodedTime now;
+ char buffer[128] = "";
+
+ // Generate envelope line in format of: From - Sat Apr 18 20:01:49 1998
+ //
+ // 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_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
+ PR_FormatTimeUSEnglish(buffer, sizeof(buffer),
+ "%a %b %d %H:%M:%S %Y",
+ &now);
+
+ // This value must be in ctime() format, with English abbreviations.
+ // PL_strftime("... %c ...") is no good, because it is localized.
+ //
+ PL_strcpy(result, "From - ");
+ PL_strcpy(result + 7, buffer);
+ PL_strcpy(result + 7 + 24, CRLF);
+ return result;
+}
+
+#define ibuffer_size FILE_IO_BUFFER_SIZE
+nsresult
+nsMsgComposeAndSend::MimeDoFCC(nsIFile *input_file,
+ nsMsgDeliverMode mode,
+ const char *bcc_header,
+ const char *fcc_header,
+ const char *news_url)
+{
+ nsresult status = NS_OK;
+ char *ibuffer = nullptr;
+ uint32_t n;
+ bool folderIsLocal = true;
+ nsCString turi;
+ char16_t *printfString = nullptr;
+ nsString msg;
+ nsCOMPtr<nsIMsgFolder> folder;
+
+ // Before continuing, just check the user has not cancel the operation
+ if (mSendProgress)
+ {
+ bool canceled = false;
+ mSendProgress->GetProcessCanceledByUser(&canceled);
+ if (canceled)
+ return NS_ERROR_ABORT;
+ else
+ mSendProgress->OnProgressChange(nullptr, nullptr, 0, 0, 0, -1);
+ }
+
+ //
+ // Ok, this is here to keep track of this for 2 copy operations...
+ //
+ if (mCopyFile)
+ {
+ mCopyFile2 = mCopyFile;
+ mCopyFile = nullptr;
+ }
+
+ //
+ // Create the file that will be used for the copy service!
+ //
+ nsresult rv = nsMsgCreateTempFile("nscopy.tmp", getter_AddRefs(mCopyFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIOutputStream> tempOutfile;
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(tempOutfile), mCopyFile, -1, 00600);
+ if (NS_FAILED(rv))
+ {
+ if (mSendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithTmpFile(mCopyFile, error_msg);
+ mSendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ status = NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
+
+ mCopyFile = nullptr;
+ return status;
+ }
+
+ //
+ // Get our files ready...
+ //
+ nsCOMPtr<nsIInputStream> inputFile;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), input_file);
+ if (NS_FAILED(rv))
+ {
+ if (mSendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithFile(input_file, error_msg);
+ mSendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ status = NS_MSG_UNABLE_TO_OPEN_FILE;
+ goto FAIL;
+ }
+
+ // now the buffers...
+ ibuffer = (char *) PR_Malloc(ibuffer_size);
+ if (!ibuffer)
+ {
+ status = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+
+ //
+ // First, we we need to put a Berkeley "From - " delimiter at the head of
+ // the file for parsing...
+ //
+
+ if (fcc_header && *fcc_header)
+ GetExistingFolder(nsDependentCString(fcc_header), getter_AddRefs(folder));
+
+ if ((mode == nsMsgDeliverNow || mode == nsMsgSendUnsent) && folder)
+ turi = fcc_header;
+ else
+ GetFolderURIFromUserPrefs(mode, mUserIdentity, turi);
+ status = MessageFolderIsLocal(mUserIdentity, mode, turi.get(), &folderIsLocal);
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ // Tell the user we are copying the message...
+ mComposeBundle->GetStringFromName(u"copyMessageStart",
+ getter_Copies(msg));
+ if (!msg.IsEmpty())
+ {
+ nsCOMPtr<nsIRDFService> rdfService = do_GetService(kRDFServiceCID);
+ if (rdfService)
+ {
+ nsCOMPtr<nsIRDFResource> res;
+ rdfService->GetResource(turi, getter_AddRefs(res));
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(res);
+ if (folder)
+ folder->GetName(mSavedToFolderName);
+ }
+ if (!mSavedToFolderName.IsEmpty())
+ printfString = nsTextFormatter::smprintf(msg.get(), mSavedToFolderName.get());
+ else
+ printfString = nsTextFormatter::smprintf(msg.get(), "?");
+ if (printfString)
+ {
+ SetStatusMessage(nsDependentString(printfString));
+ PR_Free(printfString);
+ }
+ }
+
+ if (folderIsLocal)
+ {
+ char *envelopeLine = nsMsgGetEnvelopeLine();
+ uint32_t len = PL_strlen(envelopeLine);
+
+ rv = tempOutfile->Write(envelopeLine, len, &n);
+ if (NS_FAILED(rv) || n != len)
+ {
+ status = NS_ERROR_FAILURE;
+ 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 nsMsgMessageFlags::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.
+ //
+ //
+ // Need to add these lines for POP3 ONLY! IMAP servers will handle
+ // this status information for summary file regeneration for us.
+ if ((mode == nsMsgQueueForLater || mode == nsMsgSaveAsDraft ||
+ mode == nsMsgSaveAsTemplate || mode == nsMsgDeliverNow ||
+ mode == nsMsgSendUnsent || mode == nsMsgDeliverBackground) &&
+ folderIsLocal)
+ {
+ char *buf = 0;
+ uint16_t flags = 0;
+
+ // for save as draft and send later, we want to leave the message as unread.
+ // See Bug #198087
+ // Messages sent with mode nsMsgDeliverBackground must not have the Queued
+ // flag sent so that they get picked up by the background send function.
+ if (mode == nsMsgQueueForLater)
+ flags |= nsMsgMessageFlags::Queued;
+ else if (mode != nsMsgSaveAsDraft && mode != nsMsgDeliverBackground)
+ flags |= nsMsgMessageFlags::Read;
+ buf = PR_smprintf(X_MOZILLA_STATUS_FORMAT CRLF, flags);
+ if (buf)
+ {
+ uint32_t len = PL_strlen(buf);
+ rv = tempOutfile->Write(buf, len, &n);
+ PR_Free(buf);
+ if (NS_FAILED(rv) || n != len)
+ {
+ status = NS_ERROR_FAILURE;
+ goto FAIL;
+ }
+ }
+
+ uint32_t flags2 = 0;
+ if (mode == nsMsgSaveAsTemplate)
+ flags2 |= nsMsgMessageFlags::Template;
+ if (mode == nsMsgDeliverNow || mode == nsMsgSendUnsent)
+ {
+ flags2 &= ~nsMsgMessageFlags::MDNReportNeeded;
+ flags2 |= nsMsgMessageFlags::MDNReportSent;
+ }
+ buf = PR_smprintf(X_MOZILLA_STATUS2_FORMAT CRLF, flags2);
+ if (buf)
+ {
+ uint32_t len = PL_strlen(buf);
+ rv = tempOutfile->Write(buf, len, &n);
+ PR_Free(buf);
+ if (NS_FAILED(rv) || n != len)
+ {
+ status = NS_ERROR_FAILURE;
+ goto FAIL;
+ }
+ }
+ tempOutfile->Write(X_MOZILLA_KEYWORDS, sizeof(X_MOZILLA_KEYWORDS) - 1, &n);
+ }
+
+ // 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 == nsMsgQueueForLater || mode == nsMsgDeliverBackground ||
+ mode == nsMsgSaveAsDraft || mode == nsMsgSaveAsTemplate) &&
+ fcc_header && *fcc_header)
+ {
+ int32_t L = PL_strlen(fcc_header) + 20;
+ char *buf = (char *) PR_Malloc (L);
+ if (!buf)
+ {
+ status = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+
+ PR_snprintf(buf, L-1, "FCC: %s" CRLF, fcc_header);
+
+ uint32_t len = PL_strlen(buf);
+ rv = tempOutfile->Write(buf, len, &n);
+ if (NS_FAILED(rv) || n != len)
+ {
+ status = NS_ERROR_FAILURE;
+ goto FAIL;
+ }
+ }
+
+ //
+ // Ok, now I want to get the identity key and write it out if this is for a
+ // nsMsgQueueForLater operation!
+ //
+ if ((nsMsgQueueForLater == mode || nsMsgSaveAsDraft == mode ||
+ nsMsgDeliverBackground == mode || nsMsgSaveAsTemplate == mode) &&
+ mUserIdentity)
+ {
+ char *buf = nullptr;
+ nsCString key;
+
+ if (NS_SUCCEEDED(mUserIdentity->GetKey(key)) && !key.IsEmpty())
+ {
+ buf = PR_smprintf(HEADER_X_MOZILLA_IDENTITY_KEY ": %s" CRLF, key.get());
+ if (buf)
+ {
+ uint32_t len = strlen(buf);
+ rv = tempOutfile->Write(buf, len, &n);
+ PR_Free(buf);
+ if (NS_FAILED(rv) || n != len)
+ {
+ status = NS_ERROR_FAILURE;
+ goto FAIL;
+ }
+ }
+ }
+
+ if (!mAccountKey.IsEmpty())
+ {
+ buf = PR_smprintf(HEADER_X_MOZILLA_ACCOUNT_KEY ": %s" CRLF, mAccountKey.get());
+ if (buf)
+ {
+ uint32_t len = strlen(buf);
+ rv = tempOutfile->Write(buf, len, &n);
+ PR_Free(buf);
+ if (NS_FAILED(rv) || n != len)
+ {
+ status = NS_ERROR_FAILURE;
+ goto FAIL;
+ }
+ }
+ }
+ }
+
+ if (bcc_header && *bcc_header
+#ifndef SAVE_BCC_IN_FCC_FILE
+ && (mode == MSG_QueueForLater || mode == MSG_SaveAsDraft ||
+ mode == MSG_SaveAsTemplate)
+#endif
+ )
+ {
+ char *convBcc;
+ convBcc = nsMsgI18NEncodeMimePartIIStr(bcc_header, true,
+ mCompFields->GetCharacterSet(), sizeof("BCC: "),
+ nsMsgMIMEGetConformToStandard());
+
+ int32_t L = strlen(convBcc ? convBcc : bcc_header) + 20;
+ char *buf = (char *) PR_Malloc (L);
+ if (!buf)
+ {
+ status = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+
+ PR_snprintf(buf, L-1, "BCC: %s" CRLF, convBcc ? convBcc : bcc_header);
+ uint32_t len = strlen(buf);
+ rv = tempOutfile->Write(buf, len, &n);
+ PR_Free(buf);
+ PR_Free(convBcc);
+ if (NS_FAILED(rv) || n != len)
+ {
+ status = NS_ERROR_FAILURE;
+ 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 == nsMsgQueueForLater || mode == nsMsgSaveAsDraft ||
+ mode == nsMsgSaveAsTemplate || mode == nsMsgDeliverBackground) &&
+ news_url && *news_url)
+ {
+ bool secure_p = (news_url[0] == 's' || news_url[0] == 'S');
+ char *orig_hap = nsMsgParseURLHost (news_url);
+ 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 = PL_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" CRLF,
+ host_and_port ? host_and_port : "",
+ secure_p ? "/secure" : "");
+ PR_FREEIF(orig_hap);
+ if (!line)
+ {
+ status = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+
+ uint32_t len = PL_strlen(line);
+ rv = tempOutfile->Write(line, len, &n);
+ PR_Free(line);
+ if (NS_FAILED(rv) || n != len)
+ {
+ status = NS_ERROR_FAILURE;
+ goto FAIL;
+ }
+ }
+
+ PR_Free(orig_hap);
+ }
+
+ //
+ // 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...
+ //
+ uint64_t available;
+ rv = inputFile->Available(&available);
+ NS_ENSURE_SUCCESS(rv, rv);
+ while (available > 0)
+ {
+ // check *ibuffer in case that ibuffer isn't big enough
+ uint32_t readCount;
+ rv = inputFile->Read(ibuffer, ibuffer_size, &readCount);
+ if (NS_FAILED(rv) || readCount == 0 || *ibuffer == 0)
+ {
+ status = NS_ERROR_FAILURE;
+ goto FAIL;
+ }
+
+ rv = tempOutfile->Write(ibuffer, readCount, &n);
+ if (NS_FAILED(rv) || n != readCount) // write failed
+ {
+ status = NS_MSG_ERROR_WRITING_FILE;
+ goto FAIL;
+ }
+
+ rv = inputFile->Available(&available);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+FAIL:
+ PR_Free(ibuffer);
+
+ if (NS_FAILED(tempOutfile->Flush()))
+ status = NS_MSG_ERROR_WRITING_FILE;
+
+ tempOutfile->Close();
+
+ if (inputFile)
+ inputFile->Close();
+
+
+ // here we should clone mCopyFile, since it has changed on disk.
+ nsCOMPtr <nsIFile> clonedFile;
+ mCopyFile->Clone(getter_AddRefs(clonedFile));
+ mCopyFile = clonedFile;
+
+ // When we get here, we have to see if we have been successful so far.
+ // If we have, then we should start up the async copy service operation.
+ // If we weren't successful, then we should just return the error and
+ // bail out.
+ if (NS_SUCCEEDED(status))
+ {
+ // If we are here, time to start the async copy service operation!
+ status = StartMessageCopyOperation(mCopyFile, mode, turi);
+ }
+ return status;
+}
+
+//
+// This is pretty much a wrapper to the functionality that lives in the
+// nsMsgCopy class
+//
+nsresult
+nsMsgComposeAndSend::StartMessageCopyOperation(nsIFile *aFile,
+ nsMsgDeliverMode mode,
+ const nsCString& dest_uri)
+{
+ mCopyObj = new nsMsgCopy();
+ if (!mCopyObj)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ //
+ // Actually, we need to pick up the proper folder from the prefs and not
+ // default to the default "Flagged" folder choices
+ //
+ nsresult rv;
+ if (!dest_uri.IsEmpty())
+ m_folderName = dest_uri;
+ else
+ GetFolderURIFromUserPrefs(mode, mUserIdentity, m_folderName);
+
+ if (mListener)
+ mListener->OnGetDraftFolderURI(m_folderName.get());
+
+ rv = mCopyObj->StartCopyOperation(mUserIdentity, aFile, mode,
+ this, m_folderName.get(), mMsgToReplace);
+ return rv;
+}
+
+//I'm getting this each time without holding onto the feedback so that 3 pane windows can be closed
+//without any chance of crashing due to holding onto a deleted feedback.
+nsresult
+nsMsgComposeAndSend::SetStatusMessage(const nsString &aMsgString)
+{
+ if (mSendProgress)
+ mSendProgress->OnStatusChange(nullptr, nullptr, NS_OK, aMsgString.get());
+ return NS_OK;
+}
+
+// For GUI notification...
+nsresult
+nsMsgComposeAndSend::SetGUINotificationState(bool aEnableFlag)
+{
+ mGUINotificationEnabled = aEnableFlag;
+ return NS_OK;
+}
+
+/* readonly attribute nsIMsgSendReport sendReport; */
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetSendReport(nsIMsgSendReport * *aSendReport)
+{
+ NS_ENSURE_ARG_POINTER(aSendReport);
+ NS_IF_ADDREF(*aSendReport = mSendReport);
+ return NS_OK;
+}
+
+nsresult nsMsgComposeAndSend::Abort()
+{
+ uint32_t i;
+ nsresult rv;
+
+ if (mAbortInProcess)
+ return NS_OK;
+
+ mAbortInProcess = true;
+
+ if (m_plaintext)
+ rv = m_plaintext->Abort();
+
+ for (i = 0; i < m_attachment_count; i ++)
+ {
+ nsMsgAttachmentHandler *ma = m_attachments[i];
+ if (ma)
+ rv = ma->Abort();
+ }
+
+ /* stop the current running url */
+ if (mRunningRequest)
+ {
+ mRunningRequest->Cancel(NS_ERROR_ABORT);
+ mRunningRequest = nullptr;
+ }
+
+ if (mCopyObj)
+ {
+ nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ copyService->NotifyCompletion(mCopyFile, mCopyObj->mDstFolder, NS_ERROR_ABORT);
+ }
+ mAbortInProcess = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetProcessAttachmentsSynchronously(bool *_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = m_be_synchronous_p;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetAttachmentHandlers(nsTArray<RefPtr<nsMsgAttachmentHandler>> **_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ *_retval = &m_attachments;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetAttachmentCount(uint32_t *aAttachmentCount)
+{
+ NS_ENSURE_ARG(aAttachmentCount);
+ *aAttachmentCount = m_attachment_count;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetPendingAttachmentCount(uint32_t *aPendingAttachmentCount)
+{
+ NS_ENSURE_ARG(aPendingAttachmentCount);
+ *aPendingAttachmentCount = m_attachment_pending_count;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeAndSend::SetPendingAttachmentCount(uint32_t aPendingAttachmentCount)
+{
+ m_attachment_pending_count = aPendingAttachmentCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetDeliveryMode(nsMsgDeliverMode *aDeliveryMode)
+{
+ NS_ENSURE_ARG(aDeliveryMode);
+ *aDeliveryMode = m_deliver_mode;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetProgress(nsIMsgProgress **_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ NS_IF_ADDREF(*_retval = mSendProgress);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetOutputStream(nsIOutputStream **_retval)
+{
+ NS_ENSURE_ARG(_retval);
+ NS_IF_ADDREF(*_retval = mOutputFile);
+ return NS_OK;
+}
+
+
+/* [noscript] attribute nsIURI runningURL; */
+NS_IMETHODIMP nsMsgComposeAndSend::GetRunningRequest(nsIRequest **request)
+{
+ NS_ENSURE_ARG(request);
+ NS_IF_ADDREF(*request = mRunningRequest);
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgComposeAndSend::SetRunningRequest(nsIRequest *request)
+{
+ mRunningRequest = request;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetStatus(nsresult *aStatus)
+{
+ NS_ENSURE_ARG(aStatus);
+ *aStatus = m_status;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::SetStatus(nsresult aStatus)
+{
+ m_status = aStatus;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::GetCryptoclosure(nsIMsgComposeSecure ** aCryptoclosure)
+{
+ NS_ENSURE_ARG(aCryptoclosure);
+ NS_IF_ADDREF(*aCryptoclosure = m_crypto_closure);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgComposeAndSend::SetCryptoclosure(nsIMsgComposeSecure * aCryptoclosure)
+{
+ m_crypto_closure = aCryptoclosure;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetSendCompFields(nsIMsgCompFields** aCompFields)
+{
+ NS_ENSURE_ARG_POINTER(aCompFields);
+ nsCOMPtr<nsIMsgCompFields> qiCompFields(mCompFields);
+ NS_ENSURE_STATE(qiCompFields);
+ qiCompFields.forget(aCompFields);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetSendBody(nsAString& aBody)
+{
+ nsCString charSet;
+ if (mCompFields)
+ mCompFields->GetCharacterSet(getter_Copies(charSet));
+ if (!m_attachment1_body) {
+ aBody.Truncate();
+ return NS_OK;
+ }
+ return ConvertToUnicode(charSet.get(), m_attachment1_body, aBody);
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetSendBodyType(nsACString& aBodyType)
+{
+ if (m_attachment1_type && *m_attachment1_type)
+ aBodyType.Assign(nsDependentCString(m_attachment1_type));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetIdentity(nsIMsgIdentity **aIdentity)
+{
+ NS_ENSURE_ARG_POINTER(aIdentity);
+ *aIdentity = mUserIdentity;
+ NS_IF_ADDREF(*aIdentity);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetAttachment(uint32_t aIndex,
+ nsIMsgAttachmentHandler **aAttachment)
+{
+ NS_ENSURE_ARG_POINTER(aAttachment);
+ if (aIndex >= m_attachment_count)
+ return NS_ERROR_ILLEGAL_VALUE;
+ *aAttachment = m_attachments[aIndex];
+ NS_IF_ADDREF(*aAttachment);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::SetSavedToFolderName(const nsAString& aName)
+{
+ mSavedToFolderName.Assign(aName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetSavedToFolderName(nsAString& aName)
+{
+ aName.Assign(mSavedToFolderName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgComposeAndSend::SetDontDeliver(bool aDontDeliver)
+{
+ m_dont_deliver_p = aDontDeliver;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsMsgComposeAndSend::GetDontDeliver(bool *aDontDeliver)
+{
+ NS_ENSURE_ARG_POINTER(aDontDeliver);
+ *aDontDeliver = m_dont_deliver_p;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsMsgAttachmentData, nsIMsgAttachmentData)
+
+nsMsgAttachmentData::nsMsgAttachmentData() : m_size(0), m_sizeExternalStr("-1"),
+ m_isExternalAttachment(false), m_isExternalLinkAttachment(false),
+ m_isDownloaded(false), m_hasFilename(false), m_displayableInline(false)
+{
+}
+
+nsMsgAttachmentData::~nsMsgAttachmentData()
+{
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::GetUrl(nsIURI **aUrl)
+{
+ NS_ENSURE_ARG_POINTER(aUrl);
+ NS_IF_ADDREF(*aUrl = m_url);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::SetUrl(nsIURI *aUrl)
+{
+ m_url = aUrl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::GetDesiredType(nsACString &aDesiredType)
+{
+ aDesiredType = m_desiredType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::SetDesiredType(const nsACString &aDesiredType)
+{
+ m_desiredType = aDesiredType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::GetRealType(nsACString &aRealType)
+{
+ aRealType = m_realType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::SetRealType(const nsACString &aRealType)
+{
+ m_realType = aRealType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::GetRealEncoding(nsACString &aRealEncoding)
+{
+ aRealEncoding = m_realEncoding;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::SetRealEncoding(const nsACString &aRealEncoding)
+{
+ m_realEncoding = aRealEncoding;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::GetRealName(nsACString &aRealName)
+{
+ aRealName = m_realName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::SetRealName(const nsACString &aRealName)
+{
+ m_realName = aRealName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::GetDescription(nsACString &aDescription)
+{
+ aDescription = m_description;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::SetDescription(const nsACString &aDescription)
+{
+ m_description = aDescription;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::GetXMacType(nsACString & aXMacType)
+{
+ aXMacType = m_xMacType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::SetXMacType(const nsACString & aXMacType)
+{
+ m_xMacType = aXMacType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::GetXMacCreator(nsACString & aXMacCreator)
+{
+ aXMacCreator = m_xMacCreator;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachmentData::SetXMacCreator(const nsACString & aXMacCreator)
+{
+ m_xMacCreator = aXMacCreator;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsMsgAttachedFile, nsIMsgAttachedFile)
+
+nsMsgAttachedFile::nsMsgAttachedFile() : m_size(0), m_unprintableCount(0),
+ m_highbitCount(0), m_ctlCount(0), m_nullCount(0), m_maxLineLength(0)
+{
+}
+
+nsMsgAttachedFile::~nsMsgAttachedFile()
+{
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetOrigUrl(nsIURI **aOrigUrl)
+{
+ NS_ENSURE_ARG_POINTER(aOrigUrl);
+ NS_IF_ADDREF(*aOrigUrl = m_origUrl);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetOrigUrl(nsIURI *aOrigUrl)
+{
+ m_origUrl = aOrigUrl;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetTmpFile(nsIFile **aTmpFile)
+{
+ NS_ENSURE_ARG_POINTER(aTmpFile);
+ NS_IF_ADDREF(*aTmpFile = m_tmpFile);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetTmpFile(nsIFile *aTmpFile)
+{
+ m_tmpFile = aTmpFile;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetType(nsACString &aType)
+{
+ aType = m_type;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetType(const nsACString &aType)
+{
+ m_type = aType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetEncoding(nsACString &aEncoding)
+{
+ aEncoding = m_encoding;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetEncoding(const nsACString &aEncoding)
+{
+ m_encoding = aEncoding;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetDescription(nsACString &aDescription)
+{
+ aDescription = m_description;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetDescription(const nsACString &aDescription)
+{
+ m_description = aDescription;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetCloudPartInfo(nsACString &aCloudPartInfo)
+{
+ aCloudPartInfo = m_cloudPartInfo;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetCloudPartInfo(const nsACString &aCloudPartInfo)
+{
+ m_cloudPartInfo = aCloudPartInfo;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetXMacType(nsACString & aXMacType)
+{
+ aXMacType = m_xMacType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetXMacType(const nsACString & aXMacType)
+{
+ m_xMacType = aXMacType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetXMacCreator(nsACString & aXMacCreator)
+{
+ aXMacCreator = m_xMacCreator;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetXMacCreator(const nsACString & aXMacCreator)
+{
+ m_xMacCreator = aXMacCreator;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetRealName(nsACString & aRealName)
+{
+ aRealName = m_realName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetRealName(const nsACString & aRealName)
+{
+ m_realName = aRealName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetSize(uint32_t *aSize)
+{
+ NS_ENSURE_ARG_POINTER(aSize);
+ *aSize = m_size;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetSize(uint32_t aSize)
+{
+ m_size = aSize;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetUnprintableCount(uint32_t *aUnprintableCount)
+{
+ NS_ENSURE_ARG_POINTER(aUnprintableCount);
+ *aUnprintableCount = m_unprintableCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetUnprintableCount(uint32_t aUnprintableCount)
+{
+ m_unprintableCount = aUnprintableCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetHighbitCount(uint32_t *aHighbitCount)
+{
+ NS_ENSURE_ARG_POINTER(aHighbitCount);
+ *aHighbitCount = m_highbitCount;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgAttachedFile::SetHighbitCount(uint32_t aHighbitCount)
+{
+ m_highbitCount = aHighbitCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetCtlCount(uint32_t *aCtlCount)
+{
+ NS_ENSURE_ARG_POINTER(aCtlCount);
+ *aCtlCount = m_ctlCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetCtlCount(uint32_t aCtlCount)
+{
+ m_ctlCount = aCtlCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetNullCount(uint32_t *aNullCount)
+{
+ NS_ENSURE_ARG_POINTER(aNullCount);
+ *aNullCount = m_nullCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetNullCount(uint32_t aNullCount)
+{
+ m_nullCount = aNullCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::GetMaxLineLength(uint32_t *aMaxLineLength)
+{
+ NS_ENSURE_ARG_POINTER(aMaxLineLength);
+ *aMaxLineLength = m_maxLineLength;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgAttachedFile::SetMaxLineLength(uint32_t aMaxLineLength)
+{
+ m_maxLineLength = aMaxLineLength;
+ return NS_OK;
+}
diff --git a/mailnews/compose/src/nsMsgSend.h b/mailnews/compose/src/nsMsgSend.h
new file mode 100644
index 000000000..558c8e53e
--- /dev/null
+++ b/mailnews/compose/src/nsMsgSend.h
@@ -0,0 +1,405 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __MSGSEND_H__
+#define __MSGSEND_H__
+
+/* 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 990 bytes (see LINELENGTH_ENCODING_THRESHOLD below)
+ and it is not of type message/rfc822.
+
+ - 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.
+ */
+
+/* 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
+
+
+//
+// Necessary includes
+//
+#include "nsCOMPtr.h"
+#include "nsIMsgSend.h"
+#include "nsIStringBundle.h"
+#include "msgCore.h"
+#include "prprf.h"
+#include "nsIOutputStream.h"
+#include "nsMsgMessageFlags.h"
+#include "nsIURL.h"
+#include "nsMsgAttachmentHandler.h"
+#include "nsMsgCompFields.h"
+#include "nsIMsgSendListener.h"
+#include "nsIDOMNode.h"
+#include "nsIEditor.h"
+#include "nsIUrlListener.h"
+#include "nsIMsgStatusFeedback.h"
+#include "nsIMsgIdentity.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgIdentity.h"
+#include "nsWeakReference.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDOMWindow.h"
+#include "nsIMsgComposeSecure.h"
+#include "nsAutoPtr.h"
+#include "nsMsgAttachmentData.h"
+#include "nsIMsgFilterService.h"
+#include "nsIMsgOperationListener.h"
+
+//
+// Some necessary defines...
+//
+#define MIME_BUFFER_SIZE 4096 // must be greater than 1000
+ // SMTP (RFC821) limit
+// Maximum number of bytes we allow in a line before we force
+// encoding to base64 if not already QR-encoded or of type message/rfc822.
+#define LINELENGTH_ENCODING_THRESHOLD 990
+
+//
+// Utilities for string handling
+//
+#define PUSH_STRING(S) \
+ do { PL_strcpy (buffer_tail, S); buffer_tail += PL_strlen (S); } while(0)
+#define PUSH_STRINGN(S,N) \
+ do { memcpy(buffer_tail, (S), (N)); buffer_tail += (N); } while(0)
+#define PUSH_NEWLINE() \
+ do { *buffer_tail++ = '\r'; *buffer_tail++ = '\n'; *buffer_tail = '\0'; } while(0)
+
+//
+// Forward declarations...
+//
+class nsMsgSendPart;
+class nsMsgCopy;
+class nsIPrompt;
+class nsIInterfaceRequestor;
+
+namespace mozilla {
+namespace mailnews {
+class MimeEncoder;
+}
+}
+
+class nsMsgComposeAndSend : public nsIMsgSend,
+ public nsIMsgOperationListener,
+ public nsSupportsWeakReference
+{
+ typedef mozilla::mailnews::MimeEncoder MimeEncoder;
+public:
+ //
+ // Define QueryInterface, AddRef and Release for this class
+ //
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIMSGSEND
+ NS_DECL_NSIMSGOPERATIONLISTENER
+
+ nsMsgComposeAndSend();
+
+
+ // Delivery and completion callback routines...
+ NS_IMETHOD DeliverMessage();
+ NS_IMETHOD DeliverFileAsMail();
+ NS_IMETHOD DeliverFileAsNews();
+ void DoDeliveryExitProcessing(nsIURI * aUrl, nsresult aExitCode, bool aCheckForMail);
+ nsresult FormatStringWithSMTPHostNameByName(const char16_t* aMsgName, char16_t **aString);
+
+ nsresult DoFcc();
+ nsresult StartMessageCopyOperation(nsIFile *aFileSpec,
+ nsMsgDeliverMode mode,
+ const nsCString& dest_uri);
+
+
+ nsresult SendToMagicFolder(nsMsgDeliverMode flag);
+
+ // Check to see if it's ok to save msgs to the configured folder.
+ bool CanSaveMessagesToFolder(const char *folderURL);
+
+ //
+ // FCC operations...
+ //
+ nsresult MimeDoFCC (nsIFile *input_file,
+ nsMsgDeliverMode mode,
+ const char *bcc_header,
+ const char *fcc_header,
+ const char *news_url);
+
+ // Init() will allow for either message creation without delivery or full
+ // message creation and send operations
+ //
+ nsresult Init(
+ nsIMsgIdentity *aUserIdentity,
+ const char *aAccountKey,
+ nsMsgCompFields *fields,
+ nsIFile *sendFile,
+ bool digest_p,
+ bool dont_deliver_p,
+ nsMsgDeliverMode mode,
+ nsIMsgDBHdr *msgToReplace,
+ const char *attachment1_type,
+ const nsACString &attachment1_body,
+ nsIArray *attachments,
+ nsIArray *preloaded_attachments,
+ const char *password,
+ const nsACString &aOriginalMsgURI,
+ MSG_ComposeType aType);
+
+ //
+ // Setup the composition fields
+ //
+ nsresult InitCompositionFields(nsMsgCompFields *fields,
+ const nsACString &aOriginalMsgURI,
+ MSG_ComposeType aType);
+
+ NS_IMETHOD GetBodyFromEditor();
+
+
+ //
+ // Attachment processing...
+ //
+ nsresult HackAttachments(nsIArray *attachments,
+ nsIArray *preloaded_attachments);
+ nsresult CountCompFieldAttachments();
+ nsresult AddCompFieldLocalAttachments();
+ nsresult AddCompFieldRemoteAttachments(uint32_t aStartLocation, int32_t *aMailboxCount, int32_t *aNewsCount);
+
+ // Deal with multipart related data
+ nsresult ProcessMultipartRelated(int32_t *aMailboxCount, int32_t *aNewsCount);
+ nsresult GetEmbeddedObjectInfo(nsIDOMNode *node, nsMsgAttachmentData *attachment, bool *acceptObject);
+ uint32_t GetMultipartRelatedCount(bool forceToBeCalculated = false);
+ nsCOMPtr<nsIArray> mEmbeddedObjectList; // it's initialized when calling GetMultipartRelatedCount
+
+ // Body processing
+ nsresult SnarfAndCopyBody(const nsACString &attachment1_body,
+ const char *attachment1_type);
+
+ int32_t PreProcessPart(nsMsgAttachmentHandler *ma,
+ nsMsgSendPart *toppart); // The very top most container of the message
+ // For part processing
+
+ nsresult SetStatusMessage(const nsString &aMsgString); // Status message method
+
+ //
+ // All vars necessary for this implementation
+ //
+ nsMsgKey m_messageKey; // jt -- Draft/Template support; newly created key
+ nsCOMPtr<nsIMsgIdentity> mUserIdentity;
+ nsCString mAccountKey;
+ RefPtr<nsMsgCompFields> mCompFields; // All needed composition fields (header, etc...)
+ nsCOMPtr<nsIFile> mTempFile; // our temporary file
+
+ nsCOMPtr<nsIOutputStream> mOutputFile; // the actual output file stream
+ uint32_t mMessageWarningSize; // Warn if a message is over this size!
+
+ bool m_dont_deliver_p; // If set, we just return the nsIFile of the file
+ // created, instead of actually delivering message.
+ nsMsgDeliverMode m_deliver_mode; // nsMsgDeliverNow, nsMsgQueueForLater, nsMsgSaveAsDraft,
+ // nsMsgSaveAsTemplate and nsMsgSendUnsent
+ nsCOMPtr<nsIMsgDBHdr> mMsgToReplace; // If the mode is nsMsgSaveAsDraft, this is the message it will
+ // replace
+ nsString mSavedToFolderName; // Name of folder we're saving to, used when
+ // displaying error on save.
+ // These are needed for callbacks to the FE...
+ nsCOMPtr<nsPIDOMWindowOuter> mParentWindow;
+ nsCOMPtr<nsIMsgProgress> mSendProgress;
+ nsCOMPtr<nsIMsgSendListener> mListener;
+ nsCOMPtr<nsIMsgStatusFeedback> mStatusFeedback;
+ nsCOMPtr<nsIRequest> mRunningRequest;
+ bool mSendMailAlso;
+ nsCOMPtr<nsIFile> mReturnFile; // a holder for file spec's to be returned to caller
+
+ // File where we stored our HTML so that we could make the plaintext form.
+ nsCOMPtr<nsIFile> mHTMLFile;
+
+ // Variable for storing the draft name;
+ nsCString m_folderName;
+
+ // mapping between editor dom node indexes and saved mime part numbers.
+ nsTArray<nsCString> m_partNumbers;
+ //
+ // These variables are needed for message Copy operations!
+ //
+ nsCOMPtr<nsIFile> mCopyFile;
+ nsCOMPtr<nsIFile> mCopyFile2;
+ RefPtr<nsMsgCopy> mCopyObj;
+ bool mNeedToPerformSecondFCC;
+ bool mPerformingSecondFCC;
+
+ // For MHTML message creation
+ nsCOMPtr<nsIEditor> mEditor;
+
+ //
+ // The first attachment, if any (typed in by the user.)
+ //
+ char *m_attachment1_type;
+ char *m_attachment1_encoding;
+ nsAutoPtr<MimeEncoder> m_attachment1_encoder;
+ char *m_attachment1_body;
+ uint32_t m_attachment1_body_length;
+ char *mOriginalHTMLBody;
+
+ // The plaintext form of the first attachment, if needed.
+ RefPtr<nsMsgAttachmentHandler> m_plaintext;
+
+ // The multipart/related save object for HTML text.
+ nsMsgSendPart *m_related_part;
+ nsMsgSendPart *m_related_body_part;
+
+ //
+ // Subsequent attachments, if any.
+ //
+ uint32_t m_attachment_count;
+ uint32_t m_attachment_pending_count;
+ nsTArray< RefPtr<nsMsgAttachmentHandler> > m_attachments;
+ nsresult m_status; // in case some attachments fail but not all
+
+ uint32_t mPreloadedAttachmentCount;
+ uint32_t mRemoteAttachmentCount;
+ int32_t mMultipartRelatedAttachmentCount; // the number of mpart related attachments, -1 means it has not been yet initialized
+
+ uint32_t mCompFieldLocalAttachments; // the number of file:// attachments in the comp fields
+ uint32_t mCompFieldRemoteAttachments; // the number of remote attachments in the comp fields
+
+ //
+ // attachment states and other info...
+ //
+ bool m_pre_snarfed_attachments_p; // If true, then the attachments were
+ // loaded by in the background and therefore
+ // we shouldn't delete the tmp files (but should
+ // leave that to the caller.)
+
+ bool m_digest_p; // Whether to be multipart/digest instead of
+ // multipart/mixed.
+
+ bool m_be_synchronous_p; // If true, we will load one URL after another,
+ // rather than starting all URLs going at once
+ // and letting them load in parallel. This is
+ // more efficient if (for example) all URLs are
+ // known to be coming from the same news server
+ // or mailbox: loading them in parallel would
+ // cause multiple connections to the news
+ // server to be opened, or would cause much seek()ing.
+
+ bool mGUINotificationEnabled; // Should we throw up the GUI alerts on errors?
+ bool mAbortInProcess; // Used by Abort to avoid reentrance.
+
+ nsCOMPtr<nsIMsgComposeSecure> m_crypto_closure;
+
+protected:
+ nsCOMPtr<nsIStringBundle> mComposeBundle;
+ nsresult GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks);
+
+ virtual ~nsMsgComposeAndSend();
+ nsresult FilterSentMessage();
+ nsresult MaybePerformSecondFCC(nsresult aStatus);
+
+ // generates a message id for our message, if necessary
+ void GenerateMessageId( );
+
+ // add default custom headers to the message
+ nsresult AddDefaultCustomHeaders();
+
+ // add Mail-Followup-To and Mail-Reply-To header
+ nsresult AddMailFollowupToHeader();
+ nsresult AddMailReplyToHeader();
+ nsresult AddXForwardedMessageIdHeader();
+
+ nsCOMPtr<nsIMsgSendReport> mSendReport;
+ nsCString mSmtpPassword; // store the smtp Password use during a send
+};
+
+//
+// These C routines should only be used by the nsMsgSendPart class.
+//
+extern nsresult mime_write_message_body(nsIMsgSend *state, const char *buf, uint32_t size);
+extern char *mime_get_stream_write_buffer(void);
+extern nsresult mime_encoder_output_fn (const char *buf, int32_t size, void *closure);
+extern bool UseQuotedPrintable(void);
+
+#endif /* __MSGSEND_H__ */
diff --git a/mailnews/compose/src/nsMsgSendLater.cpp b/mailnews/compose/src/nsMsgSendLater.cpp
new file mode 100644
index 000000000..97206f12b
--- /dev/null
+++ b/mailnews/compose/src/nsMsgSendLater.cpp
@@ -0,0 +1,1552 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsMsgSendLater.h"
+#include "nsMsgCopy.h"
+#include "nsIMsgSend.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIMsgMessageService.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgCompCID.h"
+#include "nsMsgCompUtils.h"
+#include "nsMsgUtils.h"
+#include "nsMailHeaders.h"
+#include "nsMsgPrompts.h"
+#include "nsISmtpUrl.h"
+#include "nsIChannel.h"
+#include "nsNetUtil.h"
+#include "prlog.h"
+#include "prmem.h"
+#include "nsIMimeConverter.h"
+#include "nsMsgMimeCID.h"
+#include "nsComposeStrings.h"
+#include "nsIMutableArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsIObserverService.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIMsgDatabase.h"
+#include "mozilla/Services.h"
+#include "nsArrayUtils.h"
+
+// Consts for checking and sending mail in milliseconds
+
+// 1 second from mail into the unsent messages folder to initially trying to
+// send it.
+const uint32_t kInitialMessageSendTime = 1000;
+
+NS_IMPL_ISUPPORTS(nsMsgSendLater,
+ nsIMsgSendLater,
+ nsIFolderListener,
+ nsIRequestObserver,
+ nsIStreamListener,
+ nsIObserver,
+ nsIUrlListener,
+ nsIMsgShutdownTask)
+
+nsMsgSendLater::nsMsgSendLater()
+{
+ mSendingMessages = false;
+ mTimerSet = false;
+ mTotalSentSuccessfully = 0;
+ mTotalSendCount = 0;
+ mLeftoverBuffer = nullptr;
+
+ m_to = nullptr;
+ m_bcc = nullptr;
+ m_fcc = nullptr;
+ m_newsgroups = nullptr;
+ m_newshost = nullptr;
+ m_headers = nullptr;
+ m_flags = 0;
+ m_headersFP = 0;
+ m_inhead = true;
+ m_headersPosition = 0;
+
+ m_bytesRead = 0;
+ m_position = 0;
+ m_flagsPosition = 0;
+ m_headersSize = 0;
+
+ mIdentityKey = nullptr;
+ mAccountKey = nullptr;
+}
+
+nsMsgSendLater::~nsMsgSendLater()
+{
+ PR_Free(m_to);
+ PR_Free(m_fcc);
+ PR_Free(m_bcc);
+ PR_Free(m_newsgroups);
+ PR_Free(m_newshost);
+ PR_Free(m_headers);
+ PR_Free(mLeftoverBuffer);
+ PR_Free(mIdentityKey);
+ PR_Free(mAccountKey);
+}
+
+nsresult
+nsMsgSendLater::Init()
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool sendInBackground;
+ rv = prefs->GetBoolPref("mailnews.sendInBackground", &sendInBackground);
+ // If we're not sending in the background, don't do anything else
+ if (NS_FAILED(rv) || !sendInBackground)
+ return NS_OK;
+
+ // We need to know when we're shutting down.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED);
+
+ rv = observerService->AddObserver(this, "xpcom-shutdown", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = observerService->AddObserver(this, "quit-application", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = observerService->AddObserver(this, "msg-shutdown", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Subscribe to the unsent messages folder
+ // XXX This code should be set up for multiple unsent folders, however we
+ // don't support that at the moment, so for now just assume one folder.
+ rv = GetUnsentMessagesFolder(nullptr, getter_AddRefs(mMessageFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mMessageFolder->AddFolderListener(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX may want to send messages X seconds after startup if there are any.
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::Observe(nsISupports *aSubject, const char* aTopic,
+ const char16_t *aData)
+{
+ if (aSubject == mTimer && !strcmp(aTopic, "timer-callback"))
+ {
+ if (mTimer)
+ mTimer->Cancel();
+ else
+ NS_ERROR("mTimer was null in nsMsgSendLater::Observe");
+
+ mTimerSet = false;
+ // If we've already started a send since the timer fired, don't start
+ // another
+ if (!mSendingMessages)
+ InternalSendMessages(false, nullptr);
+ }
+ else if (!strcmp(aTopic, "quit-application"))
+ {
+ // If the timer is set, cancel it - we're quitting, the shutdown service
+ // interfaces will sort out sending etc.
+ if (mTimer)
+ mTimer->Cancel();
+
+ mTimerSet = false;
+ }
+ else if (!strcmp(aTopic, "xpcom-shutdown"))
+ {
+ // We're shutting down. Unsubscribe from the unsentFolder notifications
+ // they aren't any use to us now, we don't want to start sending more
+ // messages.
+ nsresult rv;
+ if (mMessageFolder)
+ {
+ rv = mMessageFolder->RemoveFolderListener(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Now remove ourselves from the observer service as well.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED);
+
+ rv = observerService->RemoveObserver(this, "xpcom-shutdown");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = observerService->RemoveObserver(this, "quit-application");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = observerService->RemoveObserver(this, "msg-shutdown");
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::SetStatusFeedback(nsIMsgStatusFeedback *aFeedback)
+{
+ mFeedback = aFeedback;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::GetStatusFeedback(nsIMsgStatusFeedback **aFeedback)
+{
+ NS_ENSURE_ARG_POINTER(aFeedback);
+ NS_IF_ADDREF(*aFeedback = mFeedback);
+ return NS_OK;
+}
+
+// Stream is done...drive on!
+NS_IMETHODIMP
+nsMsgSendLater::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
+{
+ nsresult rv;
+
+ // First, this shouldn't happen, but if
+ // it does, flush the buffer and move on.
+ if (mLeftoverBuffer)
+ {
+ DeliverQueuedLine(mLeftoverBuffer, PL_strlen(mLeftoverBuffer));
+ }
+
+ if (mOutFile)
+ mOutFile->Close();
+
+ // See if we succeeded on reading the message from the message store?
+ //
+ if (NS_SUCCEEDED(status))
+ {
+ // Message is done...send it!
+ rv = CompleteMailFileSend();
+
+#ifdef NS_DEBUG
+ printf("nsMsgSendLater: Success on getting message...\n");
+#endif
+
+ // If the send operation failed..try the next one...
+ if (NS_FAILED(rv))
+ {
+ rv = StartNextMailFileSend(rv);
+ if (NS_FAILED(rv))
+ EndSendMessages(rv, nullptr, mTotalSendCount, mTotalSentSuccessfully);
+ }
+ }
+ else
+ {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+ if(!channel) return NS_ERROR_FAILURE;
+
+ // extract the prompt object to use for the alert from the url....
+ nsCOMPtr<nsIURI> uri;
+ nsCOMPtr<nsIPrompt> promptObject;
+ if (channel)
+ {
+ channel->GetURI(getter_AddRefs(uri));
+ nsCOMPtr<nsISmtpUrl> smtpUrl (do_QueryInterface(uri));
+ if (smtpUrl)
+ smtpUrl->GetPrompt(getter_AddRefs(promptObject));
+ }
+ nsMsgDisplayMessageByName(promptObject, u"errorQueuedDeliveryFailed");
+
+ // Getting the data failed, but we will still keep trying to send the rest...
+ rv = StartNextMailFileSend(status);
+ if (NS_FAILED(rv))
+ EndSendMessages(rv, nullptr, mTotalSendCount, mTotalSentSuccessfully);
+ }
+
+ return rv;
+}
+
+char *
+FindEOL(char *inBuf, char *buf_end)
+{
+ char *buf = inBuf;
+ char *findLoc = nullptr;
+
+ while (buf <= buf_end)
+ if (*buf == 0)
+ return buf;
+ else if ( (*buf == '\n') || (*buf == '\r') )
+ {
+ findLoc = buf;
+ break;
+ }
+ else
+ ++buf;
+
+ if (!findLoc)
+ return nullptr;
+ else if ((findLoc + 1) > buf_end)
+ return buf;
+
+ if ( (*findLoc == '\n' && *(findLoc+1) == '\r') ||
+ (*findLoc == '\r' && *(findLoc+1) == '\n'))
+ findLoc++; // possibly a pair.
+ return findLoc;
+}
+
+nsresult
+nsMsgSendLater::RebufferLeftovers(char *startBuf, uint32_t aLen)
+{
+ PR_FREEIF(mLeftoverBuffer);
+ mLeftoverBuffer = (char *)PR_Malloc(aLen + 1);
+ if (!mLeftoverBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ memcpy(mLeftoverBuffer, startBuf, aLen);
+ mLeftoverBuffer[aLen] = '\0';
+ return NS_OK;
+}
+
+nsresult
+nsMsgSendLater::BuildNewBuffer(const char* aBuf, uint32_t aCount, uint32_t *totalBufSize)
+{
+ // Only build a buffer when there are leftovers...
+ NS_ENSURE_TRUE(mLeftoverBuffer, NS_ERROR_FAILURE);
+
+ int32_t leftoverSize = PL_strlen(mLeftoverBuffer);
+ char* newBuffer = (char *) PR_Realloc(mLeftoverBuffer, aCount + leftoverSize);
+ NS_ENSURE_TRUE(newBuffer, NS_ERROR_OUT_OF_MEMORY);
+ mLeftoverBuffer = newBuffer;
+
+ memcpy(mLeftoverBuffer + leftoverSize, aBuf, aCount);
+ *totalBufSize = aCount + leftoverSize;
+ return NS_OK;
+}
+
+// Got data?
+NS_IMETHODIMP
+nsMsgSendLater::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, uint64_t sourceOffset, uint32_t count)
+{
+ NS_ENSURE_ARG_POINTER(inStr);
+
+ // This is a little bit tricky since we have to chop random
+ // buffers into lines and deliver the lines...plus keeping the
+ // leftovers for next time...some fun, eh?
+ //
+ nsresult rv = NS_OK;
+ char *startBuf;
+ char *endBuf;
+ char *lineEnd;
+ char *newbuf = nullptr;
+ uint32_t size;
+
+ uint32_t aCount = count;
+ char *aBuf = (char *)PR_Malloc(aCount + 1);
+
+ inStr->Read(aBuf, count, &aCount);
+
+ // First, create a new work buffer that will
+ if (NS_FAILED(BuildNewBuffer(aBuf, aCount, &size))) // no leftovers...
+ {
+ startBuf = (char *)aBuf;
+ endBuf = (char *)(aBuf + aCount - 1);
+ }
+ else // yum, leftovers...new buffer created...sitting in mLeftoverBuffer
+ {
+ newbuf = mLeftoverBuffer;
+ startBuf = newbuf;
+ endBuf = startBuf + size - 1;
+ mLeftoverBuffer = nullptr; // null out this
+ }
+
+ while (startBuf <= endBuf)
+ {
+ lineEnd = FindEOL(startBuf, endBuf);
+ if (!lineEnd)
+ {
+ rv = RebufferLeftovers(startBuf, (endBuf - startBuf) + 1);
+ break;
+ }
+
+ rv = DeliverQueuedLine(startBuf, (lineEnd - startBuf) + 1);
+ if (NS_FAILED(rv))
+ break;
+
+ startBuf = lineEnd+1;
+ }
+
+ PR_Free(newbuf);
+ PR_Free(aBuf);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnStartRunningUrl(nsIURI *url)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnStopRunningUrl(nsIURI *url, nsresult aExitCode)
+{
+ if (NS_SUCCEEDED(aExitCode))
+ InternalSendMessages(mUserInitiated, mIdentity);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
+{
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// This is the listener class for the send operation. We have to create this class
+// to listen for message send completion and eventually notify the caller
+////////////////////////////////////////////////////////////////////////////////////
+NS_IMPL_ISUPPORTS(SendOperationListener, nsIMsgSendListener,
+ nsIMsgCopyServiceListener)
+
+SendOperationListener::SendOperationListener(nsMsgSendLater *aSendLater)
+: mSendLater(aSendLater)
+{
+}
+
+SendOperationListener::~SendOperationListener(void)
+{
+}
+
+NS_IMETHODIMP
+SendOperationListener::OnGetDraftFolderURI(const char *aFolderURI)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SendOperationListener::OnStartSending(const char *aMsgID, uint32_t aMsgSize)
+{
+#ifdef NS_DEBUG
+ printf("SendOperationListener::OnStartSending()\n");
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SendOperationListener::OnProgress(const char *aMsgID, uint32_t aProgress, uint32_t aProgressMax)
+{
+#ifdef NS_DEBUG
+ printf("SendOperationListener::OnProgress()\n");
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SendOperationListener::OnStatus(const char *aMsgID, const char16_t *aMsg)
+{
+#ifdef NS_DEBUG
+ printf("SendOperationListener::OnStatus()\n");
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SendOperationListener::OnSendNotPerformed(const char *aMsgID, nsresult aStatus)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SendOperationListener::OnStopSending(const char *aMsgID, nsresult aStatus, const char16_t *aMsg,
+ nsIFile *returnFile)
+{
+ if (mSendLater && !mSendLater->OnSendStepFinished(aStatus))
+ NS_RELEASE(mSendLater);
+
+ return NS_OK;
+}
+
+// nsIMsgCopyServiceListener
+
+NS_IMETHODIMP
+SendOperationListener::OnStartCopy(void)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SendOperationListener::OnProgress(uint32_t aProgress, uint32_t aProgressMax)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SendOperationListener::SetMessageKey(nsMsgKey aKey)
+{
+ NS_NOTREACHED("SendOperationListener::SetMessageKey()");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SendOperationListener::GetMessageId(nsACString& messageId)
+{
+ NS_NOTREACHED("SendOperationListener::GetMessageId()\n");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+SendOperationListener::OnStopCopy(nsresult aStatus)
+{
+ if (mSendLater)
+ {
+ mSendLater->OnCopyStepFinished(aStatus);
+ NS_RELEASE(mSendLater);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgSendLater::CompleteMailFileSend()
+{
+ // get the identity from the key
+ // if no key, or we fail to find the identity
+ // use the default identity on the default account
+ nsCOMPtr<nsIMsgIdentity> identity;
+ nsresult rv = GetIdentityFromKey(mIdentityKey, getter_AddRefs(identity));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // If for some reason the tmp file didn't get created, we've failed here
+ bool created;
+ mTempFile->Exists(&created);
+ if (!created)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIMsgCompFields> compFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIMsgSend> pMsgSend = do_CreateInstance(NS_MSGSEND_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Since we have already parsed all of the headers, we are simply going to
+ // set the composition fields and move on.
+ nsCString author;
+ mMessage->GetAuthor(getter_Copies(author));
+
+ nsMsgCompFields * fields = (nsMsgCompFields *)compFields.get();
+
+ fields->SetFrom(author.get());
+
+ if (m_to)
+ {
+ fields->SetTo(m_to);
+ }
+
+ if (m_bcc)
+ {
+ fields->SetBcc(m_bcc);
+ }
+
+ if (m_fcc)
+ {
+ fields->SetFcc(m_fcc);
+ }
+
+ if (m_newsgroups)
+ fields->SetNewsgroups(m_newsgroups);
+
+#if 0
+ // needs cleanup. Is this needed?
+ if (m_newshost)
+ fields->SetNewspostUrl(m_newshost);
+#endif
+
+ // Create the listener for the send operation...
+ SendOperationListener *sendListener = new SendOperationListener(this);
+ if (!sendListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(sendListener);
+
+ NS_ADDREF(this); //TODO: We should remove this!!!
+ rv = pMsgSend->SendMessageFile(identity,
+ mAccountKey,
+ compFields, // nsIMsgCompFields *fields,
+ mTempFile, // nsIFile *sendFile,
+ true, // bool deleteSendFileOnCompletion,
+ false, // bool digest_p,
+ nsIMsgSend::nsMsgSendUnsent, // nsMsgDeliverMode mode,
+ nullptr, // nsIMsgDBHdr *msgToReplace,
+ sendListener,
+ mFeedback,
+ nullptr);
+ NS_RELEASE(sendListener);
+ return rv;
+}
+
+nsresult
+nsMsgSendLater::StartNextMailFileSend(nsresult prevStatus)
+{
+ bool hasMoreElements = false;
+ if ((!mEnumerator) ||
+ NS_FAILED(mEnumerator->HasMoreElements(&hasMoreElements)) ||
+ !hasMoreElements)
+ {
+ // Notify that this message has finished being sent.
+ NotifyListenersOnProgress(mTotalSendCount, mMessagesToSend.Count(), 100, 100);
+
+ // EndSendMessages resets everything for us
+ EndSendMessages(prevStatus, nullptr, mTotalSendCount, mTotalSentSuccessfully);
+
+ // XXX Should we be releasing references so that we don't hold onto items
+ // unnecessarily.
+ return NS_OK;
+ }
+
+ // If we've already sent a message, and are sending more, send out a progress
+ // update with 100% for both send and copy as we must have finished by now.
+ if (mTotalSendCount)
+ NotifyListenersOnProgress(mTotalSendCount, mMessagesToSend.Count(), 100, 100);
+
+ nsCOMPtr<nsISupports> currentItem;
+ nsresult rv = mEnumerator->GetNext(getter_AddRefs(currentItem));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mMessage = do_QueryInterface(currentItem);
+ if (!mMessage)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ if (!mMessageFolder)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCString messageURI;
+ mMessageFolder->GetUriForMsg(mMessage, messageURI);
+
+ rv = nsMsgCreateTempFile("nsqmail.tmp", getter_AddRefs(mTempFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgMessageService> messageService;
+ rv = GetMessageServiceFromURI(messageURI, getter_AddRefs(messageService));
+ if (NS_FAILED(rv) && !messageService)
+ return NS_ERROR_FACTORY_NOT_LOADED;
+
+ ++mTotalSendCount;
+
+ nsCString identityKey;
+ rv = mMessage->GetStringProperty(HEADER_X_MOZILLA_IDENTITY_KEY,
+ getter_Copies(identityKey));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgIdentity> identity;
+ rv = GetIdentityFromKey(identityKey.get(), getter_AddRefs(identity));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Notify that we're just about to start sending this message
+ NotifyListenersOnMessageStartSending(mTotalSendCount, mMessagesToSend.Count(),
+ identity);
+
+ // Setup what we need to parse the data stream correctly
+ m_inhead = true;
+ m_headersFP = 0;
+ m_headersPosition = 0;
+ m_bytesRead = 0;
+ m_position = 0;
+ m_flagsPosition = 0;
+ m_headersSize = 0;
+ PR_FREEIF(mLeftoverBuffer);
+
+ // Now, get our stream listener interface and plug it into the DisplayMessage
+ // operation
+ AddRef();
+
+ nsCOMPtr<nsIURI> dummyNull;
+ rv = messageService->DisplayMessage(messageURI.get(),
+ static_cast<nsIStreamListener*>(this),
+ nullptr, nullptr, nullptr,
+ getter_AddRefs(dummyNull));
+
+ Release();
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::GetUnsentMessagesFolder(nsIMsgIdentity *aIdentity, nsIMsgFolder **folder)
+{
+ nsCString uri;
+ GetFolderURIFromUserPrefs(nsIMsgSend::nsMsgQueueForLater, aIdentity, uri);
+ return LocateMessageFolder(aIdentity, nsIMsgSend::nsMsgQueueForLater,
+ uri.get(), folder);
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::HasUnsentMessages(nsIMsgIdentity *aIdentity, bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ nsresult rv;
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIArray> accounts;
+ accountManager->GetAccounts(getter_AddRefs(accounts));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t cnt = 0;
+ rv = accounts->GetLength(&cnt);
+ if (cnt == 0) {
+ *aResult = false;
+ return NS_OK; // no account set up -> no unsent messages
+ }
+
+ // XXX This code should be set up for multiple unsent folders, however we
+ // don't support that at the moment, so for now just assume one folder.
+ if (!mMessageFolder)
+ {
+ rv = GetUnsentMessagesFolder(nullptr,
+ getter_AddRefs(mMessageFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ rv = ReparseDBIfNeeded(nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t totalMessages;
+ rv = mMessageFolder->GetTotalMessages(false, &totalMessages);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = totalMessages > 0;
+ return NS_OK;
+}
+
+//
+// To really finalize this capability, we need to have the ability to get
+// the message from the mail store in a stream for processing. The flow
+// would be something like this:
+//
+// foreach (message in Outbox folder)
+// get stream of Nth message
+// if (not done with headers)
+// Tack on to current buffer of headers
+// when done with headers
+// BuildHeaders()
+// Write Headers to Temp File
+// after done with headers
+// write rest of message body to temp file
+//
+// when done with the message
+// do send operation
+//
+// when send is complete
+// Copy from Outbox to FCC folder
+// Delete from Outbox folder
+//
+//
+NS_IMETHODIMP
+nsMsgSendLater::SendUnsentMessages(nsIMsgIdentity *aIdentity)
+{
+ return InternalSendMessages(true, aIdentity);
+}
+
+// Returns NS_OK if the db is OK, an error otherwise, e.g., we had to reparse.
+nsresult nsMsgSendLater::ReparseDBIfNeeded(nsIUrlListener *aListener)
+{
+ // This will kick off a reparse, if needed. So the next time we check if
+ // there are unsent messages, the db will be up to date.
+ nsCOMPtr<nsIMsgDatabase> unsentDB;
+ nsresult rv;
+ nsCOMPtr<nsIMsgLocalMailFolder> locFolder(do_QueryInterface(mMessageFolder, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return locFolder->GetDatabaseWithReparse(aListener, nullptr,
+ getter_AddRefs(unsentDB));
+}
+
+nsresult
+nsMsgSendLater::InternalSendMessages(bool aUserInitiated,
+ nsIMsgIdentity *aIdentity)
+{
+ if (WeAreOffline())
+ return NS_MSG_ERROR_OFFLINE;
+
+ // Protect against being called whilst we're already sending.
+ if (mSendingMessages)
+ {
+ NS_ERROR("nsMsgSendLater is already sending messages\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+
+ // XXX This code should be set up for multiple unsent folders, however we
+ // don't support that at the moment, so for now just assume one folder.
+ if (!mMessageFolder)
+ {
+ rv = GetUnsentMessagesFolder(nullptr,
+ getter_AddRefs(mMessageFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ nsCOMPtr<nsIMsgDatabase> unsentDB;
+ // Remember these in case we need to reparse the db.
+ mUserInitiated = aUserInitiated;
+ mIdentity = aIdentity;
+ rv = ReparseDBIfNeeded(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mIdentity = nullptr; // don't hold onto the identity since we're a service.
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = mMessageFolder->GetMessages(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // copy all the elements in the enumerator into our isupports array....
+
+ nsCOMPtr<nsISupports> currentItem;
+ nsCOMPtr<nsIMsgDBHdr> messageHeader;
+ bool hasMoreElements = false;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && hasMoreElements)
+ {
+ rv = enumerator->GetNext(getter_AddRefs(currentItem));
+ if (NS_SUCCEEDED(rv))
+ {
+ messageHeader = do_QueryInterface(currentItem, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ if (aUserInitiated)
+ // If the user initiated the send, add all messages
+ mMessagesToSend.AppendObject(messageHeader);
+ else
+ {
+ // Else just send those that are NOT marked as Queued.
+ uint32_t flags;
+ rv = messageHeader->GetFlags(&flags);
+ if (NS_SUCCEEDED(rv) && !(flags & nsMsgMessageFlags::Queued))
+ mMessagesToSend.AppendObject(messageHeader);
+ }
+ }
+ }
+ }
+
+ // Now get an enumerator for our array.
+ rv = NS_NewArrayEnumerator(getter_AddRefs(mEnumerator), mMessagesToSend);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We're now sending messages so its time to signal that and reset our counts.
+ mSendingMessages = true;
+ mTotalSentSuccessfully = 0;
+ mTotalSendCount = 0;
+
+ // Notify the listeners that we are starting a send.
+ NotifyListenersOnStartSending(mMessagesToSend.Count());
+
+ return StartNextMailFileSend(NS_OK);
+}
+
+nsresult nsMsgSendLater::SetOrigMsgDisposition()
+{
+ if (!mMessage)
+ return NS_ERROR_NULL_POINTER;
+
+ // We're finished sending a queued message. We need to look at mMessage
+ // and see if we need to set replied/forwarded
+ // flags for the original message that this message might be a reply to
+ // or forward of.
+ nsCString originalMsgURIs;
+ nsCString queuedDisposition;
+ mMessage->GetStringProperty(ORIG_URI_PROPERTY, getter_Copies(originalMsgURIs));
+ mMessage->GetStringProperty(QUEUED_DISPOSITION_PROPERTY, getter_Copies(queuedDisposition));
+ if (!queuedDisposition.IsEmpty())
+ {
+ nsTArray<nsCString> uriArray;
+ ParseString(originalMsgURIs, ',', uriArray);
+ for (uint32_t i = 0; i < uriArray.Length(); i++)
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ nsresult rv = GetMsgDBHdrFromURI(uriArray[i].get(), getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (msgHdr)
+ {
+ // get the folder for the message resource
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ msgHdr->GetFolder(getter_AddRefs(msgFolder));
+ if (msgFolder)
+ {
+ nsMsgDispositionState dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Replied;
+ if (queuedDisposition.Equals("forwarded"))
+ dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Forwarded;
+
+ msgFolder->AddMessageDispositionState(msgHdr, dispositionSetting);
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsMsgSendLater::DeleteCurrentMessage()
+{
+ if (!mMessage)
+ {
+ NS_ERROR("nsMsgSendLater: Attempt to delete an already deleted message");
+ return NS_OK;
+ }
+
+ // Get the composition fields interface
+ nsCOMPtr<nsIMutableArray> msgArray(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ if (!msgArray)
+ return NS_ERROR_FACTORY_NOT_LOADED;
+
+ if (!mMessageFolder)
+ return NS_ERROR_UNEXPECTED;
+
+ msgArray->InsertElementAt(mMessage, 0, false);
+
+ nsresult res = mMessageFolder->DeleteMessages(msgArray, nullptr, true, false, nullptr, false /*allowUndo*/);
+ if (NS_FAILED(res))
+ return NS_ERROR_FAILURE;
+
+ // Null out the message so we don't try and delete it again.
+ mMessage = nullptr;
+
+ return NS_OK;
+}
+
+//
+// This function parses the headers, and also deletes from the header block
+// any headers which should not be delivered in mail, regardless of whether
+// they were present in the queue file. Such headers include: BCC, FCC,
+// Sender, X-Mozilla-Status, X-Mozilla-News-Host, and Content-Length.
+// (Content-Length is for the disk file only, and must not be allowed to
+// escape onto the network, since it depends on the local linebreak
+// representation. Arguably, we could allow Lines to escape, but it's not
+// required by NNTP.)
+//
+nsresult
+nsMsgSendLater::BuildHeaders()
+{
+ char *buf = m_headers;
+ char *buf_end = buf + m_headersFP;
+
+ PR_FREEIF(m_to);
+ PR_FREEIF(m_bcc);
+ PR_FREEIF(m_newsgroups);
+ PR_FREEIF(m_newshost);
+ PR_FREEIF(m_fcc);
+ PR_FREEIF(mIdentityKey);
+ PR_FREEIF(mAccountKey);
+ m_flags = 0;
+
+ while (buf < buf_end)
+ {
+ bool prune_p = false;
+ bool do_flags_p = false;
+ char *colon = PL_strchr(buf, ':');
+ char *end;
+ char *value = 0;
+ char **header = 0;
+ char *header_start = buf;
+
+ if (! colon)
+ break;
+
+ end = colon;
+ while (end > buf && (*end == ' ' || *end == '\t'))
+ end--;
+
+ switch (buf [0])
+ {
+ case 'B': case 'b':
+ if (!PL_strncasecmp ("BCC", buf, end - buf))
+ {
+ header = &m_bcc;
+ prune_p = true;
+ }
+ break;
+ case 'C': case 'c':
+ if (!PL_strncasecmp ("CC", buf, end - buf))
+ header = &m_to;
+ else if (!PL_strncasecmp (HEADER_CONTENT_LENGTH, buf, end - buf))
+ prune_p = true;
+ break;
+ case 'F': case 'f':
+ if (!PL_strncasecmp ("FCC", buf, end - buf))
+ {
+ header = &m_fcc;
+ prune_p = true;
+ }
+ break;
+ case 'L': case 'l':
+ if (!PL_strncasecmp ("Lines", buf, end - buf))
+ prune_p = true;
+ break;
+ case 'N': case 'n':
+ if (!PL_strncasecmp ("Newsgroups", buf, end - buf))
+ header = &m_newsgroups;
+ break;
+ case 'S': case 's':
+ if (!PL_strncasecmp ("Sender", buf, end - buf))
+ prune_p = true;
+ break;
+ case 'T': case 't':
+ if (!PL_strncasecmp ("To", buf, end - buf))
+ header = &m_to;
+ break;
+ case 'X': case 'x':
+ {
+ if (buf + strlen(HEADER_X_MOZILLA_STATUS2) == end &&
+ !PL_strncasecmp(HEADER_X_MOZILLA_STATUS2, buf, end - buf))
+ prune_p = true;
+ else if (buf + strlen(HEADER_X_MOZILLA_STATUS) == end &&
+ !PL_strncasecmp(HEADER_X_MOZILLA_STATUS, buf, end - buf))
+ prune_p = do_flags_p = true;
+ else if (!PL_strncasecmp(HEADER_X_MOZILLA_DRAFT_INFO, buf, end - buf))
+ prune_p = true;
+ else if (!PL_strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf))
+ prune_p = true;
+ else if (!PL_strncasecmp(HEADER_X_MOZILLA_NEWSHOST, buf, end - buf))
+ {
+ prune_p = true;
+ header = &m_newshost;
+ }
+ else if (!PL_strncasecmp(HEADER_X_MOZILLA_IDENTITY_KEY, buf, end - buf))
+ {
+ prune_p = true;
+ header = &mIdentityKey;
+ }
+ else if (!PL_strncasecmp(HEADER_X_MOZILLA_ACCOUNT_KEY, buf, end - buf))
+ {
+ prune_p = true;
+ header = &mAccountKey;
+ }
+ break;
+ }
+ }
+
+ buf = colon + 1;
+ while (*buf == ' ' || *buf == '\t')
+ buf++;
+
+ value = buf;
+
+SEARCH_NEWLINE:
+ while (*buf != 0 && *buf != '\r' && *buf != '\n')
+ buf++;
+
+ if (buf+1 >= buf_end)
+ ;
+ // If "\r\n " or "\r\n\t" is next, that doesn't terminate the header.
+ else if (buf+2 < buf_end &&
+ (buf[0] == '\r' && buf[1] == '\n') &&
+ (buf[2] == ' ' || buf[2] == '\t'))
+ {
+ buf += 3;
+ goto SEARCH_NEWLINE;
+ }
+ // If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate
+ // the header either.
+ else if ((buf[0] == '\r' || buf[0] == '\n') &&
+ (buf[1] == ' ' || buf[1] == '\t'))
+ {
+ buf += 2;
+ goto SEARCH_NEWLINE;
+ }
+
+ if (header)
+ {
+ int L = buf - value;
+ if (*header)
+ {
+ char *newh = (char*) PR_Realloc ((*header),
+ PL_strlen(*header) + L + 10);
+ if (!newh) return NS_ERROR_OUT_OF_MEMORY;
+ *header = newh;
+ newh = (*header) + PL_strlen (*header);
+ *newh++ = ',';
+ *newh++ = ' ';
+ memcpy(newh, value, L);
+ newh [L] = 0;
+ }
+ else
+ {
+ *header = (char *) PR_Malloc(L+1);
+ if (!*header) return NS_ERROR_OUT_OF_MEMORY;
+ memcpy((*header), value, L);
+ (*header)[L] = 0;
+ }
+ }
+ else if (do_flags_p)
+ {
+ char *s = value;
+ PR_ASSERT(*s != ' ' && *s != '\t');
+ NS_ASSERTION(MsgIsHex(s, 4), "Expected 4 hex digits for flags.");
+ m_flags = MsgUnhex(s, 4);
+ }
+
+ if (*buf == '\r' || *buf == '\n')
+ {
+ if (*buf == '\r' && buf[1] == '\n')
+ buf++;
+ buf++;
+ }
+
+ if (prune_p)
+ {
+ char *to = header_start;
+ char *from = buf;
+ while (from < buf_end)
+ *to++ = *from++;
+ buf = header_start;
+ buf_end = to;
+ m_headersFP = buf_end - m_headers;
+ }
+ }
+
+ m_headers[m_headersFP++] = '\r';
+ m_headers[m_headersFP++] = '\n';
+
+ // Now we have parsed out all of the headers we need and we
+ // can proceed.
+ return NS_OK;
+}
+
+nsresult
+DoGrowBuffer(int32_t desired_size, int32_t element_size, int32_t quantum,
+ char **buffer, int32_t *size)
+{
+ if (*size <= desired_size)
+ {
+ char *new_buf;
+ int32_t increment = desired_size - *size;
+ if (increment < quantum) // always grow by a minimum of N bytes
+ increment = quantum;
+
+ new_buf = (*buffer
+ ? (char *) PR_Realloc (*buffer, (*size + increment)
+ * (element_size / sizeof(char)))
+ : (char *) PR_Malloc ((*size + increment)
+ * (element_size / sizeof(char))));
+ if (! new_buf)
+ return NS_ERROR_OUT_OF_MEMORY;
+ *buffer = new_buf;
+ *size += increment;
+ }
+ return NS_OK;
+}
+
+#define do_grow_headers(desired_size) \
+ (((desired_size) >= m_headersSize) ? \
+ DoGrowBuffer ((desired_size), sizeof(char), 1024, \
+ &m_headers, &m_headersSize) \
+ : NS_OK)
+
+nsresult
+nsMsgSendLater::DeliverQueuedLine(char *line, int32_t length)
+{
+ int32_t flength = length;
+
+ m_bytesRead += length;
+
+// convert existing newline to CRLF
+// Don't need this because the calling routine is taking care of it.
+// if (length > 0 && (line[length-1] == '\r' ||
+// (line[length-1] == '\n' && (length < 2 || line[length-2] != '\r'))))
+// {
+// line[length-1] = '\r';
+// line[length++] = '\n';
+// }
+//
+ //
+ // We are going to check if we are looking at a "From - " line. If so,
+ // then just eat it and return NS_OK
+ //
+ if (!PL_strncasecmp(line, "From - ", 7))
+ return NS_OK;
+
+ if (m_inhead)
+ {
+ if (m_headersPosition == 0)
+ {
+ // This line is the first line in a header block.
+ // Remember its position.
+ m_headersPosition = m_position;
+
+ // Also, since we're now processing the headers, clear out the
+ // slots which we will parse data into, so that the values that
+ // were used the last time around do not persist.
+
+ // We must do that here, and not in the previous clause of this
+ // `else' (the "I've just seen a `From ' line clause") because
+ // that clause happens before delivery of the previous message is
+ // complete, whereas this clause happens after the previous msg
+ // has been delivered. If we did this up there, then only the
+ // last message in the folder would ever be able to be both
+ // mailed and posted (or fcc'ed.)
+ PR_FREEIF(m_to);
+ PR_FREEIF(m_bcc);
+ PR_FREEIF(m_newsgroups);
+ PR_FREEIF(m_newshost);
+ PR_FREEIF(m_fcc);
+ PR_FREEIF(mIdentityKey);
+ }
+
+ if (line[0] == '\r' || line[0] == '\n' || line[0] == 0)
+ {
+ // End of headers. Now parse them; open the temp file;
+ // and write the appropriate subset of the headers out.
+ m_inhead = false;
+
+ nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mOutFile), mTempFile, -1, 00600);
+ if (NS_FAILED(rv))
+ return NS_MSG_ERROR_WRITING_FILE;
+
+ nsresult status = BuildHeaders();
+ if (NS_FAILED(status))
+ return status;
+
+ uint32_t n;
+ rv = mOutFile->Write(m_headers, m_headersFP, &n);
+ if (NS_FAILED(rv) || n != (uint32_t)m_headersFP)
+ return NS_MSG_ERROR_WRITING_FILE;
+ }
+ else
+ {
+ // Otherwise, this line belongs to a header. So append it to the
+ // header data.
+
+ if (!PL_strncasecmp (line, HEADER_X_MOZILLA_STATUS, PL_strlen(HEADER_X_MOZILLA_STATUS)))
+ // Notice the position of the flags.
+ m_flagsPosition = m_position;
+ else if (m_headersFP == 0)
+ m_flagsPosition = 0;
+
+ nsresult status = do_grow_headers (length + m_headersFP + 10);
+ if (NS_FAILED(status))
+ return status;
+
+ memcpy(m_headers + m_headersFP, line, length);
+ m_headersFP += length;
+ }
+ }
+ else
+ {
+ // This is a body line. Write it to the file.
+ PR_ASSERT(mOutFile);
+ if (mOutFile)
+ {
+ uint32_t wrote;
+ nsresult rv = mOutFile->Write(line, length, &wrote);
+ if (NS_FAILED(rv) || wrote < (uint32_t) length)
+ return NS_MSG_ERROR_WRITING_FILE;
+ }
+ }
+
+ m_position += flength;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::AddListener(nsIMsgSendLaterListener *aListener)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+ mListenerArray.AppendElement(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::RemoveListener(nsIMsgSendLaterListener *aListener)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+ return mListenerArray.RemoveElement(aListener) ? NS_OK : NS_ERROR_INVALID_ARG;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::GetSendingMessages(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mSendingMessages;
+ return NS_OK;
+}
+
+#define NOTIFY_LISTENERS(propertyfunc_, params_) \
+ PR_BEGIN_MACRO \
+ nsTObserverArray<nsCOMPtr<nsIMsgSendLaterListener> >::ForwardIterator iter(mListenerArray); \
+ nsCOMPtr<nsIMsgSendLaterListener> listener; \
+ while (iter.HasMore()) { \
+ listener = iter.GetNext(); \
+ listener->propertyfunc_ params_; \
+ } \
+ PR_END_MACRO
+
+void
+nsMsgSendLater::NotifyListenersOnStartSending(uint32_t aTotalMessageCount)
+{
+ NOTIFY_LISTENERS(OnStartSending, (aTotalMessageCount));
+}
+
+void
+nsMsgSendLater::NotifyListenersOnMessageStartSending(uint32_t aCurrentMessage,
+ uint32_t aTotalMessage,
+ nsIMsgIdentity *aIdentity)
+{
+ NOTIFY_LISTENERS(OnMessageStartSending, (aCurrentMessage, aTotalMessage,
+ mMessage, aIdentity));
+}
+
+void
+nsMsgSendLater::NotifyListenersOnProgress(uint32_t aCurrentMessage,
+ uint32_t aTotalMessage,
+ uint32_t aSendPercent,
+ uint32_t aCopyPercent)
+{
+ NOTIFY_LISTENERS(OnMessageSendProgress, (aCurrentMessage, aTotalMessage,
+ aSendPercent, aCopyPercent));
+}
+
+void
+nsMsgSendLater::NotifyListenersOnMessageSendError(uint32_t aCurrentMessage,
+ nsresult aStatus,
+ const char16_t *aMsg)
+{
+ NOTIFY_LISTENERS(OnMessageSendError, (aCurrentMessage, mMessage,
+ aStatus, aMsg));
+}
+
+/**
+ * This function is called to end sending of messages, it resets the send later
+ * system and notifies the relevant parties that we have finished.
+ */
+void
+nsMsgSendLater::EndSendMessages(nsresult aStatus, const char16_t *aMsg,
+ uint32_t aTotalTried, uint32_t aSuccessful)
+{
+ // Catch-all, we may have had an issue sending, so we may not be calling
+ // StartNextMailFileSend to fully finish the sending. Therefore set
+ // mSendingMessages to false here so that we don't think we're still trying
+ // to send messages
+ mSendingMessages = false;
+
+ // Clear out our array of messages.
+ mMessagesToSend.Clear();
+
+ // We don't need to keep hold of the database now we've finished sending.
+ (void)mMessageFolder->SetMsgDatabase(nullptr);
+
+ // or the enumerator, temp file or output stream
+ mEnumerator = nullptr;
+ mTempFile = nullptr;
+ mOutFile = nullptr;
+
+ NOTIFY_LISTENERS(OnStopSending, (aStatus, aMsg, aTotalTried, aSuccessful));
+
+ // If we've got a shutdown listener, notify it that we've finished.
+ if (mShutdownListener)
+ {
+ mShutdownListener->OnStopRunningUrl(nullptr, NS_OK);
+ mShutdownListener = nullptr;
+ }
+}
+
+/**
+ * Called when the send part of sending a message is finished. This will set up
+ * for the next step or "end" depending on the status.
+ *
+ * @param aStatus The success or fail result of the send step.
+ * @return True if the copy process will continue, false otherwise.
+ */
+bool
+nsMsgSendLater::OnSendStepFinished(nsresult aStatus)
+{
+ if (NS_SUCCEEDED(aStatus))
+ {
+ SetOrigMsgDisposition();
+ DeleteCurrentMessage();
+
+ // Send finished, so that is now 100%, copy to proceed...
+ NotifyListenersOnProgress(mTotalSendCount, mMessagesToSend.Count(), 100, 0);
+
+ ++mTotalSentSuccessfully;
+ return true;
+ }
+ else
+ {
+ // XXX we don't currently get a message string from the send service.
+ NotifyListenersOnMessageSendError(mTotalSendCount, aStatus, nullptr);
+ nsresult rv = StartNextMailFileSend(aStatus);
+ // if this is the last message we're sending, we should report
+ // the status failure.
+ if (NS_FAILED(rv))
+ EndSendMessages(rv, nullptr, mTotalSendCount, mTotalSentSuccessfully);
+ }
+ return false;
+}
+
+/**
+ * Called when the copy part of sending a message is finished. This will send
+ * the next message or handle failure as appropriate.
+ *
+ * @param aStatus The success or fail result of the copy step.
+ */
+void
+nsMsgSendLater::OnCopyStepFinished(nsresult aStatus)
+{
+ // Regardless of the success of the copy we will still keep trying
+ // to send the rest...
+ nsresult rv = StartNextMailFileSend(aStatus);
+ if (NS_FAILED(rv))
+ EndSendMessages(rv, nullptr, mTotalSendCount, mTotalSentSuccessfully);
+}
+
+// XXX todo
+// maybe this should just live in the account manager?
+nsresult
+nsMsgSendLater::GetIdentityFromKey(const char *aKey, nsIMsgIdentity **aIdentity)
+{
+ NS_ENSURE_ARG_POINTER(aIdentity);
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (aKey)
+ {
+ nsCOMPtr<nsIArray> identities;
+ if (NS_SUCCEEDED(accountManager->GetAllIdentities(getter_AddRefs(identities))))
+ {
+ nsCOMPtr<nsIMsgIdentity> lookupIdentity;
+ uint32_t count = 0;
+
+ identities->GetLength(&count);
+ for (uint32_t i = 0; i < count; i++)
+ {
+ lookupIdentity = do_QueryElementAt(identities, i, &rv);
+ if (NS_FAILED(rv))
+ continue;
+
+ nsCString key;
+ lookupIdentity->GetKey(key);
+ if (key.Equals(aKey))
+ {
+ NS_IF_ADDREF(*aIdentity = lookupIdentity);
+ return NS_OK;
+ }
+ }
+ }
+ }
+
+ // if no aKey, or we failed to find the identity from the key
+ // use the identity from the default account.
+ nsCOMPtr<nsIMsgAccount> defaultAccount;
+ rv = accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = defaultAccount->GetDefaultIdentity(aIdentity);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnItemAdded(nsIMsgFolder *aParentItem, nsISupports *aItem)
+{
+ // No need to trigger if timer is already set
+ if (mTimerSet)
+ return NS_OK;
+
+ // XXX only trigger for non-queued headers
+
+ // Items from this function return NS_OK because the callee won't care about
+ // the result anyway.
+ nsresult rv;
+ if (!mTimer)
+ {
+ mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+ }
+
+ rv = mTimer->Init(static_cast<nsIObserver*>(this), kInitialMessageSendTime,
+ nsITimer::TYPE_ONE_SHOT);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+
+ mTimerSet = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnItemRemoved(nsIMsgFolder *aParentItem, nsISupports *aItem)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnItemPropertyChanged(nsIMsgFolder *aItem, nsIAtom *aProperty,
+ const char* aOldValue,
+ const char* aNewValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnItemIntPropertyChanged(nsIMsgFolder *aItem, nsIAtom *aProperty,
+ int64_t aOldValue, int64_t aNewValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnItemBoolPropertyChanged(nsIMsgFolder *aItem, nsIAtom *aProperty,
+ bool aOldValue, bool aNewValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnItemUnicharPropertyChanged(nsIMsgFolder *aItem,
+ nsIAtom *aProperty,
+ const char16_t* aOldValue,
+ const char16_t* aNewValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnItemPropertyFlagChanged(nsIMsgDBHdr *aItem, nsIAtom *aProperty,
+ uint32_t aOldValue, uint32_t aNewValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::OnItemEvent(nsIMsgFolder* aItem, nsIAtom *aEvent)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::GetNeedsToRunTask(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mSendingMessages;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::DoShutdownTask(nsIUrlListener *aListener, nsIMsgWindow *aWindow,
+ bool *aResult)
+{
+ if (mTimer)
+ mTimer->Cancel();
+ // If we're already sending messages, nothing to do, but save the shutdown
+ // listener until we've finished.
+ if (mSendingMessages)
+ {
+ mShutdownListener = aListener;
+ return NS_OK;
+ }
+ // Else we have pending messages, we need to throw up a dialog to find out
+ // if to send them or not.
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsMsgSendLater::GetCurrentTaskName(nsAString &aResult)
+{
+ // XXX Bug 440794 will localize this, left as non-localized whilst we decide
+ // on the actual strings and try out the UI.
+ aResult = NS_LITERAL_STRING("Sending Messages");
+ return NS_OK;
+}
diff --git a/mailnews/compose/src/nsMsgSendLater.h b/mailnews/compose/src/nsMsgSendLater.h
new file mode 100644
index 000000000..734396acc
--- /dev/null
+++ b/mailnews/compose/src/nsMsgSendLater.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsMsgSendLater_H_
+#define _nsMsgSendLater_H_
+
+#include "nsCOMArray.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgSendListener.h"
+#include "nsIMsgSendLaterListener.h"
+#include "nsIMsgSendLater.h"
+#include "nsIMsgStatusFeedback.h"
+#include "nsTObserverArray.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsIMsgShutdown.h"
+
+////////////////////////////////////////////////////////////////////////////////////
+// This is the listener class for the send operation. We have to create this class
+// to listen for message send completion and eventually notify the caller
+////////////////////////////////////////////////////////////////////////////////////
+class nsMsgSendLater;
+
+class SendOperationListener : public nsIMsgSendListener,
+ public nsIMsgCopyServiceListener
+{
+public:
+ SendOperationListener(nsMsgSendLater *aSendLater);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGSENDLISTENER
+ NS_DECL_NSIMSGCOPYSERVICELISTENER
+
+private:
+ virtual ~SendOperationListener();
+ nsMsgSendLater *mSendLater;
+};
+
+class nsMsgSendLater: public nsIMsgSendLater,
+ public nsIFolderListener,
+ public nsIObserver,
+ public nsIUrlListener,
+ public nsIMsgShutdownTask
+
+{
+public:
+ nsMsgSendLater();
+ nsresult Init();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGSENDLATER
+ NS_DECL_NSIFOLDERLISTENER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIURLLISTENER
+ NS_DECL_NSIMSGSHUTDOWNTASK
+
+ // Methods needed for implementing interface...
+ nsresult StartNextMailFileSend(nsresult prevStatus);
+ nsresult CompleteMailFileSend();
+
+ nsresult DeleteCurrentMessage();
+ nsresult SetOrigMsgDisposition();
+ // Necessary for creating a valid list of recipients
+ nsresult BuildHeaders();
+ nsresult DeliverQueuedLine(char *line, int32_t length);
+ nsresult RebufferLeftovers(char *startBuf, uint32_t aLen);
+ nsresult BuildNewBuffer(const char* aBuf, uint32_t aCount, uint32_t *totalBufSize);
+
+ // methods for listener array processing...
+ void NotifyListenersOnStartSending(uint32_t aTotalMessageCount);
+ void NotifyListenersOnMessageStartSending(uint32_t aCurrentMessage,
+ uint32_t aTotalMessage,
+ nsIMsgIdentity *aIdentity);
+ void NotifyListenersOnProgress(uint32_t aCurrentMessage,
+ uint32_t aTotalMessage,
+ uint32_t aSendPercent,
+ uint32_t aCopyPercent);
+ void NotifyListenersOnMessageSendError(uint32_t aCurrentMessage,
+ nsresult aStatus,
+ const char16_t *aMsg);
+ void EndSendMessages(nsresult aStatus, const char16_t *aMsg,
+ uint32_t aTotalTried, uint32_t aSuccessful);
+
+ bool OnSendStepFinished(nsresult aStatus);
+ void OnCopyStepFinished(nsresult aStatus);
+
+ // counters and things for enumeration
+ uint32_t mTotalSentSuccessfully;
+ uint32_t mTotalSendCount;
+ nsCOMArray<nsIMsgDBHdr> mMessagesToSend;
+ nsCOMPtr<nsISimpleEnumerator> mEnumerator;
+ nsCOMPtr<nsIMsgFolder> mMessageFolder;
+ nsCOMPtr<nsIMsgStatusFeedback> mFeedback;
+
+ // Private Information
+private:
+ virtual ~nsMsgSendLater();
+ nsresult GetIdentityFromKey(const char *aKey, nsIMsgIdentity **aIdentity);
+ nsresult ReparseDBIfNeeded(nsIUrlListener *aListener);
+ nsresult InternalSendMessages(bool aUserInitiated,
+ nsIMsgIdentity *aIdentity);
+
+ nsTObserverArray<nsCOMPtr<nsIMsgSendLaterListener> > mListenerArray;
+ nsCOMPtr<nsIMsgDBHdr> mMessage;
+ nsCOMPtr<nsITimer> mTimer;
+ bool mTimerSet;
+ nsCOMPtr<nsIUrlListener> mShutdownListener;
+
+ //
+ // File output stuff...
+ //
+ nsCOMPtr<nsIFile> mTempFile;
+ nsCOMPtr<nsIOutputStream> mOutFile;
+
+ // For building headers and stream parsing...
+ char *m_to;
+ char *m_bcc;
+ char *m_fcc;
+ char *m_newsgroups;
+ char *m_newshost;
+ char *m_headers;
+ int32_t m_flags;
+ int32_t m_headersFP;
+ bool m_inhead;
+ int32_t m_headersPosition;
+ int32_t m_bytesRead;
+ int32_t m_position;
+ int32_t m_flagsPosition;
+ int32_t m_headersSize;
+ char *mLeftoverBuffer;
+ char *mIdentityKey;
+ char *mAccountKey;
+
+ bool mSendingMessages;
+ bool mUserInitiated;
+ nsCOMPtr<nsIMsgIdentity> mIdentity;
+};
+
+
+#endif /* _nsMsgSendLater_H_ */
diff --git a/mailnews/compose/src/nsMsgSendPart.cpp b/mailnews/compose/src/nsMsgSendPart.cpp
new file mode 100644
index 000000000..a33dabf33
--- /dev/null
+++ b/mailnews/compose/src/nsMsgSendPart.cpp
@@ -0,0 +1,779 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMsgSend.h"
+#include "nsMsgSendPart.h"
+#include "nsIMimeConverter.h"
+#include "nsCOMPtr.h"
+#include "nsIComponentManager.h"
+#include "nsMsgI18N.h"
+#include "nsMsgCompUtils.h"
+#include "nsMsgMimeCID.h"
+#include "nsMimeTypes.h"
+#include "prmem.h"
+#include "nsMsgPrompts.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsNetUtil.h"
+#include "nsISeekableStream.h"
+#include "nsReadLine.h"
+#include "nsILineInputStream.h"
+#include "nsComposeStrings.h"
+#include "mozilla/mailnews/MimeEncoder.h"
+
+static char *mime_mailto_stream_read_buffer = 0;
+
+int32_t nsMsgSendPart::M_counter = 0;
+
+nsMsgSendPart::nsMsgSendPart(nsIMsgSend* state, const char *part_charset)
+{
+ PL_strncpy(m_charset_name, (part_charset ? part_charset : "UTF-8"), sizeof(m_charset_name)-1);
+ m_charset_name[sizeof(m_charset_name)-1] = '\0';
+ m_children = nullptr;
+ m_numchildren = 0;
+ // if we're not added as a child, the default part number will be "1".
+ m_partNum = "1";
+ SetMimeDeliveryState(state);
+
+ m_parent = nullptr;
+ m_buffer = nullptr;
+ m_type = nullptr;
+ m_other = nullptr;
+ m_strip_sensitive_headers = false;
+
+ m_firstBlock = false;
+ m_needIntlConversion = false;
+
+ m_mainpart = false;
+ m_just_hit_CR = false;
+}
+
+
+nsMsgSendPart::~nsMsgSendPart()
+{
+ for (int i=0 ; i < m_numchildren; i++)
+ delete m_children[i];
+
+ delete [] m_children;
+ PR_FREEIF(m_buffer);
+ PR_FREEIF(m_other);
+ PR_FREEIF(m_type);
+}
+
+nsresult nsMsgSendPart::CopyString(char** dest, const char* src)
+{
+ NS_ASSERTION(src, "src null");
+
+ PR_FREEIF(*dest);
+ if (!src)
+ *dest = PL_strdup("");
+ else
+ *dest = PL_strdup(src);
+
+ return *dest? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+
+nsresult nsMsgSendPart::SetFile(nsIFile *file)
+{
+ m_file = file;
+ return NS_OK;
+}
+
+
+nsresult nsMsgSendPart::SetBuffer(const char* buffer)
+{
+ PR_FREEIF(m_buffer);
+ return CopyString(&m_buffer, buffer);
+}
+
+
+nsresult nsMsgSendPart::SetType(const char* type)
+{
+ PR_FREEIF(m_type);
+ m_type = PL_strdup(type);
+ return m_type ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+
+nsresult nsMsgSendPart::SetOtherHeaders(const char* other)
+{
+ return CopyString(&m_other, other);
+}
+
+nsresult nsMsgSendPart::SetMimeDeliveryState(nsIMsgSend *state)
+{
+ m_state = state;
+ if (GetNumChildren() > 0)
+ {
+ for (int i = 0; i < GetNumChildren(); i++)
+ {
+ nsMsgSendPart *part = GetChild(i);
+ if (part)
+ part->SetMimeDeliveryState(state);
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsMsgSendPart::AppendOtherHeaders(const char* more)
+{
+ if (!m_other)
+ return SetOtherHeaders(more);
+
+ if (!more || !*more)
+ return NS_OK;
+
+ char* tmp = (char *) PR_Malloc(sizeof(char) * (PL_strlen(m_other) + PL_strlen(more) + 2));
+ if (!tmp)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PL_strcpy(tmp, m_other);
+ PL_strcat(tmp, more);
+ PR_FREEIF(m_other);
+ m_other = tmp;
+
+ return NS_OK;
+}
+
+
+nsresult nsMsgSendPart::SetMainPart(bool value)
+{
+ m_mainpart = value;
+ return NS_OK;
+}
+
+nsresult nsMsgSendPart::AddChild(nsMsgSendPart* child)
+{
+ m_numchildren++;
+ nsMsgSendPart** tmp = new nsMsgSendPart* [m_numchildren];
+ if (tmp == nullptr) return NS_ERROR_OUT_OF_MEMORY;
+ for (int i=0 ; i<m_numchildren-1 ; i++) {
+ tmp[i] = m_children[i];
+ }
+ delete [] m_children;
+ m_children = tmp;
+ m_children[m_numchildren - 1] = child;
+ child->m_parent = this;
+ nsCString partNum(m_partNum);
+ partNum.Append(".");
+ partNum.AppendInt(m_numchildren);
+ child->m_partNum = partNum;
+ return NS_OK;
+}
+
+nsMsgSendPart * nsMsgSendPart::DetachChild(int32_t whichOne)
+{
+ nsMsgSendPart *returnValue = nullptr;
+
+ NS_ASSERTION(whichOne >= 0 && whichOne < m_numchildren, "parameter out of range");
+ if (whichOne >= 0 && whichOne < m_numchildren)
+ {
+ returnValue = m_children[whichOne];
+
+ if (m_numchildren > 1)
+ {
+ nsMsgSendPart** tmp = new nsMsgSendPart* [m_numchildren-1];
+ if (tmp != nullptr)
+ {
+ // move all the other kids over
+ for (int i=0 ; i<m_numchildren-1 ; i++)
+ {
+ if (i >= whichOne)
+ tmp[i] = m_children[i+1];
+ else
+ tmp[i] = m_children[i];
+ }
+ delete [] m_children;
+ m_children = tmp;
+ m_numchildren--;
+ }
+ }
+ else
+ {
+ delete [] m_children;
+ m_children = nullptr;
+ m_numchildren = 0;
+ }
+ }
+
+ if (returnValue)
+ returnValue->m_parent = nullptr;
+
+ return returnValue;
+}
+
+nsMsgSendPart* nsMsgSendPart::GetChild(int32_t which)
+{
+ NS_ASSERTION(which >= 0 && which < m_numchildren, "parameter out of range");
+ if (which >= 0 && which < m_numchildren) {
+ return m_children[which];
+ }
+ return nullptr;
+}
+
+
+
+nsresult nsMsgSendPart::PushBody(const char* buffer, int32_t length)
+{
+ nsresult status = NS_OK;
+ const char* encoded_data = buffer;
+
+ if (m_encoder)
+ {
+ status = m_encoder->Write(encoded_data, length);
+ }
+ else
+ {
+ // Merely translate all linebreaks to CRLF.
+ const char *in = encoded_data;
+ const char *end = in + length;
+ char *buffer, *out;
+
+
+ buffer = mime_get_stream_write_buffer();
+ // XXX -1 is not a valid nsresult
+ NS_ENSURE_TRUE(buffer, static_cast<nsresult>(-1));
+
+ NS_ASSERTION(encoded_data != buffer, "encoded_data == buffer");
+ out = buffer;
+
+ for (; in < end; in++) {
+ if (m_just_hit_CR) {
+ m_just_hit_CR = false;
+ if (*in == '\n') {
+ // The last thing we wrote was a CRLF from hitting a CR.
+ // So, we don't want to do anything from a following LF;
+ // we want to ignore it.
+ continue;
+ }
+ }
+ if (*in == '\r' || *in == '\n') {
+ /* Write out the newline. */
+ *out++ = '\r';
+ *out++ = '\n';
+
+ status = mime_write_message_body(m_state, buffer,
+ out - buffer);
+ if (NS_FAILED(status)) return status;
+ out = buffer;
+
+ if (*in == '\r') {
+ m_just_hit_CR = true;
+ }
+
+ out = buffer;
+ } else {
+
+ /* Fix for bug #95985. We can't assume that all lines are shorter
+ than 4096 chars (MIME_BUFFER_SIZE), so we need to test
+ for this here. sfraser.
+ */
+ if (out - buffer >= MIME_BUFFER_SIZE)
+ {
+ status = mime_write_message_body(m_state, buffer, out - buffer);
+ if (NS_FAILED(status)) return status;
+
+ out = buffer;
+ }
+
+ *out++ = *in;
+ }
+ }
+
+ /* Flush the last line. */
+ if (out > buffer) {
+ status = mime_write_message_body(m_state, buffer, out - buffer);
+ if (NS_FAILED(status)) return status;
+ out = buffer;
+ }
+ }
+
+ if (encoded_data && encoded_data != buffer) {
+ PR_Free((char *) encoded_data);
+ }
+
+ return status;
+}
+
+
+/* Partition the headers into those which apply to the message as a whole;
+those which apply to the message's contents; and the Content-Type header
+itself. (This relies on the fact that all body-related headers begin with
+"Content-".)
+
+ (How many header parsers are in this program now?)
+ */
+static nsresult
+divide_content_headers(const char *headers,
+ char **message_headers,
+ char **content_headers,
+ char **content_type_header)
+{
+ const char *tail;
+ char *message_tail, *content_tail, *type_tail;
+ int L = 0;
+ if (headers)
+ L = PL_strlen(headers);
+
+ if (L == 0)
+ return NS_OK;
+
+ *message_headers = (char *)PR_Malloc(L+1);
+ if (!*message_headers)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ *content_headers = (char *)PR_Malloc(L+1);
+ if (!*content_headers) {
+ PR_Free(*message_headers);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ *content_type_header = (char *)PR_Malloc(L+1);
+ if (!*content_type_header) {
+ PR_Free(*message_headers);
+ PR_Free(*content_headers);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ message_tail = *message_headers;
+ content_tail = *content_headers;
+ type_tail = *content_type_header;
+ tail = headers;
+
+ while (*tail)
+ {
+ const char *head = tail;
+ char **out;
+ while(true) {
+ /* Loop until we reach a newline that is not followed by whitespace.
+ */
+ if (tail[0] == 0 ||
+ ((tail[0] == '\r' || tail[0] == '\n') &&
+ !(tail[1] == ' ' || tail[1] == '\t' || tail[1] == '\n')))
+ {
+ /* Swallow the whole newline. */
+ if (tail[0] == '\r' && tail[1] == '\n')
+ tail++;
+ if (*tail)
+ tail++;
+ break;
+ }
+ tail++;
+ }
+
+ /* Decide which block this header goes into.
+ */
+ if (!PL_strncasecmp(head, "Content-Type:", 13))
+ out = &type_tail;
+ else
+ if (!PL_strncasecmp(head, "Content-", 8))
+ out = &content_tail;
+ else
+ out = &message_tail;
+
+ memcpy(*out, head, (tail-head));
+ *out += (tail-head);
+ }
+
+ *message_tail = 0;
+ *content_tail = 0;
+ *type_tail = 0;
+
+ if (!**message_headers) {
+ PR_Free(*message_headers);
+ *message_headers = 0;
+ }
+
+ if (!**content_headers) {
+ PR_Free(*content_headers);
+ *content_headers = 0;
+ }
+
+ if (!**content_type_header) {
+ PR_Free(*content_type_header);
+ *content_type_header = 0;
+ }
+
+#ifdef DEBUG
+ // ### mwelch Because of the extreme difficulty we've had with
+ // duplicate part headers, I'm going to put in an
+ // ASSERT here which makes sure that no duplicate
+ // Content-Type or Content-Transfer-Encoding headers
+ // leave here undetected.
+ const char* tmp;
+ if (*content_type_header) {
+ tmp = PL_strstr(*content_type_header, "Content-Type");
+ if (tmp) {
+ tmp++; // get past the first occurrence
+ NS_ASSERTION(!PL_strstr(tmp, "Content-Type"), "Content-part already present");
+ }
+ }
+
+ if (*content_headers) {
+ tmp = PL_strstr(*content_headers, "Content-Transfer-Encoding");
+ if (tmp) {
+ tmp++; // get past the first occurrence
+ NS_ASSERTION(!PL_strstr(tmp, "Content-Transfer-Encoding"), "Content-Transfert already present");
+ }
+ }
+#endif // DEBUG
+
+ return NS_OK;
+}
+
+#define SKIP_EMPTY_PART 1966
+
+nsresult
+nsMsgSendPart::Write()
+{
+ nsresult status = NS_OK;
+ char *separator = nullptr;
+ bool needToWriteCRLFAfterEncodedBody = false;
+
+#define PUSHLEN(str, length) \
+ do { \
+ status = mime_write_message_body(m_state, str, length); \
+ if (NS_FAILED(status)) goto FAIL; \
+ } while (0) \
+
+#define PUSH(str) PUSHLEN(str, PL_strlen(str))
+
+ // rhp: Suppress the output of parts that are empty!
+ if ( (m_parent) &&
+ (m_numchildren == 0) &&
+ ( (!m_buffer) || (!*m_buffer) ) &&
+ (!m_file) &&
+ (!m_mainpart) )
+ // XXX SKIP_EMPTY_PART (= 1966) is not a valid nsresult
+ return static_cast<nsresult>(SKIP_EMPTY_PART);
+
+ if (m_mainpart && m_type && PL_strcmp(m_type, TEXT_HTML) == 0)
+ {
+ if (m_file)
+ {
+ // The "insert HTML links" code requires a memory buffer,
+ // so read the file into memory.
+ NS_ASSERTION(m_buffer == nullptr, "not-null buffer");
+ int32_t length = 0;
+ int64_t fileSize;
+ if (NS_SUCCEEDED(m_file->GetFileSize(&fileSize)))
+ length = fileSize;
+
+ m_buffer = (char *) PR_Malloc(sizeof(char) * (length + 1));
+ if (m_buffer)
+ {
+ nsCOMPtr<nsIInputStream> inputFile;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputFile), m_file);
+ if (NS_SUCCEEDED(rv))
+ {
+ uint32_t bytesRead;
+ rv = inputFile->Read(m_buffer, length, &bytesRead);
+ inputFile->Close();
+ m_buffer[length] = '\0';
+ }
+ else
+ PR_Free(m_buffer);
+ }
+ }
+ }
+
+ if (m_parent && m_parent->m_type &&
+ !PL_strcasecmp(m_parent->m_type, MULTIPART_DIGEST) &&
+ m_type &&
+ (!PL_strcasecmp(m_type, MESSAGE_RFC822) ||
+ !PL_strcasecmp(m_type, MESSAGE_NEWS)))
+ {
+ // If we're in a multipart/digest, and this document is of type
+ // message/rfc822, then it's appropriate to emit no headers.
+ //
+ }
+ else
+ {
+ char *message_headers = 0;
+ char *content_headers = 0;
+ char *content_type_header = 0;
+ status = divide_content_headers(m_other,
+ &message_headers,
+ &content_headers,
+ &content_type_header);
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ /* First, write out all of the headers that refer to the message
+ itself (From, Subject, MIME-Version, etc.)
+ */
+ if (message_headers)
+ {
+ PUSH(message_headers);
+ PR_Free(message_headers);
+ message_headers = 0;
+ }
+
+ /* Now allow the crypto library to (potentially) insert some text
+ (it may want to wrap the body in an envelope.) */
+ if (!m_parent) {
+ status = m_state->BeginCryptoEncapsulation();
+ if (NS_FAILED(status)) goto FAIL;
+ }
+
+ /* Now make sure there's a Content-Type header.
+ */
+ if (!content_type_header)
+ {
+ NS_ASSERTION(m_type && *m_type, "null ptr");
+ bool needsCharset = mime_type_needs_charset(m_type ? m_type : TEXT_PLAIN);
+ if (needsCharset)
+ {
+ content_type_header = PR_smprintf("Content-Type: %s; charset=%s" CRLF,
+ (m_type ? m_type : TEXT_PLAIN), m_charset_name);
+ }
+ else
+ content_type_header = PR_smprintf("Content-Type: %s" CRLF,
+ (m_type ? m_type : TEXT_PLAIN));
+ if (!content_type_header)
+ {
+ if (content_headers)
+ PR_Free(content_headers);
+ status = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+ }
+
+ /* If this is a compound object, tack a boundary string onto the
+ Content-Type header. this
+ */
+ if (m_numchildren > 0)
+ {
+ int L;
+ char *ct2;
+ NS_ASSERTION(m_type, "null ptr");
+
+ if (!separator)
+ {
+ separator = mime_make_separator("");
+ if (!separator)
+ {
+ status = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+ }
+
+ L = PL_strlen(content_type_header);
+
+ if (content_type_header[L-1] == '\n')
+ content_type_header[--L] = 0;
+ if (content_type_header[L-1] == '\r')
+ content_type_header[--L] = 0;
+
+ ct2 = PR_smprintf("%s;\r\n boundary=\"%s\"" CRLF, content_type_header, separator);
+ PR_Free(content_type_header);
+ if (!ct2)
+ {
+ if (content_headers)
+ PR_Free(content_headers);
+ status = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+
+ content_type_header = ct2;
+ }
+
+ // Now write out the Content-Type header...
+ NS_ASSERTION(content_type_header && *content_type_header, "null ptr");
+ PUSH(content_type_header);
+ PR_Free(content_type_header);
+ content_type_header = 0;
+
+ /* ...followed by all of the other headers that refer to the body of
+ the message (Content-Transfer-Encoding, Content-Dispositon, etc.)
+ */
+ if (content_headers)
+ {
+ PUSH(content_headers);
+ PR_Free(content_headers);
+ content_headers = 0;
+ }
+ }
+
+ PUSH(CRLF); // A blank line, to mark the end of headers.
+
+ m_firstBlock = true;
+ /* only convert if we need to tag charset */
+ m_needIntlConversion = mime_type_needs_charset(m_type);
+
+ if (m_buffer)
+ {
+ status = PushBody(m_buffer, PL_strlen(m_buffer));
+ if (NS_FAILED(status))
+ goto FAIL;
+ }
+ else if (m_file)
+ {
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), m_file);
+ if (NS_FAILED(rv))
+ {
+ // mysteriously disappearing?
+ nsCOMPtr<nsIMsgSendReport> sendReport;
+ m_state->GetSendReport(getter_AddRefs(sendReport));
+ if (sendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithTmpFile(m_file, error_msg);
+ sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ }
+ status = NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
+ goto FAIL;
+ }
+
+ nsCString curLine;
+ bool more = true;
+
+ /* Kludge to avoid having to allocate memory on the toy computers... */
+ if (!mime_mailto_stream_read_buffer)
+ {
+ mime_mailto_stream_read_buffer = (char *) PR_Malloc(MIME_BUFFER_SIZE);
+ if (!mime_mailto_stream_read_buffer)
+ {
+ status = NS_ERROR_OUT_OF_MEMORY;
+ goto FAIL;
+ }
+ }
+
+ char *buffer = mime_mailto_stream_read_buffer;
+ if (m_strip_sensitive_headers)
+ {
+ // We are attaching a message, so we should be careful to
+ // strip out certain sensitive internal header fields.
+ bool skipping = false;
+ nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
+ NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ while (more)
+ {
+ // NS_ReadLine doesn't return line termination chars.
+ rv = NS_ReadLine(inputStream.get(), lineBuffer.get(), curLine, &more);
+
+ curLine.Append(CRLF);
+
+ char *line = (char *) curLine.get();
+ if (skipping) {
+ if (*line == ' ' || *line == '\t')
+ continue;
+ else
+ skipping = false;
+ }
+
+ if (!PL_strncasecmp(line, "From -", 6) ||
+ !PL_strncasecmp(line, "BCC:", 4) ||
+ !PL_strncasecmp(line, "FCC:", 4) ||
+ !PL_strncasecmp(line, CONTENT_LENGTH ":", CONTENT_LENGTH_LEN+1) ||
+ !PL_strncasecmp(line, "Lines:", 6) ||
+ !PL_strncasecmp(line, "Status:", 7) ||
+ !PL_strncasecmp(line, X_MOZILLA_STATUS ":", X_MOZILLA_STATUS_LEN+1) ||
+ !PL_strncasecmp(line, X_MOZILLA_STATUS2 ":", X_MOZILLA_STATUS2_LEN+1) ||
+ !PL_strncasecmp(line, X_MOZILLA_DRAFT_INFO ":", X_MOZILLA_DRAFT_INFO_LEN+1) ||
+ !PL_strncasecmp(line, X_MOZILLA_NEWSHOST ":", X_MOZILLA_NEWSHOST_LEN+1) ||
+ !PL_strncasecmp(line, X_UIDL ":", X_UIDL_LEN+1) ||
+ !PL_strncasecmp(line, "X-VM-", 5)) /* hi Kyle */
+ {
+ skipping = true;
+ continue;
+ }
+
+ PUSH(line);
+
+ if (curLine.Length() == 2) {
+ nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(inputStream);
+ // seek back the amount of data left in the line buffer...
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, lineBuffer->start - lineBuffer->end);
+ break; // Now can do normal reads for the body.
+ }
+ }
+ lineBuffer = nullptr;
+ }
+
+ while (NS_SUCCEEDED(status))
+ {
+ uint32_t bytesRead;
+ nsresult rv = inputStream->Read(buffer, MIME_BUFFER_SIZE, &bytesRead);
+ if (NS_FAILED(rv))
+ {
+ nsCOMPtr<nsIMsgSendReport> sendReport;
+ m_state->GetSendReport(getter_AddRefs(sendReport));
+ if (sendReport)
+ {
+ nsAutoString error_msg;
+ nsMsgBuildMessageWithFile(m_file, error_msg);
+ sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), false);
+ status = NS_MSG_UNABLE_TO_OPEN_FILE;
+ goto FAIL;
+ }
+ }
+ status = PushBody(buffer, bytesRead);
+ if (NS_FAILED(status))
+ goto FAIL;
+ if (bytesRead < MIME_BUFFER_SIZE)
+ break;
+ }
+ }
+
+ if (m_encoder)
+ {
+ nsresult rv = m_encoder->Flush();
+ m_encoder = nullptr;
+ needToWriteCRLFAfterEncodedBody = !m_parent;
+ if (NS_FAILED(rv))
+ {
+ // XXX -1 is not a valid nsresult
+ status = static_cast<nsresult>(-1);
+ goto FAIL;
+ }
+ }
+
+ //
+ // Ok, from here we loop and drive the the output of all children
+ // for this message.
+ //
+ if (m_numchildren > 0)
+ {
+ bool writeSeparator = true;
+
+ for (int i = 0 ; i < m_numchildren ; i ++)
+ {
+ if (writeSeparator)
+ {
+ PUSH(CRLF);
+ PUSH("--");
+
+ PUSH(separator);
+ PUSH(CRLF);
+ }
+
+ status = m_children[i]->Write();
+ if (NS_FAILED(status))
+ goto FAIL;
+
+ // XXX SKIP_EMPTY_PART (= 1966) is not a valid nsresult
+ if (status == static_cast<nsresult>(SKIP_EMPTY_PART))
+ writeSeparator = false;
+ else
+ writeSeparator = true;
+ }
+
+ PUSH(CRLF);
+ PUSH("--");
+ PUSH(separator);
+ PUSH("--");
+ PUSH(CRLF);
+ }
+ else if (needToWriteCRLFAfterEncodedBody)
+ PUSH(CRLF);
+
+FAIL:
+ PR_FREEIF(separator);
+ return status;
+}
+
diff --git a/mailnews/compose/src/nsMsgSendPart.h b/mailnews/compose/src/nsMsgSendPart.h
new file mode 100644
index 000000000..edb5422ea
--- /dev/null
+++ b/mailnews/compose/src/nsMsgSendPart.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _MsgSendPart_H_
+#define _MsgSendPart_H_
+
+#include "msgCore.h"
+#include "prprf.h" /* should be defined into msgCore.h? */
+#include "nsMsgSend.h"
+
+namespace mozilla {
+namespace mailnews {
+class MimeEncoder;
+}
+}
+
+typedef int (*MSG_SendPartWriteFunc)(const char* line, int32_t size,
+ bool isheader, void* closure);
+
+class nsMsgSendPart {
+ typedef mozilla::mailnews::MimeEncoder MimeEncoder;
+public:
+ nsMsgSendPart(nsIMsgSend* state, const char *part_charset = NULL);
+ virtual ~nsMsgSendPart(); // Note that the destructor also destroys
+ // any children that were added.
+
+ virtual nsresult Write();
+
+ virtual nsresult SetFile(nsIFile *filename);
+ const nsIFile *GetFile() {return m_file;}
+
+ virtual nsresult SetBuffer(const char* buffer);
+ const char *GetBuffer() {return m_buffer;}
+
+ virtual nsresult SetType(const char* type);
+ const char *GetType() {return m_type;}
+
+ const char *GetCharsetName() {return m_charset_name;}
+
+ virtual nsresult SetOtherHeaders(const char* other);
+ const char *SetOtherHeaders() {return m_other;}
+ virtual nsresult AppendOtherHeaders(const char* moreother);
+
+ virtual nsresult SetMimeDeliveryState(nsIMsgSend* state);
+
+ // Note that the nsMsgSendPart class will take over ownership of the
+ // MimeEncoderData* object, deleting it when it chooses. (This is
+ // necessary because deleting these objects is the only current way to
+ // flush out the data in them.)
+ void SetEncoder(MimeEncoder* encoder) {m_encoder = encoder;}
+ MimeEncoder *GetEncoder() {return m_encoder;}
+
+ void SetStripSensitiveHeaders(bool value)
+ {
+ m_strip_sensitive_headers = value;
+ }
+ bool GetStripSensitiveHeaders() {return m_strip_sensitive_headers;}
+
+ virtual nsresult AddChild(nsMsgSendPart* child);
+
+ int32_t GetNumChildren() {return m_numchildren;}
+ nsMsgSendPart *GetChild(int32_t which);
+ nsMsgSendPart *DetachChild(int32_t which);
+
+ virtual nsresult SetMainPart(bool value);
+ bool IsMainPart()
+ {
+ return m_mainpart;
+ }
+ nsCString m_partNum;
+protected:
+ nsresult CopyString(char** dest, const char* src);
+ nsresult PushBody(const char* buffer, int32_t length);
+
+ nsCOMPtr<nsIMsgSend> m_state;
+ nsMsgSendPart *m_parent;
+ nsCOMPtr <nsIFile> m_file;
+ char *m_buffer;
+ char *m_type;
+ char *m_other;
+ char m_charset_name[64+1]; // charset name associated with this part
+ bool m_strip_sensitive_headers;
+ nsAutoPtr<MimeEncoder> m_encoder;
+
+ nsMsgSendPart **m_children;
+ int32_t m_numchildren;
+
+ // Data used while actually writing.
+ bool m_firstBlock;
+ bool m_needIntlConversion;
+
+ bool m_mainpart;
+
+ bool m_just_hit_CR;
+
+ static int32_t M_counter;
+};
+
+#endif /* _MsgSendPart_H_ */
diff --git a/mailnews/compose/src/nsMsgSendReport.cpp b/mailnews/compose/src/nsMsgSendReport.cpp
new file mode 100644
index 000000000..1bc26ca8a
--- /dev/null
+++ b/mailnews/compose/src/nsMsgSendReport.cpp
@@ -0,0 +1,437 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMsgSendReport.h"
+
+#include "msgCore.h"
+#include "nsIMsgCompose.h"
+#include "nsMsgCompCID.h"
+#include "nsMsgPrompts.h"
+#include "nsError.h"
+#include "nsComposeStrings.h"
+#include "nsIStringBundle.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/Services.h"
+
+NS_IMPL_ISUPPORTS(nsMsgProcessReport, nsIMsgProcessReport)
+
+nsMsgProcessReport::nsMsgProcessReport()
+{
+ Reset();
+}
+
+nsMsgProcessReport::~nsMsgProcessReport()
+{
+}
+
+/* attribute boolean proceeded; */
+NS_IMETHODIMP nsMsgProcessReport::GetProceeded(bool *aProceeded)
+{
+ NS_ENSURE_ARG_POINTER(aProceeded);
+ *aProceeded = mProceeded;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgProcessReport::SetProceeded(bool aProceeded)
+{
+ mProceeded = aProceeded;
+ return NS_OK;
+}
+
+/* attribute nsresult error; */
+NS_IMETHODIMP nsMsgProcessReport::GetError(nsresult *aError)
+{
+ NS_ENSURE_ARG_POINTER(aError);
+ *aError = mError;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgProcessReport::SetError(nsresult aError)
+{
+ mError = aError;
+ return NS_OK;
+}
+
+/* attribute wstring message; */
+NS_IMETHODIMP nsMsgProcessReport::GetMessage(char16_t * *aMessage)
+{
+ NS_ENSURE_ARG_POINTER(aMessage);
+ *aMessage = ToNewUnicode(mMessage);
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgProcessReport::SetMessage(const char16_t * aMessage)
+{
+ mMessage = aMessage;
+ return NS_OK;
+}
+
+/* void Reset (); */
+NS_IMETHODIMP nsMsgProcessReport::Reset()
+{
+ mProceeded = false;
+ mError = NS_OK;
+ mMessage.Truncate();
+
+ return NS_OK;
+}
+
+
+NS_IMPL_ISUPPORTS(nsMsgSendReport, nsIMsgSendReport)
+
+nsMsgSendReport::nsMsgSendReport()
+{
+ uint32_t i;
+ for (i = 0; i <= SEND_LAST_PROCESS; i ++)
+ mProcessReport[i] = new nsMsgProcessReport();
+
+ Reset();
+}
+
+nsMsgSendReport::~nsMsgSendReport()
+{
+ uint32_t i;
+ for (i = 0; i <= SEND_LAST_PROCESS; i ++)
+ mProcessReport[i] = nullptr;
+}
+
+/* attribute long currentProcess; */
+NS_IMETHODIMP nsMsgSendReport::GetCurrentProcess(int32_t *aCurrentProcess)
+{
+ NS_ENSURE_ARG_POINTER(aCurrentProcess);
+ *aCurrentProcess = mCurrentProcess;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgSendReport::SetCurrentProcess(int32_t aCurrentProcess)
+{
+ if (aCurrentProcess < 0 || aCurrentProcess > SEND_LAST_PROCESS)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ mCurrentProcess = aCurrentProcess;
+ if (mProcessReport[mCurrentProcess])
+ mProcessReport[mCurrentProcess]->SetProceeded(true);
+
+ return NS_OK;
+}
+
+/* attribute long deliveryMode; */
+NS_IMETHODIMP nsMsgSendReport::GetDeliveryMode(int32_t *aDeliveryMode)
+{
+ NS_ENSURE_ARG_POINTER(aDeliveryMode);
+ *aDeliveryMode = mDeliveryMode;
+ return NS_OK;
+}
+NS_IMETHODIMP nsMsgSendReport::SetDeliveryMode(int32_t aDeliveryMode)
+{
+ mDeliveryMode = aDeliveryMode;
+ return NS_OK;
+}
+
+/* void Reset (); */
+NS_IMETHODIMP nsMsgSendReport::Reset()
+{
+ uint32_t i;
+ for (i = 0; i <= SEND_LAST_PROCESS; i ++)
+ if (mProcessReport[i])
+ mProcessReport[i]->Reset();
+
+ mCurrentProcess = 0;
+ mDeliveryMode = 0;
+ mAlreadyDisplayReport = false;
+
+ return NS_OK;
+}
+
+/* void setProceeded (in long process, in boolean proceeded); */
+NS_IMETHODIMP nsMsgSendReport::SetProceeded(int32_t process, bool proceeded)
+{
+ if (process < process_Current || process > SEND_LAST_PROCESS)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ if (process == process_Current)
+ process = mCurrentProcess;
+
+ if (!mProcessReport[process])
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mProcessReport[process]->SetProceeded(proceeded);
+}
+
+/* void setError (in long process, in nsresult error, in boolean overwriteError); */
+NS_IMETHODIMP nsMsgSendReport::SetError(int32_t process, nsresult newError, bool overwriteError)
+{
+ if (process < process_Current || process > SEND_LAST_PROCESS)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ if (process == process_Current)
+ {
+ if (mCurrentProcess == process_Current)
+ // We don't know what we're currently trying to do
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ process = mCurrentProcess;
+ }
+
+ if (!mProcessReport[process])
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult currError = NS_OK;
+ mProcessReport[process]->GetError(&currError);
+ if (overwriteError || NS_SUCCEEDED(currError))
+ return mProcessReport[process]->SetError(newError);
+ else
+ return NS_OK;
+}
+
+/* void setMessage (in long process, in wstring message, in boolean overwriteMessage); */
+NS_IMETHODIMP nsMsgSendReport::SetMessage(int32_t process, const char16_t *message, bool overwriteMessage)
+{
+ if (process < process_Current || process > SEND_LAST_PROCESS)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ if (process == process_Current)
+ {
+ if (mCurrentProcess == process_Current)
+ // We don't know what we're currently trying to do
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ process = mCurrentProcess;
+ }
+
+ if (!mProcessReport[process])
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsString currMessage;
+ mProcessReport[process]->GetMessage(getter_Copies(currMessage));
+ if (overwriteMessage || currMessage.IsEmpty())
+ return mProcessReport[process]->SetMessage(message);
+ else
+ return NS_OK;
+}
+
+/* nsIMsgProcessReport getProcessReport (in long process); */
+NS_IMETHODIMP nsMsgSendReport::GetProcessReport(int32_t process, nsIMsgProcessReport **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ if (process < process_Current || process > SEND_LAST_PROCESS)
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ if (process == process_Current)
+ {
+ if (mCurrentProcess == process_Current)
+ // We don't know what we're currently trying to do
+ return NS_ERROR_ILLEGAL_VALUE;
+
+ process = mCurrentProcess;
+ }
+
+ NS_IF_ADDREF(*_retval = mProcessReport[process]);
+ return NS_OK;
+}
+
+/* nsresult displayReport (in nsIPrompt prompt, in boolean showErrorOnly, in boolean dontShowReportTwice); */
+NS_IMETHODIMP nsMsgSendReport::DisplayReport(nsIPrompt *prompt, bool showErrorOnly, bool dontShowReportTwice, nsresult *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ NS_ENSURE_TRUE(mCurrentProcess >= 0 && mCurrentProcess <= SEND_LAST_PROCESS,
+ NS_ERROR_NOT_INITIALIZED);
+
+ nsresult currError = NS_OK;
+ mProcessReport[mCurrentProcess]->GetError(&currError);
+ *_retval = currError;
+
+ if (dontShowReportTwice && mAlreadyDisplayReport)
+ return NS_OK;
+
+ if (showErrorOnly && NS_SUCCEEDED(currError))
+ return NS_OK;
+
+ nsString currMessage;
+ mProcessReport[mCurrentProcess]->GetMessage(getter_Copies(currMessage));
+
+ nsresult rv; // don't step on currError.
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle));
+ if (NS_FAILED(rv))
+ {
+ //TODO need to display a generic hardcoded message
+ mAlreadyDisplayReport = true;
+ return NS_OK;
+ }
+
+ nsString dialogTitle;
+ nsString dialogMessage;
+
+ if (NS_SUCCEEDED(currError))
+ {
+ //TODO display a success error message
+ return NS_OK;
+ }
+
+ //Do we have an explanation of the error? if no, try to build one...
+ if (currMessage.IsEmpty())
+ {
+#ifdef __GNUC__
+// Temporary workaroung until bug 783526 is fixed.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#endif
+ switch (currError)
+ {
+ case NS_BINDING_ABORTED:
+ case NS_ERROR_SEND_FAILED:
+ case NS_ERROR_SEND_FAILED_BUT_NNTP_OK:
+ case NS_MSG_FAILED_COPY_OPERATION:
+ case NS_MSG_UNABLE_TO_SEND_LATER:
+ case NS_MSG_UNABLE_TO_SAVE_DRAFT:
+ case NS_MSG_UNABLE_TO_SAVE_TEMPLATE:
+ //Ignore, don't need to repeat ourself.
+ break;
+ default:
+ const char16_t* errorString = errorStringNameForErrorCode(currError);
+ nsMsgGetMessageByName(errorString, currMessage);
+ break;
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+
+ if (mDeliveryMode == nsIMsgCompDeliverMode::Now || mDeliveryMode == nsIMsgCompDeliverMode::SendUnsent)
+ {
+ // SMTP is taking care of it's own error message and will return NS_ERROR_BUT_DONT_SHOW_ALERT as error code.
+ // In that case, we must not show an alert ourself.
+ if (currError == NS_ERROR_BUT_DONT_SHOW_ALERT)
+ {
+ mAlreadyDisplayReport = true;
+ return NS_OK;
+ }
+
+ bundle->GetStringFromName(u"sendMessageErrorTitle",
+ getter_Copies(dialogTitle));
+
+ const char16_t* preStrName = u"sendFailed";
+ bool askToGoBackToCompose = false;
+ switch (mCurrentProcess)
+ {
+ case process_BuildMessage :
+ preStrName = u"sendFailed";
+ askToGoBackToCompose = false;
+ break;
+ case process_NNTP :
+ preStrName = u"sendFailed";
+ askToGoBackToCompose = false;
+ break;
+ case process_SMTP :
+ bool nntpProceeded;
+ mProcessReport[process_NNTP]->GetProceeded(&nntpProceeded);
+ if (nntpProceeded)
+ preStrName = u"sendFailedButNntpOk";
+ else
+ preStrName = u"sendFailed";
+ askToGoBackToCompose = false;
+ break;
+ case process_Copy:
+ preStrName = u"failedCopyOperation";
+ askToGoBackToCompose = (mDeliveryMode == nsIMsgCompDeliverMode::Now);
+ break;
+ case process_FCC:
+ preStrName = u"failedCopyOperation";
+ askToGoBackToCompose = (mDeliveryMode == nsIMsgCompDeliverMode::Now);
+ break;
+ }
+ bundle->GetStringFromName(preStrName, getter_Copies(dialogMessage));
+
+ //Do we already have an error message?
+ if (!askToGoBackToCompose && currMessage.IsEmpty())
+ {
+ //we don't have an error description but we can put a generic explanation
+ bundle->GetStringFromName(u"genericFailureExplanation",
+ getter_Copies(currMessage));
+ }
+
+ if (!currMessage.IsEmpty())
+ {
+ //Don't need to repeat ourself!
+ if (!currMessage.Equals(dialogMessage))
+ {
+ if (!dialogMessage.IsEmpty())
+ dialogMessage.Append(char16_t('\n'));
+ dialogMessage.Append(currMessage);
+ }
+ }
+
+ if (askToGoBackToCompose)
+ {
+ bool oopsGiveMeBackTheComposeWindow = true;
+ nsString text1;
+ bundle->GetStringFromName(u"returnToComposeWindowQuestion",
+ getter_Copies(text1));
+ if (!dialogMessage.IsEmpty())
+ dialogMessage.AppendLiteral("\n");
+ dialogMessage.Append(text1);
+ nsMsgAskBooleanQuestionByString(prompt, dialogMessage.get(), &oopsGiveMeBackTheComposeWindow, dialogTitle.get());
+ if (!oopsGiveMeBackTheComposeWindow)
+ *_retval = NS_OK;
+ }
+ else
+ nsMsgDisplayMessageByString(prompt, dialogMessage.get(), dialogTitle.get());
+ }
+ else
+ {
+ const char16_t* title;
+ const char16_t* messageName;
+
+ switch (mDeliveryMode)
+ {
+ case nsIMsgCompDeliverMode::Later:
+ title = u"sendLaterErrorTitle";
+ messageName = u"unableToSendLater";
+ break;
+
+ case nsIMsgCompDeliverMode::AutoSaveAsDraft:
+ case nsIMsgCompDeliverMode::SaveAsDraft:
+ title = u"saveDraftErrorTitle";
+ messageName = u"unableToSaveDraft";
+ break;
+
+ case nsIMsgCompDeliverMode::SaveAsTemplate:
+ title = u"saveTemplateErrorTitle";
+ messageName = u"unableToSaveTemplate";
+ break;
+
+ default:
+ /* This should never happen! */
+ title = u"sendMessageErrorTitle";
+ messageName = u"sendFailed";
+ break;
+ }
+
+ bundle->GetStringFromName(title, getter_Copies(dialogTitle));
+ bundle->GetStringFromName(messageName,
+ getter_Copies(dialogMessage));
+
+ //Do we have an error message...
+ if (currMessage.IsEmpty())
+ {
+ //we don't have an error description but we can put a generic explanation
+ bundle->GetStringFromName(u"genericFailureExplanation",
+ getter_Copies(currMessage));
+ }
+
+ if (!currMessage.IsEmpty())
+ {
+ if (!dialogMessage.IsEmpty())
+ dialogMessage.Append(char16_t('\n'));
+ dialogMessage.Append(currMessage);
+ }
+ nsMsgDisplayMessageByString(prompt, dialogMessage.get(), dialogTitle.get());
+ }
+
+ mAlreadyDisplayReport = true;
+ return NS_OK;
+}
+
diff --git a/mailnews/compose/src/nsMsgSendReport.h b/mailnews/compose/src/nsMsgSendReport.h
new file mode 100644
index 000000000..3e344f628
--- /dev/null
+++ b/mailnews/compose/src/nsMsgSendReport.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsMsgSendReport_h__
+#define __nsMsgSendReport_h__
+
+#include "nsIMsgSendReport.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+
+class nsMsgProcessReport : public nsIMsgProcessReport
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGPROCESSREPORT
+
+ nsMsgProcessReport();
+
+private:
+ virtual ~nsMsgProcessReport();
+ bool mProceeded;
+ nsresult mError;
+ nsString mMessage;
+};
+
+
+class nsMsgSendReport : public nsIMsgSendReport
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGSENDREPORT
+
+ nsMsgSendReport();
+
+private:
+ virtual ~nsMsgSendReport();
+ #define SEND_LAST_PROCESS process_FCC
+ nsCOMPtr<nsIMsgProcessReport> mProcessReport[SEND_LAST_PROCESS + 1];
+ int32_t mDeliveryMode;
+ int32_t mCurrentProcess;
+ bool mAlreadyDisplayReport;
+};
+
+#endif
diff --git a/mailnews/compose/src/nsSMTPProtocolHandler.js b/mailnews/compose/src/nsSMTPProtocolHandler.js
new file mode 100644
index 000000000..9b9e3ead0
--- /dev/null
+++ b/mailnews/compose/src/nsSMTPProtocolHandler.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+var kNetworkProtocolCIDPrefix = "@mozilla.org/network/protocol;1?name=";
+var nsIProtocolHandler = Components.interfaces.nsIProtocolHandler;
+
+function makeProtocolHandler(aProtocol, aDefaultPort, aClassID) {
+ return {
+ classID: Components.ID(aClassID),
+ QueryInterface: XPCOMUtils.generateQI([nsIProtocolHandler]),
+
+ scheme: aProtocol,
+ defaultPort: aDefaultPort,
+ protocolFlags: nsIProtocolHandler.URI_NORELATIVE |
+ nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
+ nsIProtocolHandler.URI_NON_PERSISTABLE |
+ nsIProtocolHandler.ALLOWS_PROXY |
+ nsIProtocolHandler.URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT,
+
+ newURI: function (aSpec, aOriginCharset, aBaseURI) {
+ var url = Components.classes["@mozilla.org/messengercompose/smtpurl;1"]
+ .createInstance(Components.interfaces.nsIURI);
+
+ url.spec = aSpec;
+
+ return url;
+ },
+
+ newChannel: function (aURI) {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ newChannel2: function(aURI, aLoadInfo)
+ {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ allowPort: function (port, scheme) {
+ return port == aDefaultPort;
+ }
+ };
+}
+
+function nsSMTPProtocolHandler() {}
+
+nsSMTPProtocolHandler.prototype =
+ makeProtocolHandler("smtp",
+ Components.interfaces.nsISmtpUrl.DEFAULT_SMTP_PORT,
+ "b14c2b67-8680-4c11-8d63-9403c7d4f757");
+
+function nsSMTPSProtocolHandler() {}
+
+nsSMTPSProtocolHandler.prototype =
+ makeProtocolHandler("smtps",
+ Components.interfaces.nsISmtpUrl.DEFAULT_SMTPS_PORT,
+ "057d0997-9e3a-411e-b4ee-2602f53fe05f");
+
+var components = [nsSMTPProtocolHandler, nsSMTPSProtocolHandler];
+var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
diff --git a/mailnews/compose/src/nsSMTPProtocolHandler.manifest b/mailnews/compose/src/nsSMTPProtocolHandler.manifest
new file mode 100644
index 000000000..4d371163f
--- /dev/null
+++ b/mailnews/compose/src/nsSMTPProtocolHandler.manifest
@@ -0,0 +1,4 @@
+component {b14c2b67-8680-4c11-8d63-9403c7d4f757} nsSMTPProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=smtp {b14c2b67-8680-4c11-8d63-9403c7d4f757}
+component {057d0997-9e3a-411e-b4ee-2602f53fe05f} nsSMTPProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=smtps {057d0997-9e3a-411e-b4ee-2602f53fe05f}
diff --git a/mailnews/compose/src/nsSmtpProtocol.cpp b/mailnews/compose/src/nsSmtpProtocol.cpp
new file mode 100644
index 000000000..d525d3f7f
--- /dev/null
+++ b/mailnews/compose/src/nsSmtpProtocol.cpp
@@ -0,0 +1,2249 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "msgCore.h"
+#include "nsSmtpProtocol.h"
+#include "nscore.h"
+#include "nsIStreamListener.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsISocketTransport.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgCompCID.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsStringGlue.h"
+#include "nsTextFormatter.h"
+#include "nsIMsgIdentity.h"
+#include "nsISmtpServer.h"
+#include "prtime.h"
+#include "mozilla/Logging.h"
+#include "prerror.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "plbase64.h"
+#include "prnetdb.h"
+#include "prsystem.h"
+#include "nsMsgUtils.h"
+#include "nsIPipe.h"
+#include "nsNetUtil.h"
+#include "nsIPrefService.h"
+#include "nsISSLSocketControl.h"
+#include "nsComposeStrings.h"
+#include "nsIStringBundle.h"
+#include "nsMsgCompUtils.h"
+#include "nsIMsgWindow.h"
+#include "MailNewsTypes2.h" // for nsMsgSocketType and nsMsgAuthMethod
+#include "nsIIDNService.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+#include "mozilla/Services.h"
+#include "mozilla/Attributes.h"
+#include "nsINetAddr.h"
+#include "nsIProxyInfo.h"
+
+#ifndef XP_UNIX
+#include <stdarg.h>
+#endif /* !XP_UNIX */
+
+#undef PostMessage // avoid to collision with WinUser.h
+
+static PRLogModuleInfo *SMTPLogModule = nullptr;
+
+using namespace mozilla::mailnews;
+
+/* the output_buffer_size must be larger than the largest possible line
+ * 2000 seems good for news
+ *
+ * jwz: I increased this to 4k since it must be big enough to hold the
+ * entire button-bar HTML, and with the new "mailto" format, that can
+ * contain arbitrarily long header fields like "references".
+ *
+ * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
+ */
+#define OUTPUT_BUFFER_SIZE (4096*2)
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// TEMPORARY HARD CODED FUNCTIONS
+///////////////////////////////////////////////////////////////////////////////////////////
+
+/* based on in NET_ExplainErrorDetails in mkmessag.c */
+nsresult nsExplainErrorDetails(nsISmtpUrl * aSmtpUrl, nsresult aCode, ...)
+{
+ NS_ENSURE_ARG(aSmtpUrl);
+
+ va_list args;
+
+ nsCOMPtr<nsIPrompt> dialog;
+ aSmtpUrl->GetPrompt(getter_AddRefs(dialog));
+ NS_ENSURE_TRUE(dialog, NS_ERROR_FAILURE);
+
+ char16_t * msg;
+ nsString eMsg;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsresult rv = bundleService->CreateBundle(
+ "chrome://messenger/locale/messengercompose/composeMsgs.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ va_start (args, aCode);
+
+ const char16_t* exitString;
+#ifdef __GNUC__
+// Temporary workaroung until bug 783526 is fixed.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+#endif
+ switch (aCode)
+ {
+ case NS_ERROR_ILLEGAL_LOCALPART:
+ bundle->GetStringFromName(u"errorIllegalLocalPart",
+ getter_Copies(eMsg));
+ msg = nsTextFormatter::vsmprintf(eMsg.get(), args);
+ break;
+ case NS_ERROR_SMTP_SERVER_ERROR:
+ case NS_ERROR_TCP_READ_ERROR:
+ case NS_ERROR_SMTP_TEMP_SIZE_EXCEEDED:
+ case NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_1:
+ case NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_2:
+ case NS_ERROR_SENDING_FROM_COMMAND:
+ case NS_ERROR_SENDING_RCPT_COMMAND:
+ case NS_ERROR_SENDING_DATA_COMMAND:
+ case NS_ERROR_SENDING_MESSAGE:
+ case NS_ERROR_SMTP_GREETING:
+ exitString = errorStringNameForErrorCode(aCode);
+ bundle->GetStringFromName(exitString, getter_Copies(eMsg));
+ msg = nsTextFormatter::vsmprintf(eMsg.get(), args);
+ break;
+ default:
+ NS_WARNING("falling to default error code");
+ bundle->GetStringFromName(u"communicationsError", getter_Copies(eMsg));
+ msg = nsTextFormatter::smprintf(eMsg.get(), aCode);
+ break;
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+ if (msg)
+ {
+ rv = dialog->Alert(nullptr, msg);
+ nsTextFormatter::smprintf_free(msg);
+ }
+
+ va_end (args);
+
+ return rv;
+}
+
+/* RFC 1891 -- extended smtp value encoding scheme
+
+ 5. Additional parameters for RCPT and MAIL commands
+
+ The extended RCPT and MAIL commands are issued by a client when it wishes to request a DSN from the
+ server, under certain conditions, for a particular recipient. The extended RCPT and MAIL commands are
+ identical to the RCPT and MAIL commands defined in [1], except that one or more of the following parameters
+ appear after the sender or recipient address, respectively. The general syntax for extended SMTP commands is
+ defined in [4].
+
+ NOTE: Although RFC 822 ABNF is used to describe the syntax of these parameters, they are not, in the
+ language of that document, "structured field bodies". Therefore, while parentheses MAY appear within an
+ emstp-value, they are not recognized as comment delimiters.
+
+ The syntax for "esmtp-value" in [4] does not allow SP, "=", control characters, or characters outside the
+ traditional ASCII range of 1- 127 decimal to be transmitted in an esmtp-value. Because the ENVID and
+ ORCPT parameters may need to convey values outside this range, the esmtp-values for these parameters are
+ encoded as "xtext". "xtext" is formally defined as follows:
+
+ xtext = *( xchar / hexchar )
+
+ xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive, except for "+" and "=".
+
+ ; "hexchar"s are intended to encode octets that cannot appear
+ ; as ASCII characters within an esmtp-value.
+
+ hexchar = ASCII "+" immediately followed by two upper case hexadecimal digits
+
+ When encoding an octet sequence as xtext:
+
+ + Any ASCII CHAR between "!" and "~" inclusive, except for "+" and "=",
+ MAY be encoded as itself. (A CHAR in this range MAY instead be encoded as a "hexchar", at the
+ implementor's discretion.)
+
+ + ASCII CHARs that fall outside the range above must be encoded as
+ "hexchar".
+
+ */
+/* caller must free the return buffer */
+static char *
+esmtp_value_encode(const char *addr)
+{
+ char *buffer = (char *) PR_Malloc(512); /* esmtp ORCPT allow up to 500 chars encoded addresses */
+ char *bp = buffer, *bpEnd = buffer+500;
+ int len, i;
+
+ if (!buffer) return NULL;
+
+ *bp=0;
+ if (! addr || *addr == 0) /* this will never happen */
+ return buffer;
+
+ for (i=0, len=PL_strlen(addr); i < len && bp < bpEnd; i++)
+ {
+ if (*addr >= 0x21 &&
+ *addr <= 0x7E &&
+ *addr != '+' &&
+ *addr != '=')
+ {
+ *bp++ = *addr++;
+ }
+ else
+ {
+ PR_snprintf(bp, bpEnd-bp, "+%.2X", ((int)*addr++));
+ bp += PL_strlen(bp);
+ }
+ }
+ *bp=0;
+ return buffer;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// END OF TEMPORARY HARD CODED FUNCTIONS
+///////////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS_INHERITED(nsSmtpProtocol, nsMsgAsyncWriteProtocol,
+ msgIOAuth2ModuleListener)
+
+nsSmtpProtocol::nsSmtpProtocol(nsIURI * aURL)
+ : nsMsgAsyncWriteProtocol(aURL)
+{
+}
+
+nsSmtpProtocol::~nsSmtpProtocol()
+{
+ // free our local state
+ PR_Free(m_dataBuf);
+ delete m_lineStreamBuffer;
+}
+
+void nsSmtpProtocol::Initialize(nsIURI * aURL)
+{
+ NS_PRECONDITION(aURL, "invalid URL passed into Smtp Protocol");
+ nsresult rv = NS_OK;
+
+ m_flags = 0;
+ m_prefAuthMethods = 0;
+ m_failedAuthMethods = 0;
+ m_currentAuthMethod = 0;
+ m_usernamePrompted = false;
+ m_prefSocketType = nsMsgSocketType::trySTARTTLS;
+ m_tlsInitiated = false;
+
+ m_urlErrorState = NS_ERROR_FAILURE;
+
+ if (!SMTPLogModule)
+ SMTPLogModule = PR_NewLogModule("SMTP");
+
+ if (aURL)
+ m_runningURL = do_QueryInterface(aURL);
+
+ // extract out message feedback if there is any.
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(aURL);
+ if (mailnewsUrl)
+ mailnewsUrl->GetStatusFeedback(getter_AddRefs(m_statusFeedback));
+
+ m_dataBuf = (char *) PR_Malloc(sizeof(char) * OUTPUT_BUFFER_SIZE);
+ m_dataBufSize = OUTPUT_BUFFER_SIZE;
+
+ m_nextState = SMTP_START_CONNECT;
+ m_nextStateAfterResponse = SMTP_START_CONNECT;
+ m_responseCode = 0;
+ m_previousResponseCode = 0;
+ m_continuationResponse = -1;
+ m_tlsEnabled = false;
+ m_addressesLeft = 0;
+
+ m_sendDone = false;
+
+ m_sizelimit = 0;
+ m_totalMessageSize = 0;
+ nsCOMPtr<nsIFile> file;
+ m_runningURL->GetPostMessageFile(getter_AddRefs(file));
+ if (file)
+ file->GetFileSize(&m_totalMessageSize);
+
+ m_originalContentLength = 0;
+ m_totalAmountRead = 0;
+
+ m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, true);
+ // ** may want to consider caching the server capability to save lots of
+ // round trip communication between the client and server
+ int32_t authMethod = 0;
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
+ if (smtpServer) {
+ smtpServer->GetAuthMethod(&authMethod);
+ smtpServer->GetSocketType(&m_prefSocketType);
+ smtpServer->GetHelloArgument(getter_Copies(m_helloArgument));
+
+ // Query for OAuth2 support. If the SMTP server preferences don't allow
+ // for OAuth2, then don't carry around the OAuth2 module any longer
+ // since we won't need it.
+ mOAuth2Support = do_CreateInstance(MSGIOAUTH2MODULE_CONTRACTID);
+ if (mOAuth2Support)
+ {
+ bool supportsOAuth = false;
+ mOAuth2Support->InitFromSmtp(smtpServer, &supportsOAuth);
+ if (!supportsOAuth)
+ mOAuth2Support = nullptr;
+ }
+ }
+ InitPrefAuthMethods(authMethod);
+
+ nsAutoCString hostName;
+ int32_t port = 0;
+
+ aURL->GetPort(&port);
+ aURL->GetAsciiHost(hostName);
+
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Info, ("SMTP Connecting to: %s", hostName.get()));
+
+ // When we are making a secure connection, we need to make sure that we
+ // pass an interface requestor down to the socket transport so that PSM can
+ // retrieve a nsIPrompt instance if needed.
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ nsCOMPtr<nsISmtpUrl> smtpUrl(do_QueryInterface(aURL));
+ if (smtpUrl)
+ smtpUrl->GetNotificationCallbacks(getter_AddRefs(callbacks));
+
+ nsCOMPtr<nsIProxyInfo> proxyInfo;
+ rv = MsgExamineForProxy(this, getter_AddRefs(proxyInfo));
+ if (NS_FAILED(rv)) proxyInfo = nullptr;
+
+ if (m_prefSocketType == nsMsgSocketType::SSL)
+ rv = OpenNetworkSocketWithInfo(hostName.get(), port, "ssl", proxyInfo,
+ callbacks);
+ else if (m_prefSocketType != nsMsgSocketType::plain)
+ {
+ rv = OpenNetworkSocketWithInfo(hostName.get(), port, "starttls",
+ proxyInfo, callbacks);
+ if (NS_FAILED(rv) && m_prefSocketType == nsMsgSocketType::trySTARTTLS)
+ {
+ m_prefSocketType = nsMsgSocketType::plain;
+ rv = OpenNetworkSocketWithInfo(hostName.get(), port, nullptr,
+ proxyInfo, callbacks);
+ }
+ }
+ else
+ rv = OpenNetworkSocketWithInfo(hostName.get(), port, nullptr, proxyInfo,
+ callbacks);
+}
+
+void nsSmtpProtocol::AppendHelloArgument(nsACString& aResult)
+{
+ nsresult rv;
+
+ // is a custom EHLO/HELO argument configured for the transport to be used?
+ if (!m_helloArgument.IsEmpty())
+ {
+ aResult += m_helloArgument;
+ }
+ else
+ {
+ // is a FQDN known for this system?
+ char hostName[256];
+ PR_GetSystemInfo(PR_SI_HOSTNAME_UNTRUNCATED, hostName, sizeof hostName);
+ if ((hostName[0] != '\0') && (strchr(hostName, '.') != NULL))
+ {
+ nsDependentCString cleanedHostName(hostName);
+ // avoid problems with hostnames containing newlines/whitespace
+ cleanedHostName.StripWhitespace();
+ aResult += cleanedHostName;
+ }
+ else
+ {
+ nsCOMPtr<nsINetAddr> iaddr; // IP address for this connection
+ // our transport is always a nsISocketTransport
+ nsCOMPtr<nsISocketTransport> socketTransport = do_QueryInterface(m_transport);
+ // should return the interface ip of the SMTP connection
+ // minimum case - see bug 68877 and RFC 2821, chapter 4.1.1.1
+ rv = socketTransport->GetScriptableSelfAddr(getter_AddRefs(iaddr));
+
+ if (NS_SUCCEEDED(rv))
+ {
+ // turn it into a string
+ nsCString ipAddressString;
+ rv = iaddr->GetAddress(ipAddressString);
+ if (NS_SUCCEEDED(rv))
+ {
+#ifdef DEBUG
+ bool v4mapped = false;
+ iaddr->GetIsV4Mapped(&v4mapped);
+ NS_ASSERTION(!v4mapped,
+ "unexpected IPv4-mapped IPv6 address");
+#endif
+
+ uint16_t family = nsINetAddr::FAMILY_INET;
+ iaddr->GetFamily(&family);
+
+ if (family == nsINetAddr::FAMILY_INET6) // IPv6 style address?
+ aResult.AppendLiteral("[IPv6:");
+ else
+ aResult.AppendLiteral("[");
+
+ aResult.Append(ipAddressString);
+ aResult.Append(']');
+ }
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+// we suppport the nsIStreamListener interface
+////////////////////////////////////////////////////////////////////////////////////////////
+
+// stop binding is a "notification" informing us that the stream
+// associated with aURL is going away.
+NS_IMETHODIMP nsSmtpProtocol::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
+ nsresult aStatus)
+{
+ bool connDroppedDuringAuth = NS_SUCCEEDED(aStatus) && !m_sendDone &&
+ (m_nextStateAfterResponse == SMTP_AUTH_LOGIN_STEP0_RESPONSE ||
+ m_nextStateAfterResponse == SMTP_AUTH_LOGIN_RESPONSE);
+ // ignore errors handling the QUIT command so fcc can continue.
+ if (m_sendDone && NS_FAILED(aStatus))
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Info,
+ ("SMTP connection error quitting %lx, ignoring ", aStatus));
+ aStatus = NS_OK;
+ }
+ if (NS_SUCCEEDED(aStatus) && !m_sendDone) {
+ // if we are getting OnStopRequest() with NS_OK,
+ // but we haven't finished clean, that's spells trouble.
+ // it means that the server has dropped us before we could send the whole mail
+ // for example, see bug #200647
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Info,
+ ("SMTP connection dropped after %ld total bytes read", m_totalAmountRead));
+ if (!connDroppedDuringAuth)
+ nsMsgAsyncWriteProtocol::OnStopRequest(nullptr, ctxt, NS_ERROR_NET_INTERRUPT);
+ }
+ else
+ nsMsgAsyncWriteProtocol::OnStopRequest(nullptr, ctxt, aStatus);
+
+ // okay, we've been told that the send is done and the connection is going away. So
+ // we need to release all of our state
+ nsresult rv = nsMsgAsyncWriteProtocol::CloseSocket();
+ // If the server dropped the connection when we were expecting
+ // a login response, reprompt for password, and if the user asks,
+ // retry the url.
+ if (connDroppedDuringAuth)
+ {
+ nsCOMPtr<nsIURI> runningURI = do_QueryInterface(m_runningURL);
+ nsresult rv = AuthLoginResponse(nullptr, 0);
+ if (NS_FAILED(rv))
+ return rv;
+ return LoadUrl(runningURI, ctxt);
+ }
+
+ return rv;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+// End of nsIStreamListenerSupport
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+void nsSmtpProtocol::UpdateStatus(const char16_t* aStatusName)
+{
+ if (m_statusFeedback)
+ {
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ if (!bundleService) return;
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsresult rv = bundleService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(bundle));
+ if (NS_FAILED(rv)) return;
+ nsString msg;
+ bundle->GetStringFromName(aStatusName, getter_Copies(msg));
+ UpdateStatusWithString(msg.get());
+ }
+}
+
+void nsSmtpProtocol::UpdateStatusWithString(const char16_t * aStatusString)
+{
+ if (m_statusFeedback && aStatusString)
+ m_statusFeedback->ShowStatusString(nsDependentString(aStatusString));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+// Begin protocol state machine functions...
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * gets the response code from the SMTP server and the
+ * response line
+ */
+nsresult nsSmtpProtocol::SmtpResponse(nsIInputStream * inputStream, uint32_t length)
+{
+ char * line = nullptr;
+ char cont_char;
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+
+ if (!m_lineStreamBuffer)
+ // this will force an error and at least we won't crash
+ return NS_ERROR_NULL_POINTER;
+
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData);
+
+ if (pauseForMoreData || !line)
+ {
+ SetFlag(SMTP_PAUSE_FOR_READ); /* pause */
+ PR_Free(line);
+ return NS_OK;
+ }
+
+ m_totalAmountRead += ln;
+
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Info, ("SMTP Response: %s", line));
+ cont_char = ' '; /* default */
+ // sscanf() doesn't update m_responseCode if line doesn't start
+ // with a number. That can be dangerous. So be sure to set
+ // m_responseCode to 0 if no items read.
+ if (PR_sscanf(line, "%d%c", &m_responseCode, &cont_char) <= 0)
+ m_responseCode = 0;
+
+ if (m_continuationResponse == -1)
+ {
+ if (cont_char == '-') /* begin continuation */
+ m_continuationResponse = m_responseCode;
+
+ // display the whole message if no valid response code or
+ // message shorter than 4 chars
+ m_responseText = (m_responseCode >= 100 && PL_strlen(line) > 3) ? line + 4 : line;
+ }
+ else
+ { /* have to continue */
+ if (m_continuationResponse == m_responseCode && cont_char == ' ')
+ m_continuationResponse = -1; /* ended */
+
+ if (m_responseText.IsEmpty() || m_responseText.Last() != '\n')
+ m_responseText += "\n";
+
+ m_responseText += (PL_strlen(line) > 3) ? line + 4 : line;
+ }
+
+ if (m_responseCode == 220 && m_responseText.Length() && !m_tlsInitiated &&
+ !m_sendDone)
+ m_nextStateAfterResponse = SMTP_EXTN_LOGIN_RESPONSE;
+
+ if (m_continuationResponse == -1) /* all done with this response? */
+ {
+ m_nextState = m_nextStateAfterResponse;
+ ClearFlag(SMTP_PAUSE_FOR_READ); /* don't pause */
+ }
+
+ PR_Free(line);
+ return NS_OK;
+}
+
+nsresult nsSmtpProtocol::ExtensionLoginResponse(nsIInputStream * inputStream, uint32_t length)
+{
+ nsresult status = NS_OK;
+
+ if (m_responseCode != 220)
+ {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ nsExplainErrorDetails(m_runningURL, NS_ERROR_SMTP_GREETING,
+ m_responseText.get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
+
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return NS_ERROR_SMTP_AUTH_FAILURE;
+ }
+
+ nsAutoCString buffer("EHLO ");
+ AppendHelloArgument(buffer);
+ buffer += CRLF;
+
+ status = SendData(buffer.get());
+
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_SEND_EHLO_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+
+ return(status);
+}
+
+nsresult nsSmtpProtocol::SendHeloResponse(nsIInputStream * inputStream, uint32_t length)
+{
+ nsresult status = NS_OK;
+ nsAutoCString buffer;
+ nsresult rv;
+
+ if (m_responseCode != 250)
+ {
+#ifdef DEBUG
+ rv =
+#endif
+ nsExplainErrorDetails(m_runningURL, NS_ERROR_SMTP_SERVER_ERROR,
+ m_responseText.get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
+
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return NS_ERROR_SMTP_AUTH_FAILURE;
+ }
+
+ // check if we're just verifying the ability to logon
+ nsCOMPtr<nsISmtpUrl> smtpUrl = do_QueryInterface(m_runningURL, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool verifyingLogon = false;
+ smtpUrl->GetVerifyLogon(&verifyingLogon);
+ if (verifyingLogon)
+ return SendQuit();
+
+ // extract the email address from the identity
+ nsCString emailAddress;
+ nsCOMPtr <nsIMsgIdentity> senderIdentity;
+ rv = m_runningURL->GetSenderIdentity(getter_AddRefs(senderIdentity));
+ if (NS_FAILED(rv) || !senderIdentity)
+ {
+ m_urlErrorState = NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS;
+ return(NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS);
+ }
+ senderIdentity->GetEmail(emailAddress);
+
+ if (emailAddress.IsEmpty())
+ {
+ m_urlErrorState = NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS;
+ return(NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS);
+ }
+
+ nsCString fullAddress;
+ // Quote the email address before passing it to the SMTP server.
+ MakeMimeAddress(EmptyCString(), emailAddress, fullAddress);
+
+ buffer = "MAIL FROM:<";
+ buffer += fullAddress;
+ buffer += ">";
+
+ nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ rv = prefs->GetBranch(nullptr, getter_AddRefs(prefBranch));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (TestFlag(SMTP_EHLO_DSN_ENABLED))
+ {
+ bool requestDSN = false;
+ rv = m_runningURL->GetRequestDSN(&requestDSN);
+
+ if (requestDSN)
+ {
+ bool requestRetFull = false;
+ rv = prefBranch->GetBoolPref("mail.dsn.ret_full_on", &requestRetFull);
+
+ buffer += requestRetFull ? " RET=FULL" : " RET=HDRS";
+
+ nsCString dsnEnvid;
+
+ // get the envid from the smtpUrl
+ rv = m_runningURL->GetDsnEnvid(dsnEnvid);
+
+ if (dsnEnvid.IsEmpty())
+ dsnEnvid.Adopt(msg_generate_message_id(senderIdentity));
+
+ buffer += " ENVID=";
+ buffer += dsnEnvid;
+ }
+ }
+
+ if (TestFlag(SMTP_EHLO_8BIT_ENABLED))
+ {
+ bool strictlyMime = false;
+ rv = prefBranch->GetBoolPref("mail.strictly_mime", &strictlyMime);
+
+ if (!strictlyMime)
+ buffer.Append(" BODY=8BITMIME");
+ }
+
+ if (TestFlag(SMTP_EHLO_SIZE_ENABLED))
+ {
+ buffer.Append(" SIZE=");
+ buffer.AppendInt(m_totalMessageSize);
+ }
+ buffer += CRLF;
+
+ status = SendData(buffer.get());
+
+ m_nextState = SMTP_RESPONSE;
+
+
+ m_nextStateAfterResponse = SMTP_SEND_MAIL_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+
+ return(status);
+}
+
+nsresult nsSmtpProtocol::SendEhloResponse(nsIInputStream * inputStream, uint32_t length)
+{
+ nsresult status = NS_OK;
+
+ if (m_responseCode != 250)
+ {
+ /* EHLO must not be implemented by the server, so fall back to the HELO case
+ * if command is unrecognized or unimplemented.
+ */
+ if (m_responseCode == 500 || m_responseCode == 502)
+ {
+ /* If STARTTLS is requested by the user, EHLO is required to advertise it.
+ * But only if TLS handshake is not already accomplished.
+ */
+ if (m_prefSocketType == nsMsgSocketType::alwaysSTARTTLS &&
+ !m_tlsEnabled)
+ {
+ m_nextState = SMTP_ERROR_DONE;
+ m_urlErrorState = NS_ERROR_STARTTLS_FAILED_EHLO_STARTTLS;
+ return(NS_ERROR_STARTTLS_FAILED_EHLO_STARTTLS);
+ }
+
+ nsAutoCString buffer("HELO ");
+ AppendHelloArgument(buffer);
+ buffer += CRLF;
+
+ status = SendData(buffer.get());
+
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_SEND_HELO_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ return (status);
+ }
+ /* e.g. getting 421 "Server says unauthorized, bye" or
+ * 501 "Syntax error in EHLOs parameters or arguments"
+ */
+ else
+ {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ nsExplainErrorDetails(m_runningURL, NS_ERROR_SMTP_SERVER_ERROR,
+ m_responseText.get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
+
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return NS_ERROR_SMTP_AUTH_FAILURE;
+ }
+ }
+
+ int32_t responseLength = m_responseText.Length();
+ int32_t startPos = 0;
+ int32_t endPos;
+ do
+ {
+ endPos = m_responseText.FindChar('\n', startPos + 1);
+ nsAutoCString responseLine;
+ responseLine.Assign(Substring(m_responseText, startPos,
+ (endPos >= 0 ? endPos : responseLength) - startPos));
+
+ MsgCompressWhitespace(responseLine);
+ if (responseLine.LowerCaseEqualsLiteral("starttls"))
+ {
+ SetFlag(SMTP_EHLO_STARTTLS_ENABLED);
+ }
+ else if (responseLine.LowerCaseEqualsLiteral("dsn"))
+ {
+ SetFlag(SMTP_EHLO_DSN_ENABLED);
+ }
+ else if (StringBeginsWith(responseLine, NS_LITERAL_CSTRING("AUTH"), nsCaseInsensitiveCStringComparator()))
+ {
+ SetFlag(SMTP_AUTH);
+
+ if (responseLine.Find(NS_LITERAL_CSTRING("GSSAPI"),
+ CaseInsensitiveCompare) >= 0)
+ SetFlag(SMTP_AUTH_GSSAPI_ENABLED);
+
+ if (responseLine.Find(NS_LITERAL_CSTRING("CRAM-MD5"),
+ CaseInsensitiveCompare) >= 0)
+ SetFlag(SMTP_AUTH_CRAM_MD5_ENABLED);
+
+ if (responseLine.Find(NS_LITERAL_CSTRING("NTLM"),
+ CaseInsensitiveCompare) >= 0)
+ SetFlag(SMTP_AUTH_NTLM_ENABLED);
+
+ if (responseLine.Find(NS_LITERAL_CSTRING("MSN"),
+ CaseInsensitiveCompare) >= 0)
+ SetFlag(SMTP_AUTH_MSN_ENABLED);
+
+ if (responseLine.Find(NS_LITERAL_CSTRING("PLAIN"),
+ CaseInsensitiveCompare) >= 0)
+ SetFlag(SMTP_AUTH_PLAIN_ENABLED);
+
+ if (responseLine.Find(NS_LITERAL_CSTRING("LOGIN"),
+ CaseInsensitiveCompare) >= 0)
+ SetFlag(SMTP_AUTH_LOGIN_ENABLED);
+
+ if (responseLine.Find(NS_LITERAL_CSTRING("EXTERNAL"),
+ CaseInsensitiveCompare) >= 0)
+ SetFlag(SMTP_AUTH_EXTERNAL_ENABLED);
+
+ if (responseLine.Find(NS_LITERAL_CSTRING("XOAUTH2"),
+ CaseInsensitiveCompare) >= 0)
+ SetFlag(SMTP_AUTH_OAUTH2_ENABLED);
+ }
+ else if (StringBeginsWith(responseLine, NS_LITERAL_CSTRING("SIZE"), nsCaseInsensitiveCStringComparator()))
+ {
+ SetFlag(SMTP_EHLO_SIZE_ENABLED);
+
+ m_sizelimit = atol((responseLine.get()) + 4);
+ }
+ else if (StringBeginsWith(responseLine, NS_LITERAL_CSTRING("8BITMIME"), nsCaseInsensitiveCStringComparator()))
+ {
+ SetFlag(SMTP_EHLO_8BIT_ENABLED);
+ }
+
+ startPos = endPos + 1;
+ } while (endPos >= 0);
+
+ if (TestFlag(SMTP_EHLO_SIZE_ENABLED) &&
+ m_sizelimit > 0 && (int32_t)m_totalMessageSize > m_sizelimit)
+ {
+#ifdef DEBUG
+ nsresult rv =
+#endif
+ nsExplainErrorDetails(m_runningURL,
+ NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_1, m_sizelimit);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
+
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return(NS_ERROR_SENDING_FROM_COMMAND);
+ }
+
+ m_nextState = SMTP_AUTH_PROCESS_STATE;
+ return status;
+}
+
+
+nsresult nsSmtpProtocol::SendTLSResponse()
+{
+ // only tear down our existing connection and open a new one if we received a 220 response
+ // from the smtp server after we issued the STARTTLS
+ nsresult rv = NS_OK;
+ if (m_responseCode == 220)
+ {
+ nsCOMPtr<nsISupports> secInfo;
+ nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = strans->GetSecurityInfo(getter_AddRefs(secInfo));
+
+ if (NS_SUCCEEDED(rv) && secInfo) {
+ nsCOMPtr<nsISSLSocketControl> sslControl = do_QueryInterface(secInfo, &rv);
+
+ if (NS_SUCCEEDED(rv) && sslControl)
+ rv = sslControl->StartTLS();
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ m_nextState = SMTP_EXTN_LOGIN_RESPONSE;
+ m_nextStateAfterResponse = SMTP_EXTN_LOGIN_RESPONSE;
+ m_tlsEnabled = true;
+ m_flags = 0; // resetting the flags
+ return rv;
+ }
+ }
+
+ ClearFlag(SMTP_EHLO_STARTTLS_ENABLED);
+ m_tlsInitiated = false;
+ m_nextState = SMTP_AUTH_PROCESS_STATE;
+
+ return rv;
+}
+
+void nsSmtpProtocol::InitPrefAuthMethods(int32_t authMethodPrefValue)
+{
+ // for m_prefAuthMethods, using the same flags as server capablities.
+ switch (authMethodPrefValue)
+ {
+ case nsMsgAuthMethod::none:
+ m_prefAuthMethods = SMTP_AUTH_NONE_ENABLED;
+ break;
+ //case nsMsgAuthMethod::old -- no such thing for SMTP
+ case nsMsgAuthMethod::passwordCleartext:
+ m_prefAuthMethods = SMTP_AUTH_LOGIN_ENABLED |
+ SMTP_AUTH_PLAIN_ENABLED;
+ break;
+ case nsMsgAuthMethod::passwordEncrypted:
+ m_prefAuthMethods = SMTP_AUTH_CRAM_MD5_ENABLED;
+ break;
+ case nsMsgAuthMethod::NTLM:
+ m_prefAuthMethods = SMTP_AUTH_NTLM_ENABLED |
+ SMTP_AUTH_MSN_ENABLED;
+ break;
+ case nsMsgAuthMethod::GSSAPI:
+ m_prefAuthMethods = SMTP_AUTH_GSSAPI_ENABLED;
+ break;
+ case nsMsgAuthMethod::OAuth2:
+ m_prefAuthMethods = SMTP_AUTH_OAUTH2_ENABLED;
+ break;
+ case nsMsgAuthMethod::secure:
+ m_prefAuthMethods = SMTP_AUTH_CRAM_MD5_ENABLED |
+ SMTP_AUTH_GSSAPI_ENABLED |
+ SMTP_AUTH_NTLM_ENABLED | SMTP_AUTH_MSN_ENABLED |
+ SMTP_AUTH_EXTERNAL_ENABLED; // TODO: Expose EXTERNAL? How?
+ break;
+ default:
+ NS_ASSERTION(false, "SMTP: authMethod pref invalid");
+ // TODO log to error console
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error,
+ ("SMTP: bad pref authMethod = %d\n", authMethodPrefValue));
+ // fall to any
+ MOZ_FALLTHROUGH;
+ case nsMsgAuthMethod::anything:
+ m_prefAuthMethods =
+ SMTP_AUTH_LOGIN_ENABLED | SMTP_AUTH_PLAIN_ENABLED |
+ SMTP_AUTH_CRAM_MD5_ENABLED | SMTP_AUTH_GSSAPI_ENABLED |
+ SMTP_AUTH_NTLM_ENABLED | SMTP_AUTH_MSN_ENABLED |
+ SMTP_AUTH_OAUTH2_ENABLED |
+ SMTP_AUTH_EXTERNAL_ENABLED;
+ break;
+ }
+
+ // Only enable OAuth2 support if we can do the lookup.
+ if ((m_prefAuthMethods & SMTP_AUTH_OAUTH2_ENABLED) && !mOAuth2Support)
+ m_prefAuthMethods &= ~SMTP_AUTH_OAUTH2_ENABLED;
+
+ NS_ASSERTION(m_prefAuthMethods != 0, "SMTP:InitPrefAuthMethods() failed");
+}
+
+/**
+ * Changes m_currentAuthMethod to pick the next-best one
+ * which is allowed by server and prefs and not marked failed.
+ * The order of preference and trying of auth methods is encoded here.
+ */
+nsresult nsSmtpProtocol::ChooseAuthMethod()
+{
+ int32_t serverCaps = m_flags; // from nsMsgProtocol::TestFlag()
+ int32_t availCaps = serverCaps & m_prefAuthMethods & ~m_failedAuthMethods;
+
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug,
+ ("SMTP auth: server caps 0x%X, pref 0x%X, failed 0x%X, avail caps 0x%X",
+ serverCaps, m_prefAuthMethods, m_failedAuthMethods, availCaps));
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel:: Debug,
+ ("(GSSAPI = 0x%X, CRAM = 0x%X, NTLM = 0x%X, "
+ "MSN = 0x%X, PLAIN = 0x%X, LOGIN = 0x%X, EXTERNAL = 0x%X)",
+ SMTP_AUTH_GSSAPI_ENABLED, SMTP_AUTH_CRAM_MD5_ENABLED,
+ SMTP_AUTH_NTLM_ENABLED, SMTP_AUTH_MSN_ENABLED, SMTP_AUTH_PLAIN_ENABLED,
+ SMTP_AUTH_LOGIN_ENABLED, SMTP_AUTH_EXTERNAL_ENABLED));
+
+ if (SMTP_AUTH_GSSAPI_ENABLED & availCaps)
+ m_currentAuthMethod = SMTP_AUTH_GSSAPI_ENABLED;
+ else if (SMTP_AUTH_CRAM_MD5_ENABLED & availCaps)
+ m_currentAuthMethod = SMTP_AUTH_CRAM_MD5_ENABLED;
+ else if (SMTP_AUTH_NTLM_ENABLED & availCaps)
+ m_currentAuthMethod = SMTP_AUTH_NTLM_ENABLED;
+ else if (SMTP_AUTH_MSN_ENABLED & availCaps)
+ m_currentAuthMethod = SMTP_AUTH_MSN_ENABLED;
+ else if (SMTP_AUTH_OAUTH2_ENABLED & availCaps)
+ m_currentAuthMethod = SMTP_AUTH_OAUTH2_ENABLED;
+ else if (SMTP_AUTH_PLAIN_ENABLED & availCaps)
+ m_currentAuthMethod = SMTP_AUTH_PLAIN_ENABLED;
+ else if (SMTP_AUTH_LOGIN_ENABLED & availCaps)
+ m_currentAuthMethod = SMTP_AUTH_LOGIN_ENABLED;
+ else if (SMTP_AUTH_EXTERNAL_ENABLED & availCaps)
+ m_currentAuthMethod = SMTP_AUTH_EXTERNAL_ENABLED;
+ else
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error, ("no auth method remaining"));
+ m_currentAuthMethod = 0;
+ return NS_ERROR_SMTP_AUTH_FAILURE;
+ }
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("trying auth method 0x%X", m_currentAuthMethod));
+ return NS_OK;
+}
+
+void nsSmtpProtocol::MarkAuthMethodAsFailed(int32_t failedAuthMethod)
+{
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug,
+ ("marking auth method 0x%X failed", failedAuthMethod));
+ m_failedAuthMethods |= failedAuthMethod;
+}
+
+/**
+ * Start over, trying all auth methods again
+ */
+void nsSmtpProtocol::ResetAuthMethods()
+{
+ m_currentAuthMethod = 0;
+ m_failedAuthMethods = 0;
+}
+
+nsresult nsSmtpProtocol::ProcessAuth()
+{
+ nsresult status = NS_OK;
+ nsAutoCString buffer;
+
+ if (!m_tlsEnabled)
+ {
+ if (TestFlag(SMTP_EHLO_STARTTLS_ENABLED))
+ {
+ // Do not try to combine SMTPS with STARTTLS.
+ // If nsMsgSocketType::SSL is set,
+ // we are alrady using a secure connection.
+ // Do not attempt to do STARTTLS,
+ // even if server offers it.
+ if (m_prefSocketType == nsMsgSocketType::trySTARTTLS ||
+ m_prefSocketType == nsMsgSocketType::alwaysSTARTTLS)
+ {
+ buffer = "STARTTLS";
+ buffer += CRLF;
+
+ status = SendData(buffer.get());
+
+ m_tlsInitiated = true;
+
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_TLS_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ return status;
+ }
+ }
+ else if (m_prefSocketType == nsMsgSocketType::alwaysSTARTTLS)
+ {
+ m_nextState = SMTP_ERROR_DONE;
+ m_urlErrorState = NS_ERROR_STARTTLS_FAILED_EHLO_STARTTLS;
+ return NS_ERROR_STARTTLS_FAILED_EHLO_STARTTLS;
+ }
+ }
+ // (wrong indention until here)
+
+ (void) ChooseAuthMethod(); // advance m_currentAuthMethod
+
+ // We don't need to auth, per pref, or the server doesn't advertise AUTH,
+ // so skip auth and try to send message.
+ if (m_prefAuthMethods == SMTP_AUTH_NONE_ENABLED || !TestFlag(SMTP_AUTH))
+ {
+ m_nextState = SMTP_SEND_HELO_RESPONSE;
+ // fake to 250 because SendHeloResponse() tests for this
+ m_responseCode = 250;
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_EXTERNAL_ENABLED)
+ {
+ buffer = "AUTH EXTERNAL =";
+ buffer += CRLF;
+ SendData(buffer.get());
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_AUTH_EXTERNAL_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ return NS_OK;
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_GSSAPI_ENABLED)
+ {
+ m_nextState = SMTP_SEND_AUTH_GSSAPI_FIRST;
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_CRAM_MD5_ENABLED ||
+ m_currentAuthMethod == SMTP_AUTH_PLAIN_ENABLED ||
+ m_currentAuthMethod == SMTP_AUTH_NTLM_ENABLED)
+ {
+ m_nextState = SMTP_SEND_AUTH_LOGIN_STEP1;
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_LOGIN_ENABLED ||
+ m_currentAuthMethod == SMTP_AUTH_MSN_ENABLED)
+ {
+ m_nextState = SMTP_SEND_AUTH_LOGIN_STEP0;
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_OAUTH2_ENABLED)
+ {
+ m_nextState = SMTP_AUTH_OAUTH2_STEP;
+ }
+ else // All auth methods failed
+ {
+ // show an appropriate error msg
+ if (m_failedAuthMethods == 0)
+ {
+ // we didn't even try anything, so we had a non-working config:
+ // pref doesn't match server
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error,
+ ("no working auth mech - pref doesn't match server capas"));
+
+ // pref has encrypted pw & server claims to support plaintext pw
+ if (m_prefAuthMethods == SMTP_AUTH_CRAM_MD5_ENABLED &&
+ m_flags & (SMTP_AUTH_LOGIN_ENABLED | SMTP_AUTH_PLAIN_ENABLED))
+ {
+ // have SSL
+ if (m_prefSocketType == nsMsgSocketType::SSL ||
+ m_prefSocketType == nsMsgSocketType::alwaysSTARTTLS)
+ // tell user to change to plaintext pw
+ m_urlErrorState = NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_SSL;
+ else
+ // tell user to change to plaintext pw, with big warning
+ m_urlErrorState = NS_ERROR_SMTP_AUTH_CHANGE_ENCRYPT_TO_PLAIN_NO_SSL;
+ }
+ // pref has plaintext pw & server claims to support encrypted pw
+ else if (m_prefAuthMethods == (SMTP_AUTH_LOGIN_ENABLED |
+ SMTP_AUTH_PLAIN_ENABLED) &&
+ m_flags & SMTP_AUTH_CRAM_MD5_ENABLED)
+ // tell user to change to encrypted pw
+ m_urlErrorState = NS_ERROR_SMTP_AUTH_CHANGE_PLAIN_TO_ENCRYPT;
+ else
+ {
+ // just "change auth method"
+ m_urlErrorState = NS_ERROR_SMTP_AUTH_MECH_NOT_SUPPORTED;
+ }
+ }
+ else if (m_failedAuthMethods == SMTP_AUTH_GSSAPI_ENABLED)
+ {
+ // We have only GSSAPI, and it failed, so nothing left to do.
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error, ("GSSAPI only and it failed"));
+ m_urlErrorState = NS_ERROR_SMTP_AUTH_GSSAPI;
+ }
+ else
+ {
+ // we tried to login, but it all failed
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error, ("All auth attempts failed"));
+ m_urlErrorState = NS_ERROR_SMTP_AUTH_FAILURE;
+ }
+ m_nextState = SMTP_ERROR_DONE;
+ return NS_ERROR_SMTP_AUTH_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+
+
+nsresult nsSmtpProtocol::AuthLoginResponse(nsIInputStream * stream, uint32_t length)
+{
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("SMTP Login response, code %d", m_responseCode));
+ nsresult status = NS_OK;
+
+ switch (m_responseCode/100)
+ {
+ case 2:
+ m_nextState = SMTP_SEND_HELO_RESPONSE;
+ // fake to 250 because SendHeloResponse() tests for this
+ m_responseCode = 250;
+ break;
+ case 3:
+ m_nextState = SMTP_SEND_AUTH_LOGIN_STEP2;
+ break;
+ case 5:
+ default:
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
+ if (smtpServer)
+ {
+ // If one authentication failed, mark it failed, so that we're going to
+ // fall back on a less secure login method.
+ MarkAuthMethodAsFailed(m_currentAuthMethod);
+
+ bool allFailed = NS_FAILED(ChooseAuthMethod());
+ if (allFailed && m_failedAuthMethods > 0 &&
+ m_failedAuthMethods != SMTP_AUTH_GSSAPI_ENABLED &&
+ m_failedAuthMethods != SMTP_AUTH_EXTERNAL_ENABLED)
+ {
+ // We've tried all avail. methods, and they all failed, and we have no mechanism left.
+ // Ask user to try with a new password.
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Warning,
+ ("SMTP: ask user what to do (after login failed): new password, retry or cancel"));
+
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ nsresult rv = m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString hostname;
+ rv = smtpServer->GetHostname(hostname);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int32_t buttonPressed = 1;
+ if (NS_SUCCEEDED(MsgPromptLoginFailed(nullptr, hostname,
+ &buttonPressed)))
+ {
+ if (buttonPressed == 1) // Cancel button
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Warning, ("cancel button pressed"));
+ // abort and get out of here
+ status = NS_ERROR_ABORT;
+ break;
+ }
+ else if (buttonPressed == 2) // 'New password' button
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Warning, ("new password button pressed"));
+ // Change password was pressed. For now, forget the stored
+ // password and we'll prompt for a new one next time around.
+ smtpServer->ForgetPassword();
+ if (m_usernamePrompted)
+ smtpServer->SetUsername(EmptyCString());
+
+ // Let's restore the original auth flags from SendEhloResponse
+ // so we can try them again with new password and username
+ ResetAuthMethods();
+ // except for GSSAPI and EXTERNAL, which don't care about passwords.
+ MarkAuthMethodAsFailed(SMTP_AUTH_GSSAPI_ENABLED);
+ MarkAuthMethodAsFailed(SMTP_AUTH_EXTERNAL_ENABLED);
+ }
+ else if (buttonPressed == 0) // Retry button
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Warning, ("retry button pressed"));
+ // try all again, including GSSAPI
+ ResetAuthMethods();
+ }
+ }
+ }
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error,
+ ("SMTP: login failed: failed %X, current %X", m_failedAuthMethods, m_currentAuthMethod));
+
+ m_nextState = SMTP_AUTH_PROCESS_STATE; // try auth (ProcessAuth()) again, with other method
+ }
+ else
+ status = NS_ERROR_SMTP_PASSWORD_UNDEFINED;
+ break;
+ }
+
+ return (status);
+}
+
+nsresult nsSmtpProtocol::AuthGSSAPIFirst()
+{
+ NS_ASSERTION(m_currentAuthMethod == SMTP_AUTH_GSSAPI_ENABLED, "called in invalid state");
+ nsAutoCString command("AUTH GSSAPI ");
+ nsAutoCString resp;
+ nsAutoCString service("smtp@");
+ nsCString hostName;
+ nsCString userName;
+ nsresult rv;
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ rv = smtpServer->GetUsername(userName);
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ rv = smtpServer->GetHostname(hostName);
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ service.Append(hostName);
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("SMTP: GSSAPI step 1 for user %s at server %s, service %s",
+ userName.get(), hostName.get(), service.get()));
+
+ rv = DoGSSAPIStep1(service.get(), userName.get(), resp);
+ if (NS_FAILED(rv))
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error, ("SMTP: GSSAPI step 1 failed early"));
+ MarkAuthMethodAsFailed(SMTP_AUTH_GSSAPI_ENABLED);
+ m_nextState = SMTP_AUTH_PROCESS_STATE;
+ return NS_OK;
+ }
+ else
+ command.Append(resp);
+ command.Append(CRLF);
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_SEND_AUTH_GSSAPI_STEP;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ return SendData(command.get());
+}
+
+// GSSAPI may consist of multiple round trips
+
+nsresult nsSmtpProtocol::AuthGSSAPIStep()
+{
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("SMTP: GSSAPI auth step 2"));
+ NS_ASSERTION(m_currentAuthMethod == SMTP_AUTH_GSSAPI_ENABLED, "called in invalid state");
+ nsresult rv;
+ nsAutoCString cmd;
+
+ // Check to see what the server said
+ if (m_responseCode / 100 != 3) {
+ m_nextState = SMTP_AUTH_LOGIN_RESPONSE;
+ return NS_OK;
+ }
+
+ rv = DoGSSAPIStep2(m_responseText, cmd);
+ if (NS_FAILED(rv))
+ cmd = "*";
+ cmd += CRLF;
+
+ m_nextStateAfterResponse = (rv == NS_SUCCESS_AUTH_FINISHED)?SMTP_AUTH_LOGIN_RESPONSE:SMTP_SEND_AUTH_GSSAPI_STEP;
+ m_nextState = SMTP_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+
+ return SendData(cmd.get());
+}
+
+
+// LOGIN and MSN consist of three steps (MSN not through the mechanism
+// but by non-RFC2821 compliant implementation in MS servers) not two as
+// PLAIN or CRAM-MD5, so we've to start here and continue with AuthStep1
+// if the server responds with with a 3xx code to "AUTH LOGIN" or "AUTH MSN"
+nsresult nsSmtpProtocol::AuthLoginStep0()
+{
+ NS_ASSERTION(m_currentAuthMethod == SMTP_AUTH_MSN_ENABLED ||
+ m_currentAuthMethod == SMTP_AUTH_LOGIN_ENABLED,
+ "called in invalid state");
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("SMTP: MSN or LOGIN auth, step 0"));
+ nsAutoCString command(m_currentAuthMethod == SMTP_AUTH_MSN_ENABLED
+ ? "AUTH MSN" CRLF : "AUTH LOGIN" CRLF);
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_AUTH_LOGIN_STEP0_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+
+ return SendData(command.get());
+}
+
+void nsSmtpProtocol::AuthLoginStep0Response()
+{
+ NS_ASSERTION(m_currentAuthMethod == SMTP_AUTH_MSN_ENABLED ||
+ m_currentAuthMethod == SMTP_AUTH_LOGIN_ENABLED,
+ "called in invalid state");
+ // need the test to be here instead in AuthLoginResponse() to
+ // continue with step 1 instead of 2 in case of a code 3xx
+ m_nextState = (m_responseCode/100 == 3) ?
+ SMTP_SEND_AUTH_LOGIN_STEP1 : SMTP_AUTH_LOGIN_RESPONSE;
+}
+
+nsresult nsSmtpProtocol::AuthLoginStep1()
+{
+ char buffer[512]; // TODO nsAutoCString
+ nsresult rv;
+ nsresult status = NS_OK;
+ nsCString username;
+ char *base64Str = nullptr;
+ nsAutoCString password;
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
+ if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
+
+ rv = smtpServer->GetUsername(username);
+ if (username.IsEmpty())
+ {
+ rv = GetUsernamePassword(username, password);
+ m_usernamePrompted = true;
+ if (username.IsEmpty() || password.IsEmpty())
+ return NS_ERROR_SMTP_PASSWORD_UNDEFINED;
+ }
+
+ nsCString hostname;
+ smtpServer->GetHostname(hostname);
+
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("SMTP AuthLoginStep1() for %s@%s",
+ username.get(), hostname.get()));
+
+ GetPassword(password);
+ if (password.IsEmpty())
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error, ("SMTP: password undefined"));
+ m_urlErrorState = NS_ERROR_SMTP_PASSWORD_UNDEFINED;
+ return NS_ERROR_SMTP_PASSWORD_UNDEFINED;
+ }
+
+ if (m_currentAuthMethod == SMTP_AUTH_CRAM_MD5_ENABLED)
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Error, ("CRAM auth, step 1"));
+ PR_snprintf(buffer, sizeof(buffer), "AUTH CRAM-MD5" CRLF);
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_NTLM_ENABLED ||
+ m_currentAuthMethod == SMTP_AUTH_MSN_ENABLED)
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("NTLM/MSN auth, step 1"));
+ nsAutoCString response;
+ rv = DoNtlmStep1(username.get(), password.get(), response);
+ PR_snprintf(buffer, sizeof(buffer), TestFlag(SMTP_AUTH_NTLM_ENABLED) ?
+ "AUTH NTLM %.256s" CRLF :
+ "%.256s" CRLF, response.get());
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_PLAIN_ENABLED)
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("PLAIN auth"));
+ char plain_string[512];
+ int len = 1; /* first <NUL> char */
+
+ memset(plain_string, 0, 512);
+ PR_snprintf(&plain_string[1], 510, "%s", username.get());
+ len += username.Length();
+ len++; /* second <NUL> char */
+ PR_snprintf(&plain_string[len], 511-len, "%s", password.get());
+ len += password.Length();
+
+ base64Str = PL_Base64Encode(plain_string, len, nullptr);
+ PR_snprintf(buffer, sizeof(buffer), "AUTH PLAIN %.256s" CRLF, base64Str);
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_LOGIN_ENABLED)
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("LOGIN auth"));
+ base64Str = PL_Base64Encode(username.get(),
+ username.Length(), nullptr);
+ PR_snprintf(buffer, sizeof(buffer), "%.256s" CRLF, base64Str);
+ }
+ else
+ return (NS_ERROR_COMMUNICATIONS_ERROR);
+
+ status = SendData(buffer, true);
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_AUTH_LOGIN_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ NS_Free(base64Str);
+
+ return (status);
+}
+
+nsresult nsSmtpProtocol::AuthLoginStep2()
+{
+ /* use cached smtp password first
+ * if not then use cached pop password
+ * if pop password undefined
+ * sync with smtp password
+ */
+ nsresult status = NS_OK;
+ nsresult rv;
+ nsAutoCString password;
+
+ GetPassword(password);
+ if (password.IsEmpty())
+ {
+ m_urlErrorState = NS_ERROR_SMTP_PASSWORD_UNDEFINED;
+ return NS_ERROR_SMTP_PASSWORD_UNDEFINED;
+ }
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("SMTP AuthLoginStep2"));
+
+ if (!password.IsEmpty())
+ {
+ char buffer[512];
+ if (m_currentAuthMethod == SMTP_AUTH_CRAM_MD5_ENABLED)
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("CRAM auth, step 2"));
+ unsigned char digest[DIGEST_LENGTH];
+ char * decodedChallenge = PL_Base64Decode(m_responseText.get(),
+ m_responseText.Length(), nullptr);
+
+ if (decodedChallenge)
+ rv = MSGCramMD5(decodedChallenge, strlen(decodedChallenge), password.get(), password.Length(), digest);
+ else
+ rv = NS_ERROR_FAILURE;
+
+ PR_Free(decodedChallenge);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString encodedDigest;
+ char hexVal[8];
+
+ for (uint32_t j=0; j<16; j++)
+ {
+ PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)digest[j]);
+ encodedDigest.Append(hexVal);
+ }
+
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
+ if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
+
+ nsCString userName;
+ rv = smtpServer->GetUsername(userName);
+
+ PR_snprintf(buffer, sizeof(buffer), "%s %s", userName.get(), encodedDigest.get());
+ char *base64Str = PL_Base64Encode(buffer, strlen(buffer), nullptr);
+ PR_snprintf(buffer, sizeof(buffer), "%s" CRLF, base64Str);
+ NS_Free(base64Str);
+ }
+ if (NS_FAILED(rv))
+ PR_snprintf(buffer, sizeof(buffer), "*" CRLF);
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_NTLM_ENABLED ||
+ m_currentAuthMethod == SMTP_AUTH_MSN_ENABLED)
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("NTLM/MSN auth, step 2"));
+ nsAutoCString response;
+ rv = DoNtlmStep2(m_responseText, response);
+ PR_snprintf(buffer, sizeof(buffer), "%.509s" CRLF, response.get());
+ }
+ else if (m_currentAuthMethod == SMTP_AUTH_PLAIN_ENABLED ||
+ m_currentAuthMethod == SMTP_AUTH_LOGIN_ENABLED)
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("PLAIN/LOGIN auth, step 2"));
+ char *base64Str = PL_Base64Encode(password.get(), password.Length(), nullptr);
+ PR_snprintf(buffer, sizeof(buffer), "%.256s" CRLF, base64Str);
+ NS_Free(base64Str);
+ }
+ else
+ return NS_ERROR_COMMUNICATIONS_ERROR;
+
+ status = SendData(buffer, true);
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_AUTH_LOGIN_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ return (status);
+ }
+
+ // XXX -1 is not a valid nsresult
+ return static_cast<nsresult>(-1);
+}
+
+nsresult nsSmtpProtocol::AuthOAuth2Step1()
+{
+ MOZ_ASSERT(mOAuth2Support, "Can't do anything without OAuth2 support");
+
+ nsresult rv = mOAuth2Support->Connect(true, this);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_nextState = SMTP_SUSPENDED;
+ return NS_OK;
+}
+
+nsresult nsSmtpProtocol::OnSuccess(const nsACString &aAccessToken)
+{
+ MOZ_ASSERT(mOAuth2Support, "Can't do anything without OAuth2 support");
+
+ nsCString base64Str;
+ mOAuth2Support->BuildXOAuth2String(base64Str);
+
+ // Send the AUTH XOAUTH2 command, and then siphon us back to the regular
+ // authentication login stream.
+ nsAutoCString buffer;
+ buffer.AppendLiteral("AUTH XOAUTH2 ");
+ buffer += base64Str;
+ buffer += CRLF;
+ nsresult rv = SendData(buffer.get(), true);
+ if (NS_FAILED(rv))
+ {
+ m_nextState = SMTP_ERROR_DONE;
+ }
+ else
+ {
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_AUTH_LOGIN_RESPONSE;
+ }
+
+ SetFlag(SMTP_PAUSE_FOR_READ);
+
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+}
+
+nsresult nsSmtpProtocol::OnFailure(nsresult aError)
+{
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Debug, ("OAuth2 login error %08x",
+ (uint32_t)aError));
+ m_urlErrorState = aError;
+ m_nextState = SMTP_ERROR_DONE;
+ return ProcessProtocolState(nullptr, nullptr, 0, 0);
+}
+
+
+nsresult nsSmtpProtocol::SendMailResponse()
+{
+ nsresult status = NS_OK;
+ nsAutoCString buffer;
+ nsresult rv;
+
+ if (m_responseCode/10 != 25)
+ {
+ nsresult errorcode;
+ if (TestFlag(SMTP_EHLO_SIZE_ENABLED))
+ errorcode = (m_responseCode == 452) ? NS_ERROR_SMTP_TEMP_SIZE_EXCEEDED :
+ (m_responseCode == 552) ? NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_2 :
+ NS_ERROR_SENDING_FROM_COMMAND;
+ else
+ errorcode = NS_ERROR_SENDING_FROM_COMMAND;
+
+ rv = nsExplainErrorDetails(m_runningURL, errorcode, m_responseText.get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
+
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return(NS_ERROR_SENDING_FROM_COMMAND);
+ }
+
+ /* Send the RCPT TO: command */
+ bool requestDSN = false;
+ rv = m_runningURL->GetRequestDSN(&requestDSN);
+
+ nsCOMPtr <nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ rv = prefs->GetBranch(nullptr, getter_AddRefs(prefBranch));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool requestOnSuccess = false;
+ rv = prefBranch->GetBoolPref("mail.dsn.request_on_success_on", &requestOnSuccess);
+
+ bool requestOnFailure = false;
+ rv = prefBranch->GetBoolPref("mail.dsn.request_on_failure_on", &requestOnFailure);
+
+ bool requestOnDelay = false;
+ rv = prefBranch->GetBoolPref("mail.dsn.request_on_delay_on", &requestOnDelay);
+
+ bool requestOnNever = false;
+ rv = prefBranch->GetBoolPref("mail.dsn.request_never_on", &requestOnNever);
+
+ nsCString &address = m_addresses[m_addressesLeft - 1];
+ if (TestFlag(SMTP_EHLO_DSN_ENABLED) && requestDSN && (requestOnSuccess || requestOnFailure || requestOnDelay || requestOnNever))
+ {
+ char *encodedAddress = esmtp_value_encode(address.get());
+ nsAutoCString dsnBuffer;
+
+ if (encodedAddress)
+ {
+ buffer = "RCPT TO:<";
+ buffer += address;
+ buffer += "> NOTIFY=";
+
+ if (requestOnNever)
+ dsnBuffer += "NEVER";
+ else
+ {
+ if (requestOnSuccess)
+ dsnBuffer += "SUCCESS";
+
+ if (requestOnFailure)
+ dsnBuffer += dsnBuffer.IsEmpty() ? "FAILURE" : ",FAILURE";
+
+ if (requestOnDelay)
+ dsnBuffer += dsnBuffer.IsEmpty() ? "DELAY" : ",DELAY";
+ }
+
+ buffer += dsnBuffer;
+ buffer += " ORCPT=rfc822;";
+ buffer += encodedAddress;
+ buffer += CRLF;
+ PR_FREEIF(encodedAddress);
+ }
+ else
+ {
+ m_urlErrorState = NS_ERROR_OUT_OF_MEMORY;
+ return (NS_ERROR_OUT_OF_MEMORY);
+ }
+ }
+ else
+ {
+ buffer = "RCPT TO:<";
+ buffer += address;
+ buffer += ">";
+ buffer += CRLF;
+ }
+ status = SendData(buffer.get());
+
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_SEND_RCPT_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+
+ return(status);
+}
+
+nsresult nsSmtpProtocol::SendRecipientResponse()
+{
+ nsresult status = NS_OK;
+ nsAutoCString buffer;
+ nsresult rv;
+
+ if (m_responseCode / 10 != 25)
+ {
+ nsresult errorcode;
+ if (TestFlag(SMTP_EHLO_SIZE_ENABLED))
+ errorcode = (m_responseCode == 452) ? NS_ERROR_SMTP_TEMP_SIZE_EXCEEDED :
+ (m_responseCode == 552) ? NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_2 :
+ NS_ERROR_SENDING_RCPT_COMMAND;
+ else
+ errorcode = NS_ERROR_SENDING_RCPT_COMMAND;
+
+ if (errorcode == NS_ERROR_SENDING_RCPT_COMMAND) {
+ rv = nsExplainErrorDetails(
+ m_runningURL, errorcode, NS_ConvertUTF8toUTF16(m_responseText).get(),
+ NS_ConvertUTF8toUTF16(m_addresses[m_addressesLeft - 1]).get());
+ } else {
+ rv = nsExplainErrorDetails(m_runningURL, errorcode,
+ m_responseText.get(),
+ m_addresses[m_addressesLeft - 1].get());
+ }
+
+ if (!NS_SUCCEEDED(rv))
+ NS_ASSERTION(false, "failed to explain SMTP error");
+
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return(NS_ERROR_SENDING_RCPT_COMMAND);
+ }
+
+ if (--m_addressesLeft > 0)
+ {
+ // more senders to RCPT to
+ // fake to 250 because SendMailResponse() can't handle 251
+ m_responseCode = 250;
+ m_nextState = SMTP_SEND_MAIL_RESPONSE;
+ return NS_OK;
+ }
+
+ /* else send the DATA command */
+ buffer = "DATA";
+ buffer += CRLF;
+ status = SendData(buffer.get());
+
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_SEND_DATA_RESPONSE;
+ SetFlag(SMTP_PAUSE_FOR_READ);
+
+ return(status);
+}
+
+
+nsresult nsSmtpProtocol::SendData(const char *dataBuffer, bool aSuppressLogging)
+{
+ // XXX -1 is not a valid nsresult
+ if (!dataBuffer) return static_cast<nsresult>(-1);
+
+ if (!aSuppressLogging) {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Info, ("SMTP Send: %s", dataBuffer));
+ } else {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Info, ("Logging suppressed for this command (it probably contained authentication information)"));
+ }
+ return nsMsgAsyncWriteProtocol::SendData(dataBuffer);
+}
+
+
+nsresult nsSmtpProtocol::SendDataResponse()
+{
+ nsresult status = NS_OK;
+
+ if (m_responseCode != 354)
+ {
+ mozilla::DebugOnly<nsresult> rv = nsExplainErrorDetails(m_runningURL,
+ NS_ERROR_SENDING_DATA_COMMAND,
+ m_responseText.get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
+
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return(NS_ERROR_SENDING_DATA_COMMAND);
+ }
+
+ m_nextState = SMTP_SEND_POST_DATA;
+ ClearFlag(SMTP_PAUSE_FOR_READ); /* send data directly */
+
+ UpdateStatus(u"smtpDeliveringMail");
+
+ {
+// m_runningURL->GetBodySize(&m_totalMessageSize);
+ }
+ return(status);
+}
+
+void nsSmtpProtocol::SendMessageInFile()
+{
+ nsCOMPtr<nsIFile> file;
+ nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
+ m_runningURL->GetPostMessageFile(getter_AddRefs(file));
+ if (url && file)
+ // need to fully qualify to avoid getting overwritten by a #define
+ // in some windows header file
+ nsMsgAsyncWriteProtocol::PostMessage(url, file);
+
+ SetFlag(SMTP_PAUSE_FOR_READ);
+
+ // for now, we are always done at this point..we aren't making multiple calls
+ // to post data...
+
+ UpdateStatus(u"smtpDeliveringMail");
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_SEND_MESSAGE_RESPONSE;
+}
+
+void nsSmtpProtocol::SendPostData()
+{
+ // mscott: as a first pass, I'm writing everything at once and am not
+ // doing it in chunks...
+
+ /* returns 0 on done and negative on error
+ * positive if it needs to continue.
+ */
+
+ // check to see if url is a file..if it is...call our file handler...
+ bool postMessageInFile = true;
+ m_runningURL->GetPostMessage(&postMessageInFile);
+ if (postMessageInFile)
+ {
+ SendMessageInFile();
+ }
+
+ /* Update the thermo and the status bar. This is done by hand, rather
+ than using the FE_GraphProgress* functions, because there seems to be
+ no way to make FE_GraphProgress shut up and not display anything more
+ when all the data has arrived. At the end, we want to show the
+ "message sent; waiting for reply" status; FE_GraphProgress gets in
+ the way of that. See bug #23414. */
+}
+
+
+
+nsresult nsSmtpProtocol::SendMessageResponse()
+{
+ if((m_responseCode/10 != 25))
+ {
+ mozilla::DebugOnly<nsresult> rv = nsExplainErrorDetails(m_runningURL,
+ NS_ERROR_SENDING_MESSAGE,
+ m_responseText.get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
+
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return(NS_ERROR_SENDING_MESSAGE);
+ }
+
+ UpdateStatus(u"smtpMailSent");
+
+ /* else */
+ return SendQuit();
+}
+
+nsresult nsSmtpProtocol::SendQuit(SmtpState aNextStateAfterResponse)
+{
+ m_sendDone = true;
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = aNextStateAfterResponse;
+
+ return SendData("QUIT" CRLF); // send a quit command to close the connection with the server.
+}
+
+nsresult nsSmtpProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer )
+{
+ if (!aURL)
+ return NS_OK;
+
+ Initialize(aURL);
+
+ m_continuationResponse = -1; /* init */
+ m_runningURL = do_QueryInterface(aURL);
+ if (!m_runningURL)
+ return NS_ERROR_FAILURE;
+
+ // we had a bug where we failed to bring up an alert if the host
+ // name was empty....so throw up an alert saying we don't have
+ // a host name and inform the caller that we are not going to
+ // run the url...
+ nsAutoCString hostName;
+ aURL->GetHost(hostName);
+ if (hostName.IsEmpty())
+ {
+ nsCOMPtr <nsIMsgMailNewsUrl> aMsgUrl = do_QueryInterface(aURL);
+ if (aMsgUrl)
+ {
+ aMsgUrl->SetUrlState(true, NS_OK);
+ // set the url as a url currently being run...
+ aMsgUrl->SetUrlState(false /* we aren't running the url */,
+ NS_ERROR_SMTP_AUTH_FAILURE);
+ }
+ return NS_ERROR_BUT_DONT_SHOW_ALERT;
+ }
+
+ bool postMessage = false;
+ m_runningURL->GetPostMessage(&postMessage);
+
+ if (postMessage)
+ {
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_EXTN_LOGIN_RESPONSE;
+
+ // compile a minimal list of valid target addresses by
+ // - looking only at mailboxes
+ // - dropping addresses with invalid localparts (until we implement RFC 6532)
+ // - using ACE for IDN domainparts
+ // - stripping duplicates
+ nsCString addresses;
+ m_runningURL->GetRecipients(getter_Copies(addresses));
+
+ ExtractEmails(EncodedHeader(addresses), UTF16ArrayAdapter<>(m_addresses));
+
+ nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
+ addresses.Truncate();
+ uint32_t count = m_addresses.Length();
+ for (uint32_t i = 0; i < count; i++)
+ {
+ const char *start = m_addresses[i].get();
+ // Location of the @ character
+ const char *lastAt = nullptr;
+ const char *ch = start;
+ for (; *ch; ch++)
+ {
+ if (*ch == '@')
+ lastAt = ch;
+ // Check for first illegal character (outside 0x09,0x20-0x7e)
+ else if ((*ch < ' ' || *ch > '~') && (*ch != '\t'))
+ {
+ break;
+ }
+ }
+ // validate the just parsed address
+ if (*ch || m_addresses[i].IsEmpty())
+ {
+ // Fortunately, we will always have an @ in each mailbox address.
+ // We try to fix illegal character in the domain part by converting
+ // that to ACE. Illegal characters in the local part are not fixable
+ // (which charset would it be anyway?), hence we error out in that
+ // case as well.
+ nsresult rv = NS_ERROR_FAILURE; // anything but NS_OK
+ if (lastAt)
+ {
+ // Illegal char in the domain part, hence use ACE
+ nsAutoCString domain;
+ domain.Assign(lastAt + 1);
+ rv = converter->ConvertUTF8toACE(domain, domain);
+ if (NS_SUCCEEDED(rv))
+ {
+ m_addresses[i].SetLength(lastAt - start + 1);
+ m_addresses[i] += domain;
+ }
+ }
+ if (NS_FAILED(rv))
+ {
+ // Throw an error, including the broken address
+ m_nextState = SMTP_ERROR_DONE;
+ ClearFlag(SMTP_PAUSE_FOR_READ);
+ // Unfortunately, nsExplainErrorDetails will show the error above
+ // the mailnews main window, because we don't necessarily get
+ // passed down a compose window - we might be sending in the
+ // background!
+ rv = nsExplainErrorDetails(m_runningURL,
+ NS_ERROR_ILLEGAL_LOCALPART, start);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain illegal localpart");
+ m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
+ return NS_ERROR_BUT_DONT_SHOW_ALERT;
+ }
+ }
+ }
+
+ // final cleanup
+ m_addressesLeft = m_addresses.Length();
+
+ // hmm no addresses to send message to...
+ if (m_addressesLeft == 0)
+ {
+ m_nextState = SMTP_ERROR_DONE;
+ ClearFlag(SMTP_PAUSE_FOR_READ);
+ m_urlErrorState = NS_MSG_NO_RECIPIENTS;
+ return NS_MSG_NO_RECIPIENTS;
+ }
+ } // if post message
+
+ return nsMsgProtocol::LoadUrl(aURL, aConsumer);
+}
+
+/*
+ * returns negative if the transfer is finished or error'd out
+ *
+ * returns zero or more if the transfer needs to be continued.
+ */
+nsresult nsSmtpProtocol::ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream,
+ uint64_t sourceOffset, uint32_t length)
+ {
+ nsresult status = NS_OK;
+ ClearFlag(SMTP_PAUSE_FOR_READ); /* already paused; reset */
+
+ while(!TestFlag(SMTP_PAUSE_FOR_READ))
+ {
+ MOZ_LOG(SMTPLogModule, mozilla::LogLevel::Info, ("SMTP entering state: %d",
+ m_nextState));
+ switch(m_nextState)
+ {
+ case SMTP_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = SmtpResponse(inputStream, length);
+ break;
+
+ case SMTP_START_CONNECT:
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ m_nextState = SMTP_RESPONSE;
+ m_nextStateAfterResponse = SMTP_EXTN_LOGIN_RESPONSE;
+ break;
+ case SMTP_FINISH_CONNECT:
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ break;
+ case SMTP_TLS_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = SendTLSResponse();
+ break;
+ case SMTP_EXTN_LOGIN_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = ExtensionLoginResponse(inputStream, length);
+ break;
+
+ case SMTP_SEND_HELO_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = SendHeloResponse(inputStream, length);
+ break;
+ case SMTP_SEND_EHLO_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = SendEhloResponse(inputStream, length);
+ break;
+ case SMTP_AUTH_PROCESS_STATE:
+ status = ProcessAuth();
+ break;
+
+ case SMTP_SEND_AUTH_GSSAPI_FIRST:
+ status = AuthGSSAPIFirst();
+ break;
+
+ case SMTP_SEND_AUTH_GSSAPI_STEP:
+ status = AuthGSSAPIStep();
+ break;
+
+ case SMTP_SEND_AUTH_LOGIN_STEP0:
+ status = AuthLoginStep0();
+ break;
+
+ case SMTP_AUTH_LOGIN_STEP0_RESPONSE:
+ AuthLoginStep0Response();
+ status = NS_OK;
+ break;
+
+ case SMTP_AUTH_EXTERNAL_RESPONSE:
+ case SMTP_AUTH_LOGIN_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = AuthLoginResponse(inputStream, length);
+ break;
+
+ case SMTP_SEND_AUTH_LOGIN_STEP1:
+ status = AuthLoginStep1();
+ break;
+
+ case SMTP_SEND_AUTH_LOGIN_STEP2:
+ status = AuthLoginStep2();
+ break;
+
+ case SMTP_AUTH_OAUTH2_STEP:
+ status = AuthOAuth2Step1();
+ break;
+
+
+ case SMTP_SEND_MAIL_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = SendMailResponse();
+ break;
+
+ case SMTP_SEND_RCPT_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = SendRecipientResponse();
+ break;
+
+ case SMTP_SEND_DATA_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = SendDataResponse();
+ break;
+
+ case SMTP_SEND_POST_DATA:
+ SendPostData();
+ status = NS_OK;
+ break;
+
+ case SMTP_SEND_MESSAGE_RESPONSE:
+ if (inputStream == nullptr)
+ SetFlag(SMTP_PAUSE_FOR_READ);
+ else
+ status = SendMessageResponse();
+ break;
+ case SMTP_DONE:
+ {
+ nsCOMPtr <nsIMsgMailNewsUrl> mailNewsUrl = do_QueryInterface(m_runningURL);
+ mailNewsUrl->SetUrlState(false, NS_OK);
+ }
+
+ m_nextState = SMTP_FREE;
+ break;
+
+ case SMTP_ERROR_DONE:
+ {
+ nsCOMPtr <nsIMsgMailNewsUrl> mailNewsUrl = do_QueryInterface(m_runningURL);
+ // propagate the right error code
+ mailNewsUrl->SetUrlState(false, m_urlErrorState);
+ }
+
+ m_nextState = SMTP_FREE;
+ break;
+
+ case SMTP_FREE:
+ // smtp is a one time use connection so kill it if we get here...
+ nsMsgAsyncWriteProtocol::CloseSocket();
+ return NS_OK; /* final end */
+
+ // This state means we're going into an async loop and waiting for
+ // something (say auth) to happen. ProcessProtocolState will be
+ // retriggered when necessary.
+ case SMTP_SUSPENDED:
+ return NS_OK;
+
+ default: /* should never happen !!! */
+ m_nextState = SMTP_ERROR_DONE;
+ break;
+ }
+
+ /* check for errors during load and call error
+ * state if found
+ */
+ if (NS_FAILED(status) && m_nextState != SMTP_FREE) {
+ // send a quit command to close the connection with the server.
+ if (NS_FAILED(SendQuit(SMTP_ERROR_DONE)))
+ {
+ m_nextState = SMTP_ERROR_DONE;
+ // Don't exit - loop around again and do the free case
+ ClearFlag(SMTP_PAUSE_FOR_READ);
+ }
+ }
+ } /* while(!SMTP_PAUSE_FOR_READ) */
+
+ return NS_OK;
+}
+
+nsresult
+nsSmtpProtocol::GetPassword(nsCString &aPassword)
+{
+ nsresult rv;
+ nsCOMPtr<nsISmtpUrl> smtpUrl = do_QueryInterface(m_runningURL, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = smtpUrl->GetSmtpServer(getter_AddRefs(smtpServer));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = smtpServer->GetPassword(aPassword);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!aPassword.IsEmpty())
+ return rv;
+ // empty password
+
+ nsCOMPtr <nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ rv = prefs->GetBranch(nullptr, getter_AddRefs(prefBranch));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCString username;
+ rv = smtpServer->GetUsername(username);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertASCIItoUTF16 usernameUTF16(username);
+
+ nsCString hostname;
+ rv = smtpServer->GetHostname(hostname);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString hostnameUTF16;
+ CopyASCIItoUTF16(hostname, hostnameUTF16);
+
+ const char16_t *formatStrings[] =
+ {
+ hostnameUTF16.get(),
+ usernameUTF16.get()
+ };
+
+ rv = PromptForPassword(smtpServer, smtpUrl, formatStrings, aPassword);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+/**
+ * formatStrings is an array for the prompts, item 0 is the hostname, item 1
+ * is the username.
+ */
+nsresult
+nsSmtpProtocol::PromptForPassword(nsISmtpServer *aSmtpServer, nsISmtpUrl *aSmtpUrl, const char16_t **formatStrings, nsACString &aPassword)
+{
+ nsCOMPtr<nsIStringBundleService> stringService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(stringService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> composeStringBundle;
+ nsresult rv = stringService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(composeStringBundle));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsString passwordPromptString;
+ if(formatStrings[1])
+ rv = composeStringBundle->FormatStringFromName(
+ u"smtpEnterPasswordPromptWithUsername",
+ formatStrings, 2, getter_Copies(passwordPromptString));
+ else
+ rv = composeStringBundle->FormatStringFromName(
+ u"smtpEnterPasswordPrompt",
+ formatStrings, 1, getter_Copies(passwordPromptString));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAuthPrompt> netPrompt;
+ rv = aSmtpUrl->GetAuthPrompt(getter_AddRefs(netPrompt));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString passwordTitle;
+ rv = composeStringBundle->GetStringFromName(
+ u"smtpEnterPasswordPromptTitle",
+ getter_Copies(passwordTitle));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = aSmtpServer->GetPasswordWithUI(passwordPromptString.get(), passwordTitle.get(),
+ netPrompt, aPassword);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+nsresult
+nsSmtpProtocol::GetUsernamePassword(nsACString &aUsername,
+ nsACString &aPassword)
+{
+ nsresult rv;
+ nsCOMPtr<nsISmtpUrl> smtpUrl = do_QueryInterface(m_runningURL, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = smtpUrl->GetSmtpServer(getter_AddRefs(smtpServer));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = smtpServer->GetPassword(aPassword);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!aPassword.IsEmpty())
+ {
+ rv = smtpServer->GetUsername(aUsername);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!aUsername.IsEmpty())
+ return rv;
+ }
+ // empty password
+
+ aPassword.Truncate();
+
+ nsCString hostname;
+ rv = smtpServer->GetHostname(hostname);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const char16_t *formatStrings[] =
+ {
+ NS_ConvertASCIItoUTF16(hostname).get(),
+ nullptr
+ };
+
+ rv = PromptForPassword(smtpServer, smtpUrl, formatStrings, aPassword);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
diff --git a/mailnews/compose/src/nsSmtpProtocol.h b/mailnews/compose/src/nsSmtpProtocol.h
new file mode 100644
index 000000000..c23b35dda
--- /dev/null
+++ b/mailnews/compose/src/nsSmtpProtocol.h
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSmtpProtocol_h___
+#define nsSmtpProtocol_h___
+
+#include "mozilla/Attributes.h"
+#include "msgIOAuth2Module.h"
+#include "nsMsgProtocol.h"
+#include "nsIStreamListener.h"
+#include "nsISmtpUrl.h"
+#include "nsIMsgStatusFeedback.h"
+#include "nsMsgLineBuffer.h"
+#include "nsIAuthModule.h"
+#include "MailNewsTypes2.h" // for nsMsgSocketType
+
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+
+class nsIVariant;
+class nsIWritableVariant;
+
+ /* states of the machine
+ */
+typedef enum _SmtpState {
+SMTP_RESPONSE = 0, // 0
+SMTP_START_CONNECT, // 1
+SMTP_FINISH_CONNECT, // 2
+SMTP_SEND_HELO_RESPONSE, // 3
+SMTP_SEND_EHLO_RESPONSE, // 4
+SMTP_SEND_MAIL_RESPONSE, // 5
+SMTP_SEND_RCPT_RESPONSE, // 6
+SMTP_SEND_DATA_RESPONSE, // 7
+SMTP_SEND_POST_DATA, // 8
+SMTP_SEND_MESSAGE_RESPONSE, // 9
+SMTP_DONE, // 10
+SMTP_ERROR_DONE, // 11
+SMTP_FREE, // 12
+SMTP_AUTH_LOGIN_STEP0_RESPONSE, // 13
+SMTP_EXTN_LOGIN_RESPONSE, // 14
+SMTP_SEND_AUTH_LOGIN_STEP0, // 15
+SMTP_SEND_AUTH_LOGIN_STEP1, // 16
+SMTP_SEND_AUTH_LOGIN_STEP2, // 17
+SMTP_AUTH_LOGIN_RESPONSE, // 18
+SMTP_TLS_RESPONSE, // 19
+SMTP_AUTH_EXTERNAL_RESPONSE, // 20
+SMTP_AUTH_PROCESS_STATE, // 21
+SMTP_AUTH_CRAM_MD5_CHALLENGE_RESPONSE, // 22
+SMTP_SEND_AUTH_GSSAPI_FIRST, // 23
+SMTP_SEND_AUTH_GSSAPI_STEP, // 24
+SMTP_SUSPENDED, // 25
+SMTP_AUTH_OAUTH2_STEP, // 26
+SMTP_AUTH_OAUTH2_RESPONSE, // 27
+} SmtpState;
+
+// State Flags (Note, I use the word state in terms of storing
+// state information about the connection (authentication, have we sent
+// commands, etc. I do not intend it to refer to protocol state)
+#define SMTP_PAUSE_FOR_READ 0x00000001 /* should we pause for the next read */
+#define SMTP_ESMTP_SERVER 0x00000002
+#define SMTP_EHLO_DSN_ENABLED 0x00000004
+#define SMTP_EHLO_STARTTLS_ENABLED 0x00000008
+#define SMTP_EHLO_SIZE_ENABLED 0x00000010
+#define SMTP_EHLO_8BIT_ENABLED 0x00000020
+
+// insecure mechanisms follow
+#define SMTP_AUTH_LOGIN_ENABLED 0x00000100
+#define SMTP_AUTH_PLAIN_ENABLED 0x00000200
+#define SMTP_AUTH_EXTERNAL_ENABLED 0x00000400
+// secure mechanisms follow
+#define SMTP_AUTH_GSSAPI_ENABLED 0x00000800
+#define SMTP_AUTH_DIGEST_MD5_ENABLED 0x00001000
+#define SMTP_AUTH_CRAM_MD5_ENABLED 0x00002000
+#define SMTP_AUTH_NTLM_ENABLED 0x00004000
+#define SMTP_AUTH_MSN_ENABLED 0x00008000
+#define SMTP_AUTH_OAUTH2_ENABLED 0x00010000
+// sum of all above auth mechanisms
+#define SMTP_AUTH_ANY 0x0001FF00
+// indicates that AUTH has been advertised
+#define SMTP_AUTH 0x00020000
+// No login necessary (pref)
+#define SMTP_AUTH_NONE_ENABLED 0x00040000
+
+class nsSmtpProtocol : public nsMsgAsyncWriteProtocol,
+ public msgIOAuth2ModuleListener
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_MSGIOAUTH2MODULELISTENER
+
+ // Creating a protocol instance requires the URL which needs to be run.
+ nsSmtpProtocol(nsIURI * aURL);
+
+ virtual nsresult LoadUrl(nsIURI * aURL, nsISupports * aConsumer = nullptr) override;
+ virtual nsresult SendData(const char * dataBuffer, bool aSuppressLogging = false) override;
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // we suppport the nsIStreamListener interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ // stop binding is a "notification" informing us that the stream associated with aURL is going away.
+ NS_IMETHOD OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status) override;
+
+private:
+ virtual ~nsSmtpProtocol();
+ // if we are asked to load a url while we are blocked waiting for redirection information,
+ // then we'll store the url consumer in mPendingConsumer until we can actually load
+ // the url.
+ nsCOMPtr<nsISupports> mPendingConsumer;
+
+ // the nsISmtpURL that is currently running
+ nsCOMPtr<nsISmtpUrl> m_runningURL;
+
+ // the error state we want to set on the url
+ nsresult m_urlErrorState;
+ nsCOMPtr<nsIMsgStatusFeedback> m_statusFeedback;
+
+ // Generic state information -- What state are we in? What state do we want to go to
+ // after the next response? What was the last response code? etc.
+ SmtpState m_nextState;
+ SmtpState m_nextStateAfterResponse;
+ int32_t m_responseCode; /* code returned from Smtp server */
+ int32_t m_previousResponseCode;
+ int32_t m_continuationResponse;
+ nsCString m_responseText; /* text returned from Smtp server */
+ nsMsgLineStreamBuffer *m_lineStreamBuffer; // used to efficiently extract lines from the incoming data stream
+
+ nsTArray<nsCString> m_addresses;
+ uint32_t m_addressesLeft;
+ nsCString m_mailAddr;
+ nsCString m_helloArgument;
+ int32_t m_sizelimit;
+
+ // *** the following should move to the smtp server when we support
+ // multiple smtp servers
+ bool m_usernamePrompted;
+ int32_t m_prefSocketType;
+ bool m_tlsEnabled;
+
+ bool m_tlsInitiated;
+
+ bool m_sendDone;
+
+ int32_t m_totalAmountRead;
+ int64_t m_totalMessageSize;
+
+ char *m_dataBuf;
+ uint32_t m_dataBufSize;
+
+ int32_t m_originalContentLength; /* the content length at the time of calling graph progress */
+
+ // initialization function given a new url and transport layer
+ void Initialize(nsIURI * aURL);
+ virtual nsresult ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream,
+ uint64_t sourceOffset, uint32_t length) override;
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Communication methods --> Reading and writing protocol
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ void UpdateStatus(const char16_t* aStatusName);
+ void UpdateStatusWithString(const char16_t * aStatusString);
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Protocol Methods --> This protocol is state driven so each protocol method is
+ // designed to re-act to the current "state". I've attempted to
+ // group them together based on functionality.
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ nsresult SmtpResponse(nsIInputStream * inputStream, uint32_t length);
+ nsresult ExtensionLoginResponse(nsIInputStream * inputStream, uint32_t length);
+ nsresult SendHeloResponse(nsIInputStream * inputStream, uint32_t length);
+ nsresult SendEhloResponse(nsIInputStream * inputStream, uint32_t length);
+ nsresult SendQuit(SmtpState aNextStateAfterResponse = SMTP_DONE);
+
+ nsresult AuthGSSAPIFirst();
+ nsresult AuthGSSAPIStep();
+ nsresult AuthLoginStep0();
+ void AuthLoginStep0Response();
+ nsresult AuthLoginStep1();
+ nsresult AuthLoginStep2();
+ nsresult AuthLoginResponse(nsIInputStream * stream, uint32_t length);
+ nsresult AuthOAuth2Step1();
+
+ nsresult SendTLSResponse();
+ nsresult SendMailResponse();
+ nsresult SendRecipientResponse();
+ nsresult SendDataResponse();
+ void SendPostData();
+ nsresult SendMessageResponse();
+ nsresult ProcessAuth();
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // End of Protocol Methods
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ void SendMessageInFile();
+
+ void AppendHelloArgument(nsACString& aResult);
+ nsresult GetPassword(nsCString &aPassword);
+ nsresult GetUsernamePassword(nsACString &aUsername, nsACString &aPassword);
+ nsresult PromptForPassword(nsISmtpServer *aSmtpServer, nsISmtpUrl *aSmtpUrl,
+ const char16_t **formatStrings,
+ nsACString &aPassword);
+
+ void InitPrefAuthMethods(int32_t authMethodPrefValue);
+ nsresult ChooseAuthMethod();
+ void MarkAuthMethodAsFailed(int32_t failedAuthMethod);
+ void ResetAuthMethods();
+
+ virtual const char* GetType() override {return "smtp";}
+
+ int32_t m_prefAuthMethods; // set of capability flags for auth methods
+ int32_t m_failedAuthMethods; // ditto
+ int32_t m_currentAuthMethod; // exactly one capability flag, or 0
+
+ // The support module for OAuth2 logon, only present if OAuth2 is enabled
+ // and working.
+ nsCOMPtr<msgIOAuth2Module> mOAuth2Support;
+};
+
+#endif // nsSmtpProtocol_h___
diff --git a/mailnews/compose/src/nsSmtpServer.cpp b/mailnews/compose/src/nsSmtpServer.cpp
new file mode 100644
index 000000000..4dc3e1f64
--- /dev/null
+++ b/mailnews/compose/src/nsSmtpServer.cpp
@@ -0,0 +1,629 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsSmtpServer.h"
+#include "nsNetUtil.h"
+#include "nsIAuthPrompt.h"
+#include "nsMsgUtils.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMsgBaseCID.h"
+#include "nsISmtpService.h"
+#include "nsMsgCompCID.h"
+#include "nsILoginInfo.h"
+#include "nsILoginManager.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+
+NS_IMPL_ADDREF(nsSmtpServer)
+NS_IMPL_RELEASE(nsSmtpServer)
+NS_INTERFACE_MAP_BEGIN(nsSmtpServer)
+ NS_INTERFACE_MAP_ENTRY(nsISmtpServer)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISmtpServer)
+NS_INTERFACE_MAP_END
+
+nsSmtpServer::nsSmtpServer():
+ mKey("")
+{
+ m_logonFailed = false;
+ getPrefs();
+}
+
+nsSmtpServer::~nsSmtpServer()
+{
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetKey(char * *aKey)
+{
+ if (!aKey) return NS_ERROR_NULL_POINTER;
+ if (mKey.IsEmpty())
+ *aKey = nullptr;
+ else
+ *aKey = ToNewCString(mKey);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::SetKey(const char * aKey)
+{
+ NS_ASSERTION(aKey, "Bad key pointer");
+ mKey = aKey;
+ return getPrefs();
+}
+
+nsresult nsSmtpServer::getPrefs()
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefService> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString branchName;
+ branchName.AssignLiteral("mail.smtpserver.");
+ branchName += mKey;
+ branchName.Append('.');
+ rv = prefs->GetBranch(branchName.get(), getter_AddRefs(mPrefBranch));
+ if (NS_FAILED(rv))
+ return rv;
+
+ if(!mDefPrefBranch) {
+ branchName.AssignLiteral("mail.smtpserver.default.");
+ rv = prefs->GetBranch(branchName.get(), getter_AddRefs(mDefPrefBranch));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetHostname(nsACString &aHostname)
+{
+ nsCString result;
+ nsresult rv = mPrefBranch->GetCharPref("hostname", getter_Copies(result));
+ if (NS_FAILED(rv))
+ aHostname.Truncate();
+ else
+ aHostname = result;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::SetHostname(const nsACString &aHostname)
+{
+ if (!aHostname.IsEmpty())
+ return mPrefBranch->SetCharPref("hostname", PromiseFlatCString(aHostname).get());
+
+ // If the pref value is already empty, ClearUserPref will return
+ // NS_ERROR_UNEXPECTED, so don't check the rv here.
+ mPrefBranch->ClearUserPref("hostname");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetDescription(nsACString &aDescription)
+{
+ nsCString temp;
+ mPrefBranch->GetCharPref("description", getter_Copies(temp));
+ aDescription.Assign(temp);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::SetDescription(const nsACString &aDescription)
+{
+ if (!aDescription.IsEmpty())
+ return mPrefBranch->SetCharPref("description", PromiseFlatCString(aDescription).get());
+ else
+ mPrefBranch->ClearUserPref("description");
+ return NS_OK;
+}
+
+// if GetPort returns 0, it means default port
+NS_IMETHODIMP
+nsSmtpServer::GetPort(int32_t *aPort)
+{
+ NS_ENSURE_ARG_POINTER(aPort);
+ if (NS_FAILED(mPrefBranch->GetIntPref("port", aPort)))
+ *aPort = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::SetPort(int32_t aPort)
+{
+ if (aPort)
+ return mPrefBranch->SetIntPref("port", aPort);
+
+ mPrefBranch->ClearUserPref("port");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetDisplayname(char * *aDisplayname)
+{
+ nsresult rv;
+ NS_ENSURE_ARG_POINTER(aDisplayname);
+
+ nsCString hostname;
+ rv = mPrefBranch->GetCharPref("hostname", getter_Copies(hostname));
+ if (NS_FAILED(rv)) {
+ *aDisplayname=nullptr;
+ return NS_OK;
+ }
+ int32_t port;
+ rv = mPrefBranch->GetIntPref("port", &port);
+ if (NS_FAILED(rv))
+ port = 0;
+
+ if (port) {
+ hostname.Append(':');
+ hostname.AppendInt(port);
+ }
+
+ *aDisplayname = ToNewCString(hostname);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetSocketType(int32_t *socketType)
+{
+ NS_ENSURE_ARG_POINTER(socketType);
+ getIntPrefWithDefault("try_ssl", socketType, 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::SetSocketType(int32_t socketType)
+{
+ return mPrefBranch->SetIntPref("try_ssl", socketType);
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetHelloArgument(char * *aHelloArgument)
+{
+ nsresult rv;
+ NS_ENSURE_ARG_POINTER(aHelloArgument);
+ rv = mPrefBranch->GetCharPref("hello_argument", aHelloArgument);
+ if (NS_FAILED(rv))
+ {
+ rv = mDefPrefBranch->GetCharPref("hello_argument", aHelloArgument);
+ if (NS_FAILED(rv))
+ *aHelloArgument = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetAuthMethod(int32_t *authMethod)
+{
+ NS_ENSURE_ARG_POINTER(authMethod);
+ getIntPrefWithDefault("authMethod", authMethod, 3);
+ return NS_OK;
+}
+
+void
+nsSmtpServer::getIntPrefWithDefault(const char *prefName,
+ int32_t *val,
+ int32_t defVal)
+{
+ nsresult rv = mPrefBranch->GetIntPref(prefName, val);
+ if (NS_SUCCEEDED(rv))
+ return;
+
+ rv = mDefPrefBranch->GetIntPref(prefName, val);
+ if (NS_FAILED(rv))
+ // last resort
+ *val = defVal;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::SetAuthMethod(int32_t authMethod)
+{
+ return mPrefBranch->SetIntPref("authMethod", authMethod);
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetUsername(nsACString &aUsername)
+{
+ nsCString result;
+ nsresult rv = mPrefBranch->GetCharPref("username", getter_Copies(result));
+ if (NS_FAILED(rv))
+ aUsername.Truncate();
+ else
+ aUsername = result;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::SetUsername(const nsACString &aUsername)
+{
+ if (!aUsername.IsEmpty())
+ return mPrefBranch->SetCharPref("username", PromiseFlatCString(aUsername).get());
+
+ // If the pref value is already empty, ClearUserPref will return
+ // NS_ERROR_UNEXPECTED, so don't check the rv here.
+ mPrefBranch->ClearUserPref("username");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetPassword(nsACString& aPassword)
+{
+ if (m_password.IsEmpty() && !m_logonFailed)
+ {
+ // try to avoid prompting the user for another password. If the user has set
+ // the appropriate pref, we'll use the password from an incoming server, if
+ // the user has already logged onto that server.
+
+ // if this is set, we'll only use this, and not the other prefs
+ // user_pref("mail.smtpserver.smtp1.incomingAccount", "server1");
+
+ // if this is set, we'll accept an exact match of user name and server
+ // user_pref("mail.smtp.useMatchingHostNameServer", true);
+
+ // if this is set, and we don't find an exact match of user and host name,
+ // we'll accept a match of username and domain, where domain
+ // is everything after the first '.'
+ // user_pref("mail.smtp.useMatchingDomainServer", true);
+
+ nsCString accountKey;
+ bool useMatchingHostNameServer = false;
+ bool useMatchingDomainServer = false;
+ mPrefBranch->GetCharPref("incomingAccount", getter_Copies(accountKey));
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID);
+ nsCOMPtr<nsIMsgIncomingServer> incomingServerToUse;
+ if (accountManager)
+ {
+ if (!accountKey.IsEmpty())
+ accountManager->GetIncomingServer(accountKey, getter_AddRefs(incomingServerToUse));
+ else
+ {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv,rv);
+ prefBranch->GetBoolPref("mail.smtp.useMatchingHostNameServer", &useMatchingHostNameServer);
+ prefBranch->GetBoolPref("mail.smtp.useMatchingDomainServer", &useMatchingDomainServer);
+ if (useMatchingHostNameServer || useMatchingDomainServer)
+ {
+ nsCString userName;
+ nsCString hostName;
+ GetHostname(hostName);
+ GetUsername(userName);
+ if (useMatchingHostNameServer)
+ // pass in empty type and port=0, to match imap and pop3.
+ accountManager->FindRealServer(userName, hostName, EmptyCString(), 0, getter_AddRefs(incomingServerToUse));
+ int32_t dotPos = -1;
+ if (!incomingServerToUse && useMatchingDomainServer
+ && (dotPos = hostName.FindChar('.')) != kNotFound)
+ {
+ hostName.Cut(0, dotPos);
+ nsCOMPtr<nsIArray> allServers;
+ accountManager->GetAllServers(getter_AddRefs(allServers));
+ if (allServers)
+ {
+ uint32_t count = 0;
+ allServers->GetLength(&count);
+ uint32_t i;
+ for (i = 0; i < count; i++)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryElementAt(allServers, i);
+ if (server)
+ {
+ nsCString serverUserName;
+ nsCString serverHostName;
+ server->GetRealUsername(serverUserName);
+ server->GetRealHostName(serverHostName);
+ if (serverUserName.Equals(userName))
+ {
+ int32_t serverDotPos = serverHostName.FindChar('.');
+ if (serverDotPos != kNotFound)
+ {
+ serverHostName.Cut(0, serverDotPos);
+ if (serverHostName.Equals(hostName))
+ {
+ incomingServerToUse = server;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (incomingServerToUse)
+ return incomingServerToUse->GetPassword(aPassword);
+ }
+ aPassword = m_password;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::VerifyLogon(nsIUrlListener *aUrlListener, nsIMsgWindow *aMsgWindow,
+ nsIURI **aURL)
+{
+ nsresult rv;
+ nsCOMPtr<nsISmtpService> smtpService(do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return smtpService->VerifyLogon(this, aUrlListener, aMsgWindow, aURL);
+}
+
+
+NS_IMETHODIMP
+nsSmtpServer::SetPassword(const nsACString& aPassword)
+{
+ m_password = aPassword;
+ return NS_OK;
+}
+
+nsresult
+nsSmtpServer::GetPasswordWithoutUI()
+{
+ nsresult rv;
+ nsCOMPtr<nsILoginManager> loginMgr(do_GetService(NS_LOGINMANAGER_CONTRACTID,
+ &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertASCIItoUTF16 serverUri(GetServerURIInternal(false));
+
+ uint32_t numLogins = 0;
+ nsILoginInfo** logins = nullptr;
+ rv = loginMgr->FindLogins(&numLogins, serverUri, EmptyString(),
+ serverUri, &logins);
+ // Login manager can produce valid fails, e.g. NS_ERROR_ABORT when a user
+ // cancels the master password dialog. Therefore handle that here, but don't
+ // warn about it.
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Don't abort here, if we didn't find any or failed, then we'll just have
+ // to prompt.
+ if (numLogins > 0)
+ {
+ nsCString serverCUsername;
+ rv = GetUsername(serverCUsername);
+ NS_ConvertASCIItoUTF16 serverUsername(serverCUsername);
+
+ nsString username;
+ for (uint32_t i = 0; i < numLogins; ++i)
+ {
+ rv = logins[i]->GetUsername(username);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (username.Equals(serverUsername))
+ {
+ nsString password;
+ rv = logins[i]->GetPassword(password);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ LossyCopyUTF16toASCII(password, m_password);
+ break;
+ }
+ }
+ }
+ NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(numLogins, logins);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetPasswordWithUI(const char16_t *aPromptMessage,
+ const char16_t *aPromptTitle,
+ nsIAuthPrompt* aDialog,
+ nsACString &aPassword)
+{
+ if (!m_password.IsEmpty())
+ return GetPassword(aPassword);
+
+ // We need to get a password, but see if we can get it from the password
+ // manager without requiring a prompt.
+ nsresult rv = GetPasswordWithoutUI();
+ if (rv == NS_ERROR_ABORT)
+ return NS_MSG_PASSWORD_PROMPT_CANCELLED;
+
+ // Now re-check if we've got a password or not, if we have, then we
+ // don't need to prompt the user.
+ if (!m_password.IsEmpty())
+ {
+ aPassword = m_password;
+ return NS_OK;
+ }
+
+ NS_ENSURE_ARG_POINTER(aDialog);
+
+ // PromptPassword needs the username as well.
+ nsCString serverUri(GetServerURIInternal(true));
+
+ bool okayValue = true;
+ nsString uniPassword;
+
+ rv = aDialog->PromptPassword(aPromptTitle, aPromptMessage,
+ NS_ConvertASCIItoUTF16(serverUri).get(),
+ nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
+ getter_Copies(uniPassword), &okayValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If the user pressed cancel, just return an empty string.
+ if (!okayValue)
+ {
+ aPassword.Truncate();
+ return NS_MSG_PASSWORD_PROMPT_CANCELLED;
+ }
+
+ NS_LossyConvertUTF16toASCII password(uniPassword);
+
+ rv = SetPassword(password);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aPassword = password;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetUsernamePasswordWithUI(const char16_t * aPromptMessage, const
+ char16_t *aPromptTitle,
+ nsIAuthPrompt* aDialog,
+ nsACString &aUsername,
+ nsACString &aPassword)
+{
+ nsresult rv;
+ if (!m_password.IsEmpty())
+ {
+ rv = GetUsername(aUsername);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return GetPassword(aPassword);
+ }
+
+ NS_ENSURE_ARG_POINTER(aDialog);
+
+ nsCString serverUri;
+ rv = GetServerURI(serverUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString uniUsername;
+ nsString uniPassword;
+ bool okayValue = true;
+
+ rv = aDialog->PromptUsernameAndPassword(aPromptTitle, aPromptMessage,
+ NS_ConvertASCIItoUTF16(serverUri).get(),
+ nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY,
+ getter_Copies(uniUsername),
+ getter_Copies(uniPassword),
+ &okayValue);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If the user pressed cancel, just return emtpy strings.
+ if (!okayValue)
+ {
+ aUsername.Truncate();
+ aPassword.Truncate();
+ return rv;
+ }
+
+ // We got a username and password back...so remember them.
+ NS_LossyConvertUTF16toASCII username(uniUsername);
+
+ rv = SetUsername(username);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_LossyConvertUTF16toASCII password(uniPassword);
+
+ rv = SetPassword(password);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aUsername = username;
+ aPassword = password;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::ForgetPassword()
+{
+ nsresult rv;
+ nsCOMPtr<nsILoginManager> loginMgr =
+ do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get the current server URI without the username
+ nsAutoCString serverUri(NS_LITERAL_CSTRING("smtp://"));
+
+ nsCString hostname;
+ rv = GetHostname(hostname);
+
+ if (NS_SUCCEEDED(rv) && !hostname.IsEmpty()) {
+ nsCString escapedHostname;
+ MsgEscapeString(hostname, nsINetUtil::ESCAPE_URL_PATH, escapedHostname);
+ // not all servers have a hostname
+ serverUri.Append(escapedHostname);
+ }
+
+ uint32_t count;
+ nsILoginInfo** logins;
+
+ NS_ConvertUTF8toUTF16 currServer(serverUri);
+
+ nsCString serverCUsername;
+ rv = GetUsername(serverCUsername);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertUTF8toUTF16 serverUsername(serverCUsername);
+
+ rv = loginMgr->FindLogins(&count, currServer, EmptyString(),
+ currServer, &logins);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // There should only be one-login stored for this url, however just in case
+ // there isn't.
+ nsString username;
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ if (NS_SUCCEEDED(logins[i]->GetUsername(username)) &&
+ username.Equals(serverUsername))
+ {
+ // If this fails, just continue, we'll still want to remove the password
+ // from our local cache.
+ loginMgr->RemoveLogin(logins[i]);
+ }
+ }
+ NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, logins);
+
+ rv = SetPassword(EmptyCString());
+ m_logonFailed = true;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::GetServerURI(nsACString &aResult)
+{
+ aResult = GetServerURIInternal(true);
+ return NS_OK;
+}
+
+nsCString
+nsSmtpServer::GetServerURIInternal(const bool aIncludeUsername)
+{
+ nsCString uri(NS_LITERAL_CSTRING("smtp://"));
+ nsresult rv;
+
+ if (aIncludeUsername)
+ {
+ nsCString username;
+ rv = GetUsername(username);
+
+ if (NS_SUCCEEDED(rv) && !username.IsEmpty()) {
+ nsCString escapedUsername;
+ MsgEscapeString(username, nsINetUtil::ESCAPE_XALPHAS, escapedUsername);
+ // not all servers have a username
+ uri.Append(escapedUsername);
+ uri.AppendLiteral("@");
+ }
+ }
+
+ nsCString hostname;
+ rv = GetHostname(hostname);
+
+ if (NS_SUCCEEDED(rv) && !hostname.IsEmpty()) {
+ nsCString escapedHostname;
+ MsgEscapeString(hostname, nsINetUtil::ESCAPE_URL_PATH, escapedHostname);
+ // not all servers have a hostname
+ uri.Append(escapedHostname);
+ }
+
+ return uri;
+}
+
+NS_IMETHODIMP
+nsSmtpServer::ClearAllValues()
+{
+ return mPrefBranch->DeleteBranch("");
+}
diff --git a/mailnews/compose/src/nsSmtpServer.h b/mailnews/compose/src/nsSmtpServer.h
new file mode 100644
index 000000000..cbd7dba67
--- /dev/null
+++ b/mailnews/compose/src/nsSmtpServer.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsSmtpServer_h_
+#define __nsSmtpServer_h_
+
+
+#include "nsStringGlue.h"
+#include "nsISmtpServer.h"
+#include "nsIPrefBranch.h"
+#include "nsWeakReference.h"
+
+class nsSmtpServer : public nsISmtpServer,
+ public nsSupportsWeakReference
+{
+public:
+ nsSmtpServer();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISMTPSERVER
+
+private:
+ virtual ~nsSmtpServer();
+ nsCString mKey;
+ nsCOMPtr<nsIPrefBranch> mPrefBranch;
+ nsCOMPtr<nsIPrefBranch> mDefPrefBranch;
+
+ nsresult getPrefs();
+ void getIntPrefWithDefault(const char *prefName, int32_t *val,
+ int32_t defval);
+ nsresult GetPasswordWithoutUI();
+ nsCString GetServerURIInternal(const bool aIncludeUsername);
+
+ nsCString m_password;
+ bool m_logonFailed;
+};
+
+#endif
diff --git a/mailnews/compose/src/nsSmtpService.cpp b/mailnews/compose/src/nsSmtpService.cpp
new file mode 100644
index 000000000..9f9295934
--- /dev/null
+++ b/mailnews/compose/src/nsSmtpService.cpp
@@ -0,0 +1,772 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "msgCore.h" // precompiled header...
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIIOService.h"
+#include "nsIPipe.h"
+#include "nsNetCID.h"
+#include "nsMsgUtils.h"
+#include "nsNetUtil.h"
+#include "nsSmtpService.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgCompCID.h"
+#include "nsArrayEnumerator.h"
+#include "nsSmtpUrl.h"
+#include "nsSmtpProtocol.h"
+#include "nsCOMPtr.h"
+#include "nsIMsgIdentity.h"
+#include "nsIPrompt.h"
+#include "nsIWindowWatcher.h"
+#include "nsIUTF8ConverterService.h"
+#include "nsUConvCID.h"
+#include "nsAutoPtr.h"
+#include "nsComposeStrings.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIPrincipal.h"
+
+#define SERVER_DELIMITER ','
+#define APPEND_SERVERS_VERSION_PREF_NAME "append_preconfig_smtpservers.version"
+#define MAIL_ROOT_PREF "mail."
+#define PREF_MAIL_SMTPSERVERS "mail.smtpservers"
+#define PREF_MAIL_SMTPSERVERS_APPEND_SERVERS "mail.smtpservers.appendsmtpservers"
+#define PREF_MAIL_SMTP_DEFAULTSERVER "mail.smtp.defaultserver"
+
+typedef struct _findServerByKeyEntry {
+ const char *key;
+ nsISmtpServer *server;
+} findServerByKeyEntry;
+
+typedef struct _findServerByHostnameEntry {
+ nsCString hostname;
+ nsCString username;
+ nsISmtpServer *server;
+} findServerByHostnameEntry;
+
+static NS_DEFINE_CID(kCSmtpUrlCID, NS_SMTPURL_CID);
+static NS_DEFINE_CID(kCMailtoUrlCID, NS_MAILTOURL_CID);
+
+// foward declarations...
+nsresult
+NS_MsgBuildSmtpUrl(nsIFile * aFilePath,
+ nsISmtpServer *aServer,
+ const char* aRecipients,
+ nsIMsgIdentity * aSenderIdentity,
+ nsIUrlListener * aUrlListener,
+ nsIMsgStatusFeedback *aStatusFeedback,
+ nsIInterfaceRequestor* aNotificationCallbacks,
+ nsIURI ** aUrl,
+ bool aRequestDSN);
+
+nsresult NS_MsgLoadSmtpUrl(nsIURI * aUrl, nsISupports * aConsumer, nsIRequest ** aRequest);
+
+nsSmtpService::nsSmtpService() :
+ mSmtpServersLoaded(false)
+{
+}
+
+nsSmtpService::~nsSmtpService()
+{
+ // save the SMTP servers to disk
+
+}
+
+NS_IMPL_ISUPPORTS(nsSmtpService, nsISmtpService, nsIProtocolHandler)
+
+
+NS_IMETHODIMP nsSmtpService::SendMailMessage(nsIFile * aFilePath,
+ const char * aRecipients,
+ nsIMsgIdentity * aSenderIdentity,
+ const char * aPassword,
+ nsIUrlListener * aUrlListener,
+ nsIMsgStatusFeedback *aStatusFeedback,
+ nsIInterfaceRequestor* aNotificationCallbacks,
+ bool aRequestDSN,
+ nsIURI ** aURL,
+ nsIRequest ** aRequest)
+{
+ nsIURI * urlToRun = nullptr;
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsISmtpServer> smtpServer;
+ rv = GetServerByIdentity(aSenderIdentity, getter_AddRefs(smtpServer));
+
+ if (NS_SUCCEEDED(rv) && smtpServer)
+ {
+ if (aPassword && *aPassword)
+ smtpServer->SetPassword(nsDependentCString(aPassword));
+
+ // this ref counts urlToRun
+ rv = NS_MsgBuildSmtpUrl(aFilePath, smtpServer, aRecipients, aSenderIdentity,
+ aUrlListener, aStatusFeedback,
+ aNotificationCallbacks, &urlToRun, aRequestDSN);
+ if (NS_SUCCEEDED(rv) && urlToRun)
+ rv = NS_MsgLoadSmtpUrl(urlToRun, nullptr, aRequest);
+
+ if (aURL) // does the caller want a handle on the url?
+ *aURL = urlToRun; // transfer our ref count to the caller....
+ else
+ NS_IF_RELEASE(urlToRun);
+ }
+
+ return rv;
+}
+
+
+// The following are two convience functions I'm using to help expedite building and running a mail to url...
+
+// short cut function for creating a mailto url...
+nsresult NS_MsgBuildSmtpUrl(nsIFile * aFilePath,
+ nsISmtpServer *aSmtpServer,
+ const char * aRecipients,
+ nsIMsgIdentity * aSenderIdentity,
+ nsIUrlListener * aUrlListener,
+ nsIMsgStatusFeedback *aStatusFeedback,
+ nsIInterfaceRequestor* aNotificationCallbacks,
+ nsIURI ** aUrl,
+ bool aRequestDSN)
+{
+ // mscott: this function is a convience hack until netlib actually dispatches
+ // smtp urls. in addition until we have a session to get a password, host and
+ // other stuff from, we need to use default values....
+ // ..for testing purposes....
+
+ nsCString smtpHostName;
+ nsCString smtpUserName;
+ int32_t smtpPort;
+ int32_t socketType;
+
+ aSmtpServer->GetHostname(smtpHostName);
+ aSmtpServer->GetUsername(smtpUserName);
+ aSmtpServer->GetPort(&smtpPort);
+ aSmtpServer->GetSocketType(&socketType);
+
+ if (!smtpPort)
+ smtpPort = (socketType == nsMsgSocketType::SSL) ?
+ nsISmtpUrl::DEFAULT_SMTPS_PORT : nsISmtpUrl::DEFAULT_SMTP_PORT;
+
+ nsresult rv;
+ nsCOMPtr<nsISmtpUrl> smtpUrl(do_CreateInstance(kCSmtpUrlCID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString urlSpec("smtp://");
+
+ if (!smtpUserName.IsEmpty())
+ {
+ nsCString escapedUsername;
+ MsgEscapeString(smtpUserName, nsINetUtil::ESCAPE_XALPHAS,
+ escapedUsername);
+ urlSpec.Append(escapedUsername);
+ urlSpec.Append('@');
+ }
+
+ urlSpec.Append(smtpHostName);
+ if (smtpHostName.FindChar(':') == -1)
+ {
+ urlSpec.Append(':');
+ urlSpec.AppendInt(smtpPort);
+ }
+
+ nsCOMPtr<nsIMsgMailNewsUrl> url(do_QueryInterface(smtpUrl, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = url->SetSpec(urlSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ smtpUrl->SetRecipients(aRecipients);
+ smtpUrl->SetRequestDSN(aRequestDSN);
+ smtpUrl->SetPostMessageFile(aFilePath);
+ smtpUrl->SetSenderIdentity(aSenderIdentity);
+ if (aNotificationCallbacks)
+ smtpUrl->SetNotificationCallbacks(aNotificationCallbacks);
+ smtpUrl->SetSmtpServer(aSmtpServer);
+
+ nsCOMPtr<nsIPrompt> smtpPrompt(do_GetInterface(aNotificationCallbacks));
+ nsCOMPtr<nsIAuthPrompt> smtpAuthPrompt(do_GetInterface(aNotificationCallbacks));
+ if (!smtpPrompt || !smtpAuthPrompt)
+ {
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!smtpPrompt)
+ wwatch->GetNewPrompter(0, getter_AddRefs(smtpPrompt));
+ if (!smtpAuthPrompt)
+ wwatch->GetNewAuthPrompter(0, getter_AddRefs(smtpAuthPrompt));
+ }
+
+ smtpUrl->SetPrompt(smtpPrompt);
+ smtpUrl->SetAuthPrompt(smtpAuthPrompt);
+
+ if (aUrlListener)
+ url->RegisterListener(aUrlListener);
+ if (aStatusFeedback)
+ url->SetStatusFeedback(aStatusFeedback);
+
+ return CallQueryInterface(smtpUrl, aUrl);
+}
+
+nsresult NS_MsgLoadSmtpUrl(nsIURI * aUrl, nsISupports * aConsumer, nsIRequest ** aRequest)
+{
+ NS_ENSURE_ARG_POINTER(aUrl);
+
+ // For now, assume the url is an smtp url and load it.
+ nsresult rv;
+ nsCOMPtr<nsISmtpUrl> smtpUrl(do_QueryInterface(aUrl, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create a smtp protocol instance to run the url in.
+ RefPtr<nsSmtpProtocol> smtpProtocol = new nsSmtpProtocol(aUrl);
+ if (!smtpProtocol)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Protocol will get destroyed when url is completed.
+ rv = smtpProtocol->LoadUrl(aUrl, aConsumer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(smtpProtocol.get(), aRequest);
+}
+
+NS_IMETHODIMP nsSmtpService::VerifyLogon(nsISmtpServer *aServer,
+ nsIUrlListener *aUrlListener,
+ nsIMsgWindow *aMsgWindow,
+ nsIURI **aURL)
+{
+ NS_ENSURE_ARG_POINTER(aServer);
+ nsCString popHost;
+ nsCString popUser;
+ nsCOMPtr <nsIURI> urlToRun;
+
+ nsresult rv = NS_MsgBuildSmtpUrl(nullptr, aServer,
+ nullptr, nullptr, aUrlListener, nullptr,
+ nullptr , getter_AddRefs(urlToRun), false);
+ if (NS_SUCCEEDED(rv) && urlToRun)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> url(do_QueryInterface(urlToRun, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ url->SetMsgWindow(aMsgWindow);
+ rv = NS_MsgLoadSmtpUrl(urlToRun, nullptr, nullptr /* aRequest */);
+ if (aURL)
+ urlToRun.forget(aURL);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsSmtpService::GetScheme(nsACString &aScheme)
+{
+ aScheme = "mailto";
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsSmtpService::GetDefaultPort(int32_t *aDefaultPort)
+{
+ nsresult rv = NS_OK;
+ if (aDefaultPort)
+ *aDefaultPort = nsISmtpUrl::DEFAULT_SMTP_PORT;
+ else
+ rv = NS_ERROR_NULL_POINTER;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsSmtpService::AllowPort(int32_t port, const char *scheme, bool *_retval)
+{
+ // allow smtp to run on any port
+ *_retval = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsSmtpService::GetProtocolFlags(uint32_t *result)
+{
+ *result = URI_NORELATIVE | ALLOWS_PROXY | URI_LOADABLE_BY_ANYONE |
+ URI_NON_PERSISTABLE | URI_DOES_NOT_RETURN_DATA |
+ URI_FORBIDS_COOKIE_ACCESS;
+ return NS_OK;
+}
+
+// the smtp service is also the protocol handler for mailto urls....
+
+NS_IMETHODIMP nsSmtpService::NewURI(const nsACString &aSpec,
+ const char *aOriginCharset,
+ nsIURI *aBaseURI,
+ nsIURI **_retval)
+{
+ // get a new smtp url
+ nsresult rv;
+ nsCOMPtr<nsIURI> mailtoUrl = do_CreateInstance(kCMailtoUrlCID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString utf8Spec;
+ if (aOriginCharset)
+ {
+ nsCOMPtr<nsIUTF8ConverterService>
+ utf8Converter(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv))
+ rv = utf8Converter->ConvertURISpecToUTF8(aSpec, aOriginCharset, utf8Spec);
+ }
+
+ // utf8Spec is filled up only when aOriginCharset is specified and
+ // the conversion is successful. Otherwise, fall back to aSpec.
+ if (aOriginCharset && NS_SUCCEEDED(rv))
+ rv = mailtoUrl->SetSpec(utf8Spec);
+ else
+ rv = mailtoUrl->SetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mailtoUrl.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsSmtpService::NewChannel(nsIURI *aURI, nsIChannel **_retval)
+{
+ return NewChannel2(aURI, nullptr, _retval);
+}
+
+NS_IMETHODIMP nsSmtpService::NewChannel2(nsIURI *aURI,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel **_retval)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ // create an empty pipe for use with the input stream channel.
+ nsCOMPtr<nsIAsyncInputStream> pipeIn;
+ nsCOMPtr<nsIAsyncOutputStream> pipeOut;
+ nsCOMPtr<nsIPipe> pipe = do_CreateInstance("@mozilla.org/pipe;1");
+ nsresult rv = pipe->Init(false, false, 0, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // These always succeed because the pipe is initialized above.
+ MOZ_ALWAYS_SUCCEEDS(pipe->GetInputStream(getter_AddRefs(pipeIn)));
+ MOZ_ALWAYS_SUCCEEDS(pipe->GetOutputStream(getter_AddRefs(pipeOut)));
+
+ pipeOut->Close();
+
+ if (aLoadInfo) {
+ return NS_NewInputStreamChannelInternal(_retval,
+ aURI,
+ pipeIn,
+ NS_LITERAL_CSTRING("application/x-mailto"),
+ EmptyCString(),
+ aLoadInfo);
+ }
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CreateInstance of nullprincipal failed.");
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_NewInputStreamChannel(_retval, aURI, pipeIn,
+ nullPrincipal, nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER,
+ NS_LITERAL_CSTRING("application/x-mailto"));
+}
+
+NS_IMETHODIMP
+nsSmtpService::GetServers(nsISimpleEnumerator **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ // now read in the servers from prefs if necessary
+ uint32_t serverCount = mSmtpServers.Count();
+
+ if (serverCount <= 0)
+ loadSmtpServers();
+
+ return NS_NewArrayEnumerator(aResult, mSmtpServers);
+}
+
+nsresult
+nsSmtpService::loadSmtpServers()
+{
+ if (mSmtpServersLoaded)
+ return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ nsCOMPtr<nsIPrefBranch> prefRootBranch;
+ prefService->GetBranch(nullptr, getter_AddRefs(prefRootBranch));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCString serverList;
+ rv = prefRootBranch->GetCharPref(PREF_MAIL_SMTPSERVERS, getter_Copies(serverList));
+ serverList.StripWhitespace();
+
+ nsTArray<nsCString> servers;
+ ParseString(serverList, SERVER_DELIMITER, servers);
+
+ /**
+ * Check to see if we need to add pre-configured smtp servers.
+ * Following prefs are important to note in understanding the procedure here.
+ *
+ * 1. pref("mailnews.append_preconfig_smtpservers.version", version number);
+ * This pref registers the current version in the user prefs file. A default value
+ * is stored in mailnews.js file. If a given vendor needs to add more preconfigured
+ * smtp servers, the default version number can be increased. Comparing version
+ * number from user's prefs file and the default one from mailnews.js, we
+ * can add new smtp servers and any other version level changes that need to be done.
+ *
+ * 2. pref("mail.smtpservers.appendsmtpservers", <comma separated servers list>);
+ * This pref contains the list of pre-configured smp servers that ISP/Vendor wants to
+ * to add to the existing servers list.
+ */
+ nsCOMPtr<nsIPrefBranch> defaultsPrefBranch;
+ rv = prefService->GetDefaultBranch(MAIL_ROOT_PREF, getter_AddRefs(defaultsPrefBranch));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ rv = prefService->GetBranch(MAIL_ROOT_PREF, getter_AddRefs(prefBranch));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ int32_t appendSmtpServersCurrentVersion = 0;
+ int32_t appendSmtpServersDefaultVersion = 0;
+ rv = prefBranch->GetIntPref(APPEND_SERVERS_VERSION_PREF_NAME, &appendSmtpServersCurrentVersion);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = defaultsPrefBranch->GetIntPref(APPEND_SERVERS_VERSION_PREF_NAME, &appendSmtpServersDefaultVersion);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // Update the smtp server list if needed
+ if (appendSmtpServersCurrentVersion <= appendSmtpServersDefaultVersion) {
+ // If there are pre-configured servers, add them to the existing server list
+ nsCString appendServerList;
+ rv = prefRootBranch->GetCharPref(PREF_MAIL_SMTPSERVERS_APPEND_SERVERS, getter_Copies(appendServerList));
+ appendServerList.StripWhitespace();
+ ParseString(appendServerList, SERVER_DELIMITER, servers);
+
+ // Increase the version number so that updates will happen as and when needed
+ prefBranch->SetIntPref(APPEND_SERVERS_VERSION_PREF_NAME, appendSmtpServersCurrentVersion + 1);
+ }
+
+ // use GetServerByKey to check if the key (pref) is already in
+ // in the list. If not it calls createKeyedServer directly.
+
+ for (uint32_t i = 0; i < servers.Length(); i++) {
+ nsCOMPtr<nsISmtpServer> server;
+ GetServerByKey(servers[i].get(), getter_AddRefs(server));
+ }
+
+ saveKeyList();
+
+ mSmtpServersLoaded = true;
+ return NS_OK;
+}
+
+// save the list of keys
+nsresult
+nsSmtpService::saveKeyList()
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ return prefBranch->SetCharPref(PREF_MAIL_SMTPSERVERS, mServerKeyList.get());
+}
+
+nsresult
+nsSmtpService::createKeyedServer(const char *key, nsISmtpServer** aResult)
+{
+ if (!key) return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ nsCOMPtr<nsISmtpServer> server = do_CreateInstance(NS_SMTPSERVER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ server->SetKey(key);
+ mSmtpServers.AppendObject(server);
+
+ if (mServerKeyList.IsEmpty())
+ mServerKeyList = key;
+ else {
+ mServerKeyList.Append(',');
+ mServerKeyList += key;
+ }
+
+ if (aResult)
+ server.swap(*aResult);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpService::GetSessionDefaultServer(nsISmtpServer **aServer)
+{
+ NS_ENSURE_ARG_POINTER(aServer);
+
+ if (!mSessionDefaultServer)
+ return GetDefaultServer(aServer);
+
+ NS_ADDREF(*aServer = mSessionDefaultServer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpService::SetSessionDefaultServer(nsISmtpServer *aServer)
+{
+ mSessionDefaultServer = aServer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpService::GetDefaultServer(nsISmtpServer **aServer)
+{
+ NS_ENSURE_ARG_POINTER(aServer);
+
+ loadSmtpServers();
+
+ *aServer = nullptr;
+ // always returns NS_OK, just leaving *aServer at nullptr
+ if (!mDefaultSmtpServer) {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_FAILED(rv)) return rv;
+
+ // try to get it from the prefs
+ nsCString defaultServerKey;
+ rv = prefBranch->GetCharPref(PREF_MAIL_SMTP_DEFAULTSERVER, getter_Copies(defaultServerKey));
+ if (NS_SUCCEEDED(rv) &&
+ !defaultServerKey.IsEmpty()) {
+
+ nsCOMPtr<nsISmtpServer> server;
+ rv = GetServerByKey(defaultServerKey.get(),
+ getter_AddRefs(mDefaultSmtpServer));
+ } else {
+ // no pref set, so just return the first one, and set the pref
+
+ // Ensure the list of servers is loaded
+ loadSmtpServers();
+
+ // nothing in the array, we had better create a new server
+ // (which will add it to the array & prefs anyway)
+ if (mSmtpServers.Count() == 0)
+ // if there are no smtp servers then don't create one for the default.
+ return NS_OK;
+
+ mDefaultSmtpServer = mSmtpServers[0];
+ NS_ENSURE_TRUE(mDefaultSmtpServer, NS_ERROR_NULL_POINTER);
+
+ // now we have a default server, set the prefs correctly
+ nsCString serverKey;
+ mDefaultSmtpServer->GetKey(getter_Copies(serverKey));
+ if (NS_SUCCEEDED(rv))
+ prefBranch->SetCharPref(PREF_MAIL_SMTP_DEFAULTSERVER, serverKey.get());
+ }
+ }
+
+ // at this point:
+ // * mDefaultSmtpServer has a valid server
+ // * the key has been set in the prefs
+
+ NS_IF_ADDREF(*aServer = mDefaultSmtpServer);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpService::SetDefaultServer(nsISmtpServer *aServer)
+{
+ NS_ENSURE_ARG_POINTER(aServer);
+
+ mDefaultSmtpServer = aServer;
+
+ nsCString serverKey;
+ nsresult rv = aServer->GetKey(getter_Copies(serverKey));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv,rv);
+ prefBranch->SetCharPref(PREF_MAIL_SMTP_DEFAULTSERVER, serverKey.get());
+ return NS_OK;
+}
+
+bool
+nsSmtpService::findServerByKey(nsISmtpServer *aServer, void *aData)
+{
+ findServerByKeyEntry *entry = (findServerByKeyEntry*) aData;
+
+ nsCString key;
+ nsresult rv = aServer->GetKey(getter_Copies(key));
+ if (NS_FAILED(rv))
+ return true;
+
+ if (key.Equals(entry->key))
+ {
+ entry->server = aServer;
+ return false;
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+nsSmtpService::CreateServer(nsISmtpServer **aResult)
+{
+ if (!aResult) return NS_ERROR_NULL_POINTER;
+
+ loadSmtpServers();
+ nsresult rv;
+ int32_t i = 0;
+ bool unique = false;
+
+ findServerByKeyEntry entry;
+ nsAutoCString key;
+
+ do {
+ key = "smtp";
+ key.AppendInt(++i);
+ entry.key = key.get();
+ entry.server = nullptr;
+
+ mSmtpServers.EnumerateForwards(findServerByKey, (void *)&entry);
+ if (!entry.server) unique=true;
+
+ } while (!unique);
+
+ rv = createKeyedServer(key.get(), aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return saveKeyList();
+}
+
+
+nsresult
+nsSmtpService::GetServerByKey(const char* aKey, nsISmtpServer **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (!aKey || !*aKey)
+ {
+ NS_ASSERTION(false, "bad key");
+ return NS_ERROR_FAILURE;
+ }
+ findServerByKeyEntry entry;
+ entry.key = aKey;
+ entry.server = nullptr;
+ mSmtpServers.EnumerateForwards(findServerByKey, (void *)&entry);
+
+ if (entry.server) {
+ NS_ADDREF(*aResult = entry.server);
+ return NS_OK;
+ }
+
+ // not found in array, I guess we load it
+ return createKeyedServer(aKey, aResult);
+}
+
+NS_IMETHODIMP
+nsSmtpService::DeleteServer(nsISmtpServer *aServer)
+{
+ if (!aServer) return NS_OK;
+
+ int32_t idx = mSmtpServers.IndexOf(aServer);
+ if (idx == -1)
+ return NS_OK;
+
+ nsCString serverKey;
+ aServer->GetKey(getter_Copies(serverKey));
+
+ mSmtpServers.RemoveObjectAt(idx);
+
+ if (mDefaultSmtpServer.get() == aServer)
+ mDefaultSmtpServer = nullptr;
+ if (mSessionDefaultServer.get() == aServer)
+ mSessionDefaultServer = nullptr;
+
+ nsAutoCString newServerList;
+ nsCString tmpStr = mServerKeyList;
+ char *newStr = tmpStr.BeginWriting();
+ char *token = NS_strtok(",", &newStr);
+ while (token) {
+ // only re-add the string if it's not the key
+ if (strcmp(token, serverKey.get()) != 0) {
+ if (newServerList.IsEmpty())
+ newServerList = token;
+ else {
+ newServerList += ',';
+ newServerList += token;
+ }
+ }
+ token = NS_strtok(",", &newStr);
+ }
+
+ // make sure the server clears out it's values....
+ aServer->ClearAllValues();
+
+ mServerKeyList = newServerList;
+ saveKeyList();
+ return NS_OK;
+}
+
+bool
+nsSmtpService::findServerByHostname(nsISmtpServer *aServer, void *aData)
+{
+ findServerByHostnameEntry *entry = (findServerByHostnameEntry*)aData;
+
+ nsCString hostname;
+ nsresult rv = aServer->GetHostname(hostname);
+ if (NS_FAILED(rv))
+ return true;
+
+ nsCString username;
+ rv = aServer->GetUsername(username);
+ if (NS_FAILED(rv))
+ return true;
+
+ bool checkHostname = !entry->hostname.IsEmpty();
+ bool checkUsername = !entry->username.IsEmpty();
+
+ if ((!checkHostname ||
+ (entry->hostname.Equals(hostname, nsCaseInsensitiveCStringComparator()))) &&
+ (!checkUsername ||
+ entry->username.Equals(username, nsCaseInsensitiveCStringComparator())))
+ {
+ entry->server = aServer;
+ return false; // stop when found
+ }
+ return true;
+}
+
+NS_IMETHODIMP
+nsSmtpService::FindServer(const char *aUsername,
+ const char *aHostname, nsISmtpServer ** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ findServerByHostnameEntry entry;
+ entry.server = nullptr;
+ entry.hostname = aHostname;
+ entry.username = aUsername;
+
+ mSmtpServers.EnumerateForwards(findServerByHostname, (void *)&entry);
+
+ // entry.server may be null, but that's ok.
+ // just return null if no server is found
+ NS_IF_ADDREF(*aResult = entry.server);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpService::GetServerByIdentity(nsIMsgIdentity *aSenderIdentity,
+ nsISmtpServer **aSmtpServer)
+{
+ NS_ENSURE_ARG_POINTER(aSmtpServer);
+ nsresult rv = NS_ERROR_FAILURE;
+
+ // First try the identity's preferred server
+ if (aSenderIdentity)
+ {
+ nsCString smtpServerKey;
+ rv = aSenderIdentity->GetSmtpServerKey(smtpServerKey);
+ if (NS_SUCCEEDED(rv) && !(smtpServerKey.IsEmpty()))
+ rv = GetServerByKey(smtpServerKey.get(), aSmtpServer);
+ }
+
+ // Fallback to the default
+ if (NS_FAILED(rv) || !(*aSmtpServer))
+ rv = GetDefaultServer(aSmtpServer);
+ return rv;
+}
diff --git a/mailnews/compose/src/nsSmtpService.h b/mailnews/compose/src/nsSmtpService.h
new file mode 100644
index 000000000..8aca6e7ef
--- /dev/null
+++ b/mailnews/compose/src/nsSmtpService.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSmtpService_h___
+#define nsSmtpService_h___
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsISmtpService.h"
+#include "nsISmtpServer.h"
+#include "nsIProtocolHandler.h"
+
+////////////////////////////////////////////////////////////////////////////////////////
+// The Smtp Service is an interfaced designed to make building and running mail to urls
+// easier. I'm not sure if this service will go away when the new networking model comes
+// on line (as part of the N2 project). So I reserve the right to change my mind and take
+// this service away =).
+////////////////////////////////////////////////////////////////////////////////////////
+
+class nsSmtpService : public nsISmtpService, public nsIProtocolHandler
+{
+public:
+
+ nsSmtpService();
+
+ NS_DECL_ISUPPORTS
+
+ ////////////////////////////////////////////////////////////////////////
+ // we suppport the nsISmtpService interface
+ ////////////////////////////////////////////////////////////////////////
+ NS_DECL_NSISMTPSERVICE
+
+ //////////////////////////////////////////////////////////////////////////
+ // we suppport the nsIProtocolHandler interface
+ //////////////////////////////////////////////////////////////////////////
+ NS_DECL_NSIPROTOCOLHANDLER
+
+protected:
+ nsresult loadSmtpServers();
+
+
+private:
+ virtual ~nsSmtpService();
+ static bool findServerByKey(nsISmtpServer *aServer, void *aData);
+ static bool findServerByHostname(nsISmtpServer *aServer, void *aData);
+
+ nsresult createKeyedServer(const char* key,
+ nsISmtpServer **aResult = nullptr);
+ nsresult saveKeyList();
+
+ nsCOMArray<nsISmtpServer> mSmtpServers;
+ nsCOMPtr<nsISmtpServer> mDefaultSmtpServer;
+ nsCOMPtr<nsISmtpServer> mSessionDefaultServer;
+
+ nsCString mServerKeyList;
+
+ bool mSmtpServersLoaded;
+};
+
+#endif /* nsSmtpService_h___ */
diff --git a/mailnews/compose/src/nsSmtpUrl.cpp b/mailnews/compose/src/nsSmtpUrl.cpp
new file mode 100644
index 000000000..cd40bd6cc
--- /dev/null
+++ b/mailnews/compose/src/nsSmtpUrl.cpp
@@ -0,0 +1,778 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "msgCore.h"
+
+#include "nsIURI.h"
+#include "nsNetCID.h"
+#include "nsSmtpUrl.h"
+#include "nsStringGlue.h"
+#include "nsMsgUtils.h"
+#include "nsIMimeConverter.h"
+#include "nsMsgMimeCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsCRT.h"
+#include "nsAutoPtr.h"
+
+/////////////////////////////////////////////////////////////////////////////////////
+// mailto url definition
+/////////////////////////////////////////////////////////////////////////////////////
+nsMailtoUrl::nsMailtoUrl()
+{
+ mFormat = nsIMsgCompFormat::Default;
+ m_baseURL = do_CreateInstance(NS_SIMPLEURI_CONTRACTID);
+}
+
+nsMailtoUrl::~nsMailtoUrl()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsMailtoUrl, nsIMailtoUrl, nsIURI)
+
+static void UnescapeAndConvert(nsIMimeConverter *mimeConverter,
+ const nsACString &escaped, nsACString &out)
+{
+ NS_ASSERTION(mimeConverter, "Set mimeConverter before calling!");
+ // If the string is empty, do absolutely nothing.
+ if (escaped.IsEmpty())
+ return;
+
+ MsgUnescapeString(escaped, 0, out);
+ nsAutoCString decodedString;
+ nsresult rv = mimeConverter->DecodeMimeHeaderToUTF8(out, "UTF_8", false,
+ true, decodedString);
+ if (NS_SUCCEEDED(rv) && !decodedString.IsEmpty())
+ out = decodedString;
+}
+
+nsresult nsMailtoUrl::ParseMailtoUrl(char * searchPart)
+{
+ char *rest = searchPart;
+ nsCString escapedInReplyToPart;
+ nsCString escapedToPart;
+ nsCString escapedCcPart;
+ nsCString escapedSubjectPart;
+ nsCString escapedNewsgroupPart;
+ nsCString escapedNewsHostPart;
+ nsCString escapedReferencePart;
+ nsCString escapedBodyPart;
+ nsCString escapedBccPart;
+ nsCString escapedFollowUpToPart;
+ nsCString escapedFromPart;
+ nsCString escapedHtmlPart;
+ nsCString escapedOrganizationPart;
+ nsCString escapedReplyToPart;
+ nsCString escapedPriorityPart;
+
+ // okay, first, free up all of our old search part state.....
+ CleanupMailtoState();
+ // m_toPart has the escaped address from before the query string, copy it
+ // over so we can add on any additional to= addresses and unescape them all.
+ escapedToPart = m_toPart;
+
+ if (rest && *rest == '?')
+ {
+ /* start past the '?' */
+ rest++;
+ }
+
+ if (rest)
+ {
+ char *token = NS_strtok("&", &rest);
+ while (token && *token)
+ {
+ char *value = 0;
+ char *eq = PL_strchr(token, '=');
+ if (eq)
+ {
+ value = eq+1;
+ *eq = 0;
+ }
+
+ nsCString decodedName;
+ MsgUnescapeString(nsDependentCString(token), 0, decodedName);
+
+ if (decodedName.Length() == 0)
+ break;
+
+ switch (NS_ToUpper(decodedName.First()))
+ {
+ /* DO NOT support attachment= in mailto urls. This poses a security fire hole!!!
+ case 'A':
+ if (!PL_strcasecmp (token, "attachment"))
+ m_attachmentPart = value;
+ break;
+ */
+ case 'B':
+ if (decodedName.LowerCaseEqualsLiteral("bcc"))
+ {
+ if (!escapedBccPart.IsEmpty())
+ {
+ escapedBccPart += ", ";
+ escapedBccPart += value;
+ }
+ else
+ escapedBccPart = value;
+ }
+ else if (decodedName.LowerCaseEqualsLiteral("body"))
+ {
+ if (!escapedBodyPart.IsEmpty())
+ {
+ escapedBodyPart +="\n";
+ escapedBodyPart += value;
+ }
+ else
+ escapedBodyPart = value;
+ }
+ break;
+ case 'C':
+ if (decodedName.LowerCaseEqualsLiteral("cc"))
+ {
+ if (!escapedCcPart.IsEmpty())
+ {
+ escapedCcPart += ", ";
+ escapedCcPart += value;
+ }
+ else
+ escapedCcPart = value;
+ }
+ break;
+ case 'F':
+ if (decodedName.LowerCaseEqualsLiteral("followup-to"))
+ escapedFollowUpToPart = value;
+ else if (decodedName.LowerCaseEqualsLiteral("from"))
+ escapedFromPart = value;
+ break;
+ case 'H':
+ if (decodedName.LowerCaseEqualsLiteral("html-part") ||
+ decodedName.LowerCaseEqualsLiteral("html-body"))
+ {
+ // escapedHtmlPart holds the body for both html-part and html-body.
+ escapedHtmlPart = value;
+ mFormat = nsIMsgCompFormat::HTML;
+ }
+ break;
+ case 'I':
+ if (decodedName.LowerCaseEqualsLiteral("in-reply-to"))
+ escapedInReplyToPart = value;
+ break;
+
+ case 'N':
+ if (decodedName.LowerCaseEqualsLiteral("newsgroups"))
+ escapedNewsgroupPart = value;
+ else if (decodedName.LowerCaseEqualsLiteral("newshost"))
+ escapedNewsHostPart = value;
+ break;
+ case 'O':
+ if (decodedName.LowerCaseEqualsLiteral("organization"))
+ escapedOrganizationPart = value;
+ break;
+ case 'R':
+ if (decodedName.LowerCaseEqualsLiteral("references"))
+ escapedReferencePart = value;
+ else if (decodedName.LowerCaseEqualsLiteral("reply-to"))
+ escapedReplyToPart = value;
+ break;
+ case 'S':
+ if(decodedName.LowerCaseEqualsLiteral("subject"))
+ escapedSubjectPart = value;
+ break;
+ case 'P':
+ if (decodedName.LowerCaseEqualsLiteral("priority"))
+ escapedPriorityPart = PL_strdup(value);
+ break;
+ case 'T':
+ if (decodedName.LowerCaseEqualsLiteral("to"))
+ {
+ if (!escapedToPart.IsEmpty())
+ {
+ escapedToPart += ", ";
+ escapedToPart += value;
+ }
+ else
+ escapedToPart = value;
+ }
+ break;
+ default:
+ break;
+ } // end of switch statement...
+
+ if (eq)
+ *eq = '='; /* put it back */
+ token = NS_strtok("&", &rest);
+ } // while we still have part of the url to parse...
+ } // if rest && *rest
+
+ // Get a global converter
+ nsCOMPtr<nsIMimeConverter> mimeConverter =
+ do_GetService(NS_MIME_CONVERTER_CONTRACTID);
+
+ // Now unescape everything, and mime-decode the things that can be encoded.
+ UnescapeAndConvert(mimeConverter, escapedToPart, m_toPart);
+ UnescapeAndConvert(mimeConverter, escapedCcPart, m_ccPart);
+ UnescapeAndConvert(mimeConverter, escapedBccPart, m_bccPart);
+ UnescapeAndConvert(mimeConverter, escapedSubjectPart, m_subjectPart);
+ UnescapeAndConvert(mimeConverter, escapedNewsgroupPart, m_newsgroupPart);
+ UnescapeAndConvert(mimeConverter, escapedReferencePart, m_referencePart);
+ if (!escapedBodyPart.IsEmpty())
+ MsgUnescapeString(escapedBodyPart, 0, m_bodyPart);
+ if (!escapedHtmlPart.IsEmpty())
+ MsgUnescapeString(escapedHtmlPart, 0, m_htmlPart);
+ UnescapeAndConvert(mimeConverter, escapedNewsHostPart, m_newsHostPart);
+ UnescapeAndConvert(mimeConverter, escapedFollowUpToPart, m_followUpToPart);
+ UnescapeAndConvert(mimeConverter, escapedFromPart, m_fromPart);
+ UnescapeAndConvert(mimeConverter, escapedOrganizationPart, m_organizationPart);
+ UnescapeAndConvert(mimeConverter, escapedReplyToPart, m_replyToPart);
+ UnescapeAndConvert(mimeConverter, escapedPriorityPart, m_priorityPart);
+
+ nsCString inReplyToPart; // Not a member like the others...
+ UnescapeAndConvert(mimeConverter, escapedInReplyToPart, inReplyToPart);
+
+ if (!inReplyToPart.IsEmpty())
+ {
+ // Ensure that References and In-Reply-To are consistent... The last
+ // reference will be used as In-Reply-To header.
+ if (m_referencePart.IsEmpty())
+ {
+ // If References is not set, set it to be the In-Reply-To.
+ m_referencePart = inReplyToPart;
+ }
+ else
+ {
+ // References is set. Add the In-Reply-To as last header unless it's
+ // set as last reference already.
+ int32_t lastRefStart = m_referencePart.RFindChar('<');
+ nsAutoCString lastReference;
+ if (lastRefStart != -1)
+ lastReference = StringTail(m_referencePart, lastRefStart);
+ else
+ lastReference = m_referencePart;
+
+ if (lastReference != inReplyToPart)
+ {
+ m_referencePart += " ";
+ m_referencePart += inReplyToPart;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetSpec(const nsACString &aSpec)
+{
+ nsresult rv = m_baseURL->SetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return ParseUrl();
+}
+
+nsresult nsMailtoUrl::CleanupMailtoState()
+{
+ m_ccPart = "";
+ m_subjectPart = "";
+ m_newsgroupPart = "";
+ m_newsHostPart = "";
+ m_referencePart = "";
+ m_bodyPart = "";
+ m_bccPart = "";
+ m_followUpToPart = "";
+ m_fromPart = "";
+ m_htmlPart = "";
+ m_organizationPart = "";
+ m_replyToPart = "";
+ m_priorityPart = "";
+ return NS_OK;
+}
+
+nsresult nsMailtoUrl::ParseUrl()
+{
+ // we can get the path from the simple url.....
+ nsCString escapedPath;
+ m_baseURL->GetPath(escapedPath);
+
+ int32_t startOfSearchPart = escapedPath.FindChar('?');
+ if (startOfSearchPart >= 0)
+ {
+ // now parse out the search field...
+ nsAutoCString searchPart(Substring(escapedPath, startOfSearchPart));
+
+ if (!searchPart.IsEmpty())
+ {
+ // now we need to strip off the search part from the
+ // to part....
+ escapedPath.SetLength(startOfSearchPart);
+ MsgUnescapeString(escapedPath, 0, m_toPart);
+ ParseMailtoUrl(searchPart.BeginWriting());
+ }
+ }
+ else if (!escapedPath.IsEmpty())
+ {
+ MsgUnescapeString(escapedPath, 0, m_toPart);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetMessageContents(nsACString &aToPart, nsACString &aCcPart,
+ nsACString &aBccPart, nsACString &aSubjectPart,
+ nsACString &aBodyPart, nsACString &aHtmlPart,
+ nsACString &aReferencePart,
+ nsACString &aNewsgroupPart,
+ MSG_ComposeFormat *aFormat)
+{
+ NS_ENSURE_ARG_POINTER(aFormat);
+
+ aToPart = m_toPart;
+ aCcPart = m_ccPart;
+ aBccPart = m_bccPart;
+ aSubjectPart = m_subjectPart;
+ aBodyPart = m_bodyPart;
+ aHtmlPart = m_htmlPart;
+ aReferencePart = m_referencePart;
+ aNewsgroupPart = m_newsgroupPart;
+ *aFormat = mFormat;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetFromPart(nsACString &aResult)
+{
+ aResult = m_fromPart;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetFollowUpToPart(nsACString &aResult)
+{
+ aResult = m_followUpToPart;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetOrganizationPart(nsACString &aResult)
+{
+ aResult = m_organizationPart;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetReplyToPart(nsACString &aResult)
+{
+ aResult = m_replyToPart;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetPriorityPart(nsACString &aResult)
+{
+ aResult = m_priorityPart;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetNewsHostPart(nsACString &aResult)
+{
+ aResult = m_newsHostPart;
+ return NS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Begin nsIURI support
+//////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP nsMailtoUrl::GetSpec(nsACString &aSpec)
+{
+ return m_baseURL->GetSpec(aSpec);
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetPrePath(nsACString &aPrePath)
+{
+ return m_baseURL->GetPrePath(aPrePath);
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetScheme(nsACString &aScheme)
+{
+ return m_baseURL->GetScheme(aScheme);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetScheme(const nsACString &aScheme)
+{
+ m_baseURL->SetScheme(aScheme);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetUserPass(nsACString &aUserPass)
+{
+ return m_baseURL->GetUserPass(aUserPass);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetUserPass(const nsACString &aUserPass)
+{
+ m_baseURL->SetUserPass(aUserPass);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetUsername(nsACString &aUsername)
+{
+ return m_baseURL->GetUsername(aUsername);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetUsername(const nsACString &aUsername)
+{
+ m_baseURL->SetUsername(aUsername);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetPassword(nsACString &aPassword)
+{
+ return m_baseURL->GetPassword(aPassword);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetPassword(const nsACString &aPassword)
+{
+ m_baseURL->SetPassword(aPassword);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetHostPort(nsACString &aHostPort)
+{
+ return m_baseURL->GetHost(aHostPort);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetHostPort(const nsACString &aHostPort)
+{
+ m_baseURL->SetHost(aHostPort);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetHostAndPort(const nsACString &aHostPort)
+{
+ m_baseURL->SetHostAndPort(aHostPort);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetHost(nsACString &aHost)
+{
+ return m_baseURL->GetHost(aHost);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetHost(const nsACString &aHost)
+{
+ m_baseURL->SetHost(aHost);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetPort(int32_t *aPort)
+{
+ return m_baseURL->GetPort(aPort);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetPort(int32_t aPort)
+{
+ m_baseURL->SetPort(aPort);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetPath(nsACString &aPath)
+{
+ return m_baseURL->GetPath(aPath);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetPath(const nsACString &aPath)
+{
+ m_baseURL->SetPath(aPath);
+ return ParseUrl();
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetAsciiHost(nsACString &aHostA)
+{
+ return m_baseURL->GetAsciiHost(aHostA);
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetAsciiHostPort(nsACString &aHostPortA)
+{
+ return m_baseURL->GetAsciiHostPort(aHostPortA);
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetAsciiSpec(nsACString &aSpecA)
+{
+ return m_baseURL->GetAsciiSpec(aSpecA);
+}
+
+NS_IMETHODIMP nsMailtoUrl::GetOriginCharset(nsACString &aOriginCharset)
+{
+ return m_baseURL->GetOriginCharset(aOriginCharset);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SchemeIs(const char *aScheme, bool *_retval)
+{
+ return m_baseURL->SchemeIs(aScheme, _retval);
+}
+
+NS_IMETHODIMP nsMailtoUrl::Equals(nsIURI *other, bool *_retval)
+{
+ // The passed-in URI might be an nsMailtoUrl. Pass our inner URL to its
+ // Equals method. The other nsMailtoUrl will then pass its inner URL to
+ // to the Equals method of our inner URL. Other URIs will return false.
+ if (other)
+ return other->Equals(m_baseURL, _retval);
+
+ return m_baseURL->Equals(other, _retval);
+}
+
+nsresult
+nsMailtoUrl::CloneInternal(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef, nsIURI** _retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ RefPtr<nsMailtoUrl> clone = new nsMailtoUrl();
+
+ NS_ENSURE_TRUE(clone, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv;
+ if (aRefHandlingMode == eHonorRef) {
+ rv = m_baseURL->Clone(getter_AddRefs(clone->m_baseURL));
+ } else if (aRefHandlingMode == eReplaceRef) {
+ rv = m_baseURL->CloneWithNewRef(newRef, getter_AddRefs(clone->m_baseURL));
+ } else {
+ rv = m_baseURL->CloneIgnoringRef(getter_AddRefs(clone->m_baseURL));
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ clone->ParseUrl();
+ clone.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::Clone(nsIURI **_retval)
+{
+ return CloneInternal(eHonorRef, EmptyCString(), _retval);
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::CloneIgnoringRef(nsIURI** _retval)
+{
+ return CloneInternal(eIgnoreRef, EmptyCString(), _retval);
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::CloneWithNewRef(const nsACString& newRef, nsIURI** _retval)
+{
+ return CloneInternal(eReplaceRef, newRef, _retval);
+}
+
+NS_IMETHODIMP nsMailtoUrl::Resolve(const nsACString &relativePath, nsACString &result)
+{
+ return m_baseURL->Resolve(relativePath, result);
+}
+
+NS_IMETHODIMP nsMailtoUrl::SetRef(const nsACString &aRef)
+{
+ return m_baseURL->SetRef(aRef);
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetRef(nsACString &result)
+{
+ return m_baseURL->GetRef(result);
+}
+
+NS_IMETHODIMP nsMailtoUrl::EqualsExceptRef(nsIURI *other, bool *result)
+{
+ // The passed-in URI might be an nsMailtoUrl. Pass our inner URL to its
+ // Equals method. The other nsMailtoUrl will then pass its inner URL to
+ // to the Equals method of our inner URL. Other URIs will return false.
+ if (other)
+ return other->EqualsExceptRef(m_baseURL, result);
+
+ return m_baseURL->EqualsExceptRef(other, result);
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetSpecIgnoringRef(nsACString &result)
+{
+ return m_baseURL->GetSpecIgnoringRef(result);
+}
+
+NS_IMETHODIMP
+nsMailtoUrl::GetHasRef(bool *result)
+{
+ return m_baseURL->GetHasRef(result);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+// smtp url definition
+/////////////////////////////////////////////////////////////////////////////////////
+
+nsSmtpUrl::nsSmtpUrl() : nsMsgMailNewsUrl()
+{
+ // nsISmtpUrl specific state...
+
+ m_isPostMessage = true;
+ m_requestDSN = false;
+ m_verifyLogon = false;
+}
+
+nsSmtpUrl::~nsSmtpUrl()
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsSmtpUrl, nsMsgMailNewsUrl, nsISmtpUrl)
+
+////////////////////////////////////////////////////////////////////////////////////
+// Begin nsISmtpUrl specific support
+
+////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsSmtpUrl::SetRecipients(const char * aRecipientsList)
+{
+ NS_ENSURE_ARG(aRecipientsList);
+ MsgUnescapeString(nsDependentCString(aRecipientsList), 0, m_toPart);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::GetRecipients(char ** aRecipientsList)
+{
+ NS_ENSURE_ARG_POINTER(aRecipientsList);
+ if (aRecipientsList)
+ *aRecipientsList = ToNewCString(m_toPart);
+ return NS_OK;
+}
+
+NS_IMPL_GETSET(nsSmtpUrl, PostMessage, bool, m_isPostMessage)
+
+NS_IMPL_GETSET(nsSmtpUrl, VerifyLogon, bool, m_verifyLogon)
+
+// the message can be stored in a file....allow accessors for getting and setting
+// the file name to post...
+NS_IMETHODIMP nsSmtpUrl::SetPostMessageFile(nsIFile * aFile)
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+ m_fileName = aFile;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsSmtpUrl::GetPostMessageFile(nsIFile ** aFile)
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+ if (m_fileName)
+ {
+ // Clone the file so nsLocalFile stat caching doesn't make the caller get
+ // the wrong file size.
+ m_fileName->Clone(aFile);
+ return *aFile ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMPL_GETSET(nsSmtpUrl, RequestDSN, bool, m_requestDSN)
+
+NS_IMETHODIMP
+nsSmtpUrl::SetDsnEnvid(const nsACString &aDsnEnvid)
+{
+ m_dsnEnvid = aDsnEnvid;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::GetDsnEnvid(nsACString &aDsnEnvid)
+{
+ aDsnEnvid = m_dsnEnvid;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::GetSenderIdentity(nsIMsgIdentity **aSenderIdentity)
+{
+ NS_ENSURE_ARG_POINTER(aSenderIdentity);
+ *aSenderIdentity = m_senderIdentity;
+ NS_ADDREF(*aSenderIdentity);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::SetSenderIdentity(nsIMsgIdentity *aSenderIdentity)
+{
+ NS_ENSURE_ARG_POINTER(aSenderIdentity);
+ m_senderIdentity = aSenderIdentity;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::SetPrompt(nsIPrompt *aNetPrompt)
+{
+ NS_ENSURE_ARG_POINTER(aNetPrompt);
+ m_netPrompt = aNetPrompt;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::GetPrompt(nsIPrompt **aNetPrompt)
+{
+ NS_ENSURE_ARG_POINTER(aNetPrompt);
+ NS_ENSURE_TRUE(m_netPrompt, NS_ERROR_NULL_POINTER);
+ *aNetPrompt = m_netPrompt;
+ NS_ADDREF(*aNetPrompt);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::SetAuthPrompt(nsIAuthPrompt *aNetAuthPrompt)
+{
+ NS_ENSURE_ARG_POINTER(aNetAuthPrompt);
+ m_netAuthPrompt = aNetAuthPrompt;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::GetAuthPrompt(nsIAuthPrompt **aNetAuthPrompt)
+{
+ NS_ENSURE_ARG_POINTER(aNetAuthPrompt);
+ NS_ENSURE_TRUE(m_netAuthPrompt, NS_ERROR_NULL_POINTER);
+ *aNetAuthPrompt = m_netAuthPrompt;
+ NS_ADDREF(*aNetAuthPrompt);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks)
+{
+ NS_ENSURE_ARG_POINTER(aCallbacks);
+ m_callbacks = aCallbacks;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks)
+{
+ NS_ENSURE_ARG_POINTER(aCallbacks);
+ NS_ENSURE_TRUE(m_callbacks, NS_ERROR_NULL_POINTER);
+ *aCallbacks = m_callbacks;
+ NS_ADDREF(*aCallbacks);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::SetSmtpServer(nsISmtpServer * aSmtpServer)
+{
+ NS_ENSURE_ARG_POINTER(aSmtpServer);
+ m_smtpServer = aSmtpServer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSmtpUrl::GetSmtpServer(nsISmtpServer ** aSmtpServer)
+{
+ NS_ENSURE_ARG_POINTER(aSmtpServer);
+ NS_ENSURE_TRUE(m_smtpServer, NS_ERROR_NULL_POINTER);
+ *aSmtpServer = m_smtpServer;
+ NS_ADDREF(*aSmtpServer);
+ return NS_OK;
+}
diff --git a/mailnews/compose/src/nsSmtpUrl.h b/mailnews/compose/src/nsSmtpUrl.h
new file mode 100644
index 000000000..d603fa365
--- /dev/null
+++ b/mailnews/compose/src/nsSmtpUrl.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSmtpUrl_h__
+#define nsSmtpUrl_h__
+
+#include "nsISmtpUrl.h"
+#include "nsIURI.h"
+#include "nsMsgMailNewsUrl.h"
+#include "nsIMsgIdentity.h"
+#include "nsCOMPtr.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsISmtpServer.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+
+class nsMailtoUrl : public nsIMailtoUrl, public nsIURI
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIURI
+ NS_DECL_NSIMAILTOURL
+
+ nsMailtoUrl();
+
+protected:
+ enum RefHandlingEnum {
+ eIgnoreRef,
+ eHonorRef,
+ eReplaceRef
+ };
+ virtual ~nsMailtoUrl();
+ nsresult ParseUrl();
+ nsresult CleanupMailtoState();
+ nsresult ParseMailtoUrl(char * searchPart);
+ nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef, nsIURI** _retval);
+
+ nsCOMPtr<nsIURI> m_baseURL;
+
+ // data retrieved from parsing the url: (Note the url could be a post from file or it could be in the url)
+ nsCString m_toPart;
+ nsCString m_ccPart;
+ nsCString m_subjectPart;
+ nsCString m_newsgroupPart;
+ nsCString m_newsHostPart;
+ nsCString m_referencePart;
+ nsCString m_bodyPart;
+ nsCString m_bccPart;
+ nsCString m_followUpToPart;
+ nsCString m_fromPart;
+ nsCString m_htmlPart;
+ nsCString m_organizationPart;
+ nsCString m_replyToPart;
+ nsCString m_priorityPart;
+
+ MSG_ComposeFormat mFormat;
+};
+
+class nsSmtpUrl : public nsISmtpUrl, public nsMsgMailNewsUrl
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // From nsISmtpUrl
+ NS_DECL_NSISMTPURL
+
+ // nsSmtpUrl
+ nsSmtpUrl();
+
+protected:
+ virtual ~nsSmtpUrl();
+
+ // data retrieved from parsing the url: (Note the url could be a post from
+ // file or it could be in the url)
+ nsCString m_toPart;
+
+ bool m_isPostMessage;
+ bool m_requestDSN;
+ nsCString m_dsnEnvid;
+ bool m_verifyLogon;
+
+ // Smtp specific event sinks
+ nsCOMPtr<nsIFile> m_fileName;
+ nsCOMPtr<nsIMsgIdentity> m_senderIdentity;
+ nsCOMPtr<nsIPrompt> m_netPrompt;
+ nsCOMPtr<nsIAuthPrompt> m_netAuthPrompt;
+ nsCOMPtr<nsIInterfaceRequestor> m_callbacks;
+ nsCOMPtr<nsISmtpServer> m_smtpServer;
+
+ // it is possible to encode the message to parse in the form of a url.
+ // This function is used to decompose the search and path part into the bare
+ // message components (to, fcc, bcc, etc.)
+ nsresult ParseMessageToPost(char * searchPart);
+};
+
+#endif // nsSmtpUrl_h__
diff --git a/mailnews/compose/src/nsURLFetcher.cpp b/mailnews/compose/src/nsURLFetcher.cpp
new file mode 100644
index 000000000..b564ab9a4
--- /dev/null
+++ b/mailnews/compose/src/nsURLFetcher.cpp
@@ -0,0 +1,526 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsURLFetcher.h"
+
+#include "msgCore.h" // for pre-compiled headers
+#include "nsCOMPtr.h"
+#include "nsNullPrincipal.h"
+#include <stdio.h>
+#include "nscore.h"
+#include "nsIFactory.h"
+#include "nsISupports.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "nsIComponentManager.h"
+#include "nsStringGlue.h"
+#include "nsIIOService.h"
+#include "nsIChannel.h"
+#include "nsNetUtil.h"
+#include "nsMimeTypes.h"
+#include "nsIHttpChannel.h"
+#include "nsIWebProgress.h"
+#include "nsMsgAttachmentHandler.h"
+#include "nsMsgSend.h"
+#include "nsISeekableStream.h"
+#include "nsIStreamConverterService.h"
+#include "nsIMsgProgress.h"
+#include "nsMsgUtils.h"
+
+NS_IMPL_ISUPPORTS(nsURLFetcher,
+ nsIURLFetcher,
+ nsIStreamListener,
+ nsIRequestObserver,
+ nsIURIContentListener,
+ nsIInterfaceRequestor,
+ nsIWebProgressListener,
+ nsISupportsWeakReference)
+
+
+/*
+ * Inherited methods for nsMimeConverter
+ */
+nsURLFetcher::nsURLFetcher()
+{
+ // Init member variables...
+ mTotalWritten = 0;
+ mBuffer = nullptr;
+ mBufferSize = 0;
+ mStillRunning = true;
+ mCallback = nullptr;
+ mOnStopRequestProcessed = false;
+ mIsFile=false;
+ nsURLFetcherStreamConsumer *consumer = new nsURLFetcherStreamConsumer(this);
+ mConverter = do_QueryInterface(consumer);
+}
+
+nsURLFetcher::~nsURLFetcher()
+{
+ mStillRunning = false;
+
+ PR_FREEIF(mBuffer);
+ // Remove the DocShell as a listener of the old WebProgress...
+ if (mLoadCookie)
+ {
+ nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
+
+ if (webProgress)
+ webProgress->RemoveProgressListener(this);
+ }
+}
+
+NS_IMETHODIMP nsURLFetcher::GetInterface(const nsIID & aIID, void * *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ return QueryInterface(aIID, aInstancePtr);
+}
+
+// nsIURIContentListener support
+NS_IMETHODIMP
+nsURLFetcher::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::IsPreferred(const char * aContentType,
+ char ** aDesiredContentType,
+ bool * aCanHandleContent)
+
+{
+ return CanHandleContent(aContentType, true, aDesiredContentType,
+ aCanHandleContent);
+}
+
+NS_IMETHODIMP
+nsURLFetcher::CanHandleContent(const char * aContentType,
+ bool aIsContentPreferred,
+ char ** aDesiredContentType,
+ bool * aCanHandleContent)
+
+{
+ if (!mIsFile && PL_strcasecmp(aContentType, MESSAGE_RFC822) == 0)
+ *aDesiredContentType = strdup("text/html");
+
+ // since we explicilty loaded the url, we always want to handle it!
+ *aCanHandleContent = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::DoContent(const nsACString& aContentType,
+ bool aIsContentPreferred,
+ nsIRequest *request,
+ nsIStreamListener ** aContentHandler,
+ bool * aAbortProcess)
+{
+ nsresult rv = NS_OK;
+
+ if (aAbortProcess)
+ *aAbortProcess = false;
+ QueryInterface(NS_GET_IID(nsIStreamListener), (void **) aContentHandler);
+
+ /*
+ Check the content-type to see if we need to insert a converter
+ */
+ if (PL_strcasecmp(PromiseFlatCString(aContentType).get(), UNKNOWN_CONTENT_TYPE) == 0 ||
+ PL_strcasecmp(PromiseFlatCString(aContentType).get(), MULTIPART_MIXED_REPLACE) == 0 ||
+ PL_strcasecmp(PromiseFlatCString(aContentType).get(), MULTIPART_MIXED) == 0 ||
+ PL_strcasecmp(PromiseFlatCString(aContentType).get(), MULTIPART_BYTERANGES) == 0)
+ {
+ rv = InsertConverter(PromiseFlatCString(aContentType).get());
+ if (NS_SUCCEEDED(rv))
+ mConverterContentType = PromiseFlatCString(aContentType).get();
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::GetParentContentListener(nsIURIContentListener** aParent)
+{
+ *aParent = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::SetParentContentListener(nsIURIContentListener* aParent)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::GetLoadCookie(nsISupports ** aLoadCookie)
+{
+ *aLoadCookie = mLoadCookie;
+ NS_IF_ADDREF(*aLoadCookie);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::SetLoadCookie(nsISupports * aLoadCookie)
+{
+ // Remove the DocShell as a listener of the old WebProgress...
+ if (mLoadCookie)
+ {
+ nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
+
+ if (webProgress)
+ webProgress->RemoveProgressListener(this);
+ }
+
+ mLoadCookie = aLoadCookie;
+
+ // Add the DocShell as a listener to the new WebProgress...
+ if (mLoadCookie)
+ {
+ nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
+
+ if (webProgress)
+ webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_ALL);
+ }
+ return NS_OK;
+
+}
+
+nsresult
+nsURLFetcher::StillRunning(bool *running)
+{
+ *running = mStillRunning;
+ return NS_OK;
+}
+
+
+// Methods for nsIStreamListener...
+nsresult
+nsURLFetcher::OnDataAvailable(nsIRequest *request, nsISupports * ctxt, nsIInputStream *aIStream,
+ uint64_t sourceOffset, uint32_t aLength)
+{
+ /* let our converter or consumer process the data */
+ if (!mConverter)
+ return NS_ERROR_FAILURE;
+
+ return mConverter->OnDataAvailable(request, ctxt, aIStream, sourceOffset, aLength);
+}
+
+
+// Methods for nsIStreamObserver
+nsresult
+nsURLFetcher::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
+{
+ /* check if the user has canceld the operation */
+ if (mTagData)
+ {
+ nsCOMPtr<nsIMsgSend> sendPtr;
+ mTagData->GetMimeDeliveryState(getter_AddRefs(sendPtr));
+ if (sendPtr)
+ {
+ nsCOMPtr<nsIMsgProgress> progress;
+ sendPtr->GetProgress(getter_AddRefs(progress));
+ if (progress)
+ {
+ bool cancel = false;
+ progress->GetProcessCanceledByUser(&cancel);
+ if (cancel)
+ return request->Cancel(NS_ERROR_ABORT);
+ }
+ }
+ mTagData->mRequest = request;
+ }
+
+ /* call our converter or consumer */
+ if (mConverter)
+ return mConverter->OnStartRequest(request, ctxt);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnStopRequest(nsIRequest *request, nsISupports * ctxt, nsresult aStatus)
+{
+ // it's possible we could get in here from the channel calling us with an OnStopRequest and from our
+ // onStatusChange method (in the case of an error). So we should protect against this to make sure we
+ // don't process the on stop request twice...
+
+ if (mOnStopRequestProcessed)
+ return NS_OK;
+
+ mOnStopRequestProcessed = true;
+
+ /* first, call our converter or consumer */
+ if (mConverter)
+ (void) mConverter->OnStopRequest(request, ctxt, aStatus);
+
+ if (mTagData)
+ mTagData->mRequest = nullptr;
+
+ //
+ // Now complete the stream!
+ //
+ mStillRunning = false;
+
+ // time to close the output stream...
+ if (mOutStream)
+ {
+ mOutStream->Close();
+ mOutStream = nullptr;
+
+ /* In case of multipart/x-mixed-replace, we need to truncate the file to the current part size */
+ if (MsgLowerCaseEqualsLiteral(mConverterContentType, MULTIPART_MIXED_REPLACE))
+ {
+ mLocalFile->SetFileSize(mTotalWritten);
+ }
+ }
+
+ // Now if there is a callback, we need to call it...
+ if (mCallback)
+ mCallback (aStatus, mContentType, mCharset, mTotalWritten, nullptr, mTagData);
+
+ // Time to return...
+ return NS_OK;
+}
+
+nsresult
+nsURLFetcher::Initialize(nsIFile *localFile,
+ nsIOutputStream *outputStream,
+ nsAttachSaveCompletionCallback cb,
+ nsMsgAttachmentHandler *tagData)
+{
+ if (!outputStream || !localFile)
+ return NS_ERROR_INVALID_ARG;
+
+ mOutStream = outputStream;
+ mLocalFile = localFile;
+ mCallback = cb; //JFD: Please, no more callback, use a listener...
+ mTagData = tagData;
+ return NS_OK;
+}
+
+nsresult
+nsURLFetcher::FireURLRequest(nsIURI *aURL, nsIFile *localFile, nsIOutputStream *outputStream,
+ nsAttachSaveCompletionCallback cb, nsMsgAttachmentHandler *tagData)
+{
+ nsresult rv;
+
+ rv = Initialize(localFile, outputStream, cb, tagData);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ //check to see if aURL is a local file or not
+ aURL->SchemeIs("file", &mIsFile);
+
+ // we're about to fire a new url request so make sure the on stop request flag is cleared...
+ mOnStopRequestProcessed = false;
+
+ // let's try uri dispatching...
+ nsCOMPtr<nsIURILoader> pURILoader (do_GetService(NS_URI_LOADER_CONTRACTID));
+ NS_ENSURE_TRUE(pURILoader, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ aURL,
+ nullPrincipal,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // aLoadGroup
+ this); // aCallbacks
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return pURILoader->OpenURI(channel, false, this);
+}
+
+nsresult
+nsURLFetcher::InsertConverter(const char * aContentType)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIStreamConverterService> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIStreamListener> toListener(mConverter);
+ nsCOMPtr<nsIStreamListener> fromListener;
+
+ rv = convServ->AsyncConvertData(aContentType,
+ "*/*",
+ toListener,
+ nullptr,
+ getter_AddRefs(fromListener));
+ if (NS_SUCCEEDED(rv))
+ mConverter = fromListener;
+ }
+
+ return rv;
+}
+
+// web progress listener implementation
+
+NS_IMETHODIMP
+nsURLFetcher::OnProgressChange(nsIWebProgress *aProgress, nsIRequest *aRequest,
+ int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress, int32_t aMaxTotalProgress)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnStateChange(nsIWebProgress *aProgress, nsIRequest *aRequest,
+ uint32_t aStateFlags, nsresult aStatus)
+{
+ // all we care about is the case where an error occurred (as in we were unable to locate the
+ // the url....
+
+ if (NS_FAILED(aStatus))
+ OnStopRequest(aRequest, nullptr, aStatus);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI *aURI,
+ uint32_t aFlags)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnSecurityChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ uint32_t state)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+
+/**
+ * Stream consumer used for handling special content type like multipart/x-mixed-replace
+ */
+
+NS_IMPL_ISUPPORTS(nsURLFetcherStreamConsumer, nsIStreamListener, nsIRequestObserver)
+
+nsURLFetcherStreamConsumer::nsURLFetcherStreamConsumer(nsURLFetcher* urlFetcher) :
+ mURLFetcher(urlFetcher)
+{
+}
+
+nsURLFetcherStreamConsumer::~nsURLFetcherStreamConsumer()
+{
+}
+
+/** nsIRequestObserver methods **/
+
+/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
+NS_IMETHODIMP nsURLFetcherStreamConsumer::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
+{
+ if (!mURLFetcher || !mURLFetcher->mOutStream)
+ return NS_ERROR_FAILURE;
+
+ /* In case of multipart/x-mixed-replace, we need to erase the output file content */
+ if (MsgLowerCaseEqualsLiteral(mURLFetcher->mConverterContentType, MULTIPART_MIXED_REPLACE))
+ {
+ nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(mURLFetcher->mOutStream);
+ if (seekStream)
+ seekStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ mURLFetcher->mTotalWritten = 0;
+ }
+
+ return NS_OK;
+}
+
+/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
+NS_IMETHODIMP nsURLFetcherStreamConsumer::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
+{
+ if (!mURLFetcher)
+ return NS_ERROR_FAILURE;
+
+ // Check the content type!
+ nsAutoCString contentType;
+ nsAutoCString charset;
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+ if(!channel) return NS_ERROR_FAILURE;
+
+ if (NS_SUCCEEDED(channel->GetContentType(contentType)) &&
+ !contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE))
+ {
+ nsAutoCString uriSpec;
+ nsCOMPtr <nsIURI> channelURI;
+ channel->GetURI(getter_AddRefs(channelURI));
+ nsresult rv = channelURI->GetSpec(uriSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (uriSpec.Find("&realtype=message/rfc822") >= 0)
+ mURLFetcher->mContentType = MESSAGE_RFC822;
+ else
+ mURLFetcher->mContentType = contentType;
+ }
+
+ if (NS_SUCCEEDED(channel->GetContentCharset(charset)) && !charset.IsEmpty())
+ {
+ mURLFetcher->mCharset = charset;
+ }
+
+ return NS_OK;
+}
+
+/** nsIStreamListener methods **/
+
+/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
+NS_IMETHODIMP nsURLFetcherStreamConsumer::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, uint64_t sourceOffset, uint32_t count)
+{
+ uint32_t readLen = count;
+ uint32_t wroteIt;
+
+ if (!mURLFetcher)
+ return NS_ERROR_FAILURE;
+
+ if (!mURLFetcher->mOutStream)
+ return NS_ERROR_INVALID_ARG;
+
+ if (mURLFetcher->mBufferSize < count)
+ {
+ PR_FREEIF(mURLFetcher->mBuffer);
+
+ if (count > 0x1000)
+ mURLFetcher->mBufferSize = count;
+ else
+ mURLFetcher->mBufferSize = 0x1000;
+
+ mURLFetcher->mBuffer = (char *)PR_Malloc(mURLFetcher->mBufferSize);
+ if (!mURLFetcher->mBuffer)
+ return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */
+ }
+
+ // read the data from the input stram...
+ nsresult rv = inStr->Read(mURLFetcher->mBuffer, count, &readLen);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // write to the output file...
+ mURLFetcher->mOutStream->Write(mURLFetcher->mBuffer, readLen, &wroteIt);
+
+ if (wroteIt != readLen)
+ return NS_ERROR_FAILURE;
+ else
+ {
+ mURLFetcher->mTotalWritten += wroteIt;
+ return NS_OK;
+ }
+}
diff --git a/mailnews/compose/src/nsURLFetcher.h b/mailnews/compose/src/nsURLFetcher.h
new file mode 100644
index 000000000..d252b014b
--- /dev/null
+++ b/mailnews/compose/src/nsURLFetcher.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsURLFetcher_h_
+#define nsURLFetcher_h_
+
+#include "nsIURLFetcher.h"
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIInputStream.h"
+#include "nsIStreamListener.h"
+
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsCURILoader.h"
+#include "nsIURIContentListener.h"
+#include "nsIWebProgressListener.h"
+#include "nsWeakReference.h"
+#include "nsStringGlue.h"
+
+class nsMsgAttachmentHandler;
+
+class nsURLFetcher : public nsIURLFetcher,
+ public nsIStreamListener,
+ public nsIURIContentListener,
+ public nsIInterfaceRequestor,
+ public nsIWebProgressListener,
+ public nsSupportsWeakReference
+{
+public:
+ nsURLFetcher();
+
+ /* this macro defines QueryInterface, AddRef and Release for this class */
+ NS_DECL_ISUPPORTS
+
+ // Methods for nsIURLFetcher
+ NS_DECL_NSIURLFETCHER
+
+ // Methods for nsIStreamListener
+ NS_DECL_NSISTREAMLISTENER
+
+ // Methods for nsIRequestObserver
+ NS_DECL_NSIREQUESTOBSERVER
+
+ // Methods for nsIURICOntentListener
+ NS_DECL_NSIURICONTENTLISTENER
+
+ // Methods for nsIInterfaceRequestor
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ // Methods for nsIWebProgressListener
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+protected:
+ nsresult InsertConverter(const char * aContentType);
+
+private:
+ virtual ~nsURLFetcher();
+ nsCOMPtr<nsIOutputStream> mOutStream; // the output file stream
+ nsCOMPtr<nsIFile> mLocalFile; // the output file itself
+ nsCOMPtr<nsIStreamListener> mConverter; // the stream converter, if needed
+ nsCString mConverterContentType; // The content type of the converter
+ bool mStillRunning; // Are we still running?
+ int32_t mTotalWritten; // Size counter variable
+ char *mBuffer; // Buffer used for reading the data
+ uint32_t mBufferSize; // Buffer size;
+ nsCString mContentType; // The content type retrieved from the server
+ nsCString mCharset; // The charset retrieved from the server
+ RefPtr<nsMsgAttachmentHandler> mTagData; // Tag data for callback...
+ nsAttachSaveCompletionCallback mCallback; // Callback to call once the file is saved...
+ nsCOMPtr<nsISupports> mLoadCookie; // load cookie used by the uri loader when we fetch the url
+ bool mOnStopRequestProcessed; // used to prevent calling OnStopRequest multiple times
+ bool mIsFile; // This is used to check whether the URI is a local file.
+
+ friend class nsURLFetcherStreamConsumer;
+};
+
+
+/**
+ * Stream consumer used for handling special content type like multipart/x-mixed-replace
+ */
+
+class nsURLFetcherStreamConsumer : public nsIStreamListener
+{
+public:
+ nsURLFetcherStreamConsumer(nsURLFetcher* urlFetcher);
+
+ /* additional members */
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIREQUESTOBSERVER
+
+private:
+ virtual ~nsURLFetcherStreamConsumer();
+ nsURLFetcher* mURLFetcher;
+};
+
+
+#endif /* nsURLFetcher_h_ */