/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org Code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nscore.h" #include "prthread.h" #include "nsString.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsCOMPtr.h" #include "nsIFileSpec.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsIIOService.h" #include "nsIURI.h" #include "nsIProxyObjectManager.h" #include "nsProxiedService.h" #include "nsMsgI18N.h" #include "nsNativeCharsetUtils.h" #include "nsMsgBaseCID.h" #include "nsMsgCompCID.h" #include "nsIMsgCompose.h" #include "nsIMsgCompFields.h" #include "nsIMsgSend.h" #include "nsIMsgMailSession.h" #include "nsIMsgAccountManager.h" #include "nsNetCID.h" #include "nsOutlookCompose.h" #include "OutlookDebugLog.h" #include "nsMimeTypes.h" #include "nsMsgUtils.h" static NS_DEFINE_CID( kMsgSendCID, NS_MSGSEND_CID); static NS_DEFINE_CID( kMsgCompFieldsCID, NS_MSGCOMPFIELDS_CID); static NS_DEFINE_CID( kIOServiceCID, NS_IOSERVICE_CID); static NS_DEFINE_CID( kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); // We need to do some calculations to set these numbers to something reasonable! // Unless of course, CreateAndSendMessage will NEVER EVER leave us in the lurch #define kHungCount 100000 #define kHungAbortCount 1000 #ifdef IMPORT_DEBUG static char *p_test_headers = "Received: from netppl.fi (IDENT:monitor@get.freebsd.because.microsoftsucks.net [209.3.31.115])\n\ by mail4.sirius.com (8.9.1/8.9.1) with SMTP id PAA27232;\n\ Mon, 17 May 1999 15:27:43 -0700 (PDT)\n\ Message-ID: \n\ From: \"adsales@qualityservice.com\" \n\ Subject: Re: Your College Diploma (36822)\n\ Date: Mon, 17 May 1999 15:09:29 -0400 (EDT)\n\ MIME-Version: 1.0\n\ Content-Type: TEXT/PLAIN; charset=\"US-ASCII\"\n\ Content-Transfer-Encoding: 7bit\n\ X-UIDL: 19990517.152941\n\ Status: RO"; static char *p_test_body = "Hello world?\n\ "; #else #define p_test_headers nsnull #define p_test_body nsnull #endif #define kWhitespace "\b\t\r\n " // First off, a listener class OutlookSendListener : public nsIMsgSendListener { public: OutlookSendListener() { m_done = PR_FALSE; m_location = nsnull; } virtual ~OutlookSendListener() { NS_IF_RELEASE( m_location); } // nsISupports interface NS_DECL_ISUPPORTS /* void OnStartSending (in string aMsgID, in PRUint32 aMsgSize); */ NS_IMETHOD OnStartSending(const char *aMsgID, PRUint32 aMsgSize) {return NS_OK;} /* void OnProgress (in string aMsgID, in PRUint32 aProgress, in PRUint32 aProgressMax); */ NS_IMETHOD OnProgress(const char *aMsgID, PRUint32 aProgress, PRUint32 aProgressMax) {return NS_OK;} /* void OnStatus (in string aMsgID, in wstring aMsg); */ NS_IMETHOD OnStatus(const char *aMsgID, const PRUnichar *aMsg) {return NS_OK;} /* void OnStopSending (in string aMsgID, in nsresult aStatus, in wstring aMsg, in nsIFileSpec returnFileSpec); */ NS_IMETHOD OnStopSending(const char *aMsgID, nsresult aStatus, const PRUnichar *aMsg, nsIFileSpec *returnFileSpec) { m_done = PR_TRUE; m_location = returnFileSpec; NS_IF_ADDREF( m_location); return NS_OK; } /* void OnSendNotPerformed */ NS_IMETHOD OnSendNotPerformed(const char *aMsgID, nsresult aStatus) {return NS_OK;} /* void OnGetDraftFolderURI (); */ NS_IMETHOD OnGetDraftFolderURI(const char *aFolderURI) {return NS_OK;} static nsresult CreateSendListener( nsIMsgSendListener **ppListener); void Reset() { m_done = PR_FALSE; NS_IF_RELEASE( m_location); m_location = nsnull;} public: PRBool m_done; nsIFileSpec * m_location; }; NS_IMPL_THREADSAFE_ISUPPORTS1( OutlookSendListener, nsIMsgSendListener) nsresult OutlookSendListener::CreateSendListener( nsIMsgSendListener **ppListener) { NS_PRECONDITION(ppListener != nsnull, "null ptr"); if (! ppListener) return NS_ERROR_NULL_POINTER; *ppListener = new OutlookSendListener(); if (! *ppListener) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*ppListener); return NS_OK; } ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// nsOutlookCompose::nsOutlookCompose() { m_pIOService = nsnull; m_pAttachments = nsnull; m_pListener = nsnull; m_pMsgSend = nsnull; m_pSendProxy = nsnull; m_pMsgFields = nsnull; m_pIdentity = nsnull; #ifdef IMPORT_DEBUG m_Headers = p_test_headers; m_Body = p_test_body; #endif m_readHeaders.m_convertCRs = PR_TRUE; } nsOutlookCompose::~nsOutlookCompose() { NS_IF_RELEASE( m_pSendProxy); NS_IF_RELEASE( m_pIOService); NS_IF_RELEASE( m_pMsgSend); NS_IF_RELEASE( m_pListener); NS_IF_RELEASE( m_pMsgFields); if (m_pIdentity) { nsresult rv = m_pIdentity->ClearAllValues(); NS_ASSERTION(NS_SUCCEEDED(rv),"failed to clear values"); if (NS_FAILED(rv)) return; NS_WITH_PROXIED_SERVICE(nsIMsgAccountManager, accMgr, NS_MSGACCOUNTMANAGER_CONTRACTID, NS_UI_THREAD_EVENTQ, &rv); NS_ASSERTION(NS_SUCCEEDED(rv) && accMgr,"failed to get account manager"); if (NS_FAILED(rv) || !accMgr) return; rv = accMgr->RemoveIdentity(m_pIdentity); NS_ASSERTION(NS_SUCCEEDED(rv),"failed to remove identity"); if (NS_FAILED(rv)) return; NS_RELEASE(m_pIdentity); } } nsresult nsOutlookCompose::CreateIdentity( void) { if (m_pIdentity) return( NS_OK); nsresult rv; NS_WITH_PROXIED_SERVICE(nsIMsgAccountManager, accMgr, NS_MSGACCOUNTMANAGER_CONTRACTID, NS_UI_THREAD_EVENTQ, &rv); if (NS_FAILED(rv)) return( rv); rv = accMgr->CreateIdentity( &m_pIdentity); nsString name; name.AssignLiteral("Import Identity"); if (m_pIdentity) { m_pIdentity->SetFullName( name.get()); m_pIdentity->SetIdentityName( name.get()); m_pIdentity->SetEmail( "import@import.service"); } return( rv); } nsresult nsOutlookCompose::CreateComponents( void) { nsresult rv = NS_OK; if (!m_pIOService) { IMPORT_LOG0( "Creating nsIOService\n"); NS_WITH_PROXIED_SERVICE(nsIIOService, service, kIOServiceCID, NS_UI_THREAD_EVENTQ, &rv); if (NS_FAILED(rv)) return( rv); m_pIOService = service; NS_IF_ADDREF( m_pIOService); } NS_IF_RELEASE( m_pMsgFields); if (!m_pMsgSend) { rv = CallCreateInstance( kMsgSendCID, &m_pMsgSend); if (NS_SUCCEEDED( rv) && m_pMsgSend) { nsCOMPtr proxyMgr = do_GetService(kProxyObjectManagerCID, &rv); if (NS_SUCCEEDED(rv)) { rv = proxyMgr->GetProxyForObject( NS_UI_THREAD_EVENTQ, NS_GET_IID(nsIMsgSend), m_pMsgSend, PROXY_SYNC, (void **)&m_pSendProxy); if (NS_FAILED( rv)) { m_pSendProxy = nsnull; } } if (NS_FAILED( rv)) { NS_RELEASE( m_pMsgSend); m_pMsgSend = nsnull; } } } if (!m_pListener && NS_SUCCEEDED( rv)) { rv = OutlookSendListener::CreateSendListener( &m_pListener); } if (NS_SUCCEEDED(rv) && m_pMsgSend) { rv = CallCreateInstance( kMsgCompFieldsCID, &m_pMsgFields); if (NS_SUCCEEDED(rv) && m_pMsgFields) { // IMPORT_LOG0( "nsOutlookCompose - CreateComponents succeeded\n"); m_pMsgFields->SetForcePlainText( PR_FALSE); return( NS_OK); } } return( NS_ERROR_FAILURE); } void nsOutlookCompose::GetNthHeader( const char *pData, PRInt32 dataLen, PRInt32 n, nsCString& header, nsCString& val, PRBool unwrap) { header.Truncate(); val.Truncate(); if (!pData) return; PRInt32 index = 0; PRInt32 len; PRInt32 start = 0; const char *pChar = pData; const char *pStart; if (n == 0) { pStart = pChar; len = 0; while ((start < dataLen) && (*pChar != ':')) { start++; len++; pChar++; } header.Append( pStart, len); header.Trim( kWhitespace); start++; pChar++; } else { while (start < dataLen) { if ((*pChar != ' ') && (*pChar != 9)) { if (n == index) { pStart = pChar; len = 0; while ((start < dataLen) && (*pChar != ':')) { start++; len++; pChar++; } header.Append( pStart, len); header.Trim( kWhitespace); start++; pChar++; break; } else index++; } while ((start < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) { start++; pChar++; } while ((start < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) { start++; pChar++; } } } if (start >= dataLen) return; PRInt32 lineEnd; PRInt32 end = start; while (end < dataLen) { while ((end < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) { end++; pChar++; } if (end > start) { val.Append( pData + start, end - start); } lineEnd = end; pStart = pChar; while ((end < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) { end++; pChar++; } start = end; while ((end < dataLen) && ((*pChar == ' ') || (*pChar == '\t'))) { end++; pChar++; } if (start == end) break; if (unwrap) val.Append( ' '); else { val.Append( pStart, end - lineEnd); } start = end; } val.Trim( kWhitespace); } void nsOutlookCompose::GetHeaderValue( const char *pData, PRInt32 dataLen, const char *pHeader, nsCString& val, PRBool unwrap) { val.Truncate(); if (!pData) return; PRInt32 start = 0; PRInt32 len = strlen( pHeader); const char *pChar = pData; if (!nsCRT::strncasecmp( pHeader, pData, len)) { start = len; } else { while (start < dataLen) { while ((start < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) { start++; pChar++; } while ((start < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) { start++; pChar++; } if ((start < dataLen) && !nsCRT::strncasecmp( pChar, pHeader, len)) break; } if (start < dataLen) start += len; } if (start >= dataLen) return; PRInt32 end = start; PRInt32 lineEnd; const char * pStart; pChar = pData + start; while (end < dataLen) { while ((end < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) { end++; pChar++; } if (end > start) { val.Append( pData + start, end - start); } lineEnd = end; pStart = pChar; while ((end < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) { end++; pChar++; } start = end; while ((end < dataLen) && ((*pChar == ' ') || (*pChar == '\t'))) { end++; pChar++; } if (start == end) break; if (unwrap) val.Append( ' '); else { val.Append( pStart, end - lineEnd); } start = end; } val.Trim( kWhitespace); } void nsOutlookCompose::ExtractCharset( nsString& str) { nsString tStr; PRInt32 idx = str.Find( "charset=", PR_TRUE); if (idx != -1) { idx += 8; str.Right( tStr, str.Length() - idx); idx = tStr.FindChar( ';'); if (idx != -1) tStr.Left( str, idx); else str = tStr; str.Trim( kWhitespace); if ((str.CharAt( 0) == '"') && (str.Length() > 2)) { str.Mid( tStr, 1, str.Length() - 2); str = tStr; str.Trim( kWhitespace); } } else str.Truncate(); } void nsOutlookCompose::ExtractType( nsString& str) { nsString tStr; PRInt32 idx = str.FindChar( ';'); if (idx != -1) { str.Left( tStr, idx); str = tStr; } str.Trim( kWhitespace); if ((str.CharAt( 0) == '"') && (str.Length() > 2)) { str.Mid( tStr, 1, str.Length() - 2); str = tStr; str.Trim( kWhitespace); } // if multipart then ignore it since no outlook message body is ever // valid multipart! if (str.Length() > 10) { str.Left( tStr, 10); if (tStr.LowerCaseEqualsLiteral("multipart/")) str.Truncate(); } } void nsOutlookCompose::CleanUpAttach( nsMsgAttachedFile *a, PRInt32 count) { for (PRInt32 i = 0; i < count; i++) { a[i].orig_url=nsnull; if (a[i].type) nsCRT::free( a[i].type); if (a[i].description) nsCRT::free( a[i].description); if (a[i].encoding) nsCRT::free( a[i].encoding); } delete [] a; } nsMsgAttachedFile * nsOutlookCompose::GetLocalAttachments( void) { /* nsIURI *url = nsnull; */ PRInt32 count = 0; if (m_pAttachments) count = m_pAttachments->Count(); if (!count) return( nsnull); nsMsgAttachedFile *a = (nsMsgAttachedFile *) new nsMsgAttachedFile[count + 1]; if (!a) return( nsnull); memset(a, 0, sizeof(nsMsgAttachedFile) * (count + 1)); nsresult rv; nsXPIDLCString urlStr; OutlookAttachment * pAttach; for (PRInt32 i = 0; i < count; i++) { // nsMsgNewURL(&url, "file://C:/boxster.jpg"); // a[i].orig_url = url; // NS_PRECONDITION( PR_FALSE, "Forced Break"); pAttach = (OutlookAttachment *) m_pAttachments->ElementAt( i); a[i].file_spec = new nsFileSpec; pAttach->pAttachment->GetFileSpec( a[i].file_spec); urlStr.Adopt(0); pAttach->pAttachment->GetURLString(getter_Copies(urlStr)); if (!urlStr) { CleanUpAttach( a, count); return( nsnull); } rv = m_pIOService->NewURI( urlStr, nsnull, nsnull, getter_AddRefs(a[i].orig_url)); if (NS_FAILED( rv)) { CleanUpAttach( a, count); return( nsnull); } a[i].type = nsCRT::strdup( pAttach->mimeType); a[i].real_name = nsCRT::strdup( pAttach->description); a[i].encoding = nsCRT::strdup( ENCODING_BINARY); } return( a); } // Test a message send???? nsresult nsOutlookCompose::SendTheMessage( nsIFileSpec *pMsg, nsMsgDeliverMode mode, nsCString &useThisCType) { nsresult rv = CreateComponents(); if (NS_SUCCEEDED( rv)) rv = CreateIdentity(); if (NS_FAILED( rv)) return( rv); // IMPORT_LOG0( "Outlook Compose created necessary components\n"); nsAutoString bodyType; nsAutoString charSet; nsAutoString headerVal; nsCAutoString asciiHeaderVal; GetHeaderValue( m_Headers.get(), m_Headers.Length(), "From:", headerVal); if (!headerVal.IsEmpty()) m_pMsgFields->SetFrom( headerVal); GetHeaderValue( m_Headers.get(), m_Headers.Length(), "To:", headerVal); if (!headerVal.IsEmpty()) m_pMsgFields->SetTo( headerVal); GetHeaderValue( m_Headers.get(), m_Headers.Length(), "Subject:", headerVal); if (!headerVal.IsEmpty()) m_pMsgFields->SetSubject( headerVal); GetHeaderValue( m_Headers.get(), m_Headers.Length(), "Content-type:", headerVal); // If callers pass in a content type then use it, else get it from the header. if (!useThisCType.IsEmpty()) CopyASCIItoUTF16(useThisCType, bodyType); else { bodyType = headerVal; ExtractType( bodyType); } ExtractCharset( headerVal); // Use platform charset as default if the msg doesn't specify one // (ie, no 'charset' param in the Content-Type: header). As the last // resort we'll use the mail default charset. if (headerVal.IsEmpty()) { headerVal.AssignWithConversion(nsMsgI18NFileSystemCharset()); if (headerVal.IsEmpty()) { // last resort nsXPIDLString defaultCharset; NS_GetLocalizedUnicharPreferenceWithDefault(nsnull, "mailnews.view_default_charset", NS_LITERAL_STRING("ISO-8859-1"), defaultCharset); headerVal = defaultCharset; } } m_pMsgFields->SetCharacterSet( NS_LossyConvertUTF16toASCII(headerVal).get() ); charSet = headerVal; GetHeaderValue( m_Headers.get(), m_Headers.Length(), "CC:", headerVal); if (!headerVal.IsEmpty()) m_pMsgFields->SetCc( headerVal); GetHeaderValue( m_Headers.get(), m_Headers.Length(), "Message-ID:", headerVal); if (!headerVal.IsEmpty()) { LossyCopyUTF16toASCII(headerVal, asciiHeaderVal); m_pMsgFields->SetMessageId(asciiHeaderVal.get()); } GetHeaderValue( m_Headers.get(), m_Headers.Length(), "Reply-To:", headerVal); if (!headerVal.IsEmpty()) m_pMsgFields->SetReplyTo( headerVal); // what about all of the other headers?!?!?!?!?!?! char *pMimeType = nsnull; if (!bodyType.IsEmpty()) pMimeType = ToNewCString(bodyType); if (!pMimeType) pMimeType = strdup ("text/plain"); // IMPORT_LOG0( "Outlook compose calling CreateAndSendMessage\n"); nsMsgAttachedFile *pAttach = GetLocalAttachments(); // Do body conversion here nsString uniBody; NS_CopyNativeToUnicode( m_Body, uniBody); nsCString body; rv = nsMsgI18NConvertFromUnicode( NS_LossyConvertUTF16toASCII(charSet).get(), uniBody, body); uniBody.Truncate(); rv = m_pSendProxy->CreateAndSendMessage( nsnull, // no editor shell m_pIdentity, // dummy identity nsnull, // account key m_pMsgFields, // message fields PR_FALSE, // digest = NO PR_TRUE, // dont_deliver = YES, make a file mode, // mode nsnull, // no message to replace pMimeType, // body type NS_FAILED(rv) ? m_Body.get() : body.get(), // body pointer NS_FAILED(rv) ? m_Body.Length() : body.Length(), // body length nsnull, // remote attachment data pAttach, // local attachments nsnull, // related part nsnull, // parent window nsnull, // progress listener m_pListener, // listener nsnull); // password // IMPORT_LOG0( "Returned from CreateAndSendMessage\n"); if (pAttach) delete [] pAttach; OutlookSendListener *pListen = (OutlookSendListener *)m_pListener; if (NS_FAILED( rv)) { IMPORT_LOG1( "*** Error, CreateAndSendMessage FAILED: 0x%lx\n", rv); } else { // wait for the listener to get done! PRInt32 abortCnt = 0; PRInt32 cnt = 0; PRInt32 sleepCnt = 1; while (!pListen->m_done && (abortCnt < kHungAbortCount)) { PR_Sleep( sleepCnt); cnt++; if (cnt > kHungCount) { abortCnt++; sleepCnt *= 2; cnt = 0; } } if (abortCnt >= kHungAbortCount) { IMPORT_LOG0( "**** Create and send message hung\n"); IMPORT_LOG1( "Headers: %s\n", m_Headers.get()); IMPORT_LOG1( "Body: %s\n", m_Body.get()); rv = NS_ERROR_FAILURE; } } if (pMimeType) nsCRT::free( pMimeType); if (pListen->m_location) { pMsg->FromFileSpec( pListen->m_location); rv = NS_OK; } else { rv = NS_ERROR_FAILURE; IMPORT_LOG0( "*** Error, Outlook compose unsuccessful\n"); } pListen->Reset(); return( rv); } PRBool SimpleBufferTonyRCopiedTwice::SpecialMemCpy( PRInt32 offset, const char *pData, PRInt32 len, PRInt32 *pWritten) { // Arg!!!!! Mozilla can't handle plain CRs in any mail messages. Particularly a // problem with Eudora since it doesn't give a rats a** *pWritten = len; PRInt32 sz = offset + len; if (offset) { if ((m_pBuffer[offset - 1] == 0x0D) && (*pData != 0x0A)) { sz++; if (!Grow( sz)) return( PR_FALSE); m_pBuffer[offset] = 0x0A; offset++; (*pWritten)++; } } while (len > 0) { if ((*pData == 0x0D) && (*(pData + 1) != 0x0A)) { sz++; if (!Grow( sz)) return( PR_FALSE); m_pBuffer[offset] = 0x0D; offset++; m_pBuffer[offset] = 0x0A; (*pWritten)++; } else { m_pBuffer[offset] = *pData; } offset++; pData++; len--; } return( PR_TRUE); } nsresult nsOutlookCompose::ReadHeaders( ReadFileState *pState, SimpleBufferTonyRCopiedTwice& copy, SimpleBufferTonyRCopiedTwice& header) { // This should be the headers... header.m_writeOffset = 0; nsresult rv; PRInt32 lineLen; PRInt32 endLen = -1; PRInt8 endBuffer = 0; while ((endLen = IsEndHeaders( copy)) == -1) { while ((lineLen = FindNextEndLine( copy)) == -1) { copy.m_writeOffset = copy.m_bytesInBuf; if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0( "*** ERROR, writing headers\n"); return( NS_ERROR_FAILURE); } if (NS_FAILED( rv = FillMailBuffer( pState, copy))) { IMPORT_LOG0( "*** Error reading message headers\n"); return( rv); } if (!copy.m_bytesInBuf) { IMPORT_LOG0( "*** Error, end of file while reading headers\n"); return( NS_ERROR_FAILURE); } } copy.m_writeOffset += lineLen; if ((copy.m_writeOffset + 4) >= copy.m_bytesInBuf) { if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0( "*** ERROR, writing headers 2\n"); return( NS_ERROR_FAILURE); } if (NS_FAILED( rv = FillMailBuffer( pState, copy))) { IMPORT_LOG0( "*** Error reading message headers 2\n"); return( rv); } } } if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) { IMPORT_LOG0( "*** Error writing final headers\n"); return( NS_ERROR_FAILURE); } if (!header.Write( (const char *)&endBuffer, 1)) { IMPORT_LOG0( "*** Error writing header trailing null\n"); return( NS_ERROR_FAILURE); } copy.m_writeOffset += endLen; return( NS_OK); } PRInt32 nsOutlookCompose::FindNextEndLine( SimpleBufferTonyRCopiedTwice& data) { PRInt32 len = data.m_bytesInBuf - data.m_writeOffset; if (!len) return( -1); PRInt32 count = 0; const char *pData = data.m_pBuffer + data.m_writeOffset; while (((*pData == 0x0D) || (*pData == 0x0A)) && (count < len)) { pData++; count++; } while ((*pData != 0x0D) && (*pData != 0x0A) && (count < len)) { pData++; count++; } if (count < len) return( count); return( -1); } PRInt32 nsOutlookCompose::IsEndHeaders( SimpleBufferTonyRCopiedTwice& data) { PRInt32 len = data.m_bytesInBuf - data.m_writeOffset; if (len < 2) return( -1); const char *pChar = data.m_pBuffer + data.m_writeOffset; if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0D)) return( 2); if (len < 4) return( -1); if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0A) && (*(pChar + 2) == 0x0D) && (*(pChar + 3) == 0x0A)) return( 4); return( -1); } nsresult nsOutlookCompose::CopyComposedMessage( nsCString& fromLine, nsIFileSpec *pSrc, nsIFileSpec *pDst, SimpleBufferTonyRCopiedTwice& copy) { copy.m_bytesInBuf = 0; copy.m_writeOffset = 0; ReadFileState state; state.pFile = pSrc; state.offset = 0; state.size = 0; pSrc->GetFileSize( &state.size); if (!state.size) { IMPORT_LOG0( "*** Error, unexpected zero file size for composed message\n"); return( NS_ERROR_FAILURE); } nsresult rv = pSrc->OpenStreamForReading(); if (NS_FAILED( rv)) { IMPORT_LOG0( "*** Error, unable to open composed message file\n"); return( NS_ERROR_FAILURE); } PRInt32 written; rv = pDst->Write( fromLine.get(), fromLine.Length(), &written); // well, isn't this a hoot! // Read the headers from the new message, get the ones we like // and write out only the headers we want from the new message, // along with all of the other headers from the "old" message! if (NS_SUCCEEDED( rv)) rv = FillMailBuffer( &state, copy); if (NS_SUCCEEDED( rv)) rv = ReadHeaders( &state, copy, m_readHeaders); if (NS_SUCCEEDED( rv)) { rv = WriteHeaders( pDst, m_readHeaders); } // We need to go ahead and write out the rest of the copy buffer // so that the following will properly copy the rest of the body char lastChar = 0; if (NS_SUCCEEDED( rv)) { rv = EscapeFromSpaceLine(pDst, copy.m_pBuffer + copy.m_writeOffset, copy.m_pBuffer+copy.m_bytesInBuf); if (copy.m_bytesInBuf) lastChar = copy.m_pBuffer[copy.m_bytesInBuf - 1]; if (NS_SUCCEEDED(rv)) copy.m_writeOffset = copy.m_bytesInBuf; } while ((state.offset < state.size) && NS_SUCCEEDED( rv)) { rv = FillMailBuffer( &state, copy); if (NS_SUCCEEDED( rv)) { rv = EscapeFromSpaceLine(pDst, copy.m_pBuffer + copy.m_writeOffset, copy.m_pBuffer+copy.m_bytesInBuf); lastChar = copy.m_pBuffer[copy.m_bytesInBuf - 1]; if (NS_SUCCEEDED( rv)) copy.m_writeOffset = copy.m_bytesInBuf; else IMPORT_LOG0( "*** Error writing to destination mailbox\n"); } } pSrc->CloseStream(); if ((lastChar != 0x0A) && NS_SUCCEEDED( rv)) { rv = pDst->Write( "\x0D\x0A", 2, &written); if (written != 2) rv = NS_ERROR_FAILURE; } return( rv); } nsresult nsOutlookCompose::FillMailBuffer( ReadFileState *pState, SimpleBufferTonyRCopiedTwice& read) { if (read.m_writeOffset >= read.m_bytesInBuf) { read.m_writeOffset = 0; read.m_bytesInBuf = 0; } else if (read.m_writeOffset) { memcpy( read.m_pBuffer, read.m_pBuffer + read.m_writeOffset, read.m_bytesInBuf - read.m_writeOffset); read.m_bytesInBuf -= read.m_writeOffset; read.m_writeOffset = 0; } PRInt32 count = read.m_size - read.m_bytesInBuf; if (((PRUint32)count + pState->offset) > pState->size) count = pState->size - pState->offset; if (count) { PRInt32 bytesRead = 0; char * pBuffer = read.m_pBuffer + read.m_bytesInBuf; nsresult rv = pState->pFile->Read( &pBuffer, count, &bytesRead); if (NS_FAILED( rv)) return( rv); if (bytesRead != count) return( NS_ERROR_FAILURE); read.m_bytesInBuf += bytesRead; pState->offset += bytesRead; } return( NS_OK); } #define kMaxSpecialHeaders 3 static const char *gSpecialHeaders[kMaxSpecialHeaders] = { "Content-type", "MIME-Version", "Content-transfer-encoding" }; // consider "X-Accept-Language"? #define kMaxReplaceHeaders 5 static const char *gReplaceHeaders[kMaxReplaceHeaders] = { "From", "To", "Subject", "Reply-to", "cc" }; PRBool nsOutlookCompose::IsReplaceHeader( const char *pHeader) { for (int i = 0; i < kMaxReplaceHeaders; i++) { if (!nsCRT::strcasecmp( pHeader, gReplaceHeaders[i])) return( PR_TRUE); } return( PR_FALSE); } PRInt32 nsOutlookCompose::IsSpecialHeader( const char *pHeader) { for (int i = 0; i < kMaxSpecialHeaders; i++) { if (!nsCRT::strcasecmp( pHeader, gSpecialHeaders[i])) return( (PRInt32) i); } return( -1); } nsresult nsOutlookCompose::WriteHeaders( nsIFileSpec *pDst, SimpleBufferTonyRCopiedTwice& newHeaders) { // Well, ain't this a peach? // This is rather disgusting but there really isn't much to be done about it.... // 1. For each "old" header, replace it with the new one if we want, // then right it out. // 2. Then if we haven't written the "important" new headers, write them out // 3. Terminate the headers with an extra eol. PRInt32 n = 0; nsCString header; nsCString val; nsCString replaceVal; PRInt32 written; nsresult rv = NS_OK; // it's ok if we don't have the first header on the predefined lists. PRInt32 specialHeader; PRBool specials[kMaxSpecialHeaders]; int i; for (i = 0; i < kMaxSpecialHeaders; i++) specials[i] = PR_FALSE; do { GetNthHeader( m_Headers.get(), m_Headers.Length(), n, header, val, PR_FALSE); // GetNthHeader( newHeaders.m_pBuffer, newHeaders.m_writeOffset, n, header.get(), val, PR_FALSE); if (!header.IsEmpty()) { if ((specialHeader = IsSpecialHeader( header.get())) != -1) { header.Append( ':'); GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), val, PR_FALSE); // Bug 145150 - Turn "Content-Type: application/ms-tnef" into "Content-Type: text/plain" // so the body text can be displayed normally (instead of in an attachment). if (!PL_strcasecmp(header.get(), "Content-Type:") && !PL_strcasecmp(val.get(), "application/ms-tnef")) val.Assign("text/plain"); header.Truncate( header.Length() - 1); specials[specialHeader] = PR_TRUE; } else if (IsReplaceHeader( header.get())) { replaceVal.Truncate( 0); header.Append( ':'); GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), replaceVal, PR_FALSE); header.Truncate( header.Length() - 1); if (!replaceVal.IsEmpty()) val = replaceVal; } if (!val.IsEmpty()) { rv = pDst->Write( header.get(), header.Length(), &written); if (NS_SUCCEEDED( rv)) rv = pDst->Write( ": ", 2, &written); if (NS_SUCCEEDED( rv)) rv = pDst->Write( val.get(), val.Length(), &written); if (NS_SUCCEEDED( rv)) rv = pDst->Write( "\x0D\x0A", 2, &written); } } n++; } while (NS_SUCCEEDED( rv) && !header.IsEmpty()); for (i = 0; (i < kMaxSpecialHeaders) && NS_SUCCEEDED( rv); i++) { if (!specials[i]) { header = gSpecialHeaders[i]; header.Append( ':'); GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), val, PR_FALSE); header.Truncate( header.Length() - 1); if (!val.IsEmpty()) { rv = pDst->Write( header.get(), header.Length(), &written); if (NS_SUCCEEDED( rv)) rv = pDst->Write( ": ", 2, &written); if (NS_SUCCEEDED( rv)) rv = pDst->Write( val.get(), val.Length(), &written); if (NS_SUCCEEDED( rv)) rv = pDst->Write( "\x0D\x0A", 2, &written); } } } if (NS_SUCCEEDED( rv)) rv = pDst->Write( "\x0D\x0A", 2, &written); return( rv); }