Back to index

lightning-sunbird  0.9+nobinonly
nsIMAPBodyShell.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "msgCore.h"
00039 #include "nsIMAPHostSessionList.h"
00040 #include "nsIMAPBodyShell.h"
00041 #include "nsImapProtocol.h"
00042 
00043 #include "nsHashtable.h"
00044 #include "nsMimeTypes.h"
00045 #include "nsIPrefBranch.h"
00046 #include "nsIPrefService.h"
00047 #include "nsITransport.h"
00048 
00049 // need to talk to Rich about this...
00050 #define       IMAP_EXTERNAL_CONTENT_HEADER "X-Mozilla-IMAP-Part"
00051 
00052 // imapbody.cpp
00053 // Implementation of the nsIMAPBodyShell and associated classes
00054 // These are used to parse IMAP BODYSTRUCTURE responses, and intelligently (?)
00055 // figure out what parts we need to display inline.
00056 
00057 static PRInt32 gMaxDepth = 0;      // Maximum depth that we will descend before marking a shell invalid.
00058                                 // This will be initialized from the prefs the first time a shell is created.
00059                                 // This is to protect against excessively complex (deep) BODYSTRUCTURE responses.
00060 
00061 
00062 /*
00063         Create a nsIMAPBodyShell from a full BODYSTRUCUTRE response from the parser.
00064 
00065         The body shell represents a single, top-level object, the message.  The message body
00066         might be treated as either a container or a leaf (just like any arbitrary part).
00067 
00068         Steps for creating a part:
00069         1. Pull out the paren grouping for the part
00070         2. Create a generic part object with that buffer
00071         3. The factory will return either a leaf or container, depending on what it really is.
00072         4. It is responsible for parsing its children, if there are any
00073 */
00074 
00075 
00077 
00078 nsIMAPBodyShell::nsIMAPBodyShell(nsImapProtocol *protocolConnection, nsIMAPBodypartMessage *message, PRUint32 UID, const char *folderName)
00079 {
00080   if (gMaxDepth == 0)
00081   {
00082     nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00083     if (prefBranch) {
00084       // one-time initialization
00085       prefBranch->GetIntPref("mail.imap.mime_parts_on_demand_max_depth", &gMaxDepth);
00086     }
00087   }
00088   
00089   m_isValid = PR_FALSE;
00090   m_isBeingGenerated = PR_FALSE;
00091   m_cached = PR_FALSE;
00092   m_gotAttachmentPref = PR_FALSE;
00093   m_generatingWholeMessage = PR_FALSE;
00094   m_generatingPart = NULL;
00095   m_protocolConnection = protocolConnection;
00096   m_message = message;
00097   NS_ASSERTION(m_protocolConnection, "non null connection");
00098   if (!m_protocolConnection)
00099     return;
00100   m_prefetchQueue = new nsIMAPMessagePartIDArray();
00101   if (!m_prefetchQueue)
00102     return;
00103   m_UID = "";
00104   m_UID.AppendInt(UID);
00105 #ifdef DEBUG_chrisf
00106   NS_ASSERTION(folderName);
00107 #endif
00108   if (!folderName)
00109     return;
00110   m_folderName = nsCRT::strdup(folderName);
00111   if (!m_folderName)
00112     return;
00113   
00114   SetContentModified(GetShowAttachmentsInline() ? IMAP_CONTENT_MODIFIED_VIEW_INLINE : IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS);
00115 
00116   SetIsValid(m_message != nsnull);
00117 }
00118 
00119 nsIMAPBodyShell::~nsIMAPBodyShell()
00120 {
00121   delete m_message;
00122   delete m_prefetchQueue;
00123   PR_Free(m_folderName);
00124 }
00125 
00126 void nsIMAPBodyShell::SetIsValid(PRBool valid)
00127 {
00128   //   if (!valid)
00129   //          PR_LOG(IMAP, out, ("BODYSHELL: Shell is invalid."));
00130   m_isValid = valid;
00131 }
00132 
00133 
00134 PRBool nsIMAPBodyShell::GetShowAttachmentsInline()
00135 {
00136   if (!m_gotAttachmentPref)
00137   {
00138     m_showAttachmentsInline = !m_protocolConnection || m_protocolConnection->GetShowAttachmentsInline();
00139     m_gotAttachmentPref = PR_TRUE;
00140   }
00141   
00142   return m_showAttachmentsInline;
00143 }
00144 
00145 // Fills in buffer (and adopts storage) for header object
00146 void nsIMAPBodyShell::AdoptMessageHeaders(char *headers, const char *partNum)
00147 {
00148   if (!GetIsValid())
00149     return;
00150   
00151   if (!partNum)
00152     partNum = "0";
00153   
00154   // we are going to say that a message header object only has
00155   // part data, and no header data.
00156   nsIMAPBodypart *foundPart = m_message->FindPartWithNumber(partNum);
00157   if (foundPart)
00158   {
00159     nsIMAPBodypartMessage *messageObj = foundPart->GetnsIMAPBodypartMessage();
00160     if (messageObj)
00161     {
00162       messageObj->AdoptMessageHeaders(headers);
00163       if (!messageObj->GetIsValid())
00164         SetIsValid(PR_FALSE);
00165     }
00166     else
00167     {
00168       // We were filling in message headers for a given part number.
00169       // We looked up that part number, found an object, but it
00170       // wasn't of type message/rfc822.
00171       // Something's wrong.
00172       NS_ASSERTION(PR_FALSE, "object not of type message rfc822");
00173     }
00174   }
00175   else
00176     SetIsValid(PR_FALSE);
00177 }
00178 
00179 // Fills in buffer (and adopts storage) for MIME headers in appropriate object.
00180 // If object can't be found, sets isValid to PR_FALSE.
00181 void nsIMAPBodyShell::AdoptMimeHeader(const char *partNum, char *mimeHeader)
00182 {
00183   if (!GetIsValid())
00184     return;
00185   
00186   NS_ASSERTION(partNum, "null partnum in body shell");
00187   
00188   nsIMAPBodypart *foundPart = m_message->FindPartWithNumber(partNum);
00189   
00190   if (foundPart)
00191   {
00192     foundPart->AdoptHeaderDataBuffer(mimeHeader);
00193     if (!foundPart->GetIsValid())
00194       SetIsValid(PR_FALSE);
00195   }
00196   else
00197   {
00198     SetIsValid(PR_FALSE);
00199   }
00200 }
00201 
00202 
00203 void nsIMAPBodyShell::AddPrefetchToQueue(nsIMAPeFetchFields fields, const char *partNumber)
00204 {
00205   nsIMAPMessagePartID *newPart = new nsIMAPMessagePartID(fields, partNumber);
00206   if (newPart)
00207   {
00208     m_prefetchQueue->AppendElement(newPart);
00209   }
00210   else
00211   {
00212     // HandleMemoryFailure();
00213   }
00214 }
00215 
00216 // Flushes all of the prefetches that have been queued up in the prefetch queue,
00217 // freeing them as we go
00218 void nsIMAPBodyShell::FlushPrefetchQueue()
00219 {
00220   m_protocolConnection->PipelinedFetchMessageParts(GetUID(), m_prefetchQueue);
00221   m_prefetchQueue->RemoveAndFreeAll();
00222 }
00223 
00224 // Requires that the shell is valid when called
00225 // Performs a preflight check on all message parts to see if they are all
00226 // inline.  Returns PR_TRUE if all parts are inline, PR_FALSE otherwise.
00227 PRBool nsIMAPBodyShell::PreflightCheckAllInline()
00228 {
00229   PRBool rv = m_message->PreflightCheckAllInline(this);
00230   //   if (rv)
00231   //          PR_LOG(IMAP, out, ("BODYSHELL: All parts inline.  Reverting to whole message download."));
00232   return rv;
00233 }
00234 
00235 // When partNum is NULL, Generates a whole message and intelligently
00236 // leaves out parts that are not inline.
00237 
00238 // When partNum is not NULL, Generates a MIME part that hasn't been downloaded yet
00239 // Ok, here's how we're going to do this.  Essentially, this
00240 // will be the mirror image of the "normal" generation.
00241 // All parts will be left out except a single part which is
00242 // explicitly specified.  All relevant headers will be included.
00243 // Libmime will extract only the part of interest, so we don't
00244 // have to worry about the other parts.  This also has the
00245 // advantage that it looks like it will be more workable for
00246 // nested parts.  For instance, if a user clicks on a link to
00247 // a forwarded message, then that forwarded message may be 
00248 // generated along with any images that the forwarded message
00249 // contains, for instance.
00250 
00251 
00252 PRInt32 nsIMAPBodyShell::Generate(char *partNum)
00253 {
00254   m_isBeingGenerated = PR_TRUE;
00255   m_generatingPart = partNum;
00256   PRInt32 contentLength = 0;
00257   
00258   if (!GetIsValid() || PreflightCheckAllInline())
00259   {
00260     // We don't have a valid shell, or all parts are going to be inline anyway.  Fall back to fetching the whole message.
00261 #ifdef DEBUG_chrisf
00262     NS_ASSERTION(GetIsValid());
00263 #endif
00264     m_generatingWholeMessage = PR_TRUE;
00265     PRUint32 messageSize = m_protocolConnection->GetMessageSize(GetUID().get(), PR_TRUE);
00266     m_protocolConnection->SetContentModified(IMAP_CONTENT_NOT_MODIFIED);     // So that when we cache it, we know we have the whole message
00267     if (!DeathSignalReceived())
00268       m_protocolConnection->FallbackToFetchWholeMsg(GetUID().get(), messageSize);
00269     contentLength = (PRInt32) messageSize;       // ugh
00270   }
00271   else
00272   {
00273     // We have a valid shell.
00274     PRBool streamCreated = PR_FALSE;
00275     m_generatingWholeMessage = PR_FALSE;
00276     
00278     // First, prefetch any additional headers/data that we need
00279     if (!GetPseudoInterrupted())
00280       m_message->Generate(this, PR_FALSE, PR_TRUE); // This queues up everything we need to prefetch
00281     // Now, run a single pipelined prefetch  (neato!)
00282     FlushPrefetchQueue();
00283     
00285     // Next, figure out the size from the parts that we're going to fill in,
00286     // plus all of the MIME headers, plus the message header itself
00287     if (!GetPseudoInterrupted())
00288       contentLength = m_message->Generate(this, PR_FALSE, PR_FALSE);
00289     
00290     // Setup the stream
00291     if (!GetPseudoInterrupted() && !DeathSignalReceived())
00292     {
00293       nsresult rv = 
00294         m_protocolConnection->BeginMessageDownLoad(contentLength, MESSAGE_RFC822);
00295       if (NS_FAILED(rv))
00296       {
00297         m_generatingPart = nsnull;
00298         m_protocolConnection->AbortMessageDownLoad();
00299         return 0;
00300       }
00301       else
00302       {
00303         streamCreated = PR_TRUE;
00304       }
00305     }
00306     
00308     // Generate the message
00309     if (!GetPseudoInterrupted() && !DeathSignalReceived())
00310       m_message->Generate(this, PR_TRUE, PR_FALSE);
00311     
00312     // Close the stream here - normal.  If pseudointerrupted, the connection will abort the download stream
00313     if (!GetPseudoInterrupted() && !DeathSignalReceived())
00314       m_protocolConnection->NormalMessageEndDownload();
00315     else if (streamCreated)
00316       m_protocolConnection->AbortMessageDownLoad();
00317     
00318     m_generatingPart = NULL;
00319     
00320   }
00321   
00322   m_isBeingGenerated = PR_FALSE;
00323   return contentLength;
00324 }
00325 
00326 PRBool nsIMAPBodyShell::GetPseudoInterrupted()
00327 {
00328   PRBool rv = m_protocolConnection->GetPseudoInterrupted();
00329   return rv;
00330 }
00331 
00332 PRBool nsIMAPBodyShell::DeathSignalReceived()
00333 {
00334   PRBool rv = m_protocolConnection->DeathSignalReceived();
00335   return rv;
00336 }
00337 
00338 
00340 
00341 nsIMAPBodypart::nsIMAPBodypart(char *partNumber, nsIMAPBodypart *parentPart)
00342 {
00343   SetIsValid(PR_TRUE);
00344   m_parentPart = parentPart;
00345   m_partNumberString = partNumber; // storage adopted
00346   m_partData = NULL;
00347   m_headerData = NULL;
00348   m_boundaryData = NULL;    // initialize from parsed BODYSTRUCTURE
00349   m_contentLength = 0;
00350   m_partLength = 0;
00351   
00352   m_contentType = NULL;
00353   m_bodyType = NULL;
00354   m_bodySubType = NULL;
00355   m_bodyID = NULL;
00356   m_bodyDescription = NULL;
00357   m_bodyEncoding = NULL;
00358 }
00359 
00360 nsIMAPBodypart::~nsIMAPBodypart()
00361 {
00362   PR_FREEIF(m_partNumberString);
00363   PR_FREEIF(m_contentType);
00364   PR_FREEIF(m_bodyType);
00365   PR_FREEIF(m_bodySubType);
00366   PR_FREEIF(m_bodyID);
00367   PR_FREEIF(m_bodyDescription);
00368   PR_FREEIF(m_bodyEncoding);
00369   PR_FREEIF(m_partData);
00370   PR_FREEIF(m_headerData);
00371   PR_FREEIF(m_boundaryData);
00372 }
00373 
00374 void nsIMAPBodypart::SetIsValid(PRBool valid)
00375 {
00376   m_isValid = valid;
00377   if (!m_isValid)
00378   {
00379     //PR_LOG(IMAP, out, ("BODYSHELL: Part is invalid.  Part Number: %s Content-Type: %s", m_partNumberString, m_contentType));
00380   }
00381 }
00382 
00383 // Adopts storage for part data buffer.  If NULL, sets isValid to PR_FALSE.
00384 void nsIMAPBodypart::AdoptPartDataBuffer(char *buf)
00385 {
00386   m_partData = buf;
00387   if (!m_partData)
00388   {
00389     SetIsValid(PR_FALSE);
00390   }
00391 }
00392 
00393 // Adopts storage for header data buffer.  If NULL, sets isValid to PR_FALSE.
00394 void nsIMAPBodypart::AdoptHeaderDataBuffer(char *buf)
00395 {
00396   m_headerData = buf;
00397   if (!m_headerData)
00398   {
00399     SetIsValid(PR_FALSE);
00400   }
00401 }
00402 
00403 // Finds the part with given part number
00404 // Returns a nsIMAPBodystructure of the matched part if it is this
00405 // or one of its children.  Returns NULL otherwise.
00406 nsIMAPBodypart *nsIMAPBodypart::FindPartWithNumber(const char *partNum)
00407 {
00408   // either brute force, or do it the smart way - look at the number.
00409   // (the parts should be ordered, and hopefully indexed by their number)
00410   
00411   if (m_partNumberString && !PL_strcasecmp(partNum, m_partNumberString))
00412     return this;
00413   
00414   //if (!m_partNumberString && !PL_strcasecmp(partNum, "1"))
00415   //   return this;
00416   
00417   return NULL;
00418 }
00419 
00420 /*
00421 void nsIMAPBodypart::PrefetchMIMEHeader()
00422 {
00423 if (!m_headerData && !m_shell->DeathSignalReceived())
00424 {
00425 m_shell->GetConnection()->FetchMessage(m_shell->GetUID(), kMIMEHeader, PR_TRUE, 0, 0, m_partNumberString);
00426 // m_headerLength will be filled in when it is adopted from the parser
00427 }
00428 if (!m_headerData)
00429 {
00430 SetIsValid(PR_FALSE);
00431 }
00432 }
00433 */
00434 
00435 void nsIMAPBodypart::QueuePrefetchMIMEHeader(nsIMAPBodyShell *aShell)
00436 {
00437   aShell->AddPrefetchToQueue(kMIMEHeader, m_partNumberString);
00438 }
00439 
00440 PRInt32 nsIMAPBodypart::GenerateMIMEHeader(nsIMAPBodyShell *aShell, PRBool stream, PRBool prefetch)
00441 {
00442   if (prefetch && !m_headerData)
00443   {
00444     QueuePrefetchMIMEHeader(aShell);
00445     return 0;
00446   }
00447   else if (m_headerData)
00448   {
00449     PRInt32 mimeHeaderLength = 0;
00450     
00451     if (!ShouldFetchInline(aShell))
00452     {
00453       // if this part isn't inline, add the X-Mozilla-IMAP-Part header
00454       char *xPartHeader = PR_smprintf("%s: %s", IMAP_EXTERNAL_CONTENT_HEADER, m_partNumberString);
00455       if (xPartHeader)
00456       {
00457         if (stream)
00458         {
00459           aShell->GetConnection()->Log("SHELL","GENERATE-XHeader",m_partNumberString);
00460           aShell->GetConnection()->HandleMessageDownLoadLine(xPartHeader, PR_FALSE);
00461         }
00462         mimeHeaderLength += PL_strlen(xPartHeader);
00463         PR_Free(xPartHeader);
00464       }
00465     }
00466     
00467     mimeHeaderLength += PL_strlen(m_headerData);
00468     if (stream)
00469     {
00470       aShell->GetConnection()->Log("SHELL","GENERATE-MIMEHeader",m_partNumberString);
00471       aShell->GetConnection()->HandleMessageDownLoadLine(m_headerData, PR_FALSE);  // all one line?  Can we do that?
00472     }
00473     
00474     return mimeHeaderLength;
00475   }
00476   else 
00477   {
00478     SetIsValid(PR_FALSE);   // prefetch didn't adopt a MIME header
00479     return 0;
00480   }
00481 }
00482 
00483 PRInt32 nsIMAPBodypart::GeneratePart(nsIMAPBodyShell *aShell, PRBool stream, PRBool prefetch)
00484 {
00485   if (prefetch)
00486     return 0; // don't need to prefetch anything
00487   
00488   if (m_partData)    // we have prefetched the part data
00489   {
00490     if (stream)
00491     {
00492       aShell->GetConnection()->Log("SHELL","GENERATE-Part-Prefetched",m_partNumberString);
00493       aShell->GetConnection()->HandleMessageDownLoadLine(m_partData, PR_FALSE);
00494     }
00495     return PL_strlen(m_partData);
00496   }
00497   else // we are fetching and streaming this part's body as we go
00498   {
00499     if (stream && !aShell->DeathSignalReceived())
00500     {
00501       char *generatingPart = aShell->GetGeneratingPart();
00502       PRBool fetchingSpecificPart = (generatingPart && !PL_strcmp(generatingPart, m_partNumberString));
00503       
00504       aShell->GetConnection()->Log("SHELL","GENERATE-Part-Inline",m_partNumberString);
00505       aShell->GetConnection()->FetchTryChunking(aShell->GetUID().get(), kMIMEPart, PR_TRUE, m_partNumberString, m_partLength, !fetchingSpecificPart);
00506     }
00507     return m_partLength;    // the part length has been filled in from the BODYSTRUCTURE response
00508   }
00509 }
00510 
00511 PRInt32 nsIMAPBodypart::GenerateBoundary(nsIMAPBodyShell *aShell, PRBool stream, PRBool prefetch, PRBool lastBoundary)
00512 {
00513   if (prefetch)
00514     return 0; // don't need to prefetch anything
00515   
00516   if (m_boundaryData)
00517   {
00518     if (!lastBoundary)
00519     {
00520       if (stream)
00521       {
00522         aShell->GetConnection()->Log("SHELL","GENERATE-Boundary",m_partNumberString);
00523         aShell->GetConnection()->HandleMessageDownLoadLine(m_boundaryData, PR_FALSE);
00524       }
00525       return PL_strlen(m_boundaryData);
00526     }
00527     else      // the last boundary
00528     {
00529       char *lastBoundaryData = PR_smprintf("%s--", m_boundaryData);
00530       if (lastBoundaryData)
00531       {
00532         if (stream)
00533         {
00534           aShell->GetConnection()->Log("SHELL","GENERATE-Boundary-Last",m_partNumberString);
00535           aShell->GetConnection()->HandleMessageDownLoadLine(lastBoundaryData, PR_FALSE);
00536         }
00537         PRInt32 rv = PL_strlen(lastBoundaryData);
00538         PR_Free(lastBoundaryData);
00539         return rv;
00540       }
00541       else
00542       {
00543         //HandleMemoryFailure();
00544         return 0;
00545       }
00546     }
00547   }
00548   else
00549     return 0;
00550 }
00551 
00552 PRInt32 nsIMAPBodypart::GenerateEmptyFilling(nsIMAPBodyShell *aShell, PRBool stream, PRBool prefetch)
00553 {
00554   if (prefetch)
00555     return 0; // don't need to prefetch anything
00556   
00557   const char emptyString[] = "This body part will be downloaded on demand.";
00558   // XP_GetString(MK_IMAP_EMPTY_MIME_PART);  ### need to be able to localize
00559   if (emptyString)
00560   {
00561     if (stream)
00562     {
00563       aShell->GetConnection()->Log("SHELL","GENERATE-Filling",m_partNumberString);
00564       aShell->GetConnection()->HandleMessageDownLoadLine(emptyString, PR_FALSE);
00565     }
00566     return PL_strlen(emptyString);
00567   }
00568   else
00569     return 0;
00570 }
00571 
00572 
00573 // Returns PR_TRUE if the prefs say that this content type should
00574 // explicitly be kept in when filling in the shell
00575 PRBool nsIMAPBodypart::ShouldExplicitlyFetchInline()
00576 {
00577         return PR_FALSE;
00578 }
00579 
00580 
00581 // Returns PR_TRUE if the prefs say that this content type should
00582 // explicitly be left out when filling in the shell
00583 PRBool nsIMAPBodypart::ShouldExplicitlyNotFetchInline()
00584 {
00585   return PR_FALSE;
00586 }
00587 
00588 
00590 
00591 
00592 nsIMAPBodypartLeaf::nsIMAPBodypartLeaf(char *partNum, nsIMAPBodypart *parentPart,
00593   char *bodyType, char *bodySubType, char *bodyID, char *bodyDescription, char *bodyEncoding, PRInt32 partLength) : 
00594 nsIMAPBodypart(partNum, parentPart)
00595 {
00596   m_bodyType = bodyType;
00597   m_bodySubType = bodySubType;
00598   m_bodyID = bodyID;
00599   m_bodyDescription = bodyDescription;
00600   m_bodyEncoding = bodyEncoding;
00601   m_partLength = partLength;
00602   if (m_bodyType && m_bodySubType)
00603   {
00604     m_contentType = PR_smprintf("%s/%s", m_bodyType, m_bodySubType);
00605   }
00606   SetIsValid(PR_TRUE);
00607 }
00608 
00609 nsIMAPBodypartType nsIMAPBodypartLeaf::GetType()
00610 {
00611   return IMAP_BODY_LEAF;
00612 }
00613 
00614 PRInt32 nsIMAPBodypartLeaf::Generate(nsIMAPBodyShell *aShell, PRBool stream, PRBool prefetch)
00615 {
00616   PRInt32 len = 0;
00617   
00618   if (GetIsValid())
00619   {
00620     
00621     if (stream && !prefetch)
00622       aShell->GetConnection()->Log("SHELL","GENERATE-Leaf",m_partNumberString);
00623     
00624     // Stream out the MIME part boundary
00625     //GenerateBoundary();
00626     NS_ASSERTION(m_parentPart, "part has no parent");
00627     //nsIMAPBodypartMessage *parentMessage = m_parentPart ? m_parentPart->GetnsIMAPBodypartMessage() : NULL;
00628     
00629     // Stream out the MIME header of this part, if this isn't the only body part of a message
00630     //if (parentMessage ? !parentMessage->GetIsTopLevelMessage() : PR_TRUE)
00631     if ((m_parentPart->GetType() != IMAP_BODY_MESSAGE_RFC822)
00632       && !aShell->GetPseudoInterrupted())
00633       len += GenerateMIMEHeader(aShell, stream, prefetch);
00634     
00635     if (!aShell->GetPseudoInterrupted())
00636     {
00637       if (ShouldFetchInline(aShell))
00638       {
00639         // Fetch and stream the content of this part
00640         len += GeneratePart(aShell, stream, prefetch);
00641       }
00642       else
00643       {
00644         // fill in the filling within the empty part
00645         len += GenerateEmptyFilling(aShell, stream, prefetch);
00646       }
00647     }
00648   }
00649   m_contentLength = len;
00650   return m_contentLength;
00651 }
00652 
00653 
00654 
00655 // returns PR_TRUE if this part should be fetched inline for generation.
00656 PRBool nsIMAPBodypartLeaf::ShouldFetchInline(nsIMAPBodyShell *aShell)
00657 {
00658   char *generatingPart = aShell->GetGeneratingPart();
00659   if (generatingPart)
00660   {
00661     // If we are generating a specific part
00662     if (!PL_strcmp(generatingPart, m_partNumberString))
00663     {
00664       // This is the part we're generating
00665       return PR_TRUE;
00666     }
00667     else
00668     {
00669       // If this is the only body part of a message, and that
00670       // message is the part being generated, then this leaf should
00671       // be inline as well.
00672       if ((m_parentPart->GetType() == IMAP_BODY_MESSAGE_RFC822) &&
00673         (!PL_strcmp(m_parentPart->GetPartNumberString(), generatingPart)))
00674         return PR_TRUE;
00675       
00676       // The parent of this part is a multipart
00677       if (m_parentPart->GetType() == IMAP_BODY_MULTIPART)
00678       {
00679         // This is the first text part of a forwarded message
00680         // with a multipart body, and that message is being generated,
00681         // then generate this part.
00682         nsIMAPBodypart *grandParent = m_parentPart->GetParentPart();
00683         NS_ASSERTION(grandParent, "grandparent doesn't exist for multi-part alt");         // grandParent must exist, since multiparts need parents
00684         if (grandParent && 
00685           (grandParent->GetType() == IMAP_BODY_MESSAGE_RFC822) &&
00686           (!PL_strcmp(grandParent->GetPartNumberString(), generatingPart)) &&
00687           (m_partNumberString[PL_strlen(m_partNumberString)-1] == '1') &&
00688           !PL_strcasecmp(m_bodyType, "text"))
00689           return PR_TRUE;   // we're downloading it inline
00690         
00691         
00692         // This is a child of a multipart/appledouble attachment,
00693         // and that multipart/appledouble attachment is being generated
00694         if (m_parentPart &&
00695           !PL_strcasecmp(m_parentPart->GetBodySubType(), "appledouble") &&
00696           !PL_strcmp(m_parentPart->GetPartNumberString(), generatingPart))
00697           return PR_TRUE;   // we're downloading it inline
00698       }
00699       
00700       // Leave out all other leaves if this isn't the one
00701       // we're generating.
00702       // Maybe change later to check parents, etc.
00703       return PR_FALSE;
00704     }
00705   }
00706   else
00707   {
00708     // We are generating the whole message, possibly (hopefully)
00709     // leaving out non-inline parts
00710     
00711     if (ShouldExplicitlyFetchInline())
00712       return PR_TRUE;
00713     if (ShouldExplicitlyNotFetchInline())
00714       return PR_FALSE;
00715     
00716     // If the parent is a message (this is the only body part of that
00717     // message), and that message should be inline, then its body
00718     // should inherit the inline characteristics of that message
00719     if (m_parentPart->GetType() == IMAP_BODY_MESSAGE_RFC822)
00720       return m_parentPart->ShouldFetchInline(aShell);
00721     
00722     // View Attachments As Links is on.
00723     if (!(aShell->GetContentModified() == IMAP_CONTENT_MODIFIED_VIEW_INLINE))
00724     {
00725       // The last text part is still displayed inline,
00726       // even if View Attachments As Links is on.
00727       nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
00728       PRBool preferPlainText = PR_FALSE;
00729       if (prefBranch)
00730         prefBranch->GetBoolPref("mailnews.display.prefer_plaintext", &preferPlainText);
00731 
00732       nsIMAPBodypart *grandParentPart = m_parentPart->GetParentPart();
00733       if (((preferPlainText || !PL_strcasecmp(m_parentPart->GetBodySubType(), "mixed")) 
00734               && !PL_strcmp(m_partNumberString, "1") 
00735               && !PL_strcasecmp(m_bodyType, "text"))
00736           || ((!PL_strcasecmp(m_parentPart->GetBodySubType(), "alternative") ||
00737                 (grandParentPart && !PL_strcasecmp(grandParentPart->GetBodySubType(), "alternative"))) &&
00738                 m_parentPart->IsLastTextPart(m_partNumberString)))
00739       {
00740         return PR_TRUE;     // we're downloading it inline
00741       }
00742       else
00743       {
00744         // This is the first text part of a top-level multipart.
00745         // For instance, a message with multipart body, where the first
00746         // part is multipart, and this is the first leaf of that first part.
00747         if (m_parentPart->GetType() == IMAP_BODY_MULTIPART &&
00748           (PL_strlen(m_partNumberString) >= 2) &&
00749           !PL_strcmp(m_partNumberString + PL_strlen(m_partNumberString) - 2, ".1") &&      // this is the first text type on this level
00750           (!PL_strcmp(m_parentPart->GetPartNumberString(), "1") || !PL_strcmp(m_parentPart->GetPartNumberString(), "2")) &&
00751           !PL_strcasecmp(m_bodyType, "text"))
00752           return PR_TRUE;
00753         else
00754           return PR_FALSE;  // we can leave it on the server
00755       }
00756     }
00757 #if defined(XP_MAC) || defined(XP_MACOSX)
00758     // If it is either applesingle, or a resource fork for appledouble
00759     if (!PL_strcasecmp(m_contentType, "application/applefile"))
00760     {
00761       // if it is appledouble
00762       if (m_parentPart->GetType() == IMAP_BODY_MULTIPART &&
00763         !PL_strcasecmp(m_parentPart->GetBodySubType(), "appledouble"))
00764       {
00765         // This is the resource fork of a multipart/appledouble.
00766         // We inherit the inline attributes of the parent,
00767         // which was derived from its OTHER child.  (The data fork.)
00768         return m_parentPart->ShouldFetchInline(aShell);
00769       }
00770       else    // it is applesingle
00771       {
00772         return PR_FALSE;    // we can leave it on the server
00773       }
00774     }
00775 #endif // XP_MAC
00776     
00777     // Leave out parts with type application/*
00778     if (!PL_strcasecmp(m_bodyType, "APPLICATION") &&    // If it is of type "application"
00779       PL_strncasecmp(m_bodySubType, "x-pkcs7", 7)       // and it's not a signature (signatures are inline)
00780       )
00781       return PR_FALSE;      // we can leave it on the server
00782     
00783     // Here's where we can add some more intelligence -- let's leave out
00784     // any other parts that we know we can't display inline.
00785     return PR_TRUE;  // we're downloading it inline
00786   }
00787 }
00788 
00789 
00790 
00791 PRBool nsIMAPBodypartMultipart::IsLastTextPart(const char *partNumberString)
00792 {
00793  // iterate backwards over the parent's part list and if the part is
00794   // text, compare it to the part number string
00795   for (int i = m_partList->Count() - 1; i >= 0; i--)
00796   {
00797       nsIMAPBodypart *part = (nsIMAPBodypart *)(m_partList->ElementAt(i));
00798       if (!PL_strcasecmp(part->GetBodyType(), "text"))
00799         return !PL_strcasecmp(part->GetPartNumberString(), partNumberString);
00800   }
00801   return PR_FALSE;
00802 }
00803 
00804 PRBool nsIMAPBodypartLeaf::PreflightCheckAllInline(nsIMAPBodyShell *aShell)
00805 {
00806   // only need to check this part, since it has no children.
00807   return ShouldFetchInline(aShell);
00808 }
00809 
00810 
00812 
00813 nsIMAPBodypartMessage::nsIMAPBodypartMessage(char *partNum,  nsIMAPBodypart *parentPart,
00814   PRBool topLevelMessage, char *bodyType, char *bodySubType, char *bodyID, char *bodyDescription, char *bodyEncoding, PRInt32 partLength) : nsIMAPBodypartLeaf(partNum, parentPart, bodyType, bodySubType, bodyID, bodyDescription, bodyEncoding, partLength)
00815 {
00816   m_topLevelMessage = topLevelMessage;
00817   if (m_topLevelMessage)
00818   {
00819     m_partNumberString = PR_smprintf("0");
00820     if (!m_partNumberString)
00821     {
00822       SetIsValid(PR_FALSE);
00823       return;
00824     }
00825   }
00826   m_body = NULL;
00827   m_headers = new nsIMAPMessageHeaders(m_partNumberString, this);  // We always have a Headers object
00828   if (!m_headers || !m_headers->GetIsValid())
00829   {
00830     SetIsValid(PR_FALSE);
00831     return;
00832   }
00833   SetIsValid(PR_TRUE);
00834 }
00835 
00836 void nsIMAPBodypartMessage::SetBody(nsIMAPBodypart *body)
00837 {
00838   if (m_body)
00839     delete m_body;
00840   m_body = body;
00841 }
00842 
00843 
00844 nsIMAPBodypartType nsIMAPBodypartMessage::GetType()
00845 {
00846   return IMAP_BODY_MESSAGE_RFC822;
00847 }
00848 
00849 nsIMAPBodypartMessage::~nsIMAPBodypartMessage()
00850 {
00851   delete m_headers;
00852   delete m_body;
00853 }
00854 
00855 PRInt32 nsIMAPBodypartMessage::Generate(nsIMAPBodyShell *aShell, PRBool stream, PRBool prefetch)
00856 {
00857   if (!GetIsValid())
00858     return 0;
00859   
00860   m_contentLength = 0;
00861   
00862   if (stream && !prefetch)
00863     aShell->GetConnection()->Log("SHELL","GENERATE-MessageRFC822",m_partNumberString);
00864   
00865   if (!m_topLevelMessage && !aShell->GetPseudoInterrupted())  // not the top-level message - we need the MIME header as well as the message header
00866   {
00867     // but we don't need the MIME headers of a message/rfc822 part if this content
00868     // type is in (part of) the main msg header. In other words, we still need
00869     // these MIME headers if this message/rfc822 body part is enclosed in the msg
00870     // body (most likely as a body part of a multipart/mixed msg).
00871     //       Don't fetch (bug 128888)              Do fetch (bug 168097)
00872     //  ----------------------------------  -----------------------------------
00873     //  message/rfc822  (parent part)       message/rfc822
00874     //   message/rfc822 <<<---               multipart/mixed  (parent part)
00875     //    multipart/mixed                     message/rfc822  <<<---
00876     //     text/html   (body text)             multipart/mixed
00877     //     text/plain  (attachment)             text/html   (body text)
00878     //     application/msword (attachment)      text/plain  (attachment)
00879     //                                          application/msword (attachment)
00880     // "<<<---" points to the part we're examining here.
00881     if ( PL_strcasecmp(m_bodyType, "message") || PL_strcasecmp(m_bodySubType, "rfc822") ||
00882       PL_strcasecmp(m_parentPart->GetBodyType(), "message") || PL_strcasecmp(m_parentPart->GetBodySubType(), "rfc822") )
00883       m_contentLength += GenerateMIMEHeader(aShell, stream, prefetch);
00884   }
00885   
00886   if (!aShell->GetPseudoInterrupted())
00887     m_contentLength += m_headers->Generate(aShell, stream, prefetch);
00888   if (!aShell->GetPseudoInterrupted())
00889     m_contentLength += m_body->Generate(aShell, stream, prefetch);
00890   
00891   return m_contentLength;
00892 }
00893 
00894 
00895 
00896 
00897 PRBool nsIMAPBodypartMessage::ShouldFetchInline(nsIMAPBodyShell *aShell)
00898 {
00899   if (m_topLevelMessage)    // the main message should always be defined as "inline"
00900     return PR_TRUE;
00901   
00902   char *generatingPart = aShell->GetGeneratingPart();
00903   if (generatingPart)
00904   {
00905     // If we are generating a specific part
00906     // Always generate containers (just don't fill them in)
00907     // because it is low cost (everything is cached)
00908     // and it gives the message its full MIME structure,
00909     // to avoid any potential mishap.
00910     return PR_TRUE;
00911   }
00912   else
00913   {
00914     // Generating whole message
00915     
00916     if (ShouldExplicitlyFetchInline())
00917       return PR_TRUE;
00918     if (ShouldExplicitlyNotFetchInline())
00919       return PR_FALSE;
00920     
00921     
00922     // Message types are inline, by default.
00923     return PR_TRUE;
00924   }
00925 }
00926 
00927 PRBool nsIMAPBodypartMessage::PreflightCheckAllInline(nsIMAPBodyShell *aShell)
00928 {
00929   if (!ShouldFetchInline(aShell))
00930     return PR_FALSE;
00931   
00932   return m_body->PreflightCheckAllInline(aShell);
00933 }
00934 
00935 // Fills in buffer (and adopts storage) for header object
00936 void nsIMAPBodypartMessage::AdoptMessageHeaders(char *headers)
00937 {
00938   if (!GetIsValid())
00939     return;
00940   
00941   // we are going to say that the message headers only have
00942   // part data, and no header data.
00943   m_headers->AdoptPartDataBuffer(headers);
00944   if (!m_headers->GetIsValid())
00945     SetIsValid(PR_FALSE);
00946 }
00947 
00948 // Finds the part with given part number
00949 // Returns a nsIMAPBodystructure of the matched part if it is this
00950 // or one of its children.  Returns NULL otherwise.
00951 nsIMAPBodypart *nsIMAPBodypartMessage::FindPartWithNumber(const char *partNum)
00952 {
00953   // either brute force, or do it the smart way - look at the number.
00954   // (the parts should be ordered, and hopefully indexed by their number)
00955   
00956   if (!PL_strcasecmp(partNum, m_partNumberString))
00957     return this;
00958   
00959   return m_body->FindPartWithNumber(partNum);
00960 }
00961 
00963 
00964 
00965 nsIMAPBodypartMultipart::nsIMAPBodypartMultipart(char *partNum, nsIMAPBodypart *parentPart) : 
00966 nsIMAPBodypart(partNum, parentPart)
00967 {
00968   if (!m_parentPart  || (m_parentPart->GetType() == IMAP_BODY_MESSAGE_RFC822))
00969   {
00970     // the multipart (this) will inherit the part number of its parent
00971     PR_FREEIF(m_partNumberString);
00972     if (!m_parentPart)
00973     {
00974       m_partNumberString = PR_smprintf("0");
00975     }
00976     else
00977     {
00978       m_partNumberString = nsCRT::strdup(m_parentPart->GetPartNumberString());
00979     }
00980   }
00981   m_partList = new nsVoidArray();
00982   m_bodyType = nsCRT::strdup("multipart");
00983   if (m_partList && m_parentPart && m_bodyType)
00984     SetIsValid(PR_TRUE);
00985   else
00986     SetIsValid(PR_FALSE);
00987 }
00988 
00989 nsIMAPBodypartType nsIMAPBodypartMultipart::GetType()
00990 {
00991   return IMAP_BODY_MULTIPART;
00992 }
00993 
00994 nsIMAPBodypartMultipart::~nsIMAPBodypartMultipart()
00995 {
00996   for (int i = m_partList->Count() - 1; i >= 0; i--)
00997   {
00998     delete (nsIMAPBodypart *)(m_partList->ElementAt(i));
00999   }
01000   delete m_partList;
01001 }
01002 
01003 void
01004 nsIMAPBodypartMultipart::SetBodySubType(char *bodySubType)
01005 {
01006   PR_FREEIF(m_bodySubType);
01007   PR_FREEIF(m_contentType);
01008   m_bodySubType = bodySubType;
01009   if (m_bodyType && m_bodySubType)
01010     m_contentType = PR_smprintf("%s/%s", m_bodyType, m_bodySubType);
01011 }
01012 
01013 
01014 PRInt32 nsIMAPBodypartMultipart::Generate(nsIMAPBodyShell *aShell, PRBool stream, PRBool prefetch)
01015 {
01016   PRInt32 len = 0;
01017   
01018   if (GetIsValid())
01019   {
01020     if (stream && !prefetch)
01021       aShell->GetConnection()->Log("SHELL","GENERATE-Multipart",m_partNumberString);
01022     
01023     // Stream out the MIME header of this part
01024     
01025     PRBool parentIsMessageType = GetParentPart() ? (GetParentPart()->GetType() == IMAP_BODY_MESSAGE_RFC822) : PR_TRUE;
01026     
01027     // If this is multipart/signed, then we always want to generate the MIME headers of this multipart.
01028     // Otherwise, we only want to do it if the parent is not of type "message"
01029     PRBool needMIMEHeader = !parentIsMessageType;  // !PL_strcasecmp(m_bodySubType, "signed") ? PR_TRUE : !parentIsMessageType;
01030     if (needMIMEHeader && !aShell->GetPseudoInterrupted())  // not a message body's type
01031     {
01032       len += GenerateMIMEHeader(aShell, stream, prefetch);
01033     }
01034     
01035     if (ShouldFetchInline(aShell))
01036     {
01037       for (int i = 0; i < m_partList->Count(); i++)
01038       {
01039         if (!aShell->GetPseudoInterrupted())
01040           len += GenerateBoundary(aShell, stream, prefetch, PR_FALSE);
01041         if (!aShell->GetPseudoInterrupted())
01042           len += ((nsIMAPBodypart *)(m_partList->ElementAt(i)))->Generate(aShell, stream, prefetch);
01043       }
01044       if (!aShell->GetPseudoInterrupted())
01045         len += GenerateBoundary(aShell, stream, prefetch, PR_TRUE);
01046     }
01047     else
01048     {
01049       // fill in the filling within the empty part
01050       if (!aShell->GetPseudoInterrupted())
01051         len += GenerateEmptyFilling(aShell, stream, prefetch);
01052     }
01053   }
01054   m_contentLength = len;
01055   return m_contentLength;
01056 }
01057 
01058 
01059 PRBool nsIMAPBodypartMultipart::ShouldFetchInline(nsIMAPBodyShell *aShell)
01060 {
01061   char *generatingPart = aShell->GetGeneratingPart();
01062   if (generatingPart)
01063   {
01064     // If we are generating a specific part
01065     // Always generate containers (just don't fill them in)
01066     // because it is low cost (everything is cached)
01067     // and it gives the message its full MIME structure,
01068     // to avoid any potential mishap.
01069     return PR_TRUE;
01070   }
01071   else
01072   {
01073     // Generating whole message
01074     
01075     if (ShouldExplicitlyFetchInline())
01076       return PR_TRUE;
01077     if (ShouldExplicitlyNotFetchInline())
01078       return PR_FALSE;
01079     
01080     nsIMAPBodypart *grandparentPart = m_parentPart->GetParentPart();
01081 
01082     // if we're a multipart sub-part of multipart alternative, we need to 
01083     // be fetched because mime will always display us.
01084     if (!PL_strcasecmp(m_parentPart->GetBodySubType(), "alternative") &&
01085         GetType() == IMAP_BODY_MULTIPART)
01086       return PR_TRUE;
01087     // If "Show Attachments as Links" is on, and
01088     // the parent of this multipart is not a message,
01089     // then it's not inline.
01090     if (!(aShell->GetContentModified() == IMAP_CONTENT_MODIFIED_VIEW_INLINE) &&
01091       (m_parentPart->GetType() != IMAP_BODY_MESSAGE_RFC822) &&
01092       (m_parentPart->GetType() == IMAP_BODY_MULTIPART ?
01093       (grandparentPart ? grandparentPart->GetType() != IMAP_BODY_MESSAGE_RFC822 : PR_TRUE)
01094       : PR_TRUE))
01095       return PR_FALSE;
01096     
01097     // multiparts are always inline (even multipart/appledouble)
01098     // (their children might not be, though)
01099     return PR_TRUE;
01100   }
01101 }
01102 
01103 PRBool nsIMAPBodypartMultipart::PreflightCheckAllInline(nsIMAPBodyShell *aShell)
01104 {
01105   PRBool rv = ShouldFetchInline(aShell);
01106   
01107   int i = 0;
01108   while (rv && (i < m_partList->Count()))
01109   {
01110     rv = ((nsIMAPBodypart *)(m_partList->ElementAt(i)))->PreflightCheckAllInline(aShell);
01111     i++;
01112   }
01113   
01114   return rv;
01115 }
01116 
01117 nsIMAPBodypart       *nsIMAPBodypartMultipart::FindPartWithNumber(const char *partNum)
01118 {
01119   NS_ASSERTION(partNum, "null part passed into FindPartWithNumber");
01120   
01121   // check this
01122   if (!PL_strcmp(partNum, m_partNumberString))
01123     return this;
01124   
01125   // check children
01126   for (int i = m_partList->Count() - 1; i >= 0; i--)
01127   {
01128     nsIMAPBodypart *foundPart = ((nsIMAPBodypart *)(m_partList->ElementAt(i)))->FindPartWithNumber(partNum);
01129     if (foundPart)
01130       return foundPart;
01131   }
01132   
01133   // not this, or any of this's children
01134   return NULL;
01135 }
01136 
01137 
01138 
01140 
01141 
01142 
01143 nsIMAPMessageHeaders::nsIMAPMessageHeaders(char *partNum, nsIMAPBodypart *parentPart) : 
01144 nsIMAPBodypart(partNum, parentPart)
01145 {
01146   if (!partNum)
01147   {
01148     SetIsValid(PR_FALSE);
01149     return;
01150   }
01151   m_partNumberString = nsCRT::strdup(partNum);
01152   if (!m_partNumberString)
01153   {
01154     SetIsValid(PR_FALSE);
01155     return;
01156   }
01157   if (!m_parentPart || !m_parentPart->GetnsIMAPBodypartMessage())
01158   {
01159     // Message headers created without a valid Message parent
01160     NS_ASSERTION(PR_FALSE, "creating message headers with invalid message parent");
01161     SetIsValid(PR_FALSE);
01162   }
01163 }
01164 
01165 nsIMAPBodypartType nsIMAPMessageHeaders::GetType()
01166 {
01167   return IMAP_BODY_MESSAGE_HEADER;
01168 }
01169 
01170 void nsIMAPMessageHeaders::QueuePrefetchMessageHeaders(nsIMAPBodyShell *aShell)
01171 {
01172   
01173   if (!m_parentPart->GetnsIMAPBodypartMessage()->GetIsTopLevelMessage())     // not top-level headers
01174     aShell->AddPrefetchToQueue(kRFC822HeadersOnly, m_partNumberString);
01175   else
01176     aShell->AddPrefetchToQueue(kRFC822HeadersOnly, NULL);
01177 }
01178 
01179 PRInt32 nsIMAPMessageHeaders::Generate(nsIMAPBodyShell *aShell, PRBool stream, PRBool prefetch)
01180 {
01181   // prefetch the header
01182   if (prefetch && !m_partData && !aShell->DeathSignalReceived())
01183   {
01184     QueuePrefetchMessageHeaders(aShell);
01185   }
01186   
01187   if (stream && !prefetch)
01188     aShell->GetConnection()->Log("SHELL","GENERATE-MessageHeaders",m_partNumberString);
01189   
01190   // stream out the part data
01191   if (ShouldFetchInline(aShell))
01192   {
01193     if (!aShell->GetPseudoInterrupted())
01194       m_contentLength = GeneratePart(aShell, stream, prefetch);
01195   }
01196   else
01197   {
01198     m_contentLength = 0;    // don't fill in any filling for the headers
01199   }
01200   return m_contentLength;
01201 }
01202 
01203 PRBool nsIMAPMessageHeaders::ShouldFetchInline(nsIMAPBodyShell *aShell)
01204 {
01205   return m_parentPart->ShouldFetchInline(aShell);
01206 }
01207 
01208 
01210 
01211 #if 0  // mscott - commenting out because it does not appear to be used
01212 static int
01213 imap_shell_cache_strcmp (const void *a, const void *b)
01214 {
01215   return PL_strcmp ((const char *) a, (const char *) b);
01216 }
01217 #endif
01218 
01219 nsIMAPBodyShellCache::nsIMAPBodyShellCache()
01220 {
01221   m_shellHash = new nsHashtable(20); /* , XP_StringHash, imap_shell_cache_strcmp); */
01222   m_shellList = new nsVoidArray();
01223 }
01224 
01225 /* static */ nsIMAPBodyShellCache *nsIMAPBodyShellCache::Create()
01226 {
01227   nsIMAPBodyShellCache *cache = new nsIMAPBodyShellCache();
01228   if (!cache || !cache->m_shellHash || !cache->m_shellList)
01229     return NULL;
01230   
01231   return cache;
01232 }
01233 
01234 nsIMAPBodyShellCache::~nsIMAPBodyShellCache()
01235 {
01236   while (EjectEntry()) ;
01237   delete m_shellHash;
01238   delete m_shellList;
01239 }
01240 
01241 // We'll use an LRU scheme here.
01242 // We will add shells in numerical order, so the
01243 // least recently used one will be in slot 0.
01244 PRBool nsIMAPBodyShellCache::EjectEntry()
01245 {
01246        if (m_shellList->Count() < 1)
01247               return PR_FALSE;
01248 
01249 
01250 
01251        nsIMAPBodyShell *removedShell = (nsIMAPBodyShell *) (m_shellList->ElementAt(0));
01252 
01253        m_shellList->RemoveElementAt(0);
01254        nsCStringKey hashKey (removedShell->GetUID());
01255        m_shellHash->Remove(&hashKey);
01256        delete removedShell;
01257 
01258        return PR_TRUE;
01259 }
01260 
01261 PRBool nsIMAPBodyShellCache::AddShellToCache(nsIMAPBodyShell *shell)
01262 {
01263        // If it's already in the cache, then just return.
01264        // This has the side-effect of re-ordering the LRU list
01265        // to put this at the top, which is good, because it's what we want.
01266        if (FindShellForUID(shell->GetUID(), shell->GetFolderName(), shell->GetContentModified()))
01267               return PR_TRUE;
01268 
01269        // OK, so it's not in the cache currently.
01270 
01271        // First, for safety sake, remove any entry with the given UID,
01272        // just in case we have a collision between two messages in different
01273        // folders with the same UID.
01274        nsCStringKey hashKey1(shell->GetUID());
01275        nsIMAPBodyShell *foundShell = (nsIMAPBodyShell *) m_shellHash->Get(&hashKey1);
01276        if (foundShell)
01277        {
01278               nsCStringKey hashKey(foundShell->GetUID());
01279               m_shellHash->Remove(&hashKey);
01280               m_shellList->RemoveElement(foundShell);
01281        }
01282 
01283        // Add the new one to the cache
01284        m_shellList->AppendElement(shell);
01285        
01286        nsCStringKey hashKey2 (shell->GetUID());
01287        m_shellHash->Put(&hashKey2, shell);
01288        shell->SetIsCached(PR_TRUE);
01289 
01290        // while we're not over our size limit, eject entries
01291        PRBool rv = PR_TRUE;
01292        while (GetSize() > GetMaxSize())
01293        {
01294               rv = EjectEntry();
01295        }
01296 
01297        return rv;
01298 
01299 }
01300 
01301 nsIMAPBodyShell *nsIMAPBodyShellCache::FindShellForUID(nsCString &UID, const char *mailboxName,
01302                                                        IMAP_ContentModifiedType modType)
01303 {
01304        nsCStringKey hashKey(UID);
01305        nsIMAPBodyShell *foundShell = (nsIMAPBodyShell *) m_shellHash->Get(&hashKey);
01306 
01307        if (!foundShell)
01308               return nsnull;
01309 
01310   // Make sure the content-modified types are compatible.
01311   // This allows us to work seamlessly while people switch between
01312   // View Attachments Inline and View Attachments As Links.
01313   // Enforce the invariant that any cached shell we use
01314   // match the current content-modified settings.
01315   if (modType != foundShell->GetContentModified())
01316     return nsnull;
01317 
01318        // mailbox names must match also.
01319        if (PL_strcmp(mailboxName, foundShell->GetFolderName()))
01320               return nsnull;
01321 
01322        // adjust the LRU stuff
01323        m_shellList->RemoveElement(foundShell);   // oh well, I suppose this defeats the performance gain of the hash if it actually is found
01324        m_shellList->AppendElement(foundShell);          // Adds to end
01325        
01326        return foundShell;
01327 }
01328 
01329 nsIMAPBodyShell *nsIMAPBodyShellCache::FindShellForUID(PRUint32 UID, const char *mailboxName,
01330                                                        IMAP_ContentModifiedType modType)
01331 {
01332        nsCString uidString;
01333        
01334        uidString.AppendInt(UID);
01335        nsIMAPBodyShell *rv = FindShellForUID(uidString, mailboxName, modType);
01336        return rv;
01337 }
01338 
01339 
01341 
01342 
01343 nsIMAPMessagePartID::nsIMAPMessagePartID(nsIMAPeFetchFields fields, const char *partNumberString)
01344 {
01345        m_fields = fields;
01346        m_partNumberString = partNumberString;
01347 }
01348 
01349 nsIMAPMessagePartIDArray::nsIMAPMessagePartIDArray()
01350 {
01351 }
01352 
01353 nsIMAPMessagePartIDArray::~nsIMAPMessagePartIDArray()
01354 {
01355        RemoveAndFreeAll();
01356 }
01357 
01358 void nsIMAPMessagePartIDArray::RemoveAndFreeAll()
01359 {
01360     int n = Count();
01361        for (int i = 0; i < n; i++)
01362        {
01363               nsIMAPMessagePartID *part = GetPart(i);
01364               delete part;
01365        }
01366     Clear();
01367 }