Back to index

lightning-sunbird  0.9+nobinonly
nsMsgAttachmentHandler.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) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037  
00038 #include "nsMsgAttachmentHandler.h"
00039 
00040 #include "nsMsgCopy.h"
00041 #include "nsIPrefService.h"
00042 #include "nsIPrefBranch.h"
00043 #include "nsMsgSend.h"
00044 #include "nsMsgCompUtils.h"
00045 #include "nsMsgEncoders.h"
00046 #include "nsMsgI18N.h"
00047 #include "nsURLFetcher.h"
00048 #include "nsMimeTypes.h"
00049 #include "nsIMsgStringService.h"
00050 #include "nsMsgComposeStringBundle.h"
00051 #include "nsMsgCompCID.h"
00052 #include "nsXPIDLString.h"
00053 #include "nsReadableUtils.h"
00054 #include "nsIMsgMessageService.h"
00055 #include "nsMsgUtils.h"
00056 #include "nsMsgPrompts.h"
00057 #include "nsTextFormatter.h"
00058 #include "nsIPrompt.h"
00059 #include "nsMsgSimulateError.h"
00060 #include "nsITextToSubURI.h"
00061 #include "nsEscape.h"
00062 #include "nsIURL.h"
00063 #include "nsNetCID.h"
00064 #include "nsIMimeStreamConverter.h"
00065 #include "nsMsgMimeCID.h"
00066 #include "nsNetUtil.h"
00067 #include "nsNativeCharsetUtils.h"
00068 
00069 
00071 // Mac Specific Attachment Handling for AppleDouble Encoded Files
00073 #if defined(XP_MAC) || defined(XP_MACOSX)
00074 
00075 #define AD_WORKING_BUFF_SIZE                  8192
00076 
00077 
00078 extern void         MacGetFileType(nsFileSpec *fs, PRBool *useDefault, char **type, char **encoding);
00079 
00080 #include "nsIInternetConfigService.h"
00081 
00082 #if defined(XP_MAC)
00083 #include "MoreFilesExtras.h"
00084 #else
00085 #include "MoreFilesX.h"
00086 #endif /* XP_MAC */
00087 
00088 #endif /* XP_MAC */
00089 
00090 //
00091 // Class implementation...
00092 //
00093 nsMsgAttachmentHandler::nsMsgAttachmentHandler()
00094 {
00095 #if defined(DEBUG_ducarroz)
00096   printf("CREATE nsMsgAttachmentHandler: %x\n", this);
00097 #endif
00098   mMHTMLPart = PR_FALSE;
00099   mPartUserOmissionOverride = PR_FALSE;
00100   mMainBody = PR_FALSE;
00101 
00102   m_charset = nsnull;
00103   m_override_type = nsnull;
00104   m_override_encoding = nsnull;
00105   m_desired_type = nsnull;
00106   m_description = nsnull;
00107   m_encoding = nsnull;
00108   m_real_name = nsnull;
00109   m_encoding = nsnull;
00110   m_content_id = nsnull;
00111   m_already_encoded_p = PR_FALSE;
00112   m_decrypted_p = PR_FALSE;
00113   
00114   // For analyzing the attachment file...
00115   m_file_analyzed = PR_FALSE;
00116   m_ctl_count = 0;
00117   m_null_count = 0;
00118   m_have_cr = m_have_lf = m_have_crlf = 0;
00119   m_prev_char_was_cr = PR_FALSE;
00120   m_current_column = 0;
00121   m_max_column = 0;
00122   m_lines = 0;
00123   m_unprintable_count = 0;
00124   m_highbit_count = 0;
00125 
00126   // Mime encoder...
00127   m_encoder_data = nsnull;
00128 
00129   m_done = PR_FALSE;
00130   m_type = nsnull;
00131   m_type_param = nsnull;
00132   m_size = 0;
00133 
00134   mCompFields = nsnull;   // Message composition fields for the sender
00135   mFileSpec = nsnull;
00136   mURL = nsnull;
00137   mRequest = nsnull;
00138 
00139   m_x_mac_type = nsnull;
00140   m_x_mac_creator = nsnull;
00141 
00142 #if defined(XP_MAC) || defined(XP_MACOSX)
00143   mAppleFileSpec = nsnull;
00144 #endif
00145 
00146   mDeleteFile = PR_FALSE;
00147   m_uri = nsnull;
00148 }
00149 
00150 nsMsgAttachmentHandler::~nsMsgAttachmentHandler()
00151 {
00152 #if defined(DEBUG_ducarroz)
00153   printf("DISPOSE nsMsgAttachmentHandler: %x\n", this);
00154 #endif
00155 #if defined(XP_MAC) || defined(XP_MACOSX)
00156   if (mAppleFileSpec)
00157     delete mAppleFileSpec;
00158 #endif
00159 
00160   if (mFileSpec && mDeleteFile)
00161     mFileSpec->Delete(PR_FALSE);
00162 
00163   delete mFileSpec;
00164   mFileSpec=nsnull;
00165 
00166   PR_Free(m_charset);
00167   PR_Free(m_type);
00168   PR_Free(m_type_param);
00169   PR_Free(m_content_id);
00170   PR_Free(m_desired_type);
00171   PR_Free(m_encoding);
00172   PR_Free(m_override_type);
00173   PR_Free(m_description);
00174   PR_Free(m_real_name);
00175   PR_Free(m_override_encoding);
00176   PR_Free(m_x_mac_type);
00177   PR_Free(m_x_mac_creator);
00178   PR_Free(m_uri);
00179 }
00180 
00181 void
00182 nsMsgAttachmentHandler::AnalyzeDataChunk(const char *chunk, PRInt32 length)
00183 {
00184   unsigned char *s = (unsigned char *) chunk;
00185   unsigned char *end = s + length;
00186   for (; s < end; s++)
00187   {
00188     if (*s > 126)
00189     {
00190       m_highbit_count++;
00191       m_unprintable_count++;
00192     }
00193     else if (*s < ' ' && *s != '\t' && *s != nsCRT::CR && *s != nsCRT::LF)
00194     {
00195       m_unprintable_count++;
00196       m_ctl_count++;
00197       if (*s == 0)
00198         m_null_count++;
00199     }
00200 
00201     if (*s == nsCRT::CR || *s == nsCRT::LF)
00202     {
00203       if (*s == nsCRT::CR)
00204       {
00205         if (m_prev_char_was_cr)
00206           m_have_cr = 1;
00207         else
00208           m_prev_char_was_cr = PR_TRUE;
00209       }
00210       else
00211       {
00212         if (m_prev_char_was_cr)
00213         {
00214           if (m_current_column == 0)
00215           {
00216             m_have_crlf = 1;
00217             m_lines--;
00218           }
00219           else
00220             m_have_cr = m_have_lf = 1;
00221           m_prev_char_was_cr = PR_FALSE;
00222         }
00223         else
00224           m_have_lf = 1;
00225       }
00226       if (m_max_column < m_current_column)
00227         m_max_column = m_current_column;
00228       m_current_column = 0;
00229       m_lines++;
00230     }
00231     else
00232     {
00233       m_current_column++;
00234     }
00235   }
00236 }
00237 
00238 void
00239 nsMsgAttachmentHandler::AnalyzeSnarfedFile(void)
00240 {
00241   char chunk[1024];
00242   PRInt32 numRead = 0;
00243 
00244   if (m_file_analyzed)
00245     return;
00246 
00247   if (mFileSpec)
00248   {
00249     m_size = mFileSpec->GetFileSize();
00250     nsInputFileStream fileHdl(*mFileSpec, PR_RDONLY, 0);
00251     if (fileHdl.is_open())
00252     {
00253       do
00254       {
00255         numRead = fileHdl.read(chunk, sizeof(chunk));
00256         if (numRead > 0)
00257           AnalyzeDataChunk(chunk, numRead);
00258       }
00259       while (numRead > 0);
00260       if (m_prev_char_was_cr)
00261         m_have_cr = 1;
00262 
00263       fileHdl.close();
00264       m_file_analyzed = PR_TRUE;
00265     }
00266   }
00267 }
00268 
00269 //
00270 // Given a content-type and some info about the contents of the document,
00271 // decide what encoding it should have.
00272 //
00273 int
00274 nsMsgAttachmentHandler::PickEncoding(const char *charset, nsIMsgSend *mime_delivery_state)
00275 {
00276   nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00277   
00278   // use the boolean so we only have to test for uuencode vs base64 once
00279   PRBool needsB64 = PR_FALSE;
00280   PRBool forceB64 = PR_FALSE;
00281   
00282   if (m_already_encoded_p)
00283     goto DONE;
00284   
00285   AnalyzeSnarfedFile();
00286 
00287   /* Allow users to override our percentage-wise guess on whether
00288   the file is text or binary */
00289   if (pPrefBranch) 
00290     pPrefBranch->GetBoolPref ("mail.file_attach_binary", &forceB64);
00291   
00292   if (!mMainBody && (forceB64 || mime_type_requires_b64_p (m_type) ||
00293     m_have_cr+m_have_lf+m_have_crlf != 1 || m_current_column != 0))
00294   {
00295   /* If the content-type is "image/" or something else known to be binary
00296   or several flavors of newlines are present or last line is incomplete,
00297   always use base64 (so that we don't get confused by newline
00298   conversions.)
00299      */
00300     needsB64 = PR_TRUE;
00301   }
00302   else
00303   {
00304   /* Otherwise, we need to pick an encoding based on the contents of
00305   the document.
00306      */
00307     
00308     PRBool encode_p;
00309     PRBool force_p = PR_FALSE;
00310     
00311     /*
00312       force quoted-printable if the sender does not allow
00313       conversion to 7bit
00314     */
00315     if (mCompFields) {
00316       if (mCompFields->GetForceMsgEncoding())
00317         force_p = PR_TRUE;
00318     }
00319     else if (mime_delivery_state) {
00320       if (((nsMsgComposeAndSend *)mime_delivery_state)->mCompFields->GetForceMsgEncoding())
00321         force_p = PR_TRUE;
00322     }
00323     
00324     if (force_p || (m_max_column > 900))
00325       encode_p = PR_TRUE;
00326     else if (UseQuotedPrintable() && m_unprintable_count)
00327       encode_p = PR_TRUE;
00328     
00329       else if (m_null_count)  /* If there are nulls, we must always encode,
00330         because sendmail will blow up. */
00331         encode_p = PR_TRUE;
00332       else
00333         encode_p = PR_FALSE;
00334       
00335         /* MIME requires a special case that these types never be encoded.
00336       */
00337       if (!PL_strncasecmp (m_type, "message", 7) ||
00338         !PL_strncasecmp (m_type, "multipart", 9))
00339       {
00340         encode_p = PR_FALSE;
00341         if (m_desired_type && !PL_strcasecmp (m_desired_type, TEXT_PLAIN))
00342         {
00343           PR_Free (m_desired_type);
00344           m_desired_type = 0;
00345         }
00346       }
00347 
00348       // If the Mail charset is multibyte, we force it to use Base64 for attachments.
00349       if ((!mMainBody && charset && nsMsgI18Nmultibyte_charset(charset)) &&
00350         ((PL_strcasecmp(m_type, TEXT_HTML) == 0) ||
00351         (PL_strcasecmp(m_type, TEXT_MDL) == 0) ||
00352         (PL_strcasecmp(m_type, TEXT_PLAIN) == 0) ||
00353         (PL_strcasecmp(m_type, TEXT_RICHTEXT) == 0) ||
00354         (PL_strcasecmp(m_type, TEXT_ENRICHED) == 0) ||
00355         (PL_strcasecmp(m_type, TEXT_VCARD) == 0) ||
00356         (PL_strcasecmp(m_type, APPLICATION_DIRECTORY) == 0) || /* text/x-vcard synonym */
00357         (PL_strcasecmp(m_type, TEXT_CSS) == 0) ||
00358         (PL_strcasecmp(m_type, TEXT_JSSS) == 0)))
00359       {
00360         needsB64 = PR_TRUE;
00361       }
00362       else if (charset && nsMsgI18Nstateful_charset(charset)) 
00363       {
00364         PR_FREEIF(m_encoding);
00365         m_encoding = PL_strdup (ENCODING_7BIT);
00366       }
00367       else if (encode_p &&
00368         m_unprintable_count > (m_size / 10))
00369         /* If the document contains more than 10% unprintable characters,
00370         then that seems like a good candidate for base64 instead of
00371         quoted-printable.
00372         */
00373         needsB64 = PR_TRUE;
00374       else if (encode_p) {
00375         PR_FREEIF(m_encoding);
00376         m_encoding = PL_strdup (ENCODING_QUOTED_PRINTABLE);
00377       }
00378       else if (m_highbit_count > 0) {
00379         PR_FREEIF(m_encoding);
00380         m_encoding = PL_strdup (ENCODING_8BIT);
00381       }
00382       else {
00383         PR_FREEIF(m_encoding);
00384         m_encoding = PL_strdup (ENCODING_7BIT);
00385       }
00386   }
00387   
00388   if (needsB64)
00389   {
00390     //
00391     // We might have to uuencode instead of base64 the binary data.
00392     //
00393     PR_FREEIF(m_encoding);
00394     if (UseUUEncode_p())
00395       m_encoding = PL_strdup (ENCODING_UUENCODE);
00396     else
00397       m_encoding = PL_strdup (ENCODING_BASE64);
00398   }
00399   
00400   /* Now that we've picked an encoding, initialize the filter.
00401   */
00402   NS_ASSERTION(!m_encoder_data, "not-null m_encoder_data");
00403   if (!PL_strcasecmp(m_encoding, ENCODING_BASE64))
00404   {
00405     m_encoder_data = MIME_B64EncoderInit(mime_encoder_output_fn,
00406       mime_delivery_state);
00407     if (!m_encoder_data) return NS_ERROR_OUT_OF_MEMORY;
00408   }
00409   else if (!PL_strcasecmp(m_encoding, ENCODING_UUENCODE))
00410   {
00411     char        *tailName = NULL;
00412     nsXPIDLCString turl;
00413     
00414     if (mURL)
00415     {
00416       mURL->GetSpec(turl);
00417       
00418       tailName = PL_strrchr(turl, '/');
00419       if (tailName) 
00420       {
00421         char * tmp = tailName;
00422         tailName = PL_strdup(tailName+1);
00423         PR_FREEIF(tmp);
00424       }
00425     }
00426     
00427     if (mURL && !tailName)
00428     {
00429       tailName = PL_strrchr(turl, '/');
00430       if (tailName) 
00431       {
00432         char * tmp = tailName;
00433         tailName = PL_strdup(tailName+1);
00434         PR_FREEIF(tmp);
00435       }
00436     }
00437 
00438     m_encoder_data = MIME_UUEncoderInit((char *)(tailName ? tailName : ""),
00439       mime_encoder_output_fn,
00440       mime_delivery_state);
00441     PR_FREEIF(tailName);
00442     if (!m_encoder_data) return NS_ERROR_OUT_OF_MEMORY;
00443   }
00444   else if (!PL_strcasecmp(m_encoding, ENCODING_QUOTED_PRINTABLE))
00445   {
00446     m_encoder_data = MIME_QPEncoderInit(mime_encoder_output_fn,
00447       mime_delivery_state);
00448     if (!m_encoder_data) return NS_ERROR_OUT_OF_MEMORY;
00449   }
00450   else
00451   {
00452     m_encoder_data = 0;
00453   }  
00454   
00455   /* Do some cleanup for documents with unknown content type.
00456     There are two issues: how they look to MIME users, and how they look to
00457     non-MIME users.
00458     
00459       If the user attaches a "README" file, which has unknown type because it
00460       has no extension, we still need to send it with no encoding, so that it
00461       is readable to non-MIME users.
00462       
00463         But if the user attaches some random binary file, then base64 encoding
00464         will have been chosen for it (above), and in this case, it won't be
00465         immediately readable by non-MIME users.  However, if we type it as
00466         text/plain instead of application/octet-stream, it will show up inline
00467         in a MIME viewer, which will probably be ugly, and may possibly have
00468         bad charset things happen as well.
00469         
00470           So, the heuristic we use is, if the type is unknown, then the type is
00471           set to application/octet-stream for data which needs base64 (binary data)
00472           and is set to text/plain for data which didn't need base64 (unencoded or
00473           lightly encoded data.)
00474   */
00475 DONE:
00476   if (!m_type || !*m_type || !PL_strcasecmp(m_type, UNKNOWN_CONTENT_TYPE))
00477   {
00478     PR_FREEIF(m_type);
00479     if (m_already_encoded_p)
00480       m_type = PL_strdup (APPLICATION_OCTET_STREAM);
00481     else if (m_encoding &&
00482          (!PL_strcasecmp(m_encoding, ENCODING_BASE64) ||
00483          !PL_strcasecmp(m_encoding, ENCODING_UUENCODE)))
00484          m_type = PL_strdup (APPLICATION_OCTET_STREAM);
00485     else
00486       m_type = PL_strdup (TEXT_PLAIN);
00487   }
00488   return 0;
00489 }
00490 
00491 static nsresult
00492 FetcherURLDoneCallback(nsresult aStatus, 
00493                        const char *aContentType,
00494                        const char *aCharset,
00495                        PRInt32 totalSize, 
00496                        const PRUnichar* aMsg, void *tagData)
00497 {
00498   nsMsgAttachmentHandler *ma = (nsMsgAttachmentHandler *) tagData;
00499   NS_ASSERTION(ma != nsnull, "not-null mime attachment");
00500 
00501   if (ma != nsnull)
00502   {
00503     ma->m_size = totalSize;
00504     if (aContentType)
00505     {
00506 #if defined(XP_MAC) || defined(XP_MACOSX)
00507       //Do not change the type if we are dealing with an apple double file
00508       if (!ma->mAppleFileSpec)
00509 #else
00510         // can't send appledouble on non-macs
00511         if (strcmp(aContentType, "multipart/appledouble")) 
00512 #endif
00513       {
00514         PR_FREEIF(ma->m_type);
00515         ma->m_type = PL_strdup(aContentType);
00516       }
00517     }
00518 
00519     if (aCharset)
00520     {
00521       PR_FREEIF(ma->m_charset);
00522       ma->m_charset = PL_strdup(aCharset);
00523     }
00524 
00525     return ma->UrlExit(aStatus, aMsg);
00526   }
00527   else
00528     return NS_OK;
00529 }
00530 
00531 nsresult 
00532 nsMsgAttachmentHandler::SnarfMsgAttachment(nsMsgCompFields *compFields)
00533 {
00534   nsresult rv = NS_ERROR_INVALID_ARG;
00535   nsCOMPtr <nsIMsgMessageService> messageService;
00536 
00537   if (PL_strcasestr(m_uri, "-message:"))
00538   {
00539     mFileSpec = nsMsgCreateTempFileSpec("nsmail.tmp");
00540     mDeleteFile = PR_TRUE;
00541     mCompFields = compFields;
00542     PR_FREEIF(m_type);
00543     m_type = PL_strdup(MESSAGE_RFC822);
00544     PR_FREEIF(m_override_type);
00545     m_override_type = PL_strdup(MESSAGE_RFC822);
00546     if (!mFileSpec) 
00547     {
00548       rv = NS_ERROR_FAILURE;
00549       goto done;
00550     }
00551 
00552     nsCOMPtr<nsILocalFile> localFile;
00553     nsCOMPtr<nsIOutputStream> outputStream;
00554     NS_FileSpecToIFile(mFileSpec, getter_AddRefs(localFile));
00555     rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), localFile, -1, 00600);
00556     if (NS_FAILED(rv) || !outputStream || CHECK_SIMULATED_ERROR(SIMULATED_SEND_ERROR_3))
00557     {
00558       if (m_mime_delivery_state)
00559       {
00560         nsCOMPtr<nsIMsgSendReport> sendReport;
00561         m_mime_delivery_state->GetSendReport(getter_AddRefs(sendReport));
00562         if (sendReport)
00563         {
00564           nsAutoString error_msg;
00565           nsAutoString path;
00566           NS_CopyNativeToUnicode(
00567             nsDependentCString(mFileSpec->GetNativePathCString()), path);
00568           nsMsgBuildErrorMessageByID(NS_MSG_UNABLE_TO_OPEN_TMP_FILE, error_msg, &path, nsnull);
00569           sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), PR_FALSE);
00570         }
00571       }
00572       rv =  NS_MSG_UNABLE_TO_OPEN_TMP_FILE;
00573       goto done;
00574     }
00575     mOutFile = do_QueryInterface(outputStream);
00576     
00577     nsCOMPtr<nsIURLFetcher> fetcher = do_CreateInstance(NS_URLFETCHER_CONTRACTID, &rv);
00578     if (NS_FAILED(rv) || !fetcher)
00579     {
00580       if (NS_SUCCEEDED(rv))
00581         rv =  NS_ERROR_UNEXPECTED;
00582       goto done;
00583     }
00584 
00585     rv = fetcher->Initialize(localFile, mOutFile, FetcherURLDoneCallback, this);
00586     rv = GetMessageServiceFromURI(m_uri, getter_AddRefs(messageService));
00587     if (NS_SUCCEEDED(rv) && messageService)
00588     {
00589       nsCAutoString uri(m_uri);
00590       uri += (uri.FindChar('?') == kNotFound) ? "?" : "&";
00591       uri.Append("fetchCompleteMessage=true");
00592       nsCOMPtr<nsIStreamListener> strListener;
00593       fetcher->QueryInterface(NS_GET_IID(nsIStreamListener), getter_AddRefs(strListener));
00594 
00595       // initialize a new stream converter, that uses the strListener as its input
00596       // obtain the input stream listener from the new converter,
00597       // and pass the converter's input stream listener to DisplayMessage
00598 
00599       m_mime_parser = do_CreateInstance(NS_MAILNEWS_MIME_STREAM_CONVERTER_CONTRACTID, &rv);
00600       if (NS_FAILED(rv))
00601         goto done;
00602 
00603       // Set us as the output stream for HTML data from libmime...
00604       nsCOMPtr<nsIMimeStreamConverter> mimeConverter = do_QueryInterface(m_mime_parser);
00605       if (mimeConverter)
00606       {
00607         mimeConverter->SetMimeOutputType(nsMimeOutput::nsMimeMessageDecrypt);
00608         mimeConverter->SetForwardInline(PR_FALSE);
00609         mimeConverter->SetIdentity(nsnull);
00610         mimeConverter->SetOriginalMsgURI(nsnull);
00611       }
00612 
00613       nsCOMPtr<nsIStreamListener> convertedListener = do_QueryInterface(m_mime_parser, &rv);
00614       if (NS_FAILED(rv))
00615         goto done;
00616 
00617       nsCOMPtr<nsIURI> aURL;
00618       rv = messageService->GetUrlForUri(uri.get(), getter_AddRefs(aURL), nsnull);
00619       if (aURL)
00620         aURL->SetSpec(nsDependentCString(uri.get()));
00621 
00622       rv = NS_NewInputStreamChannel(getter_AddRefs(m_converter_channel), aURL, nsnull);
00623       if (NS_FAILED(rv))
00624         goto done;
00625 
00626       rv = m_mime_parser->AsyncConvertData(
00627                   "message/rfc822",
00628                   "message/rfc822",
00629                   strListener, m_converter_channel);
00630       if (NS_FAILED(rv))
00631         goto done;
00632 
00633       rv = messageService->DisplayMessage(uri.get(), convertedListener, nsnull, nsnull, nsnull, nsnull);
00634     }
00635   }
00636 done:
00637   if (NS_FAILED(rv))
00638   {
00639       if (mOutFile)
00640       {
00641         mOutFile->Close();
00642         mOutFile = nsnull;
00643       }
00644 
00645       if (mFileSpec)
00646       {
00647         mFileSpec->Delete(PR_FALSE);
00648         delete mFileSpec;
00649         mFileSpec = nsnull;
00650       }
00651   }
00652 
00653   return rv;
00654 }
00655 
00656 #if defined(XP_MAC) || defined(XP_MACOSX)
00657 PRBool nsMsgAttachmentHandler::HasResourceFork(FSSpec *fsSpec)
00658 {
00659   FSRef fsRef;
00660   if (::FSpMakeFSRef(fsSpec, &fsRef) == noErr)
00661   {
00662     FSCatalogInfo catalogInfo;
00663     OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes, &catalogInfo, nsnull, nsnull, nsnull);
00664     return (err == noErr && catalogInfo.rsrcLogicalSize != 0);
00665   }
00666   return PR_FALSE;
00667 }
00668 #endif
00669 
00670 nsresult
00671 nsMsgAttachmentHandler::SnarfAttachment(nsMsgCompFields *compFields)
00672 {
00673   nsresult      status = 0;
00674   nsXPIDLCString url_string;
00675 
00676   NS_ASSERTION (! m_done, "Already done");
00677 
00678   if (!mURL)
00679     return SnarfMsgAttachment(compFields);
00680 
00681   mCompFields = compFields;
00682 
00683   // First, get as file spec and create the stream for the
00684   // temp file where we will save this data
00685     mFileSpec = nsMsgCreateTempFileSpec("nsmail.tmp");
00686   if (! mFileSpec )
00687     return (NS_ERROR_FAILURE);
00688   mDeleteFile = PR_TRUE;
00689 
00690   nsCOMPtr<nsILocalFile> localFile;
00691   nsCOMPtr<nsIOutputStream> outputStream;
00692   NS_FileSpecToIFile(mFileSpec, getter_AddRefs(localFile));
00693   status = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), localFile, -1, 00600);
00694   if (NS_FAILED(status) || !outputStream || CHECK_SIMULATED_ERROR(SIMULATED_SEND_ERROR_3))
00695   {
00696     if (m_mime_delivery_state)
00697     {
00698       nsCOMPtr<nsIMsgSendReport> sendReport;
00699       m_mime_delivery_state->GetSendReport(getter_AddRefs(sendReport));
00700       if (sendReport)
00701       {
00702         nsAutoString error_msg;
00703         nsAutoString path;
00704         NS_CopyNativeToUnicode(
00705           nsDependentCString(mFileSpec->GetNativePathCString()), path);
00706         nsMsgBuildErrorMessageByID(NS_MSG_UNABLE_TO_OPEN_TMP_FILE, error_msg, &path, nsnull);
00707         sendReport->SetMessage(nsIMsgSendReport::process_Current, error_msg.get(), PR_FALSE);
00708       }
00709     }
00710     mFileSpec->Delete(PR_FALSE);
00711     delete mFileSpec;
00712     mFileSpec = nsnull;
00713     return NS_MSG_UNABLE_TO_OPEN_TMP_FILE; 
00714   }
00715   mOutFile = do_QueryInterface(outputStream);
00716 
00717   mURL->GetSpec(url_string);
00718 
00719 #if defined(XP_MAC) || defined(XP_MACOSX)
00720   if ( !m_bogus_attachment && nsMsgIsLocalFile(url_string))
00721   {
00722     // convert the apple file to AppleDouble first, and then patch the
00723     // address in the url.
00724     char *src_filename = nsMsgGetLocalFileFromURL (url_string);
00725     if (!src_filename)
00726       return NS_ERROR_OUT_OF_MEMORY;
00727 
00728     // Unescape the name before making FSSpec
00729     nsCAutoString escapedFilename(src_filename);
00730     nsUnescape(escapedFilename.BeginWriting());
00731 
00732     //We need to retrieve the file type and creator...
00733     nsFileSpec scr_fileSpec(escapedFilename.get());
00734     FSSpec fsSpec;
00735 #if defined(XP_MAC)
00736     fsSpec = scr_fileSpec.GetFSSpec();
00737 #elif defined(XP_MACOSX)
00738     Boolean isDir;
00739     FSPathMakeFSSpec((UInt8 *)escapedFilename.get(), &fsSpec, &isDir);
00740 #endif
00741     FInfo info;
00742     if (FSpGetFInfo (&fsSpec, &info) == noErr)
00743     {
00744       char filetype[32];
00745       PR_snprintf(filetype, sizeof(filetype), "%X", info.fdType);
00746       PR_FREEIF(m_x_mac_type);
00747       m_x_mac_type = PL_strdup(filetype);
00748 
00749       PR_snprintf(filetype, sizeof(filetype), "%X", info.fdCreator);
00750       PR_FREEIF(m_x_mac_creator);
00751       m_x_mac_creator = PL_strdup(filetype);
00752     }
00753 
00754     PRBool sendResourceFork = PR_TRUE;
00755     PRBool icGaveNeededInfo = PR_FALSE;
00756     nsCOMPtr<nsIInternetConfigService> icService (do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID));
00757     if (icService)
00758     {
00759       PRInt32 icFlags;
00760       // be sure to look up by extension first (so pass in PR_TRUE). See Bug #229855
00761       nsresult rv = icService->GetFileMappingFlags(&fsSpec, PR_TRUE, &icFlags);
00762       if (NS_SUCCEEDED(rv) && icFlags != -1 && !(icFlags & nsIInternetConfigService::eIICMapFlag_NotOutgoingMask))
00763       {
00764         sendResourceFork = (icFlags & nsIInternetConfigService::eIICMapFlag_ResourceForkMask);
00765 
00766         if (sendResourceFork)
00767         {
00768           // before deciding to send the resource fork along the data fork, check if we have one,
00769           // else don't need to use apple double.
00770 #if defined(XP_MAC) || defined(XP_MACOSX)
00771           sendResourceFork = HasResourceFork(&fsSpec);
00772 #endif
00773         }
00774         
00775         icGaveNeededInfo = PR_TRUE;
00776       }
00777     }
00778     
00779     if (! icGaveNeededInfo)
00780     {
00781       // If InternetConfig cannot help us, then just try our best...
00782       // first check if we have a resource fork
00783       sendResourceFork = HasResourceFork(&fsSpec);
00784 
00785       // then, if we have a resource fork, check the filename extension, maybe we don't need the resource fork!
00786       if (sendResourceFork)
00787       {
00788         nsCOMPtr<nsIURL> fileUrl(do_CreateInstance(NS_STANDARDURL_CONTRACTID));
00789         if (fileUrl)
00790         {
00791           nsresult rv = fileUrl->SetSpec(url_string);
00792           if (NS_SUCCEEDED(rv))
00793           {
00794             nsCAutoString ext;
00795             rv = fileUrl->GetFileExtension(ext);
00796             if (NS_SUCCEEDED(rv) && !ext.IsEmpty())
00797             {
00798               sendResourceFork =
00799                  PL_strcasecmp(ext.get(), "TXT") &&
00800                  PL_strcasecmp(ext.get(), "JPG") &&
00801                  PL_strcasecmp(ext.get(), "GIF") &&
00802                  PL_strcasecmp(ext.get(), "TIF") &&
00803                  PL_strcasecmp(ext.get(), "HTM") &&
00804                  PL_strcasecmp(ext.get(), "HTML") &&
00805                  PL_strcasecmp(ext.get(), "ART") &&
00806                  PL_strcasecmp(ext.get(), "XUL") &&
00807                  PL_strcasecmp(ext.get(), "XML") &&
00808                  PL_strcasecmp(ext.get(), "CSS") &&
00809                  PL_strcasecmp(ext.get(), "JS");
00810             }
00811           }
00812         }
00813       }
00814     }
00815 
00816     // Only use appledouble if we aren't uuencoding.
00817     if( sendResourceFork && (! UseUUEncode_p()) )
00818     {
00819       char                *separator;
00820       nsInputFileStream   *myInputFile = new nsInputFileStream(scr_fileSpec);
00821 
00822       if ((!myInputFile) || (!myInputFile->is_open()))
00823         return NS_ERROR_OUT_OF_MEMORY;
00824 
00825       separator = mime_make_separator("ad");
00826       if (!separator)
00827       {
00828         delete myInputFile;
00829         PR_FREEIF(src_filename);
00830         return NS_ERROR_OUT_OF_MEMORY;
00831       }
00832             
00833       mAppleFileSpec = nsMsgCreateTempFileSpec("appledouble");
00834       if (!mAppleFileSpec) 
00835       {
00836         delete myInputFile;
00837         PR_FREEIF(separator);
00838         PR_FREEIF(src_filename);
00839         return NS_ERROR_OUT_OF_MEMORY;
00840       }
00841 
00842       //
00843       // RICHIE_MAC - ok, here's the deal, we have a file that we need
00844       // to encode in appledouble encoding for the resource fork and put that
00845       // into the mAppleFileSpec location. Then, we need to patch the new file 
00846       // spec into the array and send this as part of the 2 part appledouble/mime
00847       // encoded mime part. 
00848       // 
00849       AppleDoubleEncodeObject     *obj = new (AppleDoubleEncodeObject);
00850       if (obj == NULL) 
00851       {
00852         delete mAppleFileSpec;
00853         PR_FREEIF(src_filename);
00854         PR_FREEIF(separator);
00855         return NS_ERROR_OUT_OF_MEMORY;
00856       }
00857    
00858       obj->fileStream = new nsIOFileStream(*mAppleFileSpec, (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE));
00859       if ( (!obj->fileStream) || (!obj->fileStream->is_open()) )
00860       {
00861         delete myInputFile;
00862         PR_FREEIF(src_filename);
00863         PR_FREEIF(separator);
00864         delete obj;
00865         return NS_ERROR_OUT_OF_MEMORY;
00866       }
00867 
00868       PRInt32     bSize = AD_WORKING_BUFF_SIZE;
00869 
00870       char  *working_buff = nsnull;
00871       while (!working_buff && (bSize >= 512))
00872       {
00873         working_buff = (char *)PR_CALLOC(bSize);
00874         if (!working_buff)
00875           bSize /= 2;
00876       }
00877 
00878       if (!working_buff)
00879       {
00880         PR_FREEIF(src_filename);
00881         PR_FREEIF(separator);
00882         delete obj;
00883         return NS_ERROR_OUT_OF_MEMORY;
00884       }
00885 
00886       obj->buff = working_buff;
00887       obj->s_buff = bSize;  
00888 
00889       //
00890       //  Setup all the need information on the apple double encoder.
00891       //
00892       ap_encode_init(&(obj->ap_encode_obj), escapedFilename.get(), separator);
00893 
00894       PRInt32 count;
00895 
00896       status = noErr;
00897       m_size = 0;
00898       while (status == noErr)
00899       {      
00900         status = ap_encode_next(&(obj->ap_encode_obj), obj->buff, bSize, &count);
00901         if (status == noErr || status == errDone)
00902         {
00903           //
00904           // we got the encode data, so call the next stream to write it to the disk.
00905           //
00906           if (obj->fileStream->write(obj->buff, count) != count)
00907             status = NS_MSG_ERROR_WRITING_FILE;
00908         }
00909       } 
00910       
00911       delete myInputFile;  
00912       ap_encode_end(&(obj->ap_encode_obj), (status >= 0)); // if this is true, ok, false abort
00913       if (obj->fileStream)
00914         obj->fileStream->close();
00915 
00916       PR_FREEIF(obj->buff);               /* free the working buff.   */
00917       PR_FREEIF(obj);
00918 
00919       char *newURLSpec = nsMsgPlatformFileToURL(*mAppleFileSpec);
00920 
00921       if (!newURLSpec) 
00922       {
00923         PR_FREEIF(src_filename);
00924         PR_FREEIF(separator);
00925         return NS_ERROR_OUT_OF_MEMORY;
00926       }
00927 
00928       if (NS_FAILED(nsMsgNewURL(getter_AddRefs(mURL), newURLSpec)))
00929       {
00930         PR_FREEIF(src_filename);
00931         PR_FREEIF(separator);
00932         PR_FREEIF(newURLSpec);
00933         return NS_ERROR_OUT_OF_MEMORY;
00934       }
00935   
00936       PR_FREEIF(newURLSpec);
00937       
00938       // Now after conversion, also patch the types.
00939       char        tmp[128];
00940       PR_snprintf(tmp, sizeof(tmp), MULTIPART_APPLEDOUBLE ";\r\n boundary=\"%s\"", separator);
00941       PR_FREEIF(separator);
00942       PR_FREEIF (m_type);
00943       m_type = PL_strdup(tmp);
00944     }
00945     else
00946     {
00947       if ( sendResourceFork )
00948       {
00949         // The only time we want to send just the data fork of a two-fork
00950         // Mac file is if uuencoding has been requested.
00951         NS_ASSERTION(UseUUEncode_p(), "not UseUUEncode_p");
00952 
00953         // For now, just do the encoding, but in the old world we would ask the
00954         // user about doing this conversion
00955         printf("...we could ask the user about this conversion, but for now, nahh..\n");
00956       }
00957 
00958       PRBool    useDefault;
00959       char      *macType, *macEncoding;
00960       if (m_type == NULL || !PL_strcasecmp (m_type, TEXT_PLAIN))
00961       {
00962 # define TEXT_TYPE  0x54455854  /* the characters 'T' 'E' 'X' 'T' */
00963 # define text_TYPE  0x74657874  /* the characters 't' 'e' 'x' 't' */
00964 
00965         if (info.fdType != TEXT_TYPE && info.fdType != text_TYPE)
00966         {
00967           MacGetFileType(&scr_fileSpec, &useDefault, &macType, &macEncoding);
00968           PR_FREEIF(m_type);
00969           m_type = macType;
00970         }
00971       }
00972       // don't bother to set the types if we failed in getting the file info.
00973     }
00974 
00975     PR_FREEIF(src_filename);
00976   }
00977 #endif /* XP_MAC */
00978 
00979   //
00980   // Ok, here we are, we need to fire the URL off and get the data
00981   // in the temp file
00982   //
00983   // Create a fetcher for the URL attachment...
00984 
00985   nsresult rv;
00986   nsCOMPtr<nsIURLFetcher> fetcher = do_CreateInstance(NS_URLFETCHER_CONTRACTID, &rv);
00987   if (NS_FAILED(rv) || !fetcher)
00988   {
00989     if (NS_SUCCEEDED(rv))
00990       return NS_ERROR_UNEXPECTED;
00991     else
00992       return rv;
00993   }
00994 
00995   return fetcher->FireURLRequest(mURL, localFile, mOutFile, FetcherURLDoneCallback, this);
00996 }
00997 
00998 nsresult
00999 nsMsgAttachmentHandler::LoadDataFromFile(nsFileSpec& fSpec, nsString &sigData, PRBool charsetConversion)
01000 {
01001   PRInt32       readSize;
01002   char          *readBuf;
01003 
01004   nsInputFileStream tempFile(fSpec);
01005   if (!tempFile.is_open())
01006     return NS_MSG_ERROR_WRITING_FILE;        
01007   
01008   readSize = fSpec.GetFileSize();
01009   readBuf = (char *)PR_Malloc(readSize + 1);
01010   if (!readBuf)
01011     return NS_ERROR_OUT_OF_MEMORY;
01012   memset(readBuf, 0, readSize + 1);
01013 
01014   readSize = tempFile.read(readBuf, readSize);
01015   tempFile.close();
01016 
01017   if (charsetConversion)
01018   {
01019     if (NS_FAILED(ConvertToUnicode(m_charset, nsDependentCString(readBuf), sigData)))
01020       CopyASCIItoUTF16(readBuf, sigData);
01021   }
01022   else
01023     CopyASCIItoUTF16(readBuf, sigData);
01024 
01025   PR_FREEIF(readBuf);
01026   return NS_OK;
01027 }
01028 
01029 nsresult
01030 nsMsgAttachmentHandler::Abort()
01031 {
01032   NS_ASSERTION(m_mime_delivery_state != nsnull, "not-null m_mime_delivery_state");
01033 
01034   if (m_done)
01035     return NS_OK;
01036 
01037   if (mRequest)
01038     return mRequest->Cancel(NS_ERROR_ABORT);
01039   else
01040     if (m_mime_delivery_state)
01041     {
01042       m_mime_delivery_state->SetStatus(NS_ERROR_ABORT);
01043       m_mime_delivery_state->NotifyListenerOnStopSending(nsnull, NS_ERROR_ABORT, 0, nsnull);
01044     }
01045 
01046   return NS_OK;
01047 
01048 }
01049 
01050 nsresult
01051 nsMsgAttachmentHandler::UrlExit(nsresult status, const PRUnichar* aMsg)
01052 {
01053   NS_ASSERTION(m_mime_delivery_state != nsnull, "not-null m_mime_delivery_state");
01054 
01055   // Close the file, but don't delete the disk file (or the file spec.) 
01056   if (mOutFile)
01057   {
01058     mOutFile->Close();
01059     mOutFile = nsnull;
01060   }
01061   
01062   mRequest = nsnull;
01063 
01064   // First things first, we are now going to see if this is an HTML
01065   // Doc and if it is, we need to see if we can determine the charset 
01066   // for this part by sniffing the HTML file.
01067   // This is needed only when the charset is not set already.
01068   // (e.g. a charset may be specified in HTTP header)
01069   //
01070   if ( (m_type) &&  (*m_type) &&
01071        (!m_charset || !(*m_charset)) ) 
01072   {
01073     if (PL_strcasecmp(m_type, TEXT_HTML) == 0)
01074     {
01075       char *tmpCharset = (char *)nsMsgI18NParseMetaCharset(mFileSpec);
01076       if (tmpCharset[0] != '\0')
01077       {
01078         PR_FREEIF(m_charset);
01079         m_charset = PL_strdup(tmpCharset);
01080       }
01081     }
01082   }
01083 
01084   nsresult mimeDeliveryStatus;
01085   m_mime_delivery_state->GetStatus(&mimeDeliveryStatus);
01086   
01087   if (mimeDeliveryStatus == NS_ERROR_ABORT)
01088     status = NS_ERROR_ABORT;
01089  
01090   if (NS_FAILED(status) && status != NS_ERROR_ABORT && NS_SUCCEEDED(mimeDeliveryStatus))
01091   {
01092     // At this point, we should probably ask a question to the user 
01093     // if we should continue without this attachment.
01094     //
01095     PRBool            keepOnGoing = PR_TRUE;
01096     nsXPIDLCString    turl;
01097     nsXPIDLString     msg;
01098     PRUnichar         *printfString = nsnull;
01099     nsCOMPtr<nsIMsgStringService> composebundle (do_GetService(NS_MSG_COMPOSESTRINGSERVICE_CONTRACTID));
01100 
01101     nsMsgDeliverMode mode = nsIMsgSend::nsMsgDeliverNow;
01102     m_mime_delivery_state->GetDeliveryMode(&mode);
01103     if (mode == nsIMsgSend::nsMsgSaveAsDraft || mode == nsIMsgSend::nsMsgSaveAsTemplate)
01104       composebundle->GetStringByID(NS_MSG_FAILURE_ON_OBJ_EMBED_WHILE_SAVING, getter_Copies(msg));
01105     else
01106       composebundle->GetStringByID(NS_MSG_FAILURE_ON_OBJ_EMBED_WHILE_SENDING, getter_Copies(msg));
01107  
01108     if (m_real_name && *m_real_name)
01109       printfString = nsTextFormatter::smprintf(msg, m_real_name);
01110     else
01111     if (NS_SUCCEEDED(mURL->GetSpec(turl)) && (turl))
01112       {
01113         nsCAutoString unescapeUrl(turl);
01114         nsUnescape(unescapeUrl.BeginWriting());
01115         if (unescapeUrl.IsEmpty())
01116           printfString = nsTextFormatter::smprintf(msg, turl.get());
01117         else
01118           printfString = nsTextFormatter::smprintf(msg, unescapeUrl.get());
01119       }
01120     else
01121       printfString = nsTextFormatter::smprintf(msg, "?");
01122 
01123     nsCOMPtr<nsIPrompt> aPrompt;
01124     if (m_mime_delivery_state)
01125       m_mime_delivery_state->GetDefaultPrompt(getter_AddRefs(aPrompt));
01126     nsMsgAskBooleanQuestionByString(aPrompt, printfString, &keepOnGoing);
01127     PR_FREEIF(printfString);
01128 
01129     if (keepOnGoing)
01130     {
01131       status = 0;
01132       m_bogus_attachment = PR_TRUE; //That will cause this attachment to be ignored.
01133     }
01134     else
01135     {
01136       status = NS_ERROR_ABORT;
01137       m_mime_delivery_state->SetStatus(status);
01138       nsresult ignoreMe;
01139       m_mime_delivery_state->Fail(status, nsnull, &ignoreMe);
01140       m_mime_delivery_state->NotifyListenerOnStopSending(nsnull, status, 0, nsnull);
01141       SetMimeDeliveryState(nsnull);
01142       return status;
01143     }
01144   }
01145 
01146   m_done = PR_TRUE;
01147 
01148   //
01149   // Ok, now that we have the file here on disk, we need to see if there was 
01150   // a need to do conversion to plain text...if so, the magic happens here,
01151   // otherwise, just move on to other attachments...
01152   //
01153   if (NS_SUCCEEDED(status) && m_type && PL_strcasecmp(m_type, TEXT_PLAIN) ) 
01154   {
01155     if (m_desired_type && !PL_strcasecmp(m_desired_type, TEXT_PLAIN) )
01156     {
01157       //
01158       // Conversion to plain text desired.
01159       //
01160       PRInt32       width = 72;
01161       nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
01162       if (pPrefBranch)
01163         pPrefBranch->GetIntPref("mailnews.wraplength", &width);
01164       // Let sanity reign!
01165       if (width == 0) 
01166         width = 72;
01167       else if (width < 10) 
01168         width = 10;
01169       else if (width > 30000) 
01170         width = 30000;
01171 
01172       //
01173       // Now use the converter service here to do the right 
01174       // thing and convert this data to plain text for us!
01175       //
01176       nsAutoString      conData;
01177 
01178       if (NS_SUCCEEDED(LoadDataFromFile(*mFileSpec, conData, PR_TRUE)))
01179       {
01180         if (NS_SUCCEEDED(ConvertBufToPlainText(conData, UseFormatFlowed(m_charset))))
01181         {
01182           if (mDeleteFile)
01183           mFileSpec->Delete(PR_FALSE);
01184 
01185           nsOutputFileStream tempfile(*mFileSpec, PR_WRONLY | PR_CREATE_FILE, 00600);
01186           if (tempfile.is_open()) 
01187           {
01188             nsCAutoString tData;
01189             if (NS_FAILED(ConvertFromUnicode(m_charset, conData, tData)))
01190               LossyCopyUTF16toASCII(conData, tData);
01191             if (!tData.IsEmpty())
01192             {
01193               (void) tempfile.write(tData.get(), tData.Length());
01194             }
01195             tempfile.close();
01196           }
01197         }
01198       }
01199 
01200       PR_FREEIF(m_type);
01201       m_type = m_desired_type;
01202       m_desired_type = nsnull;
01203       PR_FREEIF(m_encoding);
01204       m_encoding = nsnull;
01205     }
01206   }
01207 
01208   PRUint32 pendingAttachmentCount = 0;
01209   m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
01210   NS_ASSERTION (pendingAttachmentCount > 0, "no more pending attachment");
01211   
01212   m_mime_delivery_state->SetPendingAttachmentCount(pendingAttachmentCount - 1);
01213 
01214   PRBool processAttachmentsSynchronously = PR_FALSE;
01215   m_mime_delivery_state->GetProcessAttachmentsSynchronously(&processAttachmentsSynchronously);
01216   if (NS_SUCCEEDED(status) && processAttachmentsSynchronously)
01217   {
01218     /* Find the next attachment which has not yet been loaded,
01219      if any, and start it going.
01220      */
01221     PRUint32 i;
01222     nsMsgAttachmentHandler *next = 0;
01223     nsMsgAttachmentHandler *attachments = nsnull;
01224     PRUint32 attachmentCount = 0;
01225     
01226     m_mime_delivery_state->GetAttachmentCount(&attachmentCount);
01227     if (attachmentCount)
01228       m_mime_delivery_state->GetAttachmentHandlers(&attachments);
01229       
01230     for (i = 0; i < attachmentCount; i++)
01231     {
01232       if (!attachments[i].m_done)
01233       {
01234         next = &attachments[i];
01235         //
01236         // rhp: We need to get a little more understanding to failed URL 
01237         // requests. So, at this point if most of next is NULL, then we
01238         // should just mark it fetched and move on! We probably ignored
01239         // this earlier on in the send process.
01240         //
01241         if ( (!next->mURL) && (!next->m_uri) )
01242         {
01243           attachments[i].m_done = PR_TRUE;
01244           m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
01245           m_mime_delivery_state->SetPendingAttachmentCount(pendingAttachmentCount - 1);
01246           next->mPartUserOmissionOverride = PR_TRUE;
01247           next = nsnull;
01248           continue;
01249         }
01250 
01251         break;
01252       }
01253     }
01254 
01255     if (next)
01256     {
01257       int status = next->SnarfAttachment(mCompFields);
01258       if (NS_FAILED(status))
01259       {
01260         nsresult ignoreMe;
01261         m_mime_delivery_state->Fail(status, nsnull, &ignoreMe);
01262         m_mime_delivery_state->NotifyListenerOnStopSending(nsnull, status, 0, nsnull);
01263         SetMimeDeliveryState(nsnull);
01264         return NS_ERROR_UNEXPECTED;
01265       }
01266     }
01267   }
01268 
01269   m_mime_delivery_state->GetPendingAttachmentCount(&pendingAttachmentCount);
01270   if (pendingAttachmentCount == 0)
01271   {
01272     // If this is the last attachment, then either complete the
01273     // delivery (if successful) or report the error by calling
01274     // the exit routine and terminating the delivery.
01275     if (NS_FAILED(status))
01276     {
01277       nsresult ignoreMe;
01278       m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
01279       m_mime_delivery_state->NotifyListenerOnStopSending(nsnull, status, aMsg, nsnull);
01280       SetMimeDeliveryState(nsnull);
01281       return NS_ERROR_UNEXPECTED;
01282     }
01283     else
01284     {
01285       status = m_mime_delivery_state->GatherMimeAttachments ();
01286       if (NS_FAILED(status))
01287       {
01288         nsresult ignoreMe;
01289         m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
01290         m_mime_delivery_state->NotifyListenerOnStopSending(nsnull, status, aMsg, nsnull);
01291         SetMimeDeliveryState(nsnull);
01292         return NS_ERROR_UNEXPECTED;
01293       }
01294     }
01295   }
01296   else
01297   {
01298     // If this is not the last attachment, but it got an error,
01299     // then report that error and continue 
01300     if (NS_FAILED(status))
01301     {
01302       nsresult ignoreMe;
01303       m_mime_delivery_state->Fail(status, aMsg, &ignoreMe);
01304     }
01305   }
01306 
01307   SetMimeDeliveryState(nsnull);
01308   return NS_OK;
01309 }
01310 
01311 PRBool 
01312 nsMsgAttachmentHandler::UseUUEncode_p(void)
01313 {
01314   if (mCompFields)
01315     return mCompFields->GetUuEncodeAttachments();
01316   else
01317     return PR_FALSE;
01318 }
01319 
01320 nsresult
01321 nsMsgAttachmentHandler::GetMimeDeliveryState(nsIMsgSend** _retval)
01322 {
01323   NS_ENSURE_ARG(_retval);
01324   *_retval = m_mime_delivery_state;
01325   NS_IF_ADDREF(*_retval);
01326   return NS_OK;
01327 }
01328 
01329 nsresult
01330 nsMsgAttachmentHandler::SetMimeDeliveryState(nsIMsgSend* mime_delivery_state)
01331 {
01332   /*
01333     Because setting m_mime_delivery_state to null could destroy ourself as
01334     m_mime_delivery_state it's our parent, we need to protect ourself against
01335     that!
01336 
01337     This extra comptr is necessary,
01338     see bug http://bugzilla.mozilla.org/show_bug.cgi?id=78967
01339   */
01340   nsCOMPtr<nsIMsgSend> temp = m_mime_delivery_state; /* Should lock our parent until the end of the function */
01341   m_mime_delivery_state = mime_delivery_state;
01342   return NS_OK;
01343 }