Back to index

lightning-sunbird  0.9+nobinonly
nsMsgMdnGenerator.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Jeff Tsai <jt95070@netscape.net>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "msgCore.h"
00040 #include "nsMsgMdnGenerator.h"
00041 #include "nsImapCore.h"
00042 #include "nsIMsgImapMailFolder.h"
00043 #include "nsMsgMimeCID.h"
00044 #include "nsIMsgAccountManager.h"
00045 #include "nsMsgBaseCID.h"
00046 #include "nsSpecialSystemDirectory.h"
00047 #include "nsCRT.h"
00048 #include "nsMimeTypes.h"
00049 #include "prmem.h"
00050 #include "prprf.h"
00051 #include "nsMsgI18N.h"
00052 #include "nsMailHeaders.h"
00053 #include "nsMsgLocalFolderHdrs.h"
00054 #include "nsNetCID.h"
00055 #include "nsIHttpProtocolHandler.h"
00056 #include "nsIRequest.h"
00057 #include "nsMsgKeyArray.h"
00058 #include "nsISmtpService.h"  // for actually sending the message...
00059 #include "nsMsgCompCID.h"
00060 #include "nsIPrompt.h"
00061 #include "nsIMsgCompUtils.h"
00062 #include "nsIPrefService.h"
00063 #include "nsIPrefBranch.h"
00064 #include "nsIStringBundle.h"
00065 #include "nsNativeCharsetUtils.h"
00066 
00067 #define MDN_NOT_IN_TO_CC          ((int) 0x0001)
00068 #define MDN_OUTSIDE_DOMAIN        ((int) 0x0002)
00069 
00070 #define HEADER_RETURN_PATH          "Return-Path"
00071 #define HEADER_DISPOSITION_NOTIFICATION_TO  "Disposition-Notification-To"
00072 #define HEADER_APPARENTLY_TO        "Apparently-To"
00073 #define HEADER_ORIGINAL_RECIPIENT     "Original-Recipient"
00074 #define HEADER_REPORTING_UA                 "Reporting-UA"
00075 #define HEADER_MDN_GATEWAY                  "MDN-Gateway"
00076 #define HEADER_FINAL_RECIPIENT              "Final-Recipient"
00077 #define HEADER_DISPOSITION                  "Disposition"
00078 #define HEADER_ORIGINAL_MESSAGE_ID          "Original-Message-ID"
00079 #define HEADER_FAILURE                      "Failure"
00080 #define HEADER_ERROR                        "Error"
00081 #define HEADER_WARNING                      "Warning"
00082 #define HEADER_RETURN_RECEIPT_TO            "Return-Receipt-To"
00083 #define HEADER_X_ACCEPT_LANGUAGE      "X-Accept-Language"
00084 
00085 #define PUSH_N_FREE_STRING(p) \
00086   do { if (p) { rv = WriteString(p); PR_smprintf_free(p); p=0; \
00087            if (NS_FAILED(rv)) return rv; } \
00088      else { return NS_ERROR_OUT_OF_MEMORY; } } while (0)
00089 
00090 // String bundle for mdn. Class static.
00091 #define MDN_STRINGBUNDLE_URL "chrome://messenger/locale/msgmdn.properties"
00092 
00093 #if defined(DEBUG_jefft)
00094 #define DEBUG_MDN(s) printf("%s\n", s)
00095 #else
00096 #define DEBUG_MDN(s) 
00097 #endif
00098 
00099 // machine parsible string; should not be localized
00100 char DispositionTypes[7][16] = {
00101     "displayed",
00102     "dispatched",
00103     "processed",
00104     "deleted",
00105     "denied",
00106     "failed",
00107     ""
00108 };
00109 
00110 NS_IMPL_ISUPPORTS2(nsMsgMdnGenerator, nsIMsgMdnGenerator, nsIUrlListener)
00111 
00112 nsMsgMdnGenerator::nsMsgMdnGenerator()
00113 {
00114     m_disposeType = eDisplayed;
00115     m_outputStream = nsnull;
00116     m_reallySendMdn = PR_FALSE;
00117     m_autoSend = PR_FALSE;
00118     m_autoAction = PR_FALSE;
00119     m_mdnEnabled = PR_FALSE;
00120     m_notInToCcOp = eNeverSendOp;
00121     m_outsideDomainOp = eNeverSendOp;
00122     m_otherOp = eNeverSendOp;
00123 }
00124 
00125 nsMsgMdnGenerator::~nsMsgMdnGenerator()
00126 {
00127 }
00128 
00129 nsresult nsMsgMdnGenerator::FormatStringFromName(const PRUnichar *aName, 
00130                                                  const PRUnichar *aString, 
00131                                                  PRUnichar **aResultString)
00132 {
00133     DEBUG_MDN("nsMsgMdnGenerator::FormatStringFromName");
00134     nsresult rv;
00135 
00136     nsCOMPtr<nsIStringBundleService>
00137         bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
00138     NS_ENSURE_SUCCESS(rv,rv);
00139 
00140     nsCOMPtr <nsIStringBundle> bundle;
00141     rv = bundleService->CreateBundle(MDN_STRINGBUNDLE_URL, 
00142                                      getter_AddRefs(bundle));
00143     NS_ENSURE_SUCCESS(rv,rv);
00144 
00145     const PRUnichar *formatStrings[1] = { aString };
00146     rv = bundle->FormatStringFromName(aName,
00147                     formatStrings, 1, aResultString);
00148     NS_ENSURE_SUCCESS(rv,rv);
00149     return rv;
00150 }
00151 
00152 nsresult nsMsgMdnGenerator::GetStringFromName(const PRUnichar *aName,
00153                                                PRUnichar **aResultString)
00154 {
00155     DEBUG_MDN("nsMsgMdnGenerator::GetStringFromName");
00156     nsresult rv;
00157 
00158     nsCOMPtr<nsIStringBundleService>
00159         bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
00160     NS_ENSURE_SUCCESS(rv,rv);
00161 
00162     nsCOMPtr <nsIStringBundle> bundle;
00163     rv = bundleService->CreateBundle(MDN_STRINGBUNDLE_URL, 
00164                                      getter_AddRefs(bundle));
00165     NS_ENSURE_SUCCESS(rv,rv);
00166 
00167     rv = bundle->GetStringFromName(aName, aResultString);
00168     NS_ENSURE_SUCCESS(rv,rv);
00169     return rv;
00170 }
00171 
00172 nsresult nsMsgMdnGenerator::StoreMDNSentFlag(nsIMsgFolder *folder,
00173                                              nsMsgKey key)
00174 {
00175     DEBUG_MDN("nsMsgMdnGenerator::StoreMDNSentFlag");
00176     
00177     // Store the $MDNSent flag if the folder is an Imap Mail Folder
00178     // otherwise, do nothing.
00179     nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder);
00180     if (!imapFolder)
00181       return NS_OK;
00182 
00183     nsMsgKeyArray keyArray;
00184     keyArray.Add(key);
00185     return imapFolder->StoreImapFlags(kImapMsgMDNSentFlag, PR_TRUE,
00186                                keyArray.GetArray(), keyArray.GetSize(), nsnull);
00187 }
00188 
00189 PRBool nsMsgMdnGenerator::ProcessSendMode()
00190 {
00191     DEBUG_MDN("nsMsgMdnGenerator::ProcessSendMode");
00192     PRInt32 miscState = 0;
00193     
00194     if (m_identity)
00195     {
00196         m_identity->GetEmail(getter_Copies(m_email));
00197         if (!m_email)
00198             return m_reallySendMdn;
00199 
00200         const char *accountDomain = strchr(m_email.get(), '@');
00201         if (!accountDomain)
00202             return m_reallySendMdn;
00203 
00204         if (MailAddrMatch(m_email.get(), m_dntRrt.get())) // return address is self, don't send
00205           return PR_FALSE;
00206 
00207         // *** fix me see Bug 132504 for more information
00208         // *** what if the message has been filtered to different account
00209         if (!PL_strcasestr(m_dntRrt, accountDomain))
00210             miscState |= MDN_OUTSIDE_DOMAIN;
00211         if (NotInToOrCc())
00212             miscState |= MDN_NOT_IN_TO_CC;
00213         m_reallySendMdn = PR_TRUE;
00214         // *********
00215         // How are we gona deal with the auto forwarding issues? Some server
00216         // didn't bother to add addition header or modify existing header to
00217         // thev message when forwarding. They simply copy the exact same
00218         // message to another user's mailbox. Some change To: to
00219         // Apparently-To: 
00220         // Unfortunately, there is nothing we can do. It's out of our control.
00221         // *********
00222         // starting from lowest denominator to highest
00223         if (!miscState)
00224         {   // under normal situation: recipent is in to and cc list,
00225             // and the sender is from the same domain
00226             switch (m_otherOp) 
00227             {
00228             default:
00229             case eNeverSendOp:
00230                 m_reallySendMdn = PR_FALSE;
00231                 break;
00232             case eAutoSendOp:
00233                 m_autoSend = PR_TRUE;
00234                 break;
00235             case eAskMeOp:
00236                 m_autoSend = PR_FALSE;
00237                 break;
00238             case eDeniedOp:
00239                 m_autoSend = PR_TRUE;
00240                 m_disposeType = eDenied;
00241                 break;
00242             }
00243         }
00244         else if (miscState == (MDN_OUTSIDE_DOMAIN | MDN_NOT_IN_TO_CC))
00245         {
00246             if (m_outsideDomainOp != m_notInToCcOp)
00247             {
00248                 m_autoSend = PR_FALSE; // ambiguous; always ask user
00249             }
00250             else
00251             {
00252                 switch (m_outsideDomainOp) 
00253                 {
00254                 default:
00255                 case eNeverSendOp:
00256                     m_reallySendMdn = PR_FALSE;
00257                     break;
00258                 case eAutoSendOp:
00259                     m_autoSend = PR_TRUE;
00260                     break;
00261                 case eAskMeOp:
00262                     m_autoSend = PR_FALSE;
00263                     break;
00264                 }
00265             }
00266         }
00267         else if (miscState & MDN_OUTSIDE_DOMAIN)
00268         {
00269             switch (m_outsideDomainOp) 
00270             {
00271             default:
00272             case eNeverSendOp:
00273                 m_reallySendMdn = PR_FALSE;
00274                 break;
00275             case eAutoSendOp:
00276                 m_autoSend = PR_TRUE;
00277                 break;
00278             case eAskMeOp:
00279                 m_autoSend = PR_FALSE;
00280                 break;
00281             }
00282         }
00283         else if (miscState & MDN_NOT_IN_TO_CC)
00284         {
00285             switch (m_notInToCcOp) 
00286             {
00287             default:
00288             case eNeverSendOp:
00289                 m_reallySendMdn = PR_FALSE;
00290                 break;
00291             case eAutoSendOp:
00292                 m_autoSend = PR_TRUE;
00293                 break;
00294             case eAskMeOp:
00295                 m_autoSend = PR_FALSE;
00296                 break;
00297             }
00298         }
00299     }
00300     return m_reallySendMdn;
00301 }
00302 
00303 PRBool nsMsgMdnGenerator::MailAddrMatch(const char *addr1, const char *addr2)
00304 {
00305     // Comparing two email addresses returns true if matched; local/account
00306     // part comparison is case sensitive; domain part comparison is case
00307     // insensitive 
00308     DEBUG_MDN("nsMsgMdnGenerator::MailAddrMatch");
00309     PRBool isMatched = PR_TRUE;
00310     const char *atSign1 = nsnull, *atSign2 = nsnull;
00311     const char *lt = nsnull, *local1 = nsnull, *local2 = nsnull;
00312     const char *end1 = nsnull, *end2 = nsnull;
00313 
00314     if (!addr1 || !addr2)
00315         return PR_FALSE;
00316     
00317     lt = strchr(addr1, '<');
00318     local1 = !lt ? addr1 : lt+1;
00319     lt = strchr(addr2, '<');
00320     local2 = !lt ? addr2 : lt+1;
00321     end1 = strchr(local1, '>');
00322     if (!end1)
00323         end1 = addr1 + strlen(addr1);
00324     end2 = strchr(local2, '>');
00325     if (!end2)
00326         end2 = addr2 + strlen(addr2);
00327     atSign1 = strchr(local1, '@');
00328     atSign2 = strchr(local2, '@');
00329     if (!atSign1 || !atSign2 // ill formed addr spec
00330         || (atSign1 - local1) != (atSign2 - local2))
00331         isMatched = PR_FALSE;
00332     else if (strncmp(local1, local2, (atSign1-local1))) // case sensitive
00333         // compare for local part
00334         isMatched = PR_FALSE;
00335     else if ((end1 - atSign1) != (end2 - atSign2) ||
00336              PL_strncasecmp(atSign1, atSign2, (end1-atSign1))) // case
00337         // insensitive compare for domain part
00338         isMatched = PR_FALSE;
00339     return isMatched;
00340 }
00341 
00342 PRBool nsMsgMdnGenerator::NotInToOrCc()
00343 {
00344     DEBUG_MDN("nsMsgMdnGenerator::NotInToOrCc");
00345     nsXPIDLCString reply_to;
00346     nsXPIDLCString to;
00347     nsXPIDLCString cc;
00348 
00349     m_identity->GetReplyTo(getter_Copies(reply_to));
00350     m_headers->ExtractHeader(HEADER_TO, PR_TRUE, getter_Copies(to));
00351     m_headers->ExtractHeader(HEADER_CC, PR_TRUE, getter_Copies(cc));
00352   
00353   // start with a simple check
00354   if ((to.Length() && PL_strcasestr(to.get(), m_email.get())) || 
00355       (cc.Length() && PL_strcasestr(cc.get(), m_email.get()))) {
00356       return PR_FALSE;
00357   }
00358 
00359   if ((reply_to.Length() && to.Length() && PL_strcasestr(to.get(), reply_to.get())) ||
00360       (reply_to.Length() && cc.Length() && PL_strcasestr(cc.get(), reply_to.get()))) {
00361       return PR_FALSE;
00362   }
00363   return PR_TRUE;
00364 }
00365 
00366 PRBool nsMsgMdnGenerator::ValidateReturnPath()
00367 {
00368     DEBUG_MDN("nsMsgMdnGenerator::ValidateReturnPath");
00369     // ValidateReturnPath applies to Automatic Send Mode only. If we were not
00370     // in auto send mode we simply by passing the check
00371     if (!m_autoSend)
00372         return m_reallySendMdn;
00373     
00374     nsXPIDLCString returnPath;
00375     m_headers->ExtractHeader(HEADER_RETURN_PATH, PR_FALSE,
00376                              getter_Copies(returnPath));
00377     if (!returnPath || !*returnPath)
00378     {
00379         m_autoSend = PR_FALSE;
00380         return m_reallySendMdn;
00381     }
00382     m_autoSend = MailAddrMatch(returnPath, m_dntRrt);
00383     return m_reallySendMdn;
00384 }
00385 
00386 nsresult nsMsgMdnGenerator::CreateMdnMsg()
00387 {
00388     DEBUG_MDN("nsMsgMdnGenerator::CreateMdnMsg");
00389     nsresult rv;
00390     if (!m_autoSend)
00391     {
00392         nsCOMPtr<nsIPrompt> dialog;
00393         rv = m_window->GetPromptDialog(getter_AddRefs(dialog));
00394         if (NS_SUCCEEDED(rv))
00395         {
00396             nsXPIDLString wishToSend;
00397             rv = GetStringFromName(
00398                 NS_LITERAL_STRING("MsgMdnWishToSend").get(),
00399                 getter_Copies(wishToSend));
00400             if (NS_SUCCEEDED(rv))
00401             {
00402                 PRBool bVal = PR_FALSE;
00403                 rv = dialog->Confirm(nsnull, wishToSend, &bVal);
00404                 if (NS_SUCCEEDED(rv))
00405                     m_reallySendMdn = bVal;
00406             }
00407         }
00408     }
00409     if (!m_reallySendMdn)
00410         return NS_OK;
00411 
00412     nsSpecialSystemDirectory
00413         tmpFile(nsSpecialSystemDirectory::OS_TemporaryDirectory); 
00414     tmpFile += "mdnmsg";
00415     tmpFile.MakeUnique();
00416 
00417     rv = NS_NewFileSpecWithSpec(tmpFile, getter_AddRefs(m_fileSpec));
00418 
00419     NS_ASSERTION(NS_SUCCEEDED(rv),"creating mdn: failed to create");
00420     if (NS_FAILED(rv)) 
00421         return NS_OK;
00422 
00423     rv = m_fileSpec->GetOutputStream(getter_AddRefs(m_outputStream));
00424     NS_ASSERTION(NS_SUCCEEDED(rv),"creating mdn: failed to output stream");
00425     if (NS_FAILED(rv)) 
00426         return NS_OK;
00427 
00428     rv = CreateFirstPart();
00429     if (NS_SUCCEEDED(rv))
00430     {
00431         rv = CreateSecondPart();
00432         if (NS_SUCCEEDED(rv))
00433             rv = CreateThirdPart();
00434     }
00435 
00436     if (m_outputStream)
00437     {
00438         m_outputStream->Flush();
00439         m_outputStream->Close();
00440     }
00441     if (m_fileSpec)
00442         m_fileSpec->CloseStream();
00443     if (NS_FAILED(rv))
00444         m_fileSpec->Delete(PR_FALSE);
00445     else
00446         rv = SendMdnMsg();
00447 
00448     return NS_OK;
00449 }
00450 
00451 nsresult nsMsgMdnGenerator::CreateFirstPart()
00452 {
00453     DEBUG_MDN("nsMsgMdnGenerator::CreateFirstPart");
00454     char *convbuf = nsnull, *tmpBuffer = nsnull;
00455     char *parm = nsnull;
00456     nsXPIDLString firstPart1;
00457     nsXPIDLString firstPart2;
00458     nsresult rv = NS_OK;
00459     nsCOMPtr <nsIMsgCompUtils> compUtils;
00460     
00461     if (!m_mimeSeparator)
00462     {
00463       compUtils = do_GetService(NS_MSGCOMPUTILS_CONTRACTID, &rv);
00464       NS_ENSURE_SUCCESS(rv, rv);
00465       rv = compUtils->MimeMakeSeparator("mdn", getter_Copies(m_mimeSeparator));
00466       NS_ENSURE_SUCCESS(rv, rv);
00467     }
00468     if (!m_mimeSeparator)
00469         return NS_ERROR_OUT_OF_MEMORY;
00470 
00471     tmpBuffer = (char *) PR_CALLOC(256);
00472 
00473     if (!tmpBuffer)
00474         return NS_ERROR_OUT_OF_MEMORY;
00475 
00476     PRExplodedTime now;
00477     PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
00478     
00479     int gmtoffset = (now.tm_params.tp_gmt_offset + now.tm_params.tp_dst_offset)
00480         / 60;
00481   /* Use PR_FormatTimeUSEnglish() to format the date in US English format,
00482      then figure out what our local GMT offset is, and append it (since
00483      PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as
00484      per RFC 1123 (superceding RFC 822.)
00485   */
00486     PR_FormatTimeUSEnglish(tmpBuffer, 100,
00487                            "Date: %a, %d %b %Y %H:%M:%S ",
00488                            &now);
00489 
00490     PR_snprintf(tmpBuffer + strlen(tmpBuffer), 100,
00491                 "%c%02d%02d" CRLF,
00492                 (gmtoffset >= 0 ? '+' : '-'),
00493                 ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) / 60),
00494                 ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) % 60));
00495 
00496     rv = WriteString(tmpBuffer);
00497     PR_Free(tmpBuffer);
00498     if (NS_FAILED(rv)) 
00499         return rv;
00500     
00501     PRBool conformToStandard = PR_FALSE;
00502     if (compUtils)
00503       compUtils->GetMsgMimeConformToStandard(&conformToStandard);
00504 
00505     convbuf = nsMsgI18NEncodeMimePartIIStr(
00506         m_email.get(), PR_TRUE, m_charset.get(), 0,
00507         conformToStandard);
00508     parm = PR_smprintf("From: %s" CRLF, convbuf ? convbuf : m_email.get());
00509 
00510     rv = FormatStringFromName(NS_LITERAL_STRING("MsgMdnMsgSentTo").get(), NS_ConvertASCIItoUCS2(m_email).get(),
00511                             getter_Copies(firstPart1));
00512     if (NS_FAILED(rv)) 
00513         return rv;
00514 
00515     PUSH_N_FREE_STRING (parm);
00516     
00517     PR_Free(convbuf);
00518     
00519     if (compUtils)
00520     {
00521       nsXPIDLCString msgId;
00522       rv = compUtils->MsgGenerateMessageId(m_identity, getter_Copies(msgId));
00523       tmpBuffer = PR_smprintf("Message-ID: %s" CRLF, msgId.get());
00524       PUSH_N_FREE_STRING(tmpBuffer);
00525     }
00526 
00527     nsXPIDLString receipt_string;
00528     switch (m_disposeType)
00529     {
00530     case nsIMsgMdnGenerator::eDisplayed:
00531         rv = GetStringFromName(
00532             NS_LITERAL_STRING("MdnDisplayedReceipt").get(),
00533             getter_Copies(receipt_string));
00534         break;
00535     case nsIMsgMdnGenerator::eDispatched:
00536         rv = GetStringFromName(
00537             NS_LITERAL_STRING("MdnDispatchedReceipt").get(),
00538             getter_Copies(receipt_string));
00539         break;
00540     case nsIMsgMdnGenerator::eProcessed:
00541         rv = GetStringFromName(
00542             NS_LITERAL_STRING("MdnProcessedReceipt").get(),
00543             getter_Copies(receipt_string));
00544         break;
00545     case nsIMsgMdnGenerator::eDeleted:
00546         rv = GetStringFromName(
00547             NS_LITERAL_STRING("MdnDeletedReceipt").get(),
00548             getter_Copies(receipt_string));
00549         break;
00550     case nsIMsgMdnGenerator::eDenied:
00551         rv = GetStringFromName(
00552             NS_LITERAL_STRING("MdnDeniedReceipt").get(),
00553             getter_Copies(receipt_string));
00554         break;
00555     case nsIMsgMdnGenerator::eFailed:
00556         rv = GetStringFromName(
00557             NS_LITERAL_STRING("MdnFailedReceipt").get(),
00558             getter_Copies(receipt_string));
00559         break;
00560     default:
00561         rv = NS_ERROR_INVALID_ARG;
00562         break;
00563     }
00564 
00565     if (NS_FAILED(rv)) 
00566         return rv;
00567     
00568     receipt_string.Append(NS_LITERAL_STRING(" - "));
00569         
00570     char * encodedReceiptString = nsMsgI18NEncodeMimePartIIStr(NS_ConvertUCS2toUTF8(receipt_string).get(), PR_FALSE, 
00571                                                                "UTF-8", 0, conformToStandard);
00572    
00573     nsXPIDLCString subject;
00574     m_headers->ExtractHeader(HEADER_SUBJECT, PR_FALSE, getter_Copies(subject));
00575     convbuf = nsMsgI18NEncodeMimePartIIStr(subject.Length() ? subject.get() : "[no subject]", 
00576                                            PR_FALSE, m_charset.get(), 0, conformToStandard);
00577     tmpBuffer = PR_smprintf("Subject: %s%s" CRLF, 
00578                              encodedReceiptString,
00579                             (convbuf ? convbuf : (subject.Length() ? subject.get() : 
00580                               "[no subject]")));
00581 
00582     PUSH_N_FREE_STRING(tmpBuffer);
00583     PR_Free(convbuf);
00584     PR_Free(encodedReceiptString);
00585 
00586     convbuf = nsMsgI18NEncodeMimePartIIStr(m_dntRrt, PR_TRUE, m_charset.get(), 0, conformToStandard);
00587     tmpBuffer = PR_smprintf("To: %s" CRLF, convbuf ? convbuf :
00588                             m_dntRrt.get());
00589     PUSH_N_FREE_STRING(tmpBuffer);
00590 
00591     PR_Free(convbuf);
00592 
00593   // *** This is not in the spec. I am adding this so we could do
00594   // threading
00595     m_headers->ExtractHeader(HEADER_MESSAGE_ID, PR_FALSE,
00596                              getter_Copies(m_messageId));
00597     
00598     if (!m_messageId.IsEmpty())
00599     {
00600       if (*m_messageId.get() == '<')
00601           tmpBuffer = PR_smprintf("References: %s" CRLF, m_messageId.get());
00602       else
00603           tmpBuffer = PR_smprintf("References: <%s>" CRLF, m_messageId.get());
00604       PUSH_N_FREE_STRING(tmpBuffer);
00605     }
00606     tmpBuffer = PR_smprintf("%s" CRLF, "MIME-Version: 1.0");
00607     PUSH_N_FREE_STRING(tmpBuffer);
00608 
00609     tmpBuffer = PR_smprintf("Content-Type: multipart/report; \
00610 report-type=disposition-notification;\r\n\tboundary=\"%s\"" CRLF CRLF,
00611                             m_mimeSeparator.get()); 
00612     PUSH_N_FREE_STRING(tmpBuffer);
00613     
00614     tmpBuffer = PR_smprintf("--%s" CRLF, m_mimeSeparator.get());
00615     PUSH_N_FREE_STRING(tmpBuffer);
00616 
00617     tmpBuffer = PR_smprintf("Content-Type: text/plain; charset=UTF-8" CRLF);
00618     PUSH_N_FREE_STRING(tmpBuffer);
00619 
00620     tmpBuffer = PR_smprintf("Content-Transfer-Encoding: %s" CRLF CRLF,
00621                             ENCODING_8BIT);
00622     PUSH_N_FREE_STRING(tmpBuffer);
00623   
00624     if (!firstPart1.IsEmpty())
00625     {
00626         tmpBuffer = PR_smprintf("%s" CRLF CRLF, NS_ConvertUCS2toUTF8(firstPart1).get());
00627         PUSH_N_FREE_STRING(tmpBuffer);
00628     }
00629 
00630     switch (m_disposeType)
00631     {
00632     case nsIMsgMdnGenerator::eDisplayed:
00633         rv = GetStringFromName(
00634             NS_LITERAL_STRING("MsgMdnDisplayed").get(),
00635             getter_Copies(firstPart2));
00636         break;
00637     case nsIMsgMdnGenerator::eDispatched:
00638         rv = GetStringFromName(
00639             NS_LITERAL_STRING("MsgMdnDisptched").get(),
00640             getter_Copies(firstPart2));
00641         break;
00642     case nsIMsgMdnGenerator::eProcessed:
00643         rv = GetStringFromName(
00644             NS_LITERAL_STRING("MsgMdnProcessed").get(),
00645             getter_Copies(firstPart2));
00646         break;
00647     case nsIMsgMdnGenerator::eDeleted:
00648         rv = GetStringFromName(
00649             NS_LITERAL_STRING("MsgMdnDeleted").get(),
00650             getter_Copies(firstPart2));
00651         break;
00652     case nsIMsgMdnGenerator::eDenied:
00653         rv = GetStringFromName(
00654             NS_LITERAL_STRING("MsgMdnDenied").get(),
00655             getter_Copies(firstPart2));
00656         break;
00657     case nsIMsgMdnGenerator::eFailed:
00658         rv = GetStringFromName(
00659             NS_LITERAL_STRING("MsgMdnFailed").get(),
00660             getter_Copies(firstPart2));
00661         break;
00662     default:
00663         rv = NS_ERROR_INVALID_ARG;
00664         break;
00665     }
00666     
00667     if (NS_FAILED(rv)) 
00668         return rv;
00669     
00670     if (!firstPart2.IsEmpty())
00671     {
00672         tmpBuffer = 
00673             PR_smprintf("%s" CRLF CRLF, 
00674                         NS_ConvertUCS2toUTF8(firstPart2).get()); 
00675         PUSH_N_FREE_STRING(tmpBuffer);
00676     }
00677     
00678     return rv;
00679 }
00680 
00681 nsresult nsMsgMdnGenerator::CreateSecondPart()
00682 {
00683     DEBUG_MDN("nsMsgMdnGenerator::CreateSecondPart");
00684     char *tmpBuffer = nsnull;
00685     char *convbuf = nsnull;
00686     nsresult rv = NS_OK;
00687     nsCOMPtr <nsIMsgCompUtils> compUtils;
00688     PRBool conformToStandard = PR_FALSE;
00689     
00690     tmpBuffer = PR_smprintf("--%s" CRLF, m_mimeSeparator.get());
00691     PUSH_N_FREE_STRING(tmpBuffer);
00692     
00693     tmpBuffer = PR_smprintf("%s" CRLF, "Content-Type: message/disposition-notification; name=\042MDNPart2.txt\042");
00694     PUSH_N_FREE_STRING(tmpBuffer);
00695     
00696     tmpBuffer = PR_smprintf("%s" CRLF, "Content-Disposition: inline");
00697     PUSH_N_FREE_STRING(tmpBuffer);
00698     
00699     tmpBuffer = PR_smprintf("Content-Transfer-Encoding: %s" CRLF CRLF,
00700                             ENCODING_7BIT);
00701     PUSH_N_FREE_STRING(tmpBuffer);
00702     
00703     nsCOMPtr<nsIHttpProtocolHandler> pHTTPHandler = 
00704         do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv); 
00705     if (NS_SUCCEEDED(rv) && pHTTPHandler)
00706     {
00707         nsCAutoString userAgentString;
00708         pHTTPHandler->GetUserAgent(userAgentString);
00709 
00710         if (!userAgentString.IsEmpty())
00711         {
00712             tmpBuffer = PR_smprintf("Reporting-UA: %s" CRLF,
00713                                     userAgentString.get());
00714             PUSH_N_FREE_STRING(tmpBuffer);
00715         }
00716     }
00717 
00718     nsXPIDLCString originalRecipient;
00719     m_headers->ExtractHeader(HEADER_ORIGINAL_RECIPIENT, PR_FALSE,
00720                              getter_Copies(originalRecipient));
00721     
00722     if (originalRecipient && *originalRecipient)
00723     {
00724         tmpBuffer = PR_smprintf("Original-Recipient: %s" CRLF,
00725                                 originalRecipient.get());
00726         PUSH_N_FREE_STRING(tmpBuffer);
00727     }
00728 
00729     compUtils = do_GetService(NS_MSGCOMPUTILS_CONTRACTID, &rv);
00730     if (compUtils)
00731       compUtils->GetMsgMimeConformToStandard(&conformToStandard);
00732 
00733     convbuf = nsMsgI18NEncodeMimePartIIStr(
00734         m_email.get(), PR_TRUE, m_charset.get(), 0,
00735         conformToStandard);
00736     tmpBuffer = PR_smprintf("Final-Recipient: rfc822;%s" CRLF, convbuf ?
00737                             convbuf : m_email.get()); 
00738     PUSH_N_FREE_STRING(tmpBuffer);
00739 
00740     PR_Free (convbuf);
00741 
00742     if (!m_messageId.IsEmpty())
00743     {
00744       if (*m_messageId.get() == '<')
00745           tmpBuffer = PR_smprintf("Original-Message-ID: %s" CRLF, m_messageId.get());
00746       else
00747           tmpBuffer = PR_smprintf("Original-Message-ID: <%s>" CRLF, m_messageId.get());
00748       PUSH_N_FREE_STRING(tmpBuffer);
00749     }
00750     
00751     tmpBuffer = PR_smprintf("Disposition: %s/%s; %s" CRLF CRLF,
00752                             (m_autoAction ? "automatic-action" :
00753                              "manual-action"),
00754                             (m_autoSend ? "MDN-sent-automatically" :
00755                              "MDN-sent-manually"), 
00756                             DispositionTypes[(int) m_disposeType]);
00757     PUSH_N_FREE_STRING(tmpBuffer);
00758   
00759     return rv;
00760 }
00761 
00762 nsresult nsMsgMdnGenerator::CreateThirdPart()
00763 {
00764     DEBUG_MDN("nsMsgMdnGenerator::CreateThirdPart");
00765     char *tmpBuffer = nsnull;
00766     nsresult rv = NS_OK;
00767 
00768     tmpBuffer = PR_smprintf("--%s" CRLF, m_mimeSeparator.get());
00769     PUSH_N_FREE_STRING(tmpBuffer);
00770 
00771     tmpBuffer = PR_smprintf("%s" CRLF, "Content-Type: text/rfc822-headers; name=\042MDNPart3.txt\042");
00772     PUSH_N_FREE_STRING(tmpBuffer);
00773 
00774     tmpBuffer = PR_smprintf("%s" CRLF, "Content-Transfer-Encoding: 7bit");
00775     PUSH_N_FREE_STRING(tmpBuffer);
00776 
00777     tmpBuffer = PR_smprintf("%s" CRLF CRLF, "Content-Disposition: inline");
00778     PUSH_N_FREE_STRING(tmpBuffer);
00779    
00780     rv = OutputAllHeaders();
00781 
00782     if (NS_FAILED(rv)) 
00783         return rv;
00784 
00785     rv = WriteString(CRLF);
00786     if (NS_FAILED(rv)) 
00787         return rv;
00788 
00789     tmpBuffer = PR_smprintf("--%s--" CRLF, m_mimeSeparator.get());
00790     PUSH_N_FREE_STRING(tmpBuffer);
00791 
00792     return rv;
00793 }
00794 
00795 
00796 nsresult nsMsgMdnGenerator::OutputAllHeaders()
00797 { 
00798     DEBUG_MDN("nsMsgMdnGenerator::OutputAllHeaders");
00799     nsXPIDLCString all_headers;
00800     PRInt32 all_headers_size = 0;
00801     nsresult rv = NS_OK;
00802     
00803     rv = m_headers->GetAllHeaders(getter_Copies(all_headers));
00804     if (NS_FAILED(rv)) 
00805         return rv;
00806     all_headers_size = all_headers.Length();
00807     char *buf = (char *) all_headers.get(), 
00808         *buf_end = (char *) all_headers.get()+all_headers_size;
00809     char *start = buf, *end = buf;
00810     PRInt32 count = 0;
00811   
00812     while (buf < buf_end)
00813     {
00814         switch (*buf)
00815         {
00816         case 0:
00817             if (*(buf+1) == nsCRT::LF)
00818             {
00819                 // *buf = nsCRT::CR;
00820                 end = buf;
00821             }
00822             else if (*(buf+1) == 0)
00823             {
00824                 // the case of message id
00825                 *buf = '>';
00826             }
00827             break;
00828         case nsCRT::CR:
00829             end = buf;
00830             *buf = 0;
00831             break;
00832         case nsCRT::LF:
00833             if (buf > start && *(buf-1) == 0)
00834             {
00835                 start = buf + 1;
00836                 end = start;
00837             }
00838             else
00839             {
00840                 end = buf;
00841             }
00842             *buf = 0;
00843             break;
00844         default:
00845             break;
00846         }
00847         buf++;
00848     
00849         if (end > start && *end == 0)
00850         {
00851             // strip out private X-Mozilla-Status header & X-Mozilla-Draft-Info && envelope header
00852             if (!PL_strncasecmp(start, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN)
00853                 || !PL_strncasecmp(start, X_MOZILLA_DRAFT_INFO, X_MOZILLA_DRAFT_INFO_LEN)
00854                 || !PL_strncasecmp(start, "From ", 5))
00855             {
00856                 while ( end < buf_end && 
00857                         (*end == nsCRT::LF || *end == nsCRT::CR || *end == 0))
00858                     end++;
00859                 start = end;
00860             }
00861             else
00862             {
00863                 NS_ASSERTION (*end == 0, "content of end should be null");
00864                 rv = WriteString(start);
00865                 if (NS_FAILED(rv)) 
00866                     return rv;
00867                 rv = WriteString(CRLF);
00868                 while ( end < buf_end && 
00869                         (*end == nsCRT::LF || *end == nsCRT::CR || *end == 0))
00870                     end++;
00871                 start = end;
00872             }
00873             buf = start;
00874         }
00875     }
00876     return count;
00877 }
00878 
00879 nsresult nsMsgMdnGenerator::SendMdnMsg()
00880 {
00881     DEBUG_MDN("nsMsgMdnGenerator::SendMdnMsg");
00882     nsresult rv;
00883     nsCOMPtr<nsISmtpService> smtpService = do_GetService(NS_SMTPSERVICE_CONTRACTID, &rv);
00884     NS_ENSURE_SUCCESS(rv,rv);
00885 
00886     nsCOMPtr<nsIRequest> aRequest;
00887     smtpService->SendMailMessage(m_fileSpec, m_dntRrt, m_identity,
00888                                      nsnull, this, nsnull, nsnull, nsnull,
00889                                      getter_AddRefs(aRequest));
00890     
00891     return NS_OK;
00892 }
00893 
00894 nsresult nsMsgMdnGenerator::WriteString( const char *str )
00895 {
00896   NS_ENSURE_ARG (str);
00897   PRUint32 len = strlen(str);
00898   PRUint32 wLen = 0;
00899   
00900   return m_outputStream->Write(str, len, &wLen);
00901 }
00902 
00903 nsresult nsMsgMdnGenerator::InitAndProcess()
00904 {
00905     DEBUG_MDN("nsMsgMdnGenerator::InitAndProcess");
00906     nsresult rv = m_folder->GetServer(getter_AddRefs(m_server));
00907     nsCOMPtr<nsIMsgAccountManager> accountManager = 
00908         do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
00909     if (accountManager && m_server)
00910     {
00911         if (!m_identity)
00912         {
00913           // check if this is a message delivered to the global inbox,
00914           // in which case we find the originating account's identity.
00915           nsXPIDLCString accountKey;
00916           m_headers->ExtractHeader(HEADER_X_MOZILLA_ACCOUNT_KEY, PR_FALSE,
00917                                getter_Copies(accountKey));
00918           nsCOMPtr <nsIMsgAccount> account;
00919           if (!accountKey.IsEmpty())
00920             accountManager->GetAccount(accountKey, getter_AddRefs(account));
00921           if (account)
00922             account->GetIncomingServer(getter_AddRefs(m_server));
00923 
00924           if (m_server)
00925           {
00926             // Find the correct identity based on the "To:" and "Cc:" header
00927             nsXPIDLCString mailTo;
00928             nsXPIDLCString mailCC;
00929             m_headers->ExtractHeader(HEADER_TO, PR_TRUE, getter_Copies(mailTo));
00930             m_headers->ExtractHeader(HEADER_CC, PR_TRUE, getter_Copies(mailCC));
00931             nsCOMPtr<nsISupportsArray> servIdentities;
00932             accountManager->GetIdentitiesForServer(m_server, getter_AddRefs(servIdentities));
00933             if (servIdentities)
00934             {
00935               nsCOMPtr<nsIMsgIdentity> ident;
00936               nsXPIDLCString identEmail;
00937               PRUint32 count = 0;
00938               servIdentities->Count(&count);
00939               // First check in the "To:" header
00940               for (PRUint32 i = 0; i < count; i++)
00941               {
00942                 rv = servIdentities->QueryElementAt(i, NS_GET_IID(nsIMsgIdentity),getter_AddRefs(ident));
00943                 if (NS_FAILED(rv))
00944                   continue;
00945                 ident->GetEmail(getter_Copies(identEmail));
00946                 if (!mailTo.IsEmpty() && !identEmail.IsEmpty() && mailTo.Find(identEmail, PR_TRUE) != kNotFound)
00947                 {
00948                   m_identity = ident;
00949                   break;
00950                 }
00951               }
00952               // If no match, check the "Cc:" header
00953               if (!m_identity)
00954               {
00955                 for (PRUint32 i = 0; i < count; i++)
00956                 {
00957                   rv = servIdentities->QueryElementAt(i, NS_GET_IID(nsIMsgIdentity),getter_AddRefs(ident));
00958                   if (NS_FAILED(rv))
00959                     continue;
00960                   ident->GetEmail(getter_Copies(identEmail));
00961                   if (!mailCC.IsEmpty() && !identEmail.IsEmpty() && mailCC.Find(identEmail, PR_TRUE) != kNotFound)
00962                   {
00963                     m_identity = ident;
00964                     break;
00965                   }
00966                 }
00967               }
00968             }
00969 
00970             // If no match again, use the first identity
00971             if (!m_identity)
00972               rv = accountManager->GetFirstIdentityForServer(m_server, getter_AddRefs(m_identity));
00973           }
00974         }
00975         NS_ENSURE_SUCCESS(rv,rv);
00976 
00977         if (m_identity)
00978         {
00979             PRBool useCustomPrefs = PR_FALSE;
00980             m_identity->GetBoolAttribute("use_custom_prefs", &useCustomPrefs);
00981             if (useCustomPrefs)
00982             {
00983                 PRBool bVal = PR_FALSE;
00984                 m_server->GetBoolValue("mdn_report_enabled", &bVal);
00985                 m_mdnEnabled = bVal;
00986                 m_server->GetIntValue("mdn_not_in_to_cc", &m_notInToCcOp);
00987                 m_server->GetIntValue("mdn_outside_domain",
00988                                       &m_outsideDomainOp);
00989                 m_server->GetIntValue("mdn_other", &m_otherOp);
00990             }
00991             else
00992             {
00993                 PRBool bVal = PR_FALSE;
00994 
00995                 nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00996                 if (NS_FAILED(rv))
00997                     return rv;
00998 
00999                 if(prefBranch)
01000                 {
01001                     prefBranch->GetBoolPref("mail.mdn.report.enabled",
01002                                            &bVal);
01003                     m_mdnEnabled = bVal;
01004                     prefBranch->GetIntPref("mail.mdn.report.not_in_to_cc",
01005                                            &m_notInToCcOp);
01006                     prefBranch->GetIntPref("mail.mdn.report.outside_domain",
01007                                            &m_outsideDomainOp);
01008                     prefBranch->GetIntPref("mail.mdn.report.other",
01009                                            &m_otherOp);
01010                 }
01011             }
01012         }
01013     }
01014 
01015     rv = m_folder->GetCharset(getter_Copies(m_charset));
01016 
01017     if (m_mdnEnabled)
01018     {
01019         m_headers->ExtractHeader(HEADER_DISPOSITION_NOTIFICATION_TO, PR_FALSE,
01020                                  getter_Copies(m_dntRrt));
01021         if (!m_dntRrt)
01022             m_headers->ExtractHeader(HEADER_RETURN_RECEIPT_TO, PR_FALSE,
01023                                      getter_Copies(m_dntRrt));
01024         if (m_dntRrt && ProcessSendMode() && ValidateReturnPath())
01025             rv = CreateMdnMsg();
01026     }
01027     return NS_OK;
01028 }
01029 
01030 NS_IMETHODIMP nsMsgMdnGenerator::Process(EDisposeType type, 
01031                                          nsIMsgWindow *aWindow,
01032                                          nsIMsgFolder *folder,
01033                                          nsMsgKey key,
01034                                          nsIMimeHeaders *headers,
01035                                          PRBool autoAction)
01036 {
01037     DEBUG_MDN("nsMsgMdnGenerator::Process");
01038     NS_ENSURE_ARG_POINTER(folder);
01039     NS_ENSURE_ARG_POINTER(headers);
01040     NS_ENSURE_ARG_POINTER(aWindow);
01041     NS_ENSURE_TRUE(key != nsMsgKey_None, NS_ERROR_INVALID_ARG);
01042     m_disposeType = type;
01043     m_autoAction = autoAction;
01044     m_window = aWindow;
01045     m_folder = folder;
01046     m_headers = headers;
01047 
01048     nsresult rv = StoreMDNSentFlag(folder, key);
01049     NS_ASSERTION(NS_SUCCEEDED(rv), "StoreMDNSentFlag failed");
01050 
01051     rv = InitAndProcess();
01052     NS_ASSERTION(NS_SUCCEEDED(rv), "InitAndProcess failed");
01053     return NS_OK;
01054 }
01055 
01056 NS_IMETHODIMP nsMsgMdnGenerator::OnStartRunningUrl(nsIURI *url)
01057 {
01058     DEBUG_MDN("nsMsgMdnGenerator::OnStartRunningUrl");
01059     return NS_OK;
01060 }
01061 
01062 NS_IMETHODIMP nsMsgMdnGenerator::OnStopRunningUrl(nsIURI *url, 
01063                                                   nsresult aExitCode)
01064 {
01065     DEBUG_MDN("nsMsgMdnGenerator::OnStopRunningUrl");
01066     m_fileSpec->Delete(PR_FALSE);
01067     return NS_OK;
01068 }