Back to index

lightning-sunbird  0.9+nobinonly
msgMapiImp.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corp.
00018  * Portions created by the Initial Developer are Copyright (C) 2001
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *                 Krishna Mohan Khandrika (kkhandrika@netscape.com)
00023  *                 Rajiv Dayal (rdayal@netscape.com)
00024  *                 David Bienvenu <bienvenu@nventure.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #ifdef MOZ_LOGGING
00041 // this has to be before the pre-compiled header
00042 #define FORCE_PR_LOG /* Allow logging in the release build */
00043 #endif
00044 #include <mapidefs.h>
00045 #include <mapi.h>
00046 #include "msgMapi.h"
00047 #include "msgMapiImp.h"
00048 #include "msgMapiFactory.h"
00049 #include "msgMapiMain.h"
00050 
00051 #include "nsMsgCompFields.h"
00052 #include "msgMapiHook.h"
00053 #include "nsString.h"
00054 #include "nsCOMPtr.h"
00055 #include "nsISupports.h"
00056 #include "nsMsgCompCID.h"
00057 #include "nsIMsgDatabase.h"
00058 #include "nsMsgFolderFlags.h"
00059 #include "nsIMsgHdr.h"
00060 #include "nsMsgBaseCID.h"
00061 #include "nsIMsgAccountManager.h"
00062 #include "nsIMsgFolder.h"
00063 #include "nsIMsgImapMailFolder.h"
00064 #include <time.h>
00065 #include "nsIInputStream.h"
00066 #include "nsIMsgHeaderParser.h"
00067 #include "nsILineInputStream.h"
00068 #include "nsISeekableStream.h"
00069 #include "nsIFileSpec.h"
00070 #include "nsFileSpec.h"
00071 #include "nsILocalFile.h"
00072 #include "nsIFileStreams.h"
00073 #include "nsNetCID.h"
00074 
00075 PRLogModuleInfo *MAPI;
00076 
00077 CMapiImp::CMapiImp()
00078 : m_cRef(1)
00079 {
00080     m_Lock = PR_NewLock();
00081   if (!MAPI)
00082     MAPI = PR_NewLogModule("MAPI");
00083 }
00084 
00085 CMapiImp::~CMapiImp() 
00086 { 
00087     if (m_Lock)
00088         PR_DestroyLock(m_Lock);
00089 }
00090 
00091 STDMETHODIMP CMapiImp::QueryInterface(const IID& aIid, void** aPpv)
00092 {    
00093     if (aIid == IID_IUnknown)
00094     {
00095         *aPpv = static_cast<nsIMapi*>(this); 
00096     }
00097     else if (aIid == IID_nsIMapi)
00098     {
00099         *aPpv = static_cast<nsIMapi*>(this);
00100     }
00101     else
00102     {
00103         *aPpv = nsnull;
00104         return E_NOINTERFACE;
00105     }
00106 
00107     reinterpret_cast<IUnknown*>(*aPpv)->AddRef();
00108     return S_OK;
00109 }
00110 
00111 STDMETHODIMP_(ULONG) CMapiImp::AddRef()
00112 {
00113     return PR_AtomicIncrement(&m_cRef);
00114 }
00115 
00116 STDMETHODIMP_(ULONG) CMapiImp::Release() 
00117 {
00118     PRInt32 temp;
00119     temp = PR_AtomicDecrement(&m_cRef);
00120     if (m_cRef == 0)
00121     {
00122         delete this;
00123         return 0;
00124     }
00125 
00126     return temp;
00127 }
00128 
00129 STDMETHODIMP CMapiImp::IsValid()
00130 {
00131     return S_OK;
00132 }
00133 
00134 STDMETHODIMP CMapiImp::IsValidSession(unsigned long aSession)
00135 {
00136     nsMAPIConfiguration *pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
00137     if (pConfig && pConfig->IsSessionValid(aSession))
00138         return S_OK;
00139 
00140     return E_FAIL;
00141 }
00142 
00143 STDMETHODIMP CMapiImp::Initialize()
00144 {
00145     HRESULT hr = E_FAIL;
00146 
00147     if (!m_Lock)
00148         return E_FAIL;
00149 
00150     PR_Lock(m_Lock);
00151 
00152     // Initialize MAPI Configuration
00153 
00154     nsMAPIConfiguration *pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
00155     if (pConfig != nsnull)
00156         if (nsMapiHook::Initialize())
00157             hr = S_OK;
00158 
00159     PR_Unlock(m_Lock);
00160 
00161     return hr;
00162 }
00163 
00164 STDMETHODIMP CMapiImp::Login(unsigned long aUIArg, LOGIN_PW_TYPE aLogin, LOGIN_PW_TYPE aPassWord,
00165                 unsigned long aFlags, unsigned long *aSessionId)
00166 {
00167     HRESULT hr = E_FAIL;
00168      PRBool bNewSession = PR_FALSE;
00169     char *id_key = nsnull;
00170 
00171     PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::Login using flags %d\n", aFlags));
00172     if (aFlags & MAPI_NEW_SESSION)
00173         bNewSession = PR_TRUE;
00174 
00175     // Check For Profile Name
00176     if (aLogin != nsnull && aLogin[0] != '\0')
00177     {
00178         if (nsMapiHook::VerifyUserName(aLogin, &id_key) == PR_FALSE)
00179         {
00180             *aSessionId = MAPI_E_LOGIN_FAILURE;
00181             PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::Login failed for username %s\n", aLogin));
00182             NS_ASSERTION(PR_FALSE, "failed verifying user name");
00183             return hr;
00184         }
00185     }
00186     else
00187     {
00188       // get default account
00189       nsresult rv;
00190       nsCOMPtr <nsIMsgAccountManager> accountManager = 
00191         do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); 
00192       NS_ENSURE_SUCCESS(rv,MAPI_E_LOGIN_FAILURE);
00193       nsCOMPtr <nsIMsgAccount> account;
00194       nsCOMPtr <nsIMsgIdentity> identity;
00195        rv = accountManager->GetDefaultAccount(getter_AddRefs(account));
00196       NS_ENSURE_SUCCESS(rv,MAPI_E_LOGIN_FAILURE);
00197       account->GetDefaultIdentity(getter_AddRefs(identity));
00198       NS_ENSURE_SUCCESS(rv,MAPI_E_LOGIN_FAILURE);
00199       identity->GetKey(&id_key);
00200 
00201     }
00202 
00203     // finally register(create) the session.
00204     PRUint32 nSession_Id;
00205     PRInt16 nResult = 0;
00206 
00207     nsMAPIConfiguration *pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
00208     if (pConfig != nsnull)
00209         nResult = pConfig->RegisterSession(aUIArg, aLogin, aPassWord,
00210                                            (aFlags & MAPI_FORCE_DOWNLOAD), bNewSession,
00211                                            &nSession_Id, id_key);
00212     switch (nResult)
00213     {
00214         case -1 :
00215         {
00216             *aSessionId = MAPI_E_TOO_MANY_SESSIONS;
00217             return hr;
00218         }
00219         case 0 :
00220         {
00221             *aSessionId = MAPI_E_INSUFFICIENT_MEMORY;
00222             return hr;
00223         }
00224         default :
00225         {
00226             *aSessionId = nSession_Id;
00227             PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::Login succeeded\n"));
00228             break;
00229         }
00230     }
00231 
00232     return S_OK;
00233 }
00234 
00235 STDMETHODIMP CMapiImp::SendMail( unsigned long aSession, lpnsMapiMessage aMessage,
00236      short aRecipCount, lpnsMapiRecipDesc aRecips , short aFileCount, lpnsMapiFileDesc aFiles , 
00237      unsigned long aFlags, unsigned long aReserved)
00238 {
00239     nsresult rv = NS_OK ;
00240 
00241     PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::SendMail using flags %d\n", aFlags));
00242     // Assign the pointers in the aMessage struct to the array of Recips and Files
00243     // recieved here from MS COM. These are used in BlindSendMail and ShowCompWin fns 
00244     aMessage->lpRecips = aRecips ;
00245     aMessage->lpFiles = aFiles ;
00246 
00247     PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::SendMail flags=%x subject: %s sender: %s\n", 
00248       aFlags, (char *) aMessage->lpszSubject, (aMessage->lpOriginator) ? aMessage->lpOriginator->lpszAddress : ""));
00249 
00251     nsCOMPtr<nsIMsgCompFields> pCompFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv) ;
00252     if (NS_FAILED(rv) || (!pCompFields) ) return MAPI_E_INSUFFICIENT_MEMORY ;
00253 
00254     if (aFlags & MAPI_UNICODE)
00255         rv = nsMapiHook::PopulateCompFields(aMessage, pCompFields) ;
00256     else
00257         rv = nsMapiHook::PopulateCompFieldsWithConversion(aMessage, pCompFields) ;
00258     
00259     if (NS_SUCCEEDED (rv))
00260     {
00261         // see flag to see if UI needs to be brought up
00262         if (!(aFlags & MAPI_DIALOG))
00263         {
00264             rv = nsMapiHook::BlindSendMail(aSession, pCompFields);
00265         }
00266         else
00267         {
00268             rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
00269         }
00270     }
00271     
00272     return nsMAPIConfiguration::GetMAPIErrorFromNSError (rv) ;
00273 }
00274 
00275 
00276 STDMETHODIMP CMapiImp::SendDocuments( unsigned long aSession, LPTSTR aDelimChar,
00277                             LPTSTR aFilePaths, LPTSTR aFileNames, ULONG aFlags)
00278 {
00279     nsresult rv = NS_OK ;
00280 
00281     PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::SendDocument using flags %d\n", aFlags));
00283     nsCOMPtr<nsIMsgCompFields> pCompFields = do_CreateInstance(NS_MSGCOMPFIELDS_CONTRACTID, &rv) ;
00284     if (NS_FAILED(rv) || (!pCompFields) ) return MAPI_E_INSUFFICIENT_MEMORY ;
00285 
00286     if (aFilePaths)
00287     {
00288         rv = nsMapiHook::PopulateCompFieldsForSendDocs(pCompFields, aFlags, aDelimChar, aFilePaths) ;
00289     }
00290 
00291     if (NS_SUCCEEDED (rv)) 
00292         rv = nsMapiHook::ShowComposerWindow(aSession, pCompFields);
00293     else
00294       PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::SendDocument error rv = %lx, paths = %s names = %s\n", rv, aFilePaths, aFileNames));
00295 
00296     return nsMAPIConfiguration::GetMAPIErrorFromNSError (rv) ;
00297 }
00298 
00299 nsresult CMapiImp::GetDefaultInbox(nsIMsgFolder **inboxFolder)
00300 {
00301   // get default account
00302   nsresult rv;
00303   nsCOMPtr <nsIMsgAccountManager> accountManager = 
00304     do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); 
00305   NS_ENSURE_SUCCESS(rv,rv);
00306 
00307   nsCOMPtr <nsIMsgAccount> account;
00308   rv = accountManager->GetDefaultAccount(getter_AddRefs(account));
00309   NS_ENSURE_SUCCESS(rv,rv);
00310 
00311   // get incoming server
00312   nsCOMPtr <nsIMsgIncomingServer> server;
00313   rv = account->GetIncomingServer(getter_AddRefs(server));
00314   NS_ENSURE_SUCCESS(rv,rv);
00315 
00316   nsXPIDLCString type;
00317   rv = server->GetType(getter_Copies(type));
00318   NS_ENSURE_SUCCESS(rv,rv);
00319 
00320   // we only care about imap and pop3
00321   if (!(nsCRT::strcmp(type.get(), "imap")) ||
00322       !(nsCRT::strcmp(type.get(), "pop3"))) 
00323   {
00324     // imap and pop3 account should have an Inbox
00325     nsCOMPtr<nsIMsgFolder> rootMsgFolder;
00326     rv = server->GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
00327     NS_ENSURE_SUCCESS(rv,rv);
00328  
00329     if (!rootMsgFolder)
00330       return NS_ERROR_FAILURE;
00331  
00332     PRUint32 numFolders = 0;
00333     rv = rootMsgFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, inboxFolder);
00334     NS_ENSURE_SUCCESS(rv,rv);
00335  
00336     if (!*inboxFolder)
00337      return NS_ERROR_FAILURE;
00338  
00339   }
00340   return NS_OK;
00341 }
00342 
00343 //*****************************************************************************
00344 // Encapsulate the XP DB stuff required to enumerate messages
00345 
00346 class MsgMapiListContext 
00347 {
00348 public:
00349   MsgMapiListContext () {}
00350   ~MsgMapiListContext ();
00351   
00352   nsresult OpenDatabase (nsIMsgFolder *folder);
00353   
00354   nsMsgKey GetNext ();
00355   nsresult MarkRead (nsMsgKey key, PRBool read);
00356   
00357   lpnsMapiMessage GetMessage (nsMsgKey, unsigned long flFlags);
00358   PRBool IsIMAPHost(void);
00359   PRBool DeleteMessage(nsMsgKey key);
00360   
00361 protected:
00362   
00363   char *ConvertDateToMapiFormat (time_t);
00364   char *ConvertBodyToMapiFormat (nsIMsgDBHdr *hdr);
00365   void ConvertRecipientsToMapiFormat (nsIMsgHeaderParser *parser,  
00366                           const char *ourRecips, lpnsMapiRecipDesc mapiRecips,
00367                           int mapiRecipClass);
00368   
00369   nsCOMPtr <nsIMsgFolder> m_folder;
00370   nsCOMPtr <nsIMsgDatabase> m_db;
00371   nsCOMPtr <nsISimpleEnumerator> m_msgEnumerator;
00372 };
00373 
00374 
00375 LONG CMapiImp::InitContext(unsigned long session, MsgMapiListContext **listContext)
00376 {
00377   nsMAPIConfiguration * pMapiConfig = nsMAPIConfiguration::GetMAPIConfiguration() ;
00378   if (!pMapiConfig)
00379     return NS_ERROR_FAILURE ;  // get the singelton obj
00380   *listContext = (MsgMapiListContext *) pMapiConfig->GetMapiListContext(session);
00381   // This is the first message
00382   if (!*listContext)
00383   {
00384     nsCOMPtr <nsIMsgFolder> inboxFolder;
00385     nsresult rv = GetDefaultInbox(getter_AddRefs(inboxFolder));
00386     if (NS_FAILED(rv))
00387     {
00388       NS_ASSERTION(PR_FALSE, "in init context, no inbox");
00389       return(MAPI_E_NO_MESSAGES);
00390     }
00391 
00392     *listContext = new MsgMapiListContext;
00393     if (!*listContext)
00394       return MAPI_E_INSUFFICIENT_MEMORY;
00395 
00396     rv = (*listContext)->OpenDatabase(inboxFolder);
00397     if (NS_FAILED(rv))
00398     {
00399       pMapiConfig->SetMapiListContext(session, NULL);
00400       delete *listContext;
00401       NS_ASSERTION(PR_FALSE, "in init context, unable to open db");
00402       return MAPI_E_NO_MESSAGES;
00403     }
00404     else
00405       pMapiConfig->SetMapiListContext(session, *listContext);
00406   }
00407   return SUCCESS_SUCCESS;
00408 }
00409 
00410 STDMETHODIMP CMapiImp::FindNext(unsigned long aSession, unsigned long ulUIParam, LPTSTR lpszMessageType,
00411                               LPTSTR lpszSeedMessageID, unsigned long flFlags, unsigned long ulReserved,
00412                               unsigned char lpszMessageID[64])
00413 
00414 {
00415   //
00416   // If this is true, then this is the first call to this FindNext function
00417   // and we should start the enumeration operation.
00418   //
00419 
00420   *lpszMessageID = '\0';
00421   nsMAPIConfiguration * pMapiConfig = nsMAPIConfiguration::GetMAPIConfiguration() ;
00422   if (!pMapiConfig) 
00423   {
00424     NS_ASSERTION(PR_FALSE, "failed to get config in findnext");
00425     return NS_ERROR_FAILURE ;  // get the singelton obj
00426   }
00427   MsgMapiListContext *listContext;
00428   LONG ret = InitContext(aSession, &listContext);
00429   if (ret != SUCCESS_SUCCESS)
00430   {
00431     NS_ASSERTION(PR_FALSE, "init context failed");
00432     return ret;
00433   }
00434   NS_ASSERTION(listContext, "initContext returned null context");
00435   if (listContext)
00436   {
00437 //    NS_ASSERTION(PR_FALSE, "find next init context succeeded");
00438     nsMsgKey nextKey = listContext->GetNext();
00439     if (nextKey == nsMsgKey_None)
00440     {
00441       pMapiConfig->SetMapiListContext(aSession, NULL);
00442       delete listContext;
00443       return(MAPI_E_NO_MESSAGES);
00444     }
00445 
00446 //    TRACE("MAPI: ProcessMAPIFindNext() Found message id = %d\n", nextKey);
00447 
00448     sprintf((char *) lpszMessageID, "%d", nextKey);
00449   }
00450 
00451   PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::FindNext returning key %s\n", (char *) lpszMessageID));
00452   return(SUCCESS_SUCCESS);
00453 }
00454 
00455 STDMETHODIMP CMapiImp::ReadMail(unsigned long aSession, unsigned long ulUIParam, LPTSTR lpszMessageID,
00456                               unsigned long flFlags, unsigned long ulReserved, lpnsMapiMessage *lppMessage)
00457 {
00458   PRInt32 irv;
00459   nsCAutoString keyString((char *) lpszMessageID);
00460   PR_LOG(MAPI, PR_LOG_DEBUG, ("CMapiImp::ReadMail asking for key %s\n", (char *) lpszMessageID));
00461   nsMsgKey msgKey = keyString.ToInteger(&irv);
00462   if (irv)
00463   {
00464     NS_ASSERTION(PR_FALSE, "invalid lpszMessageID");
00465     return MAPI_E_INVALID_MESSAGE;
00466   }
00467   MsgMapiListContext *listContext;
00468   LONG ret = InitContext(aSession, &listContext);
00469   if (ret != SUCCESS_SUCCESS)
00470   {
00471     NS_ASSERTION(PR_FALSE, "init context failed in ReadMail");
00472     return ret;
00473   }
00474   *lppMessage = listContext->GetMessage (msgKey, flFlags);
00475   NS_ASSERTION(*lppMessage, "get message failed");
00476 
00477   return (*lppMessage) ? SUCCESS_SUCCESS : E_FAIL;
00478 }
00479 
00480 
00481 STDMETHODIMP CMapiImp::DeleteMail(unsigned long aSession, unsigned long ulUIParam, LPTSTR lpszMessageID,
00482                               unsigned long flFlags, unsigned long ulReserved)
00483 {
00484   PRInt32 irv;
00485   nsCAutoString keyString((char *) lpszMessageID);
00486   nsMsgKey msgKey = keyString.ToInteger(&irv);
00487   if (irv)
00488     return SUCCESS_SUCCESS;
00489   MsgMapiListContext *listContext;
00490   LONG ret = InitContext(aSession, &listContext);
00491   if (ret != SUCCESS_SUCCESS)
00492     return ret;
00493   return (listContext->DeleteMessage(msgKey)) ? SUCCESS_SUCCESS : MAPI_E_INVALID_MESSAGE;
00494 }
00495 
00496 STDMETHODIMP CMapiImp::SaveMail(unsigned long aSession, unsigned long ulUIParam,  lpnsMapiMessage lppMessage,
00497                               unsigned long flFlags, unsigned long ulReserved, LPTSTR lpszMessageID)
00498 {
00499   MsgMapiListContext *listContext;
00500   LONG ret = InitContext(aSession, &listContext);
00501   if (ret != SUCCESS_SUCCESS)
00502     return ret;
00503   return S_OK;
00504 }
00505 
00506 
00507 STDMETHODIMP CMapiImp::Logoff (unsigned long aSession)
00508 {
00509     nsMAPIConfiguration *pConfig = nsMAPIConfiguration::GetMAPIConfiguration();
00510 
00511     if (pConfig->UnRegisterSession((PRUint32)aSession))
00512         return S_OK;
00513 
00514     return E_FAIL;
00515 }
00516 
00517 STDMETHODIMP CMapiImp::CleanUp()
00518 {
00519     nsMapiHook::CleanUp();
00520     return S_OK;
00521 }
00522 
00523 
00524 #define           MAX_NAME_LEN    256
00525 
00526 
00527 MsgMapiListContext::~MsgMapiListContext ()
00528 {
00529   if (m_db)
00530     m_db->Close(PR_FALSE);
00531 }
00532 
00533 
00534 nsresult MsgMapiListContext::OpenDatabase (nsIMsgFolder *folder)
00535 {
00536   nsresult dbErr = NS_ERROR_FAILURE;
00537   if (folder)
00538   {
00539     m_folder = folder;
00540     dbErr = folder->GetMsgDatabase(nsnull, getter_AddRefs(m_db));
00541     if (m_db)
00542       dbErr = m_db->EnumerateMessages(getter_AddRefs(m_msgEnumerator));
00543   }
00544   return dbErr;
00545 }
00546 
00547 PRBool 
00548 MsgMapiListContext::IsIMAPHost(void)
00549 {
00550   if (!m_folder) 
00551     return FALSE;
00552   nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_folder);
00553 
00554   return imapFolder != nsnull;
00555 }
00556 
00557 nsMsgKey MsgMapiListContext::GetNext ()
00558 {
00559   nsMsgKey key = nsMsgKey_None;
00560   PRBool    keepTrying = TRUE;
00561   
00562 //  NS_ASSERTION (m_msgEnumerator && m_db, "need enumerator and db");
00563   if (m_msgEnumerator && m_db)
00564   {
00565     
00566     do 
00567     {
00568       keepTrying = FALSE;  
00569       nsCOMPtr <nsISupports> hdrISupports;
00570       nsCOMPtr <nsIMsgDBHdr> msgHdr;
00571       if (NS_SUCCEEDED(m_msgEnumerator->GetNext(getter_AddRefs(hdrISupports))) && hdrISupports)
00572       {
00573         msgHdr = do_QueryInterface(hdrISupports);
00574         msgHdr->GetMessageKey(&key);
00575         
00576         // Check here for IMAP message...if not, just return...
00577         if (!IsIMAPHost())
00578           return key;
00579         
00580         // If this is an IMAP message, we have to make sure we have a valid
00581         // body to work with.
00582         PRUint32  flags = 0;
00583         
00584         (void) msgHdr->GetFlags(&flags);
00585         if (flags & MSG_FLAG_OFFLINE) 
00586           return key;
00587         
00588         // Ok, if we get here, we have an IMAP message without a body!
00589         // We need to keep trying by calling the GetNext member recursively...
00590         keepTrying = TRUE;
00591       }
00592     } while (keepTrying);
00593   }
00594   
00595   return key;
00596 }
00597 
00598 
00599 nsresult MsgMapiListContext::MarkRead (nsMsgKey key, PRBool read)
00600 {
00601   nsresult err = NS_ERROR_FAILURE;
00602   NS_ASSERTION(m_db, "no db");
00603   if (m_db)
00604     err = m_db->MarkRead (key, read, nsnull);
00605   return err;
00606 }
00607 
00608 
00609 lpnsMapiMessage MsgMapiListContext::GetMessage (nsMsgKey key, unsigned long flFlags)
00610 {
00611   lpnsMapiMessage message = (lpnsMapiMessage) CoTaskMemAlloc (sizeof(nsMapiMessage));
00612   memset(message, 0, sizeof(nsMapiMessage));
00613   if (message)
00614   {
00615     nsXPIDLCString subject;
00616     nsXPIDLCString author;
00617     nsCOMPtr <nsIMsgDBHdr> msgHdr;
00618 
00619     nsresult rv = m_db->GetMsgHdrForKey (key, getter_AddRefs(msgHdr));
00620     if (msgHdr)
00621     {
00622       msgHdr->GetSubject (getter_Copies(subject));
00623       message->lpszSubject = (char *) CoTaskMemAlloc(subject.Length() + 1);
00624       strcpy((char *) message->lpszSubject, subject.get());
00625       PRUint32 date;
00626       (void) msgHdr->GetDateInSeconds(&date);
00627       message->lpszDateReceived = ConvertDateToMapiFormat (date);
00628       
00629       // Pull out the flags info
00630       // anything to do with MAPI_SENT? Since we're only reading the Inbox, I guess not
00631       PRUint32 ourFlags;
00632       (void) msgHdr->GetFlags(&ourFlags);
00633       if (!(ourFlags & MSG_FLAG_READ))
00634         message->flFlags |= MAPI_UNREAD;
00635       if (ourFlags & (MSG_FLAG_MDN_REPORT_NEEDED | MSG_FLAG_MDN_REPORT_SENT))
00636         message->flFlags |= MAPI_RECEIPT_REQUESTED;
00637       
00638       nsCOMPtr<nsIMsgHeaderParser> parser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
00639       if (!parser)
00640         return nsnull;
00641       // Pull out the author/originator info
00642       message->lpOriginator = (lpnsMapiRecipDesc) CoTaskMemAlloc (sizeof(nsMapiRecipDesc));
00643       memset(message->lpOriginator, 0, sizeof(nsMapiRecipDesc));
00644       if (message->lpOriginator)
00645       {
00646         msgHdr->GetAuthor (getter_Copies(author));
00647         ConvertRecipientsToMapiFormat (parser, author.get(), message->lpOriginator, MAPI_ORIG);
00648       }
00649       // Pull out the To/CC info
00650       nsXPIDLCString recipients, ccList;
00651       msgHdr->GetRecipients(getter_Copies(recipients));
00652       msgHdr->GetCcList(getter_Copies(ccList));
00653 
00654       PRUint32 numToRecips;
00655       PRUint32 numCCRecips;
00656       parser->ParseHeaderAddresses(nsnull, recipients, nsnull, nsnull, &numToRecips);
00657       parser->ParseHeaderAddresses(nsnull, ccList, nsnull, nsnull, &numCCRecips);
00658 
00659       message->lpRecips = (lpnsMapiRecipDesc) CoTaskMemAlloc ((numToRecips + numCCRecips) * sizeof(MapiRecipDesc));
00660       memset(message->lpRecips, 0, (numToRecips + numCCRecips) * sizeof(MapiRecipDesc));
00661       if (message->lpRecips)
00662       {
00663         ConvertRecipientsToMapiFormat (parser, recipients, message->lpRecips, MAPI_TO);
00664         ConvertRecipientsToMapiFormat (parser, ccList, &message->lpRecips[numToRecips], MAPI_CC);
00665       }
00666   
00667       PR_LOG(MAPI, PR_LOG_DEBUG, ("MsgMapiListContext::GetMessage flags=%x subject %s date %s sender %s\n", 
00668         flFlags, (char *) message->lpszSubject,(char *) message->lpszDateReceived, author.get()) );
00669 
00670       // Convert any body text that we have locally
00671       if (!(flFlags & MAPI_ENVELOPE_ONLY))
00672         message->lpszNoteText = (char *) ConvertBodyToMapiFormat (msgHdr);
00673       
00674     }
00675     if (! (flFlags & (MAPI_PEEK | MAPI_ENVELOPE_ONLY)))
00676       m_db->MarkRead(key, PR_TRUE, nsnull);
00677   }
00678   return message;
00679 }
00680 
00681 
00682 char *MsgMapiListContext::ConvertDateToMapiFormat (time_t ourTime)
00683 {
00684   char *date = (char*) CoTaskMemAlloc(32);
00685   if (date)
00686   {
00687     // MAPI time format is YYYY/MM/DD HH:MM
00688     // Note that we're not using XP_StrfTime because that localizes the time format,
00689     // and the way I read the MAPI spec is that their format is canonical, not localized.
00690     struct tm *local = localtime (&ourTime);
00691     if (local)
00692       strftime (date, 32, "%Y/%m/%d %I:%M", local); //use %H if hours should be 24 hour format
00693   }
00694   return date;
00695 }
00696 
00697 
00698 void MsgMapiListContext::ConvertRecipientsToMapiFormat (nsIMsgHeaderParser *parser, const char *recipients, lpnsMapiRecipDesc mapiRecips,
00699                                                         int mapiRecipClass)
00700 {
00701   char *names = nsnull;
00702   char *addresses = nsnull;
00703   
00704   if (!parser)
00705     return ;
00706   PRUint32 numAddresses = 0;
00707   parser->ParseHeaderAddresses(nsnull, recipients, &names, &addresses, &numAddresses);
00708   
00709   if (numAddresses > 0)
00710   {
00711     char *walkNames = names;
00712     char *walkAddresses = addresses;
00713     for (int i = 0; i < numAddresses; i++)
00714     {
00715       if (walkNames)
00716       {
00717         if (*walkNames)
00718         {
00719           mapiRecips[i].lpszName = (char *) CoTaskMemAlloc(strlen(walkNames) + 1);
00720           if (mapiRecips[i].lpszName )
00721             strcpy((char *) mapiRecips[i].lpszName, walkNames);
00722         }
00723         walkNames += strlen (walkNames) + 1;
00724       }
00725       
00726       if (walkAddresses)
00727       {
00728         if (*walkAddresses)
00729         {
00730           mapiRecips[i].lpszAddress = (char *) CoTaskMemAlloc(strlen(walkAddresses) + 1);
00731           if (mapiRecips[i].lpszAddress)
00732             strcpy((char *) mapiRecips[i].lpszAddress, walkAddresses);
00733         }
00734         walkAddresses += strlen (walkAddresses) + 1;
00735       }
00736       
00737       mapiRecips[i].ulRecipClass = mapiRecipClass;
00738     }
00739   }
00740   
00741   PR_Free(names);
00742   PR_Free(addresses);
00743 }
00744 
00745 
00746 char *MsgMapiListContext::ConvertBodyToMapiFormat (nsIMsgDBHdr *hdr)
00747 {
00748   const int kBufLen = 64000; // I guess we only return the first 64K of a message.
00749   int bytesUsed = 0;
00750 #define EMPTY_MESSAGE_LINE(buf) (buf[0] == nsCRT::CR || buf[0] == nsCRT::LF || buf[0] == '\0')
00751 
00752   nsCOMPtr <nsIMsgFolder> folder;
00753   hdr->GetFolder(getter_AddRefs(folder));
00754   if (!folder)
00755     return nsnull;
00756 
00757   nsCOMPtr <nsIInputStream> inputStream;
00758   nsCOMPtr <nsIFileSpec> fileSpec;
00759   folder->GetPath(getter_AddRefs(fileSpec));
00760   nsFileSpec realSpec;
00761   fileSpec->GetFileSpec(&realSpec);
00762   nsCOMPtr <nsILocalFile> localFile;
00763   NS_FileSpecToIFile(&realSpec, getter_AddRefs(localFile));
00764 
00765   nsresult rv;
00766   nsCOMPtr<nsIFileInputStream> fileStream = do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
00767   NS_ENSURE_SUCCESS(rv, nsnull);
00768 
00769   rv = fileStream->Init(localFile,  PR_RDONLY, 0664, PR_FALSE);  //just have to read the messages
00770   inputStream = do_QueryInterface(fileStream);
00771 
00772   if (inputStream)
00773   {
00774     nsCOMPtr <nsILineInputStream> fileLineStream = do_QueryInterface(inputStream);
00775     if (!fileLineStream)
00776       return nsnull;
00777     // ### really want to skip past headers...
00778     PRUint32 messageOffset;
00779     PRUint32 lineCount;
00780     hdr->GetMessageOffset(&messageOffset);
00781     hdr->GetLineCount(&lineCount);
00782     nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(inputStream);
00783     seekableStream->Seek(PR_SEEK_SET, messageOffset);
00784     PRBool hasMore = PR_TRUE;
00785     nsCAutoString curLine;
00786     PRBool inMessageBody = PR_FALSE;
00787     nsresult rv = NS_OK;
00788     while (hasMore) // advance past message headers
00789     {
00790       nsresult rv = fileLineStream->ReadLine(curLine, &hasMore);
00791       if (NS_FAILED(rv) || EMPTY_MESSAGE_LINE(curLine))
00792         break;
00793     }
00794     PRUint32 msgSize;
00795     hdr->GetMessageSize(&msgSize);
00796     if (msgSize > kBufLen)
00797       msgSize = kBufLen - 1;
00798     // this is too big, since it includes the msg hdr size...oh well
00799     char *body = (char*) CoTaskMemAlloc (msgSize + 1);
00800 
00801     if (!body)
00802       return nsnull;
00803     PRInt32 bytesCopied = 0;
00804     for (hasMore = TRUE; lineCount > 0 && hasMore && NS_SUCCEEDED(rv); lineCount--)
00805     {
00806       rv = fileLineStream->ReadLine(curLine, &hasMore);
00807       if (NS_FAILED(rv))
00808         break;
00809       curLine.Append(CRLF);
00810       // make sure we have room left
00811       if (bytesCopied + curLine.Length() < msgSize)
00812       {
00813         strcpy(body + bytesCopied, curLine.get());
00814         bytesCopied += curLine.Length();
00815       }
00816     }
00817     PR_LOG(MAPI, PR_LOG_DEBUG, ("ConvertBodyToMapiFormat size=%x allocated size %x body = %100.100s\n", 
00818         bytesCopied, msgSize + 1, (char *) body) );
00819     body[bytesCopied] = '\0';   // rhp - fix last line garbage...
00820     return body;
00821   }
00822   return nsnull;
00823 }
00824 
00825 
00826 //*****************************************************************************
00827 // MSGMAPI API implementation
00828 
00829 
00830 
00831 static void msg_FreeMAPIFile(lpMapiFileDesc f)
00832 {
00833   if (f)
00834   {
00835     CoTaskMemFree(f->lpszPathName);
00836     CoTaskMemFree(f->lpszFileName);
00837   }
00838 }
00839 
00840 static void msg_FreeMAPIRecipient(lpMapiRecipDesc rd)
00841 {
00842   if (rd)
00843   {
00844     if (rd->lpszName)
00845       CoTaskMemFree(rd->lpszName);
00846     if (rd->lpszAddress)
00847       CoTaskMemFree(rd->lpszAddress);
00848     // CoTaskMemFree(rd->lpEntryID);  
00849   }
00850 }
00851 
00852 extern "C" void MSG_FreeMapiMessage (lpMapiMessage msg)
00853 {
00854   ULONG i;
00855   
00856   if (msg)
00857   {
00858     CoTaskMemFree(msg->lpszSubject);
00859     CoTaskMemFree(msg->lpszNoteText);
00860     CoTaskMemFree(msg->lpszMessageType);
00861     CoTaskMemFree(msg->lpszDateReceived);
00862     CoTaskMemFree(msg->lpszConversationID);
00863     
00864     if (msg->lpOriginator)
00865       msg_FreeMAPIRecipient(msg->lpOriginator);
00866     
00867     for (i=0; i<msg->nRecipCount; i++)
00868       if (&(msg->lpRecips[i]) != nsnull)
00869         msg_FreeMAPIRecipient(&(msg->lpRecips[i]));
00870       
00871       CoTaskMemFree(msg->lpRecips);
00872       
00873       for (i=0; i<msg->nFileCount; i++)
00874         if (&(msg->lpFiles[i]) != nsnull)
00875           msg_FreeMAPIFile(&(msg->lpFiles[i]));
00876         
00877       CoTaskMemFree(msg->lpFiles);
00878       
00879       CoTaskMemFree(msg);
00880   }
00881 }
00882 
00883 
00884 extern "C" PRBool MsgMarkMapiMessageRead (nsIMsgFolder *folder, nsMsgKey key, PRBool read)
00885 {
00886   PRBool success = FALSE;
00887   MsgMapiListContext *context = new MsgMapiListContext();
00888   if (context)
00889   {
00890     if (NS_SUCCEEDED(context->OpenDatabase(folder)))
00891     {
00892       if (NS_SUCCEEDED(context->MarkRead (key, read)))
00893         success = TRUE;
00894     }
00895     delete context;
00896   }
00897   return success;
00898 }
00899 
00900 PRBool 
00901 MsgMapiListContext::DeleteMessage(nsMsgKey key)
00902 {
00903   if (!m_db)
00904     return FALSE;
00905   
00906   nsMsgKeyArray messageKeys;      
00907   messageKeys.InsertAt(0, key);
00908   
00909   if ( !IsIMAPHost() )
00910   {
00911     return NS_SUCCEEDED((m_db->DeleteMessages(&messageKeys, nsnull)));
00912   }
00913   else
00914   {
00915     return FALSE;
00916 #if 0 
00917     if ( m_folder->GetIMAPFolderInfoMail() )
00918     {
00919       (m_folder->GetIMAPFolderInfoMail())->DeleteSpecifiedMessages(pane, messageKeys, nsMsgKey_None);
00920       m_db->DeleteMessage(key, nsnull, FALSE);
00921       return TRUE;
00922     }
00923     else
00924     {
00925       return FALSE;
00926     }
00927 #endif
00928   }
00929 }
00930 
00931 /* Return TRUE on success, FALSE on failure */
00932 extern "C" PRBool MSG_DeleteMapiMessage(nsIMsgFolder *folder, nsMsgKey key)
00933 {
00934   PRBool success = FALSE;
00935   MsgMapiListContext *context = new MsgMapiListContext();
00936   if (context) 
00937   {
00938     if (NS_SUCCEEDED(context->OpenDatabase(folder)))
00939     {
00940       success = context->DeleteMessage(key);
00941     }
00942     
00943     delete context;
00944   }
00945   
00946   return success;
00947 }
00948