Back to index

lightning-sunbird  0.9+nobinonly
nsEudoraCompose.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org Code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
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 
00040 #include "nscore.h"
00041 #include "prthread.h"
00042 #include "nsString.h"
00043 #include "nsReadableUtils.h"
00044 #include "nsUnicharUtils.h"
00045 #include "nsCOMPtr.h"
00046 #include "nsIFileSpec.h"
00047 #include "nsIComponentManager.h"
00048 #include "nsIServiceManager.h"
00049 #include "nsIIOService.h"
00050 #include "nsIURI.h"
00051 #include "nsIProxyObjectManager.h"
00052 #include "nsProxiedService.h"
00053 
00054 #include "nsMsgBaseCID.h"
00055 #include "nsMsgCompCID.h"
00056 
00057 #include "nsIMsgCompose.h"
00058 #include "nsIMsgCompFields.h"
00059 #include "nsIMsgSend.h"
00060 #include "nsIMsgMailSession.h"
00061 #include "nsIMsgAccountManager.h"
00062 #include "nsMsgI18N.h"
00063 
00064 #include "nsNetCID.h"
00065 
00066 #include "nsEudoraCompose.h"
00067 
00068 #include "EudoraDebugLog.h"
00069 
00070 #include "nsMimeTypes.h"
00071 #include "nsMsgUtils.h"
00072 
00073 static NS_DEFINE_CID( kMsgSendCID, NS_MSGSEND_CID);
00074 static NS_DEFINE_CID( kMsgCompFieldsCID, NS_MSGCOMPFIELDS_CID); 
00075 static NS_DEFINE_CID( kIOServiceCID, NS_IOSERVICE_CID);
00076 static NS_DEFINE_CID( kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID);
00077 
00078 
00079 // We need to do some calculations to set these numbers to something reasonable!
00080 // Unless of course, CreateAndSendMessage will NEVER EVER leave us in the lurch
00081 #define kHungCount          100000
00082 #define kHungAbortCount     1000
00083 
00084 
00085 #ifdef IMPORT_DEBUG
00086 static char *p_test_headers = 
00087 "Received: from netppl.fi (IDENT:monitor@get.freebsd.because.microsoftsucks.net [209.3.31.115])\n\
00088        by mail4.sirius.com (8.9.1/8.9.1) with SMTP id PAA27232;\n\
00089        Mon, 17 May 1999 15:27:43 -0700 (PDT)\n\
00090 Message-ID: <ikGD3jRTsKklU.Ggm2HmE2A1Jsqd0p@netppl.fi>\n\
00091 From: \"adsales@qualityservice.com\" <adsales@qualityservice.com>\n\
00092 Subject: Re: Your College Diploma (36822)\n\
00093 Date: Mon, 17 May 1999 15:09:29 -0400 (EDT)\n\
00094 MIME-Version: 1.0\n\
00095 Content-Type: TEXT/PLAIN; charset=\"US-ASCII\"\n\
00096 Content-Transfer-Encoding: 7bit\n\
00097 X-UIDL: 19990517.152941\n\
00098 Status: RO";
00099 
00100 static char *p_test_body = 
00101 "Hello world?\n\
00102 ";
00103 #else
00104 #define       p_test_headers       nsnull
00105 #define       p_test_body nsnull
00106 #endif
00107 
00108 
00109 #define kWhitespace  "\b\t\r\n "
00110 
00111 
00112 // First off, a listener
00113 class EudoraSendListener : public nsIMsgSendListener
00114 {
00115 public:
00116        EudoraSendListener() {
00117               m_done = PR_FALSE;
00118               m_location = nsnull;
00119        }
00120 
00121        virtual ~EudoraSendListener() { NS_IF_RELEASE( m_location); }
00122 
00123        // nsISupports interface
00124        NS_DECL_ISUPPORTS
00125 
00126        /* void OnStartSending (in string aMsgID, in PRUint32 aMsgSize); */
00127        NS_IMETHOD OnStartSending(const char *aMsgID, PRUint32 aMsgSize) {return NS_OK;}
00128 
00129        /* void OnProgress (in string aMsgID, in PRUint32 aProgress, in PRUint32 aProgressMax); */
00130        NS_IMETHOD OnProgress(const char *aMsgID, PRUint32 aProgress, PRUint32 aProgressMax) {return NS_OK;}
00131 
00132        /* void OnStatus (in string aMsgID, in wstring aMsg); */
00133        NS_IMETHOD OnStatus(const char *aMsgID, const PRUnichar *aMsg) {return NS_OK;}
00134 
00135        /* void OnStopSending (in string aMsgID, in nsresult aStatus, in wstring aMsg, in nsIFileSpec returnFileSpec); */
00136        NS_IMETHOD OnStopSending(const char *aMsgID, nsresult aStatus, const PRUnichar *aMsg, 
00137                                              nsIFileSpec *returnFileSpec) {
00138               m_done = PR_TRUE;
00139               m_location = returnFileSpec;
00140               NS_IF_ADDREF( m_location);
00141               return NS_OK;
00142        }
00143 
00144     /* void OnSendNotPerformed */
00145     NS_IMETHOD OnSendNotPerformed(const char *aMsgID, nsresult aStatus) {return NS_OK;}
00146 
00147   /* void OnGetDraftFolderURI (); */
00148   NS_IMETHOD OnGetDraftFolderURI(const char *aFolderURI) {return NS_OK;}
00149 
00150        static nsresult CreateSendListener( nsIMsgSendListener **ppListener);
00151 
00152        void Reset() { m_done = PR_FALSE; NS_IF_RELEASE( m_location); m_location = nsnull;}
00153 
00154 public:
00155        PRBool               m_done;
00156        nsIFileSpec * m_location;
00157 };
00158 
00159 
00160 NS_IMPL_THREADSAFE_ISUPPORTS1(EudoraSendListener, nsIMsgSendListener)
00161 
00162 nsresult EudoraSendListener::CreateSendListener( nsIMsgSendListener **ppListener)
00163 {
00164     NS_PRECONDITION(ppListener != nsnull, "null ptr");
00165     if (! ppListener)
00166         return NS_ERROR_NULL_POINTER;
00167 
00168     *ppListener = new EudoraSendListener();
00169     if (! *ppListener)
00170         return NS_ERROR_OUT_OF_MEMORY;
00171 
00172     NS_ADDREF(*ppListener);
00173     return NS_OK;
00174 }
00175 
00176 
00181 
00182 
00183 
00184 nsEudoraCompose::nsEudoraCompose()
00185 {
00186        m_pIOService = nsnull;
00187        m_pAttachments = nsnull;
00188        m_pListener = nsnull;
00189        m_pMsgSend = nsnull;
00190        m_pSendProxy = nsnull;
00191        m_pMsgFields = nsnull;
00192        m_pIdentity = nsnull;
00193        m_pHeaders = p_test_headers;
00194        if (m_pHeaders)
00195               m_headerLen = strlen( m_pHeaders);
00196        else
00197               m_headerLen = 0;
00198        m_pBody = p_test_body;
00199        if (m_pBody)
00200               m_bodyLen = strlen( m_pBody);
00201        else
00202               m_bodyLen = 0;
00203 
00204        m_readHeaders.m_convertCRs = PR_TRUE;
00205 }
00206 
00207 
00208 nsEudoraCompose::~nsEudoraCompose()
00209 {
00210        NS_IF_RELEASE( m_pSendProxy);
00211        NS_IF_RELEASE( m_pIOService);
00212        NS_IF_RELEASE( m_pMsgSend);
00213        NS_IF_RELEASE( m_pListener);
00214        NS_IF_RELEASE( m_pMsgFields);
00215 
00216        if (m_pIdentity) {   
00217               nsresult rv = m_pIdentity->ClearAllValues();
00218         NS_ASSERTION(NS_SUCCEEDED(rv),"failed to clear values");
00219               if (NS_FAILED(rv)) return;
00220 
00221               NS_WITH_PROXIED_SERVICE(nsIMsgAccountManager, accMgr, NS_MSGACCOUNTMANAGER_CONTRACTID, NS_UI_THREAD_EVENTQ, &rv);
00222         NS_ASSERTION(NS_SUCCEEDED(rv) && accMgr,"failed to get account manager");
00223               if (NS_FAILED(rv) || !accMgr) return;
00224 
00225               rv = accMgr->RemoveIdentity(m_pIdentity);
00226         NS_ASSERTION(NS_SUCCEEDED(rv),"failed to remove identity");
00227               if (NS_FAILED(rv)) return;
00228 
00229            NS_RELEASE(m_pIdentity);
00230        }
00231 
00232 }
00233 
00234 nsresult nsEudoraCompose::CreateIdentity( void)
00235 {
00236        if (m_pIdentity)
00237               return( NS_OK);
00238 
00239        nsresult      rv;
00240     NS_WITH_PROXIED_SERVICE(nsIMsgAccountManager, accMgr, NS_MSGACCOUNTMANAGER_CONTRACTID, NS_UI_THREAD_EVENTQ, &rv);
00241     if (NS_FAILED(rv)) return( rv);
00242        rv = accMgr->CreateIdentity( &m_pIdentity);
00243        nsString      name(NS_LITERAL_STRING("Import Identity"));
00244        if (m_pIdentity) {
00245               m_pIdentity->SetFullName( name.get());
00246               m_pIdentity->SetIdentityName( name.get());
00247               m_pIdentity->SetEmail( "import@import.service");
00248        }
00249        
00250        return( rv);
00251 }
00252 
00253 nsresult nsEudoraCompose::CreateComponents( void)
00254 {
00255        nsresult      rv = NS_OK;
00256        
00257        if (!m_pIOService) {
00258               IMPORT_LOG0( "Creating nsIOService\n");
00259 
00260               NS_WITH_PROXIED_SERVICE(nsIIOService, service, kIOServiceCID, NS_UI_THREAD_EVENTQ, &rv);
00261               if (NS_FAILED(rv)) 
00262                      return( rv);
00263               m_pIOService = service;
00264               NS_IF_ADDREF( m_pIOService);
00265        }
00266        
00267        NS_IF_RELEASE( m_pMsgFields);
00268        if (!m_pMsgSend) {
00269               rv = CallCreateInstance( kMsgSendCID, &m_pMsgSend); 
00270               if (NS_SUCCEEDED( rv) && m_pMsgSend) {
00271                      nsCOMPtr<nsIProxyObjectManager> proxyMgr = 
00272                               do_GetService(kProxyObjectManagerCID, &rv);
00273                      if (NS_SUCCEEDED(rv)) {
00274                             rv = proxyMgr->GetProxyForObject( NS_UI_THREAD_EVENTQ, NS_GET_IID(nsIMsgSend),
00275                                                                       m_pMsgSend, PROXY_SYNC, (void**)&m_pSendProxy);
00276                             if (NS_FAILED( rv)) {
00277                                    m_pSendProxy = nsnull;
00278                             }
00279                      }
00280                      if (NS_FAILED( rv)) {
00281                             NS_RELEASE( m_pMsgSend);
00282                             m_pMsgSend = nsnull;
00283                      }
00284               }
00285        }
00286        if (!m_pListener && NS_SUCCEEDED( rv)) {
00287               rv = EudoraSendListener::CreateSendListener( &m_pListener);
00288        }
00289 
00290        if (NS_SUCCEEDED(rv) && m_pMsgSend) { 
00291            rv = CallCreateInstance( kMsgCompFieldsCID, &m_pMsgFields); 
00292               if (NS_SUCCEEDED(rv) && m_pMsgFields) {
00293                      // IMPORT_LOG0( "nsOutlookCompose - CreateComponents succeeded\n");
00294                      m_pMsgFields->SetForcePlainText( PR_FALSE);
00295                      return( NS_OK);
00296               }
00297        }
00298 
00299        return( NS_ERROR_FAILURE);
00300 }
00301 
00302 void nsEudoraCompose::GetNthHeader( const char *pData, PRInt32 dataLen, PRInt32 n, nsCString& header, nsCString& val, PRBool unwrap)
00303 {
00304        header.Truncate();
00305        val.Truncate();
00306        if (!pData)
00307               return;
00308 
00309        PRInt32       index = 0;
00310        PRInt32       len;
00311        PRInt32       start = 0;
00312        const char *pChar = pData;
00313        const char *pStart;
00314        if (n == 0) {
00315               pStart = pChar;
00316               len = 0;
00317               while ((start < dataLen) && (*pChar != ':')) {
00318                      start++;
00319                      len++;
00320                      pChar++;
00321               }
00322               header.Append( pStart, len);
00323               header.Trim( kWhitespace);
00324               start++;
00325               pChar++;
00326        }
00327        else {
00328               while (start < dataLen) {
00329                      if ((*pChar != ' ') && (*pChar != 9)) {
00330                             if (n == index) {
00331                                    pStart = pChar;
00332                                    len = 0;
00333                                    while ((start < dataLen) && (*pChar != ':')) {
00334                                           start++;
00335                                           len++;
00336                                           pChar++;
00337                                    }
00338                                    header.Append( pStart, len);
00339                                    header.Trim( kWhitespace);
00340                                    start++;
00341                                    pChar++;
00342                                    break;
00343                             }
00344                             else
00345                                    index++;
00346                      }
00347 
00348                      while ((start < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) {
00349                             start++;
00350                             pChar++;
00351                      }
00352                      while ((start < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) {
00353                             start++;
00354                             pChar++;
00355                      }
00356               }
00357        }
00358 
00359        if (start >= dataLen)
00360               return;
00361 
00362        PRInt32              lineEnd;
00363        PRInt32              end = start;
00364        while (end < dataLen) {
00365               while ((end < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) {
00366                      end++;
00367                      pChar++;
00368               }
00369               if (end > start) {
00370                      val.Append( pData + start, end - start);
00371               }
00372               
00373               lineEnd = end;
00374               pStart = pChar;
00375               while ((end < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) {
00376                      end++;
00377                      pChar++;
00378               }
00379               
00380               start = end;
00381 
00382               while ((end < dataLen) && ((*pChar == ' ') || (*pChar == '\t'))) {
00383                      end++;
00384                      pChar++;
00385               }
00386 
00387               if (start == end)
00388                      break;
00389               
00390               if (unwrap)
00391                      val.Append( ' ');
00392               else {
00393                      val.Append( pStart, end - lineEnd);
00394               }
00395 
00396               start = end;
00397        }
00398        
00399        val.Trim( kWhitespace);
00400 }
00401 
00402 
00403 void nsEudoraCompose::GetHeaderValue( const char *pData, PRInt32 dataLen, const char *pHeader, nsCString& val, PRBool unwrap)
00404 {
00405        val.Truncate();
00406        if (!pData)
00407               return;
00408 
00409        PRInt32       start = 0;
00410        PRInt32 len = strlen( pHeader);
00411        const char *pChar = pData;
00412        if (!nsCRT::strncasecmp( pHeader, pData, len)) {
00413               start = len;
00414        }
00415        else {
00416               while (start < dataLen) {
00417                      while ((start < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) {
00418                             start++;
00419                             pChar++;
00420                      }
00421                      while ((start < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) {
00422                             start++;
00423                             pChar++;
00424                      }
00425                      if ((start < dataLen) && !nsCRT::strncasecmp( pChar, pHeader, len))
00426                             break;
00427               }
00428               if (start < dataLen)
00429                      start += len;
00430        }
00431 
00432        if (start >= dataLen)
00433               return;
00434 
00435        PRInt32                     end = start;
00436        PRInt32                     lineEnd;
00437        const char *  pStart;
00438 
00439        pChar =              pData + start;
00440 
00441        while (end < dataLen) {
00442               while ((end < dataLen) && (*pChar != 0x0D) && (*pChar != 0x0A)) {
00443                      end++;
00444                      pChar++;
00445               }
00446               if (end > start) {
00447                      val.Append( pData + start, end - start);
00448               }
00449               
00450               lineEnd = end;
00451               pStart = pChar;
00452               while ((end < dataLen) && ((*pChar == 0x0D) || (*pChar == 0x0A))) {
00453                      end++;
00454                      pChar++;
00455               }
00456               
00457               start = end;
00458 
00459               while ((end < dataLen) && ((*pChar == ' ') || (*pChar == '\t'))) {
00460                      end++;
00461                      pChar++;
00462               }
00463 
00464               if (start == end)
00465                      break;
00466               
00467               if (unwrap)
00468                      val.Append( ' ');
00469               else {
00470                      val.Append( pStart, end - lineEnd);
00471               }
00472 
00473               start = end;
00474        }
00475               
00476        val.Trim( kWhitespace);
00477 }
00478 
00479 
00480 void nsEudoraCompose::ExtractCharset( nsString& str)
00481 {
00482        nsString      tStr;
00483        PRInt32 idx = str.Find( "charset=", PR_TRUE);
00484        if (idx != -1) {
00485               idx += 8;
00486               str.Right( tStr, str.Length() - idx);
00487               idx = tStr.FindChar( ';');
00488               if (idx != -1)
00489                      tStr.Left( str, idx);
00490               else
00491                      str = tStr;
00492               str.Trim( kWhitespace);
00493               if ((str.CharAt( 0) == '"') && (str.Length() > 2)) {
00494                      str.Mid( tStr, 1, str.Length() - 2);
00495                      str = tStr;
00496                      str.Trim( kWhitespace);
00497               }
00498        }
00499        else
00500               str.Truncate();
00501 }
00502 
00503 void nsEudoraCompose::ExtractType( nsString& str)
00504 {
00505        nsString      tStr;
00506        PRInt32       idx = str.FindChar( ';');
00507        if (idx != -1) {
00508               str.Left( tStr, idx);
00509               str = tStr;
00510        }
00511        str.Trim( kWhitespace);
00512 
00513        if ((str.CharAt( 0) == '"') && (str.Length() > 2)) {
00514               str.Mid( tStr, 1, str.Length() - 2);
00515               str = tStr;
00516               str.Trim( kWhitespace);
00517        }
00518 
00519        // if multipart then ignore it since no outlook message body is ever
00520        // valid multipart!
00521        if (str.Length() > 10) {
00522               str.Left( tStr, 10);
00523               if (tStr.LowerCaseEqualsLiteral("multipart/"))
00524                      str.Truncate();
00525        }
00526 }
00527 
00528 void nsEudoraCompose::CleanUpAttach( nsMsgAttachedFile *a, PRInt32 count)
00529 {
00530   for (PRInt32 i = 0; i < count; i++) {
00531     a[i].orig_url=nsnull;
00532     if (a[i].type)
00533       nsCRT::free( a[i].type);
00534     if (a[i].description)
00535       nsCRT::free( a[i].description);
00536     if (a[i].encoding)
00537       nsCRT::free( a[i].encoding);
00538   }
00539   delete [] a;
00540 }
00541 
00542 nsMsgAttachedFile * nsEudoraCompose::GetLocalAttachments( void)
00543 {  
00544        /*
00545        nsIURI      *url = nsnull;
00546        */
00547 
00548        PRInt32              count = 0;
00549        if (m_pAttachments)
00550               count = m_pAttachments->Count();
00551        if (!count)
00552               return( nsnull);
00553 
00554        nsMsgAttachedFile *a = (nsMsgAttachedFile *) new nsMsgAttachedFile[count + 1];
00555        if (!a)
00556               return( nsnull);
00557        memset(a, 0, sizeof(nsMsgAttachedFile) * (count + 1));
00558        
00559        nsresult                    rv;
00560        nsXPIDLCString       urlStr;
00561        ImportAttachment *   pAttach;
00562 
00563        for (PRInt32 i = 0; i < count; i++) {
00564               // nsMsgNewURL(&url, "file://C:/boxster.jpg");
00565               // a[i].orig_url = url;
00566 
00567               // NS_PRECONDITION( PR_FALSE, "Forced Break");
00568 
00569               pAttach = (ImportAttachment *) m_pAttachments->ElementAt( i);
00570               a[i].file_spec = new nsFileSpec;
00571               pAttach->pAttachment->GetFileSpec( a[i].file_spec);
00572               urlStr.Adopt(0);
00573               pAttach->pAttachment->GetURLString(getter_Copies(urlStr));
00574               if (!urlStr) {
00575                      CleanUpAttach( a, count);
00576                      return( nsnull);
00577               }
00578               rv = m_pIOService->NewURI( urlStr, nsnull, nsnull, getter_AddRefs(a[i].orig_url));
00579               if (NS_FAILED( rv)) {
00580                      CleanUpAttach( a, count);
00581                      return( nsnull);
00582               }
00583 
00584               a[i].type = nsCRT::strdup( pAttach->mimeType);
00585               a[i].real_name = nsCRT::strdup( pAttach->description);
00586               a[i].encoding = nsCRT::strdup( ENCODING_BINARY);
00587        }
00588 
00589        return( a);
00590 }
00591 
00592 // Test a message send????
00593 nsresult nsEudoraCompose::SendTheMessage( nsIFileSpec *pMsg)
00594 {
00595        nsresult      rv = CreateComponents();
00596        if (NS_SUCCEEDED( rv))
00597               rv = CreateIdentity();
00598        if (NS_FAILED( rv))
00599               return( rv);
00600        
00601        // IMPORT_LOG0( "Outlook Compose created necessary components\n");
00602 
00603        nsString      bodyType;
00604        nsString      charSet;
00605        nsString      headerVal;
00606        GetHeaderValue( m_pHeaders, m_headerLen, "From:", headerVal);
00607        if (!headerVal.IsEmpty())
00608               m_pMsgFields->SetFrom( headerVal);
00609        GetHeaderValue( m_pHeaders, m_headerLen, "To:", headerVal);
00610        if (!headerVal.IsEmpty())
00611               m_pMsgFields->SetTo( headerVal);
00612        GetHeaderValue( m_pHeaders, m_headerLen, "Subject:", headerVal);
00613        if (!headerVal.IsEmpty())
00614               m_pMsgFields->SetSubject( headerVal);
00615        GetHeaderValue( m_pHeaders, m_headerLen, "Content-type:", headerVal);
00616        bodyType = headerVal;
00617        ExtractType( bodyType);
00618        ExtractCharset( headerVal);
00619   // Use platform charset as default if the msg doesn't specify one
00620   // (ie, no 'charset' param in the Content-Type: header). As the last
00621   // resort we'll use the mail default charset.
00622   if (headerVal.IsEmpty())
00623   {
00624     headerVal.AssignWithConversion(nsMsgI18NFileSystemCharset());
00625     if (headerVal.IsEmpty())
00626     { // last resort
00627       if (m_defCharset.IsEmpty())
00628       {
00629         nsXPIDLString defaultCharset;
00630         NS_GetLocalizedUnicharPreferenceWithDefault(nsnull, "mailnews.view_default_charset",
00631                                                     NS_LITERAL_STRING("ISO-8859-1"), defaultCharset);
00632         m_defCharset = defaultCharset;
00633       }
00634       headerVal = m_defCharset;
00635     }
00636   }
00637   m_pMsgFields->SetCharacterSet( NS_LossyConvertUCS2toASCII(headerVal).get() );
00638   charSet = headerVal;
00639        GetHeaderValue( m_pHeaders, m_headerLen, "CC:", headerVal);
00640        if (!headerVal.IsEmpty())
00641               m_pMsgFields->SetCc( headerVal);
00642        GetHeaderValue( m_pHeaders, m_headerLen, "Message-ID:", headerVal);
00643        if (!headerVal.IsEmpty())
00644               m_pMsgFields->SetMessageId( NS_LossyConvertUCS2toASCII(headerVal).get() );
00645        GetHeaderValue( m_pHeaders, m_headerLen, "Reply-To:", headerVal);
00646        if (!headerVal.IsEmpty())
00647               m_pMsgFields->SetReplyTo( headerVal);
00648 
00649        // what about all of the other headers?!?!?!?!?!?!
00650   char *pMimeType; 
00651        if (!bodyType.IsEmpty())
00652               pMimeType = ToNewCString(bodyType);
00653   else
00654     pMimeType = ToNewCString(m_bodyType);
00655        
00656        // IMPORT_LOG0( "Outlook compose calling CreateAndSendMessage\n");
00657        nsMsgAttachedFile *pAttach = GetLocalAttachments();
00658 
00659 
00660        /*
00661               l10n - I have the body of the message in the system charset,
00662               I need to "encode" it to be the charset for the message
00663               *UNLESS* of course, I don't know what the charset of the message
00664               should be?  How do I determine what the charset should
00665               be if it doesn't exist?
00666 
00667        */
00668        
00669        nsString      uniBody;
00670        NS_CopyNativeToUnicode( nsDependentCString(m_pBody), uniBody);
00671 
00672        nsCString     body;
00673 
00674        rv = nsMsgI18NConvertFromUnicode( NS_LossyConvertUTF16toASCII(charSet).get(),
00675                                     uniBody, body);
00676        if (NS_FAILED( rv)) {
00677               // in this case, if we did not use the default compose
00678               // charset, then try that.
00679               if (!charSet.Equals( m_defCharset)) {
00680                      body.Truncate();
00681                      rv = nsMsgI18NConvertFromUnicode( NS_LossyConvertUTF16toASCII(charSet).get(),
00682                                         uniBody, body);
00683               }
00684        }
00685        uniBody.Truncate();
00686 
00687 
00688   // See if it's a draft msg (ie, no From: or no To: AND no Cc: AND no Bcc:).
00689   // Eudora saves sent and draft msgs in Out folder (ie, mixed) and it does
00690   // store Bcc: header in the msg itself.
00691   nsMsgDeliverMode mode = nsIMsgSend::nsMsgDeliverNow;
00692   nsAutoString from, to, cc, bcc;
00693   rv = m_pMsgFields->GetFrom(from);
00694   rv = m_pMsgFields->GetTo(to);
00695   rv = m_pMsgFields->GetCc(cc);
00696   rv = m_pMsgFields->GetBcc(bcc);
00697   if ( from.IsEmpty() || to.IsEmpty() && cc.IsEmpty() && bcc.IsEmpty() )
00698     mode = nsIMsgSend::nsMsgSaveAsDraft;
00699 
00700        if (NS_FAILED( rv)) {
00701 
00702               rv = m_pSendProxy->CreateAndSendMessage(
00703                     nsnull,                                 // no editor shell
00704                                                                       m_pIdentity,                  // dummy identity
00705                                                                                 nsnull,                         // account key
00706                                                                       m_pMsgFields,                 // message fields
00707                                                                       PR_FALSE,                              // digest = NO
00708                                                                       PR_TRUE,                               // dont_deliver = YES, make a file
00709                                                                       mode,                         // mode
00710                                                                       nsnull,                                       // no message to replace
00711                                                                       pMimeType,                           // body type
00712                                                                       m_pBody,                               // body pointer
00713                                                                       m_bodyLen,                           // body length
00714                                                                       nsnull,                                       // remote attachment data
00715                                                                       pAttach,                               // local attachments
00716                                                                       nsnull,                                       // related part
00717                                                                       nsnull,                       // parent window
00718                                                                       nsnull,                       // progress listener
00719                                                                       m_pListener,                       // listener
00720                                                                       nsnull,                       // password
00721                                                                       EmptyCString(),               // originalMsgURI
00722                     nsnull);                      // message compose type
00723 
00724        }
00725        else {
00726               rv = m_pSendProxy->CreateAndSendMessage(
00727                     nsnull,                                 // no editor shell
00728                                                                       m_pIdentity,                  // dummy identity
00729                                                                                 nsnull,                         // account key
00730                                                                       m_pMsgFields,                 // message fields
00731                                                                       PR_FALSE,                              // digest = NO
00732                                                                       PR_TRUE,                               // dont_deliver = YES, make a file
00733                                                                       mode,                         // mode
00734                                                                       nsnull,                                       // no message to replace
00735                                                                       pMimeType,                           // body type
00736                                                                       body.get(),                                // body pointer
00737                                                                       body.Length(),                          // body length
00738                                                                       nsnull,                                       // remote attachment data
00739                                                                       pAttach,                               // local attachments
00740                                                                       nsnull,                                       // related part
00741                                                                       nsnull,                       // parent window
00742                                                                       nsnull,                       // progress listener
00743                                                                       m_pListener,                       // listener
00744                                                                       nsnull,                       // password
00745                                                                       EmptyCString(),               // originalMsgURI
00746                     nsnull);                      // message compose type
00747 
00748        }
00749 
00750        // IMPORT_LOG0( "Returned from CreateAndSendMessage\n");
00751 
00752        if (pAttach)
00753               delete [] pAttach;
00754 
00755        EudoraSendListener *pListen = (EudoraSendListener *)m_pListener;
00756        if (NS_FAILED( rv)) {
00757               IMPORT_LOG1( "*** Error, CreateAndSendMessage FAILED: 0x%lx\n", rv);
00758               // IMPORT_LOG1( "Headers: %80s\n", m_pHeaders);
00759        }
00760        else {
00761               // wait for the listener to get done!
00762               PRInt32       abortCnt = 0;
00763               PRInt32       cnt = 0;
00764               PRInt32       sleepCnt = 1;        
00765               while (!pListen->m_done && (abortCnt < kHungAbortCount)) {
00766                      PR_Sleep( sleepCnt);
00767                      cnt++;
00768                      if (cnt > kHungCount) {
00769                             abortCnt++;
00770                             sleepCnt *= 2;
00771                             cnt = 0;
00772                      }
00773               }
00774 
00775               if (abortCnt >= kHungAbortCount) {
00776                      IMPORT_LOG0( "**** Create and send message hung\n");
00777                      IMPORT_LOG1( "Headers: %s\n", m_pHeaders);
00778                      IMPORT_LOG1( "Body: %s\n", m_pBody);
00779                      rv = NS_ERROR_FAILURE;
00780               }
00781 
00782        }
00783 
00784        if (pMimeType)
00785               nsCRT::free( pMimeType);
00786 
00787        if (pListen->m_location) {
00788               pMsg->FromFileSpec( pListen->m_location);
00789               rv = NS_OK;
00790        }
00791        else {
00792               rv = NS_ERROR_FAILURE;
00793               IMPORT_LOG0( "*** Error, Outlook compose unsuccessful\n");
00794        }
00795        
00796        pListen->Reset();
00797 
00798        return( rv);
00799 }
00800 
00801 
00802 PRBool SimpleBufferTonyRCopiedOnce::SpecialMemCpy( PRInt32 offset, const char *pData, PRInt32 len, PRInt32 *pWritten)
00803 {
00804        // Arg!!!!!  Mozilla can't handle plain CRs in any mail messages.  Particularly a 
00805        // problem with Eudora since it doesn't give a rats a**
00806        *pWritten = len;
00807        PRInt32       sz = offset + len;
00808        if (offset) {
00809               if ((m_pBuffer[offset - 1] == 0x0D) && (*pData != 0x0A)) {
00810                      sz++;
00811                      if (!Grow( sz)) return( PR_FALSE);
00812                      m_pBuffer[offset] = 0x0A;
00813                      offset++;
00814                      (*pWritten)++;
00815               }
00816        }
00817        while (len > 0) {
00818               if ((*pData == 0x0D) && (*(pData + 1) != 0x0A)) {
00819                      sz++;
00820                      if (!Grow( sz)) return( PR_FALSE);
00821                      m_pBuffer[offset] = 0x0D;
00822                      offset++;
00823                      m_pBuffer[offset] = 0x0A;                                                    
00824                      (*pWritten)++;
00825               }
00826               else {
00827                      m_pBuffer[offset] = *pData;
00828               }
00829               offset++;
00830               pData++;
00831               len--;
00832        }
00833        
00834        return( PR_TRUE);
00835 }
00836 
00837 nsresult nsEudoraCompose::ReadHeaders( ReadFileState *pState, SimpleBufferTonyRCopiedOnce& copy, SimpleBufferTonyRCopiedOnce& header)
00838 {
00839        // This should be the headers...
00840        header.m_writeOffset = 0;
00841 
00842        nsresult      rv;
00843        PRInt32              lineLen;
00844        PRInt32              endLen = -1;
00845        PRInt8        endBuffer = 0;
00846 
00847        while ((endLen = IsEndHeaders( copy)) == -1) {
00848               while ((lineLen = FindNextEndLine( copy)) == -1) {
00849                      copy.m_writeOffset = copy.m_bytesInBuf;
00850                      if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00851                             IMPORT_LOG0( "*** ERROR, writing headers\n");
00852                             return( NS_ERROR_FAILURE);
00853                      }
00854                      if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00855                             IMPORT_LOG0( "*** Error reading message headers\n");
00856                             return( rv);
00857                      }
00858                      if (!copy.m_bytesInBuf) {
00859                             IMPORT_LOG0( "*** Error, end of file while reading headers\n");
00860                             return( NS_ERROR_FAILURE);
00861                      }
00862               }
00863               copy.m_writeOffset += lineLen;
00864               if ((copy.m_writeOffset + 4) >= copy.m_bytesInBuf) {
00865                      if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00866                             IMPORT_LOG0( "*** ERROR, writing headers 2\n");
00867                             return( NS_ERROR_FAILURE);
00868                      }
00869                      if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00870                             IMPORT_LOG0( "*** Error reading message headers 2\n");
00871                             return( rv);
00872                      }
00873               }
00874        }
00875 
00876        if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00877               IMPORT_LOG0( "*** Error writing final headers\n");
00878               return( NS_ERROR_FAILURE);
00879        }
00880        if (!header.Write( (const char *)&endBuffer, 1)) {
00881               IMPORT_LOG0( "*** Error writing header trailing null\n");
00882               return( NS_ERROR_FAILURE);
00883        }
00884 
00885        copy.m_writeOffset += endLen;
00886        
00887        return( NS_OK);
00888 }
00889 
00890 PRInt32 nsEudoraCompose::FindNextEndLine( SimpleBufferTonyRCopiedOnce& data)
00891 {
00892        PRInt32 len = data.m_bytesInBuf - data.m_writeOffset;
00893        if (!len)
00894               return( -1);
00895        PRInt32       count = 0;
00896        const char *pData = data.m_pBuffer + data.m_writeOffset;
00897        while (((*pData == 0x0D) || (*pData == 0x0A)) && (count < len)) {
00898               pData++;
00899               count++;
00900        }
00901        while ((*pData != 0x0D) && (*pData != 0x0A) && (count < len)) {
00902               pData++;
00903               count++;
00904        }
00905        
00906        if (count < len)
00907               return( count);
00908 
00909        return( -1);
00910 }
00911 
00912 PRInt32 nsEudoraCompose::IsEndHeaders( SimpleBufferTonyRCopiedOnce& data)
00913 {
00914        PRInt32 len = data.m_bytesInBuf - data.m_writeOffset;
00915        if (len < 2)
00916               return( -1);
00917        const char *pChar = data.m_pBuffer + data.m_writeOffset;
00918        if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0D))
00919               return( 2);
00920 
00921        if (len < 4)
00922               return( -1);
00923        if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0A) &&
00924               (*(pChar + 2) == 0x0D) && (*(pChar + 3) == 0x0A))
00925               return( 4);
00926 
00927        return( -1);
00928 }
00929 
00930 
00931 nsresult nsEudoraCompose::CopyComposedMessage( nsCString& fromLine, nsIFileSpec *pSrc, nsIFileSpec *pDst, SimpleBufferTonyRCopiedOnce& copy)
00932 {
00933        copy.m_bytesInBuf = 0;
00934        copy.m_writeOffset = 0;
00935        ReadFileState state;
00936        state.pFile = pSrc;
00937        state.offset = 0;
00938        state.size = 0;
00939        pSrc->GetFileSize( &state.size);
00940        if (!state.size) {
00941               IMPORT_LOG0( "*** Error, unexpected zero file size for composed message\n");
00942               return( NS_ERROR_FAILURE);
00943        }
00944 
00945        nsresult rv = pSrc->OpenStreamForReading();
00946        if (NS_FAILED( rv)) {
00947               IMPORT_LOG0( "*** Error, unable to open composed message file\n");
00948               return( NS_ERROR_FAILURE);
00949        }
00950        
00951        PRInt32 written;
00952        rv = pDst->Write( fromLine.get(), fromLine.Length(), &written);
00953 
00954        // well, isn't this a hoot!
00955        // Read the headers from the new message, get the ones we like
00956        // and write out only the headers we want from the new message,
00957        // along with all of the other headers from the "old" message!
00958        if (NS_SUCCEEDED( rv))
00959               rv = FillMailBuffer( &state, copy);
00960        if (NS_SUCCEEDED( rv))
00961               rv = ReadHeaders( &state, copy, m_readHeaders);
00962        
00963        if (NS_SUCCEEDED( rv)) {
00964               rv = WriteHeaders( pDst, m_readHeaders);
00965        }
00966 
00967        // We need to go ahead and write out the rest of the copy buffer
00968        // so that the following will properly copy the rest of the body
00969        char   lastChar = 0;
00970 
00971        if (NS_SUCCEEDED( rv)) {
00972     rv = EscapeFromSpaceLine(pDst, copy.m_pBuffer + copy.m_writeOffset, copy.m_pBuffer+copy.m_bytesInBuf);
00973               if (copy.m_bytesInBuf)
00974                      lastChar = copy.m_pBuffer[copy.m_bytesInBuf - 1];
00975     if (NS_SUCCEEDED(rv))
00976                 copy.m_writeOffset = copy.m_bytesInBuf;
00977        }
00978 
00979        while ((state.offset < state.size) && NS_SUCCEEDED( rv)) {
00980               rv = FillMailBuffer( &state, copy);
00981               if (NS_SUCCEEDED( rv)) {
00982       rv = EscapeFromSpaceLine(pDst, copy.m_pBuffer + copy.m_writeOffset, copy.m_pBuffer+copy.m_bytesInBuf);
00983                      lastChar = copy.m_pBuffer[copy.m_bytesInBuf - 1];
00984                      if (NS_SUCCEEDED( rv))
00985                   copy.m_writeOffset = copy.m_bytesInBuf;
00986            else
00987         IMPORT_LOG0( "*** Error writing to destination mailbox\n");
00988               }
00989        }
00990 
00991        pSrc->CloseStream();
00992        
00993        if ((lastChar != 0x0A) && NS_SUCCEEDED( rv)) {
00994               rv = pDst->Write( "\x0D\x0A", 2, &written);
00995               if (written != 2)
00996                      rv = NS_ERROR_FAILURE;
00997        }
00998 
00999        return( rv);
01000 }
01001 
01002 nsresult nsEudoraCompose::FillMailBuffer( ReadFileState *pState, SimpleBufferTonyRCopiedOnce& read)
01003 {
01004        if (read.m_writeOffset >= read.m_bytesInBuf) {
01005               read.m_writeOffset = 0;
01006               read.m_bytesInBuf = 0;
01007        }
01008        else if (read.m_writeOffset) {
01009               memcpy( read.m_pBuffer, read.m_pBuffer + read.m_writeOffset, read.m_bytesInBuf - read.m_writeOffset);
01010               read.m_bytesInBuf -= read.m_writeOffset;
01011               read.m_writeOffset = 0;
01012        }
01013 
01014        PRInt32       count = read.m_size - read.m_bytesInBuf;
01015        if (((PRUint32)count + pState->offset) > pState->size)
01016               count = pState->size - pState->offset;
01017        if (count) {
01018               PRInt32              bytesRead = 0;
01019               char *        pBuffer = read.m_pBuffer + read.m_bytesInBuf;
01020               nsresult      rv = pState->pFile->Read( &pBuffer, count, &bytesRead);
01021               if (NS_FAILED( rv)) return( rv);
01022               if (bytesRead != count) return( NS_ERROR_FAILURE);
01023               read.m_bytesInBuf += bytesRead;
01024               pState->offset += bytesRead;
01025        }
01026 
01027        return( NS_OK);
01028 }
01029 
01030 
01031 #define kMaxSpecialHeaders  3
01032 static const char *gSpecialHeaders[kMaxSpecialHeaders] = {
01033        "Content-type",
01034        "MIME-Version",
01035        "Content-transfer-encoding"
01036 };
01037 // consider "X-Accept-Language"?
01038 
01039 #define kMaxReplaceHeaders  5
01040 static const char *gReplaceHeaders[kMaxReplaceHeaders] = {
01041        "From",
01042        "To",
01043        "Subject",
01044        "Reply-to",
01045        "cc"
01046 };
01047 
01048 PRBool nsEudoraCompose::IsReplaceHeader( const char *pHeader)
01049 {
01050        for (int i = 0; i < kMaxReplaceHeaders; i++) {
01051               if (!nsCRT::strcasecmp( pHeader, gReplaceHeaders[i]))
01052                      return( PR_TRUE);
01053        }
01054 
01055        return( PR_FALSE);
01056 }
01057 
01058 PRInt32 nsEudoraCompose::IsSpecialHeader( const char *pHeader)
01059 {
01060        for (int i = 0; i < kMaxSpecialHeaders; i++) {
01061               if (!nsCRT::strcasecmp( pHeader, gSpecialHeaders[i]))
01062                      return( (PRInt32) i);
01063        }
01064 
01065        return( -1);
01066 }
01067 
01068 
01069 nsresult nsEudoraCompose::WriteHeaders( nsIFileSpec *pDst, SimpleBufferTonyRCopiedOnce& newHeaders)
01070 {
01071        // Well, ain't this a peach?
01072        // This is rather disgusting but there really isn't much to be done about it....
01073 
01074        // 1. For each "old" header, replace it with the new one if we want,
01075        // then right it out.
01076        // 2. Then if we haven't written the "important" new headers, write them out
01077        // 3. Terminate the headers with an extra eol.
01078        
01079        PRInt32              n = 0;
01080        nsCString     header;
01081        nsCString     val;
01082        nsCString     replaceVal;
01083        PRInt32              written;
01084        nsresult      rv = NS_OK; // it's ok if we don't have the first header on the predefined lists.
01085        PRInt32              specialHeader;
01086        PRBool        specials[kMaxSpecialHeaders];
01087   PRBool    hasDateHeader = PR_FALSE;
01088        int                  i;
01089 
01090        for (i = 0; i < kMaxSpecialHeaders; i++)
01091               specials[i] = PR_FALSE;
01092 
01093   // m_pHeaders - contains headers from a Eudora msg.
01094   // newHeaders - contains headers from a mozilla msg (more headers here).
01095        do {
01096               GetNthHeader( m_pHeaders, m_headerLen, n, header, val, PR_FALSE);
01097               // GetNthHeader( newHeaders.m_pBuffer, newHeaders.m_writeOffset, n, header, val, PR_FALSE);
01098               if (!header.IsEmpty()) {
01099                      if ((specialHeader = IsSpecialHeader( header.get())) != -1) {
01100                             header.Append( ':');
01101                             GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), val, PR_FALSE);
01102                             header.Truncate( header.Length() - 1);
01103                             specials[specialHeader] = PR_TRUE;
01104                      }
01105                      else if (IsReplaceHeader( header.get())) {
01106                             replaceVal.Truncate( 0);
01107                             header.Append( ':');
01108                             GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), replaceVal, PR_FALSE);
01109                             header.Truncate( header.Length() - 1);
01110                             if (!replaceVal.IsEmpty())
01111                                    val = replaceVal;
01112                      }
01113                      if (!val.IsEmpty()) {
01114         // See if we're writing out a Date: header.
01115         if (!nsCRT::strcasecmp(header.get(), "Date"))
01116           hasDateHeader = PR_TRUE;
01117                             rv = pDst->Write( header.get(), header.Length(), &written);
01118                             if (NS_SUCCEEDED( rv))
01119                                    rv = pDst->Write( ": ", 2, &written);
01120                             if (NS_SUCCEEDED( rv))
01121                                    rv = pDst->Write( val.get(), val.Length(), &written);
01122                             if (NS_SUCCEEDED( rv))
01123                                    rv = pDst->Write( "\x0D\x0A", 2, &written);
01124 
01125                      }
01126               }
01127               n++;
01128        } while (NS_SUCCEEDED( rv) && !header.IsEmpty());
01129 
01130   // If we don't have Date: header so far then use the default one (taken from Eudora "From " line).
01131   if (!hasDateHeader)
01132   {
01133     rv = pDst->Write(m_defaultDate.get(), m_defaultDate.Length(), &written);
01134     if (NS_SUCCEEDED( rv))
01135       rv = pDst->Write( "\x0D\x0A", 2, &written);
01136   }
01137 
01138        for (i = 0; (i < kMaxSpecialHeaders) && NS_SUCCEEDED( rv); i++) {
01139               if (!specials[i]) {
01140                      header = gSpecialHeaders[i];
01141                      header.Append( ':');
01142                      GetHeaderValue( newHeaders.m_pBuffer, newHeaders.m_writeOffset - 1, header.get(), val, PR_FALSE);
01143                      header.Truncate( header.Length() - 1);
01144                      if (!val.IsEmpty()) {
01145                             rv = pDst->Write( header.get(), header.Length(), &written);
01146                             if (NS_SUCCEEDED( rv))
01147                                    rv = pDst->Write( ": ", 2, &written);
01148                             if (NS_SUCCEEDED( rv))
01149                                    rv = pDst->Write( val.get(), val.Length(), &written);
01150                             if (NS_SUCCEEDED( rv))
01151                                    rv = pDst->Write( "\x0D\x0A", 2, &written);
01152                      }
01153               }
01154        }
01155        
01156 
01157        if (NS_SUCCEEDED( rv))
01158               rv = pDst->Write( "\x0D\x0A", 2, &written);
01159        return( rv);
01160 }
01161 
01162