Back to index

lightning-sunbird  0.9+nobinonly
nsImapServerResponseParser.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Lorenzo Colitti <lorenzo@colitti.com>
00025  *   Hans-Andreas Engel <engel@physics.harvard.edu>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #ifdef MOZ_LOGGING
00042 // sorry, this has to be before the pre-compiled header
00043 #define FORCE_PR_LOG /* Allow logging in the release build */
00044 #endif
00045 
00046 #include "msgCore.h"  // for pre-compiled headers
00047 #include "nsMimeTypes.h"
00048 #include "nsImapCore.h"
00049 #include "nsImapProtocol.h"
00050 #include "nsImapServerResponseParser.h"
00051 #include "nsIMAPBodyShell.h"
00052 #include "nsImapFlagAndUidState.h"
00053 #include "nsIMAPNamespace.h"
00054 #include "nsImapStringBundle.h"
00055 #include "nsImapUtils.h"
00056 
00058 
00059 extern PRLogModuleInfo* IMAP;
00060 
00061 nsImapServerResponseParser::nsImapServerResponseParser(nsImapProtocol &imapProtocolConnection) 
00062                             : nsIMAPGenericParser(),
00063     fReportingErrors(PR_TRUE),
00064     fCurrentFolderReadOnly(PR_FALSE),
00065     fCurrentLineContainedFlagInfo(PR_FALSE),
00066     fFetchEverythingRFC822(PR_FALSE),
00067     fServerIsNetscape3xServer(PR_FALSE),
00068     fNumberOfUnseenMessages(0),
00069     fNumberOfExistingMessages(0),
00070     fNumberOfRecentMessages(0),
00071     fSizeOfMostRecentMessage(0),
00072     fTotalDownloadSize(0),
00073     fCurrentCommandTag(nsnull),
00074     fSelectedMailboxName(nsnull),
00075     fIMAPstate(kNonAuthenticated),
00076     fLastChunk(PR_FALSE),
00077     m_shell(nsnull),
00078     fServerConnection(imapProtocolConnection),
00079     fHostSessionList(nsnull)
00080 {
00081   fSearchResults = nsImapSearchResultSequence::CreateSearchResultSequence();
00082   fMailAccountUrl = nsnull;
00083   fManageFiltersUrl = nsnull;
00084   fManageListsUrl = nsnull;
00085   fFolderAdminUrl = nsnull;
00086   fNetscapeServerVersionString = nsnull;
00087   fXSenderInfo = nsnull;
00088   fSupportsUserDefinedFlags = 0;
00089   fSettablePermanentFlags = 0;
00090   fCapabilityFlag = kCapabilityUndefined; 
00091   fLastAlert = nsnull;
00092   fDownloadingHeaders = PR_FALSE;
00093   fGotPermanentFlags = PR_FALSE;
00094   fFolderUIDValidity = 0;
00095   fAuthChallenge = nsnull;
00096   fStatusUnseenMessages = 0;
00097   fStatusRecentMessages = 0;
00098   fStatusNextUID = nsMsgKey_None;
00099   fStatusExistingMessages = 0;
00100   fReceivedHeaderOrSizeForUID = nsMsgKey_None;
00101 }
00102 
00103 nsImapServerResponseParser::~nsImapServerResponseParser()
00104 {
00105   PR_Free( fCurrentCommandTag );
00106   delete fSearchResults; 
00107   PR_Free( fMailAccountUrl );
00108   PR_Free( fFolderAdminUrl );
00109   PR_Free( fNetscapeServerVersionString );
00110   PR_Free( fXSenderInfo );
00111   PR_Free( fLastAlert );
00112   PR_Free( fManageListsUrl );
00113   PR_Free( fManageFiltersUrl );
00114   PR_Free( fSelectedMailboxName );
00115   PR_Free(fAuthChallenge);
00116 
00117   NS_IF_RELEASE (fHostSessionList);
00118 }
00119 
00120 PRBool nsImapServerResponseParser::LastCommandSuccessful()
00121 {
00122   return (!CommandFailed() && 
00123     !fServerConnection.DeathSignalReceived() &&
00124     nsIMAPGenericParser::LastCommandSuccessful());
00125 }
00126 
00127 // returns PR_TRUE if things look ok to continue
00128 PRBool nsImapServerResponseParser::GetNextLineForParser(char **nextLine)
00129 {
00130   PRBool rv = PR_TRUE;
00131   *nextLine = fServerConnection.CreateNewLineFromSocket();
00132   if (fServerConnection.DeathSignalReceived() || (fServerConnection.GetConnectionStatus() <= 0))
00133     rv = PR_FALSE;
00134   // we'd really like to try to silently reconnect, but we shouldn't put this
00135   // message up just in the interrupt case
00136   if (fServerConnection.GetConnectionStatus() <= 0 && !fServerConnection.DeathSignalReceived())
00137     fServerConnection.AlertUserEventUsingId(IMAP_SERVER_DISCONNECTED);
00138   return rv;
00139 }
00140 
00141 PRBool nsImapServerResponseParser::CommandFailed()
00142 {
00143   return fCurrentCommandFailed;
00144 }
00145 
00146 void nsImapServerResponseParser::SetFlagState(nsIImapFlagAndUidState *state)
00147 {
00148   fFlagState = state;
00149 }
00150 
00151 PRInt32 nsImapServerResponseParser::SizeOfMostRecentMessage()
00152 {
00153   return fSizeOfMostRecentMessage;
00154 }
00155 
00156 // Call this when adding a pipelined command to the session
00157 void nsImapServerResponseParser::IncrementNumberOfTaggedResponsesExpected(const char *newExpectedTag)
00158 {
00159   fNumberOfTaggedResponsesExpected++;
00160   PR_Free( fCurrentCommandTag );
00161   fCurrentCommandTag = PL_strdup(newExpectedTag);
00162   if (!fCurrentCommandTag)
00163     HandleMemoryFailure();
00164 }
00165 /* 
00166  response        ::= *response_data response_done
00167 */
00168 
00169 
00170 void nsImapServerResponseParser::InitializeState()
00171 {
00172   fProcessingTaggedResponse = PR_FALSE;
00173   fCurrentCommandFailed = PR_FALSE;
00174   fNumberOfRecentMessages = 0;
00175   fReceivedHeaderOrSizeForUID = nsMsgKey_None;
00176 }
00177 
00178 void nsImapServerResponseParser::ParseIMAPServerResponse(const char *currentCommand, PRBool aIgnoreBadAndNOResponses)
00179 {
00180   
00181   NS_ASSERTION(currentCommand && *currentCommand != '\r' && 
00182     *currentCommand != '\n' && *currentCommand != ' ', "Invailid command string");
00183   PRBool sendingIdleDone = !strcmp(currentCommand, "DONE"CRLF);
00184   if (sendingIdleDone)
00185     fWaitingForMoreClientInput = PR_FALSE;
00186 
00187   // Reinitialize the parser
00188   SetConnected(PR_TRUE);
00189   SetSyntaxError(PR_FALSE);
00190   
00191   // Reinitialize our state
00192   InitializeState();
00193   
00194   // the default is to not pipeline
00195   fNumberOfTaggedResponsesExpected = 1;
00196   int numberOfTaggedResponsesReceived = 0;
00197   
00198   char *copyCurrentCommand = PL_strdup(currentCommand);
00199   if (!copyCurrentCommand)
00200   {
00201     HandleMemoryFailure();
00202     return;
00203   }
00204   if (!fServerConnection.DeathSignalReceived())
00205   {
00206     char *placeInTokenString = nsnull;
00207     char *tagToken = nsnull;
00208     char *commandToken = nsnull;
00209     PRBool inIdle = PR_FALSE;
00210     if (!sendingIdleDone)
00211     {
00212       tagToken = nsCRT::strtok(copyCurrentCommand, WHITESPACE, &placeInTokenString);
00213       commandToken = nsCRT::strtok(placeInTokenString, WHITESPACE,&placeInTokenString);
00214     }
00215     else
00216       commandToken = "DONE";
00217     if (tagToken)
00218     {
00219       PR_Free( fCurrentCommandTag );
00220       fCurrentCommandTag = PL_strdup(tagToken);
00221       if (!fCurrentCommandTag)
00222         HandleMemoryFailure();
00223       inIdle = commandToken && !strcmp(commandToken, "IDLE");
00224     }
00225     
00226     if (commandToken && ContinueParse())
00227       PreProcessCommandToken(commandToken, currentCommand);
00228     
00229     if (ContinueParse())
00230     {
00231       ResetLexAnalyzer();
00232       
00233       do {
00234         AdvanceToNextToken();
00235         while (ContinueParse() && !PL_strcmp(fNextToken, "*") )
00236         {
00237           response_data();
00238           if (ContinueParse())
00239           {
00240             if (!fAtEndOfLine)
00241               SetSyntaxError(PR_TRUE);
00242             else if (!inIdle && !fCurrentCommandFailed)
00243               AdvanceToNextToken();
00244           }
00245         }
00246         
00247         if (ContinueParse() && *fNextToken == '+')      // never pipeline APPEND or AUTHENTICATE
00248         {
00249           NS_ASSERTION((fNumberOfTaggedResponsesExpected - numberOfTaggedResponsesReceived) == 1, 
00250             " didn't get the number of tagged responses we expected");
00251           numberOfTaggedResponsesReceived = fNumberOfTaggedResponsesExpected;
00252           if (commandToken && !nsCRT::strcasecmp(commandToken, "authenticate") && placeInTokenString && 
00253             (!nsCRT::strncasecmp(placeInTokenString, "CRAM-MD5", strlen("CRAM-MD5"))
00254              || !nsCRT::strncasecmp(placeInTokenString, "NTLM", strlen("NTLM"))
00255              || !nsCRT::strncasecmp(placeInTokenString, "GSSAPI", strlen("GSSAPI"))
00256              || !nsCRT::strncasecmp(placeInTokenString, "MSN", strlen("MSN"))))
00257           {
00258             // we need to store the challenge from the server if we are using CRAM-MD5 or NTLM. 
00259             authChallengeResponse_data();
00260           }
00261         }
00262         else
00263           numberOfTaggedResponsesReceived++;
00264         
00265         if (numberOfTaggedResponsesReceived < fNumberOfTaggedResponsesExpected)
00266         {
00267           response_tagged();
00268           fProcessingTaggedResponse = PR_FALSE;
00269         }
00270         
00271       } while (ContinueParse() && !inIdle && (numberOfTaggedResponsesReceived < fNumberOfTaggedResponsesExpected));
00272       
00273       // check and see if the server is waiting for more input
00274       // it's possible that we ate this + while parsing certain responses (like cram data),
00275       // in these cases, the parsing routine for that specific command will manually set
00276       // fWaitingForMoreClientInput so we don't lose that information....
00277       if (*fNextToken == '+' || inIdle)
00278       {
00279         fWaitingForMoreClientInput = PR_TRUE;
00280       }
00281       else if (!fWaitingForMoreClientInput) // if we aren't still waiting for more input....
00282       {
00283         if (ContinueParse())
00284           response_done();
00285         
00286         if (ContinueParse() && !CommandFailed())
00287         {
00288           // a successful command may change the eIMAPstate
00289           ProcessOkCommand(commandToken);
00290         }
00291         else if (CommandFailed())
00292         {
00293           // a failed command may change the eIMAPstate
00294           ProcessBadCommand(commandToken);
00295           if (fReportingErrors && !aIgnoreBadAndNOResponses)
00296             fServerConnection.AlertUserEventFromServer(fCurrentLine);
00297         }
00298       }
00299     }
00300   }
00301   else
00302     SetConnected(PR_FALSE);
00303   PL_strfree(copyCurrentCommand);
00304 }
00305 
00306 void nsImapServerResponseParser::HandleMemoryFailure()
00307 {
00308   fServerConnection.AlertUserEventUsingId(IMAP_OUT_OF_MEMORY);
00309   nsIMAPGenericParser::HandleMemoryFailure();
00310 }
00311 
00312 
00313 // SEARCH is the only command that requires pre-processing for now.
00314 // others will be added here.
00315 void nsImapServerResponseParser::PreProcessCommandToken(const char *commandToken,
00316                                                         const char *currentCommand)
00317 {
00318   fCurrentCommandIsSingleMessageFetch = PR_FALSE;
00319   fWaitingForMoreClientInput = PR_FALSE;
00320   
00321   if (!PL_strcasecmp(commandToken, "SEARCH"))
00322     fSearchResults->ResetSequence();
00323   else if (!PL_strcasecmp(commandToken, "SELECT") && currentCommand)
00324   {
00325     // the mailbox name must be quoted, so strip the quotes
00326     const char *openQuote = PL_strstr(currentCommand, "\"");
00327     NS_ASSERTION(openQuote, "expected open quote in imap server response");
00328     if (!openQuote)
00329     { // ill formed select command
00330       openQuote = PL_strstr(currentCommand, " ");
00331     }
00332     PR_Free( fSelectedMailboxName);
00333     fSelectedMailboxName = PL_strdup(openQuote + 1);
00334     if (fSelectedMailboxName)
00335     {
00336       // strip the escape chars and the ending quote
00337       char *currentChar = fSelectedMailboxName;
00338       while (*currentChar)
00339       {
00340         if (*currentChar == '\\')
00341         {
00342           PL_strcpy(currentChar, currentChar+1);
00343           currentChar++;    // skip what we are escaping
00344         }
00345         else if (*currentChar == '\"')
00346           *currentChar = 0; // end quote
00347         else
00348           currentChar++;
00349       }
00350     }
00351     else
00352       HandleMemoryFailure();
00353     
00354     // we don't want bogus info for this new box
00355     //delete fFlagState;    // not our object
00356     //fFlagState = nsnull;
00357   }
00358   else if (!PL_strcasecmp(commandToken, "CLOSE"))
00359   {
00360     return;   // just for debugging
00361     // we don't want bogus info outside the selected state
00362     //delete fFlagState;    // not our object
00363     //fFlagState = nsnull;
00364   }
00365   else if (!PL_strcasecmp(commandToken, "UID"))
00366   {
00367     char *copyCurrentCommand = PL_strdup(currentCommand);
00368     if (!copyCurrentCommand)
00369     {
00370       HandleMemoryFailure();
00371       return;
00372     }
00373     if (!fServerConnection.DeathSignalReceived())
00374     {
00375       char *placeInTokenString = nsnull;
00376       char *tagToken           = nsCRT::strtok(copyCurrentCommand, WHITESPACE,&placeInTokenString);
00377       char *uidToken           = nsCRT::strtok(placeInTokenString, WHITESPACE,&placeInTokenString);
00378       char *fetchToken         = nsCRT::strtok(placeInTokenString, WHITESPACE,&placeInTokenString);
00379       uidToken = nsnull; // use variable to quiet compiler warning
00380       tagToken = nsnull; // use variable to quiet compiler warning
00381       if (!PL_strcasecmp(fetchToken, "FETCH") )
00382       {
00383         char *uidStringToken = nsCRT::strtok(placeInTokenString, WHITESPACE, &placeInTokenString);
00384         if (!PL_strchr(uidStringToken, ',') && !PL_strchr(uidStringToken, ':'))     // , and : are uid delimiters
00385         {
00386           fCurrentCommandIsSingleMessageFetch = PR_TRUE;
00387           fUidOfSingleMessageFetch = atoi(uidStringToken);
00388         }
00389       }
00390     }
00391     PL_strfree(copyCurrentCommand);
00392   }
00393 }
00394 
00395 const char *nsImapServerResponseParser::GetSelectedMailboxName()
00396 {
00397   return fSelectedMailboxName;
00398 }
00399 
00400 nsImapSearchResultIterator *nsImapServerResponseParser::CreateSearchResultIterator()
00401 {
00402   return new nsImapSearchResultIterator(*fSearchResults);
00403 }
00404 
00405 nsImapServerResponseParser::eIMAPstate nsImapServerResponseParser::GetIMAPstate()
00406 {
00407   return fIMAPstate;
00408 }
00409 
00410 void nsImapServerResponseParser::PreauthSetAuthenticatedState()
00411 {
00412   fIMAPstate = kAuthenticated;
00413 }
00414 
00415 void nsImapServerResponseParser::ProcessOkCommand(const char *commandToken)
00416 {
00417   if (!PL_strcasecmp(commandToken, "LOGIN") ||
00418     !PL_strcasecmp(commandToken, "AUTHENTICATE"))
00419     fIMAPstate = kAuthenticated;
00420   else if (!PL_strcasecmp(commandToken, "LOGOUT"))
00421     fIMAPstate = kNonAuthenticated;
00422   else if (!PL_strcasecmp(commandToken, "SELECT") ||
00423     !PL_strcasecmp(commandToken, "EXAMINE"))
00424     fIMAPstate = kFolderSelected;
00425   else if (!PL_strcasecmp(commandToken, "CLOSE"))
00426   {
00427     fIMAPstate = kAuthenticated;
00428     // we no longer have a selected mailbox.
00429     PR_FREEIF( fSelectedMailboxName );
00430   }
00431   else if ((!PL_strcasecmp(commandToken, "LIST")) ||
00432                       (!PL_strcasecmp(commandToken, "LSUB")))
00433   {
00434     //fServerConnection.MailboxDiscoveryFinished();
00435     // This used to be reporting that we were finished
00436     // discovering folders for each time we issued a
00437     // LIST or LSUB.  So if we explicitly listed the
00438     // INBOX, or Trash, or namespaces, we would get multiple
00439     // "done" states, even though we hadn't finished.
00440     // Move this to be called from the connection object
00441     // itself.
00442   }
00443   else if (!PL_strcasecmp(commandToken, "FETCH"))
00444   {
00445     if (!fZeroLengthMessageUidString.IsEmpty())
00446     {
00447       // "Deleting zero length message");
00448       fServerConnection.Store(fZeroLengthMessageUidString.get(), "+Flags (\\Deleted)", PR_TRUE);
00449       if (LastCommandSuccessful())
00450         fServerConnection.Expunge();
00451       
00452       fZeroLengthMessageUidString.Truncate();
00453     }
00454   }
00455   if (GetFillingInShell())
00456   {
00457     // There is a BODYSTRUCTURE response.  Now let's generate the stream...
00458     // that is, if we're not doing it already
00459     if (!m_shell->IsBeingGenerated())
00460     {
00461       nsImapProtocol *navCon = &fServerConnection;
00462       
00463       char *imapPart = nsnull;
00464       
00465       fServerConnection.GetCurrentUrl()->GetImapPartToFetch(&imapPart);
00466       m_shell->Generate(imapPart);
00467       PR_Free(imapPart);
00468       
00469       if ((navCon && navCon->GetPseudoInterrupted())
00470         || fServerConnection.DeathSignalReceived())
00471       {
00472         // we were pseudointerrupted or interrupted
00473         if (!m_shell->IsShellCached())
00474         {
00475           // if it's not in the cache, then we were (pseudo)interrupted while generating
00476           // for the first time.  Delete it.
00477           delete m_shell;
00478         }
00479         navCon->PseudoInterrupt(PR_FALSE);
00480       }
00481       else if (m_shell->GetIsValid())
00482       {
00483         // If we have a valid shell that has not already been cached, then cache it.
00484         if (!m_shell->IsShellCached() && fHostSessionList)     // cache is responsible for destroying it
00485         {
00486           PR_LOG(IMAP, PR_LOG_ALWAYS, 
00487             ("BODYSHELL:  Adding shell to cache."));
00488           const char *serverKey = fServerConnection.GetImapServerKey();
00489           fHostSessionList->AddShellToCacheForHost(
00490             serverKey, m_shell);
00491         }
00492       }
00493       else
00494       {
00495         // The shell isn't valid, so we don't cache it.
00496         // Therefore, we have to destroy it here.
00497         delete m_shell;
00498       }
00499       m_shell = nsnull;
00500     }
00501   }
00502 }
00503 
00504 void nsImapServerResponseParser::ProcessBadCommand(const char *commandToken)
00505 {
00506   if (!PL_strcasecmp(commandToken, "LOGIN") ||
00507     !PL_strcasecmp(commandToken, "AUTHENTICATE"))
00508     fIMAPstate = kNonAuthenticated;
00509   else if (!PL_strcasecmp(commandToken, "LOGOUT"))
00510     fIMAPstate = kNonAuthenticated;       // ??
00511   else if (!PL_strcasecmp(commandToken, "SELECT") ||
00512     !PL_strcasecmp(commandToken, "EXAMINE"))
00513     fIMAPstate = kAuthenticated;   // nothing selected
00514   else if (!PL_strcasecmp(commandToken, "CLOSE"))
00515     fIMAPstate = kAuthenticated;   // nothing selected
00516   if (GetFillingInShell())
00517   {
00518     if (!m_shell->IsBeingGenerated())
00519     {
00520       delete m_shell;
00521       m_shell = nsnull;
00522     }
00523   }
00524 }
00525 
00526 
00527 /*
00528  response_data   ::= "*" SPACE (resp_cond_state / resp_cond_bye /
00529                               mailbox_data / message_data / capability_data)
00530                               CRLF
00531  
00532  The RFC1730 grammar spec did not allow one symbol look ahead to determine
00533  between mailbox_data / message_data so I combined the numeric possibilities
00534  of mailbox_data and all of message_data into numeric_mailbox_data.
00535  
00536  The production implemented here is 
00537 
00538  response_data   ::= "*" SPACE (resp_cond_state / resp_cond_bye /
00539                               mailbox_data / numeric_mailbox_data / 
00540                               capability_data)
00541                               CRLF
00542 
00543 Instead of comparing lots of strings and make function calls, try to pre-flight
00544 the possibilities based on the first letter of the token.
00545 
00546 */
00547 void nsImapServerResponseParser::response_data()
00548 {
00549   AdvanceToNextToken();
00550   
00551   if (ContinueParse())
00552   {
00553     switch (toupper(fNextToken[0]))
00554     {
00555     case 'O':               // OK
00556       if (toupper(fNextToken[1]) == 'K')
00557         resp_cond_state();
00558       else SetSyntaxError(PR_TRUE);
00559       break;
00560     case 'N':               // NO
00561       if (toupper(fNextToken[1]) == 'O')
00562         resp_cond_state();
00563       else if (!PL_strcasecmp(fNextToken, "NAMESPACE"))
00564         namespace_data();
00565       else SetSyntaxError(PR_TRUE);
00566       break;
00567     case 'B':               // BAD
00568       if (!PL_strcasecmp(fNextToken, "BAD"))
00569         resp_cond_state();
00570       else if (!PL_strcasecmp(fNextToken, "BYE"))
00571         resp_cond_bye();
00572       else SetSyntaxError(PR_TRUE);
00573       break;
00574     case 'F':
00575       if (!PL_strcasecmp(fNextToken, "FLAGS"))
00576         mailbox_data();
00577       else SetSyntaxError(PR_TRUE);
00578       break;
00579     case 'P':
00580       if (PL_strcasecmp(fNextToken, "PERMANENTFLAGS"))
00581         mailbox_data();
00582       else SetSyntaxError(PR_TRUE);
00583       break;
00584     case 'L':
00585       if (!PL_strcasecmp(fNextToken, "LIST")  || !PL_strcasecmp(fNextToken, "LSUB"))
00586         mailbox_data();
00587       else if (!PL_strcasecmp(fNextToken, "LANGUAGE"))
00588         language_data();
00589       else
00590         SetSyntaxError(PR_TRUE);
00591       break;
00592     case 'M':
00593       if (!PL_strcasecmp(fNextToken, "MAILBOX"))
00594         mailbox_data();
00595       else if (!PL_strcasecmp(fNextToken, "MYRIGHTS"))
00596         myrights_data();
00597       else SetSyntaxError(PR_TRUE);
00598       break;
00599     case 'S':
00600       if (!PL_strcasecmp(fNextToken, "SEARCH"))
00601         mailbox_data();
00602       else if (!PL_strcasecmp(fNextToken, "STATUS"))
00603       {
00604         AdvanceToNextToken();
00605         if (fNextToken)
00606         {
00607           char *mailboxName = CreateAstring();
00608           PL_strfree( mailboxName); 
00609         }
00610         while (      ContinueParse() &&
00611           !fAtEndOfLine )
00612         {
00613           AdvanceToNextToken();
00614           if (!fNextToken)
00615             break;
00616           
00617           if (*fNextToken == '(') fNextToken++;
00618           if (!PL_strcasecmp(fNextToken, "UIDNEXT"))
00619           {
00620             AdvanceToNextToken();
00621             if (fNextToken)
00622             {
00623               fStatusNextUID = atoi(fNextToken);
00624               // if this token ends in ')', then it is the last token
00625               // else we advance
00626               if ( *(fNextToken + strlen(fNextToken) - 1) == ')')
00627                 fNextToken += strlen(fNextToken) - 1;
00628             }
00629           }
00630           else if (!PL_strcasecmp(fNextToken, "MESSAGES"))
00631           {
00632             AdvanceToNextToken();
00633             if (fNextToken)
00634             {
00635               fStatusExistingMessages = atoi(fNextToken);
00636               // if this token ends in ')', then it is the last token
00637               // else we advance
00638               if ( *(fNextToken + strlen(fNextToken) - 1) == ')')
00639                 fNextToken += strlen(fNextToken) - 1;
00640             }
00641           }
00642           else if (!PL_strcasecmp(fNextToken, "UNSEEN"))
00643           {
00644             AdvanceToNextToken();
00645             if (fNextToken)
00646             {
00647               fStatusUnseenMessages = atoi(fNextToken);
00648               // if this token ends in ')', then it is the last token
00649               // else we advance
00650               if ( *(fNextToken + strlen(fNextToken) - 1) == ')')
00651                 fNextToken += strlen(fNextToken) - 1;
00652             }
00653           }
00654           else if (!PL_strcasecmp(fNextToken, "RECENT"))
00655           {
00656             AdvanceToNextToken();
00657             if (fNextToken)
00658             {
00659               fStatusRecentMessages = atoi(fNextToken);
00660               // if this token ends in ')', then it is the last token
00661               // else we advance
00662               if ( *(fNextToken + strlen(fNextToken) - 1) == ')')
00663                 fNextToken += strlen(fNextToken) - 1;
00664             }
00665           }
00666           else if (*fNextToken == ')')
00667             break;
00668           else if (!fAtEndOfLine)
00669             SetSyntaxError(PR_TRUE);
00670         } 
00671       } else SetSyntaxError(PR_TRUE);
00672       break;
00673     case 'C':
00674       if (!PL_strcasecmp(fNextToken, "CAPABILITY"))
00675         capability_data();
00676       else SetSyntaxError(PR_TRUE);
00677       break;
00678     case 'V':
00679       if (!PL_strcasecmp(fNextToken, "VERSION"))
00680       {
00681         // figure out the version of the Netscape server here
00682         PR_FREEIF(fNetscapeServerVersionString);
00683         AdvanceToNextToken();
00684         if (! fNextToken) 
00685           SetSyntaxError(PR_TRUE);
00686         else
00687         {
00688           fNetscapeServerVersionString = CreateAstring();
00689           AdvanceToNextToken();
00690           if (fNetscapeServerVersionString)
00691           {
00692             fServerIsNetscape3xServer = (*fNetscapeServerVersionString == '3');
00693           }
00694         }
00695         skip_to_CRLF();
00696       }
00697       else SetSyntaxError(PR_TRUE);
00698       break;
00699     case 'A':
00700       if (!PL_strcasecmp(fNextToken, "ACL"))
00701       {
00702         acl_data();
00703       }
00704       else if (!PL_strcasecmp(fNextToken, "ACCOUNT-URL"))
00705       {
00706         PR_FREEIF(fMailAccountUrl);
00707         AdvanceToNextToken();
00708         if (! fNextToken) 
00709           SetSyntaxError(PR_TRUE);
00710         else
00711         {
00712           fMailAccountUrl = CreateAstring();
00713           AdvanceToNextToken();
00714         }
00715       } 
00716       else SetSyntaxError(PR_TRUE);
00717       break;
00718     case 'X':
00719       if (!PL_strcasecmp(fNextToken, "XSERVERINFO"))
00720         xserverinfo_data();
00721       else if (!PL_strcasecmp(fNextToken, "XMAILBOXINFO"))
00722         xmailboxinfo_data();
00723       else if (!PL_strcasecmp(fNextToken, "XAOL-OPTION"))
00724         skip_to_CRLF();
00725       else 
00726       {
00727         // check if custom command
00728         nsXPIDLCString customCommand;
00729         fServerConnection.GetCurrentUrl()->GetCommand(getter_Copies(customCommand));
00730         if (customCommand.Equals(fNextToken))
00731         {
00732           nsCAutoString customCommandResponse;
00733           while (Connected() && !fAtEndOfLine)
00734           {
00735             AdvanceToNextToken();
00736             customCommandResponse.Append(fNextToken);
00737             customCommandResponse.Append(" ");
00738           }
00739           fServerConnection.GetCurrentUrl()->SetCustomCommandResult(customCommandResponse.get());
00740         }
00741         else
00742           SetSyntaxError(PR_TRUE);
00743       }
00744       break;
00745     case 'Q':
00746       if (!PL_strcasecmp(fNextToken, "QUOTAROOT")  || !PL_strcasecmp(fNextToken, "QUOTA"))
00747         quota_data();
00748       else
00749         SetSyntaxError(PR_TRUE);
00750       break;
00751     default:
00752       if (IsNumericString(fNextToken))
00753         numeric_mailbox_data();
00754       else
00755         SetSyntaxError(PR_TRUE);
00756       break;
00757     }
00758     
00759     if (ContinueParse())
00760       PostProcessEndOfLine();
00761   }
00762 }
00763 
00764 
00765 void nsImapServerResponseParser::PostProcessEndOfLine()
00766 {
00767   // for now we only have to do one thing here
00768   // a fetch response to a 'uid store' command might return the flags
00769   // before it returns the uid of the message.  So we need both before
00770   // we report the new flag info to the front end
00771   
00772   // also check and be sure that there was a UID in the current response
00773   if (fCurrentLineContainedFlagInfo && CurrentResponseUID())
00774   {
00775     fCurrentLineContainedFlagInfo = PR_FALSE;
00776     fServerConnection.NotifyMessageFlags(fSavedFlagInfo, CurrentResponseUID());
00777   }
00778 }
00779 
00780 
00781 /*
00782  mailbox_data    ::=  "FLAGS" SPACE flag_list /
00783                                "LIST" SPACE mailbox_list /
00784                                "LSUB" SPACE mailbox_list /
00785                                "MAILBOX" SPACE text /
00786                                "SEARCH" [SPACE 1#nz_number] /
00787                                number SPACE "EXISTS" / number SPACE "RECENT"
00788 
00789 This production was changed to accomodate predictive parsing 
00790 
00791  mailbox_data    ::=  "FLAGS" SPACE flag_list /
00792                                "LIST" SPACE mailbox_list /
00793                                "LSUB" SPACE mailbox_list /
00794                                "MAILBOX" SPACE text /
00795                                "SEARCH" [SPACE 1#nz_number]
00796 */
00797 void nsImapServerResponseParser::mailbox_data()
00798 {
00799   if (!PL_strcasecmp(fNextToken, "FLAGS")) 
00800   {
00801     // this handles the case where we got the permanent flags response
00802     // before the flags response, in which case, we want to ignore thes flags.
00803     if (fGotPermanentFlags)
00804       skip_to_CRLF();
00805     else
00806       parse_folder_flags();
00807   }
00808   else if (!PL_strcasecmp(fNextToken, "LIST"))
00809   {
00810     AdvanceToNextToken();
00811     if (ContinueParse())
00812       mailbox_list(PR_FALSE);
00813   }
00814   else if (!PL_strcasecmp(fNextToken, "LSUB"))
00815   {
00816     AdvanceToNextToken();
00817     if (ContinueParse())
00818       mailbox_list(PR_TRUE);
00819   }
00820   else if (!PL_strcasecmp(fNextToken, "MAILBOX"))
00821     skip_to_CRLF();
00822   else if (!PL_strcasecmp(fNextToken, "SEARCH"))
00823   {
00824     fSearchResults->AddSearchResultLine(fCurrentLine);
00825     fServerConnection.NotifySearchHit(fCurrentLine);
00826     skip_to_CRLF();
00827   }
00828 }
00829 
00830 /*
00831  mailbox_list    ::= "(" #("\Marked" / "\Noinferiors" /
00832                               "\Noselect" / "\Unmarked" / flag_extension) ")"
00833                               SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox
00834 */
00835 
00836 void nsImapServerResponseParser::mailbox_list(PRBool discoveredFromLsub)
00837 {
00838   nsImapMailboxSpec *boxSpec = new nsImapMailboxSpec;
00839   NS_ADDREF(boxSpec);
00840   PRBool needsToFreeBoxSpec = PR_TRUE;
00841   if (!boxSpec)
00842     HandleMemoryFailure();
00843   else
00844   {
00845     boxSpec->folderSelected = PR_FALSE;
00846     boxSpec->box_flags = kNoFlags;
00847     boxSpec->allocatedPathName = nsnull;
00848     boxSpec->hostName = nsnull;
00849     boxSpec->connection = &fServerConnection;
00850     boxSpec->flagState = nsnull;
00851     boxSpec->discoveredFromLsub = discoveredFromLsub;
00852     boxSpec->onlineVerified = PR_TRUE;
00853     boxSpec->box_flags &= ~kNameSpace;
00854     
00855     PRBool endOfFlags = PR_FALSE;
00856     fNextToken++;    // eat the first "("
00857     do {
00858       if (!PL_strncasecmp(fNextToken, "\\Marked", 7))
00859         boxSpec->box_flags |= kMarked;    
00860       else if (!PL_strncasecmp(fNextToken, "\\Unmarked", 9))
00861         boxSpec->box_flags |= kUnmarked;  
00862       else if (!PL_strncasecmp(fNextToken, "\\Noinferiors", 12))
00863         boxSpec->box_flags |= kNoinferiors;      
00864       else if (!PL_strncasecmp(fNextToken, "\\Noselect", 9))
00865         boxSpec->box_flags |= kNoselect;  
00866       // we ignore flag extensions
00867       
00868       endOfFlags = *(fNextToken + strlen(fNextToken) - 1) == ')';
00869       AdvanceToNextToken();
00870     } while (!endOfFlags && ContinueParse());
00871     
00872     if (ContinueParse())
00873     {
00874       if (*fNextToken == '"')
00875       {
00876         fNextToken++;
00877         if (*fNextToken == '\\')   // handle escaped char
00878           boxSpec->hierarchySeparator = *(fNextToken + 1);
00879         else
00880           boxSpec->hierarchySeparator = *fNextToken;
00881       }
00882       else    // likely NIL.  Discovered late in 4.02 that we do not handle literals here (e.g. {10} <10 chars>), although this is almost impossibly unlikely
00883         boxSpec->hierarchySeparator = kOnlineHierarchySeparatorNil;
00884       AdvanceToNextToken(); 
00885       if (ContinueParse())
00886       {
00887         // nsImapProtocol::DiscoverMailboxSpec() eventually frees the
00888         // boxSpec
00889         needsToFreeBoxSpec = PR_FALSE;
00890         mailbox(boxSpec);
00891       }
00892     }
00893   }
00894   if (needsToFreeBoxSpec)
00895     NS_RELEASE(boxSpec); // mscott - do we have any fields we need to
00896   // release?
00897 }
00898 
00899 /* mailbox         ::= "INBOX" / astring
00900 */
00901 void nsImapServerResponseParser::mailbox(nsImapMailboxSpec *boxSpec)
00902 {
00903   char *boxname = nsnull;
00904   const char *serverKey = fServerConnection.GetImapServerKey();
00905   
00906   if (!PL_strcasecmp(fNextToken, "INBOX"))
00907   {
00908     boxname = PL_strdup("INBOX");
00909     AdvanceToNextToken();
00910   }
00911   else 
00912   {
00913     boxname = CreateAstring();
00914     AdvanceToNextToken();
00915   }
00916   
00917   if (boxname && fHostSessionList)
00918   {
00919     // should the namespace check go before or after the Utf7 conversion?
00920     fHostSessionList->SetNamespaceHierarchyDelimiterFromMailboxForHost(
00921       serverKey, boxname, boxSpec->hierarchySeparator);
00922     
00923     
00924     nsIMAPNamespace *ns = nsnull;
00925     fHostSessionList->GetNamespaceForMailboxForHost(serverKey, boxname, ns);
00926     if (ns)
00927     {
00928       switch (ns->GetType())
00929       {
00930       case kPersonalNamespace:
00931         boxSpec->box_flags |= kPersonalMailbox;
00932         break;
00933       case kPublicNamespace:
00934         boxSpec->box_flags |= kPublicMailbox;
00935         break;
00936       case kOtherUsersNamespace:
00937         boxSpec->box_flags |= kOtherUsersMailbox;
00938         break;
00939       default:       // (kUnknownNamespace)
00940         break;
00941       }
00942       boxSpec->namespaceForFolder = ns;
00943     }
00944     
00945     //        char *convertedName =
00946     //            fServerConnection.CreateUtf7ConvertedString(boxname, PR_FALSE);
00947     //        PRUnichar *unicharName;
00948     //        unicharName = fServerConnection.CreatePRUnicharStringFromUTF7(boxname);
00949     //        PL_strfree(boxname);
00950     //        boxname = convertedName;
00951   }
00952   
00953   if (!boxname)
00954   {
00955     if (!fServerConnection.DeathSignalReceived())
00956       HandleMemoryFailure();
00957   }
00958   else
00959   {
00960     NS_ASSERTION(boxSpec->connection, "box spec has null connection");
00961     NS_ASSERTION(boxSpec->connection->GetCurrentUrl(), "box spec has connection with null url");
00962     //boxSpec->hostName = nsnull;
00963     //if (boxSpec->connection && boxSpec->connection->GetCurrentUrl())
00964     boxSpec->connection->GetCurrentUrl()->AllocateCanonicalPath(boxname, boxSpec->hierarchySeparator, &boxSpec->allocatedPathName);
00965     nsIURI * aURL = nsnull;
00966     boxSpec->connection->GetCurrentUrl()->QueryInterface(NS_GET_IID(nsIURI), (void **) &aURL);
00967     if (aURL) {
00968       nsCAutoString host;
00969       aURL->GetHost(host);
00970       boxSpec->hostName = ToNewCString(host);
00971     }
00972     NS_IF_RELEASE(aURL);
00973     if (boxname)
00974       PL_strfree( boxname);
00975     // storage for the boxSpec is now owned by server connection
00976     fServerConnection.DiscoverMailboxSpec(boxSpec);
00977     
00978     // if this was cancelled by the user,then we sure don't want to
00979     // send more mailboxes their way
00980     if (fServerConnection.GetConnectionStatus() < 0)
00981       SetConnected(PR_FALSE);
00982   }
00983 }
00984 
00985 
00986 /*
00987  message_data    ::= nz_number SPACE ("EXPUNGE" /
00988                               ("FETCH" SPACE msg_fetch) / msg_obsolete)
00989 
00990 was changed to
00991 
00992 numeric_mailbox_data ::=  number SPACE "EXISTS" / number SPACE "RECENT"
00993  / nz_number SPACE ("EXPUNGE" /
00994                               ("FETCH" SPACE msg_fetch) / msg_obsolete)
00995 
00996 */
00997 void nsImapServerResponseParser::numeric_mailbox_data()
00998 {
00999   PRInt32 tokenNumber = atoi(fNextToken);
01000   AdvanceToNextToken();
01001   
01002   if (ContinueParse())
01003   {
01004     if (!PL_strcasecmp(fNextToken, "FETCH"))
01005     {
01006       fFetchResponseIndex = tokenNumber;
01007       AdvanceToNextToken();
01008       if (ContinueParse())
01009         msg_fetch(); 
01010     }
01011     else if (!PL_strcasecmp(fNextToken, "EXISTS"))
01012     {
01013       fNumberOfExistingMessages = tokenNumber;
01014       AdvanceToNextToken();
01015     }
01016     else if (!PL_strcasecmp(fNextToken, "RECENT"))
01017     {
01018       fNumberOfRecentMessages = tokenNumber;
01019       AdvanceToNextToken();
01020     }
01021     else if (!PL_strcasecmp(fNextToken, "EXPUNGE"))
01022     {
01023       if (!fServerConnection.GetIgnoreExpunges())
01024         fFlagState->ExpungeByIndex((PRUint32) tokenNumber);
01025       skip_to_CRLF();
01026     }
01027     else
01028       msg_obsolete();
01029   }
01030 }
01031 
01032 /*
01033 msg_fetch       ::= "(" 1#("BODY" SPACE body /
01034 "BODYSTRUCTURE" SPACE body /
01035 "BODY[" section "]" SPACE nstring /
01036 "ENVELOPE" SPACE envelope /
01037 "FLAGS" SPACE "(" #(flag / "\Recent") ")" /
01038 "INTERNALDATE" SPACE date_time /
01039 "RFC822" [".HEADER" / ".TEXT"] SPACE nstring /
01040 "RFC822.SIZE" SPACE number /
01041 "UID" SPACE uniqueid) ")"
01042 
01043 */
01044 
01045 void nsImapServerResponseParser::msg_fetch()
01046 {
01047   nsresult res;
01048   PRBool bNeedEndMessageDownload = PR_FALSE;
01049   
01050   // we have not seen a uid response or flags for this fetch, yet
01051   fCurrentResponseUID = 0;
01052   fCurrentLineContainedFlagInfo = PR_FALSE;
01053   fSizeOfMostRecentMessage = 0;  
01054   // show any incremental progress, for instance, for header downloading
01055   fServerConnection.ShowProgress();
01056   
01057   fNextToken++;      // eat the '(' character
01058   
01059   // some of these productions are ignored for now
01060   while (ContinueParse() && (*fNextToken != ')') )
01061   {
01062     if (!PL_strcasecmp(fNextToken, "FLAGS"))
01063     {
01064       if (fCurrentResponseUID == 0)
01065         res = fFlagState->GetUidOfMessage(fFetchResponseIndex - 1, &fCurrentResponseUID);
01066       
01067       AdvanceToNextToken();
01068       if (ContinueParse())
01069         flags();
01070       
01071       if (ContinueParse())
01072       {       // eat the closing ')'
01073         fNextToken++;
01074         // there may be another ')' to close out
01075         // msg_fetch.  If there is then don't advance
01076         if (*fNextToken != ')')
01077           AdvanceToNextToken();
01078       }
01079     }
01080     else if (!PL_strcasecmp(fNextToken, "UID"))
01081     {
01082       AdvanceToNextToken();
01083       if (ContinueParse())
01084       {
01085         fCurrentResponseUID = atoi(fNextToken);
01086         if (fCurrentResponseUID > fHighestRecordedUID)
01087           fHighestRecordedUID = fCurrentResponseUID;
01088         // size came before UID
01089         if (fSizeOfMostRecentMessage)
01090           fReceivedHeaderOrSizeForUID = CurrentResponseUID();
01091         // if this token ends in ')', then it is the last token
01092         // else we advance
01093         if ( *(fNextToken + strlen(fNextToken) - 1) == ')')
01094           fNextToken += strlen(fNextToken) - 1;
01095         else
01096           AdvanceToNextToken();
01097       }
01098     }
01099     else if (!PL_strcasecmp(fNextToken, "RFC822") ||
01100       !PL_strcasecmp(fNextToken, "RFC822.HEADER") ||
01101       !PL_strncasecmp(fNextToken, "BODY[HEADER",11) ||
01102       !PL_strncasecmp(fNextToken, "BODY[]", 6) ||
01103       !PL_strcasecmp(fNextToken, "RFC822.TEXT") ||
01104       (!PL_strncasecmp(fNextToken, "BODY[", 5) &&
01105                               PL_strstr(fNextToken, "HEADER"))
01106                                   )
01107     {
01108       if (fCurrentResponseUID == 0)
01109         fFlagState->GetUidOfMessage(fFetchResponseIndex - 1, &fCurrentResponseUID);
01110       
01111       if (!PL_strcasecmp(fNextToken, "RFC822.HEADER") ||
01112         !PL_strcasecmp(fNextToken, "BODY[HEADER]"))
01113       {
01114         // all of this message's headers
01115         AdvanceToNextToken();
01116         fDownloadingHeaders = PR_TRUE;
01117         BeginMessageDownload(MESSAGE_RFC822); // initialize header parser
01118         bNeedEndMessageDownload = PR_FALSE;
01119         if (ContinueParse())
01120           msg_fetch_headers(nsnull);
01121       }
01122       else if (!PL_strncasecmp(fNextToken, "BODY[HEADER.FIELDS",19))
01123       {
01124         fDownloadingHeaders = PR_TRUE;
01125         BeginMessageDownload(MESSAGE_RFC822); // initialize header parser
01126         // specific message header fields
01127         while (ContinueParse() && fNextToken[strlen(fNextToken)-1] != ']')
01128           AdvanceToNextToken();
01129         if (ContinueParse())
01130         {
01131           bNeedEndMessageDownload = PR_FALSE;
01132           AdvanceToNextToken();
01133           if (ContinueParse())
01134             msg_fetch_headers(nsnull);
01135         }
01136       }
01137       else
01138       {
01139         char *whereHeader = PL_strstr(fNextToken, "HEADER");
01140         if (whereHeader)
01141         {
01142           char *startPartNum = fNextToken + 5;
01143           if (whereHeader > startPartNum)
01144           {
01145             PRInt32 partLength = whereHeader - startPartNum - 1; //-1 for the dot!
01146             char *partNum = (char *)PR_CALLOC((partLength + 1) * sizeof (char));
01147             if (partNum)
01148             {
01149               PL_strncpy(partNum, startPartNum, partLength);
01150               if (ContinueParse())
01151               {
01152                 if (PL_strstr(fNextToken, "FIELDS"))
01153                 {
01154                   while (ContinueParse() && fNextToken[strlen(fNextToken)-1] != ']')
01155                     AdvanceToNextToken();
01156                 }
01157                 if (ContinueParse())
01158                 {
01159                   AdvanceToNextToken();
01160                   if (ContinueParse())
01161                     msg_fetch_headers(partNum);
01162                 }
01163               }
01164               PR_Free(partNum);
01165             }
01166           }
01167           else
01168             SetSyntaxError(PR_TRUE);
01169         }
01170         else
01171         {
01172           fDownloadingHeaders = PR_FALSE;
01173           
01174           PRBool chunk = PR_FALSE;
01175           PRInt32 origin = 0;
01176           if (!PL_strncasecmp(fNextToken, "BODY[]<", 7))
01177           {
01178             char *tokenCopy = 0;
01179             tokenCopy = PL_strdup(fNextToken);
01180             if (tokenCopy)
01181             {
01182               char *originString = tokenCopy + 7;       // where the byte number starts
01183               char *closeBracket = PL_strchr(tokenCopy,'>');
01184               if (closeBracket && originString && *originString)
01185               {
01186                 *closeBracket = 0;
01187                 origin = atoi(originString);
01188                 chunk = PR_TRUE;
01189               }
01190               PR_Free(tokenCopy);
01191             }
01192           }
01193           
01194           AdvanceToNextToken();
01195           if (ContinueParse())
01196           {
01197             msg_fetch_content(chunk, origin, MESSAGE_RFC822);
01198           }
01199         }
01200       }
01201       }
01202       else if (!PL_strcasecmp(fNextToken, "RFC822.SIZE") || !PL_strcasecmp(fNextToken, "XAOL.SIZE"))
01203       {
01204         AdvanceToNextToken();
01205         if (ContinueParse())
01206         {
01207           PRBool sendEndMsgDownload = (GetDownloadingHeaders() 
01208                                         && fReceivedHeaderOrSizeForUID == CurrentResponseUID());
01209           fSizeOfMostRecentMessage = atoi(fNextToken);
01210           fReceivedHeaderOrSizeForUID = CurrentResponseUID();
01211           if (sendEndMsgDownload)
01212           {
01213             fServerConnection.NormalMessageEndDownload();
01214             fReceivedHeaderOrSizeForUID = nsMsgKey_None;
01215           }
01216 
01217           // if we are in the process of fetching everything RFC822 then we should
01218           // turn around and force the total download size to be set to this value.
01219           // this helps if the server gaves us a bogus size for the message in response to the 
01220           // envelope command.
01221           if (fFetchEverythingRFC822)
01222             SetTotalDownloadSize(fSizeOfMostRecentMessage);
01223           
01224           if (fSizeOfMostRecentMessage == 0 && CurrentResponseUID())
01225           {
01226             // on no, bogus Netscape 2.0 mail server bug
01227             char uidString[100];
01228             sprintf(uidString, "%ld", (long)CurrentResponseUID());
01229             
01230             if (!fZeroLengthMessageUidString.IsEmpty())
01231               fZeroLengthMessageUidString += ",";
01232             
01233             fZeroLengthMessageUidString += uidString;
01234           }
01235           
01236           // if this token ends in ')', then it is the last token
01237           // else we advance
01238           if ( *(fNextToken + strlen(fNextToken) - 1) == ')')
01239             fNextToken += strlen(fNextToken) - 1;
01240           else
01241             AdvanceToNextToken();
01242         }
01243       }
01244       else if (!PL_strcasecmp(fNextToken, "XSENDER"))
01245       {
01246         PR_FREEIF(fXSenderInfo);
01247         AdvanceToNextToken();
01248         if (! fNextToken) 
01249           SetSyntaxError(PR_TRUE);
01250         else
01251         {
01252           fXSenderInfo = CreateAstring(); 
01253           AdvanceToNextToken();
01254         }
01255       }
01256       // I only fetch RFC822 so I should never see these BODY responses
01257       else if (!PL_strcasecmp(fNextToken, "BODY"))
01258         skip_to_CRLF(); // I never ask for this
01259       else if (!PL_strcasecmp(fNextToken, "BODYSTRUCTURE"))
01260       {
01261         if (fCurrentResponseUID == 0)
01262           fFlagState->GetUidOfMessage(fFetchResponseIndex - 1, &fCurrentResponseUID);
01263         bodystructure_data();
01264       }
01265       else if (!PL_strncasecmp(fNextToken, "BODY[TEXT", 9))
01266       {
01267         mime_data();
01268       }
01269       else if (!PL_strncasecmp(fNextToken, "BODY[", 5) && PL_strncasecmp(fNextToken, "BODY[]", 6))
01270       {
01271         fDownloadingHeaders = PR_FALSE;
01272         // A specific MIME part, or MIME part header
01273         mime_data();
01274       }
01275       else if (!PL_strcasecmp(fNextToken, "ENVELOPE"))
01276       {
01277         fDownloadingHeaders = PR_TRUE;
01278         bNeedEndMessageDownload = PR_TRUE;
01279         BeginMessageDownload(MESSAGE_RFC822);
01280         envelope_data(); 
01281       }
01282       else if (!PL_strcasecmp(fNextToken, "INTERNALDATE"))
01283       {
01284         fDownloadingHeaders = PR_TRUE; // we only request internal date while downloading headers
01285         if (!bNeedEndMessageDownload)
01286           BeginMessageDownload(MESSAGE_RFC822);
01287         bNeedEndMessageDownload = PR_TRUE;
01288         internal_date(); 
01289       }
01290       else if (!PL_strcasecmp(fNextToken, "XAOL-ENVELOPE"))
01291       {
01292         fDownloadingHeaders = PR_TRUE;
01293         if (!bNeedEndMessageDownload)
01294           BeginMessageDownload(MESSAGE_RFC822);
01295         bNeedEndMessageDownload = PR_TRUE;
01296         xaolenvelope_data();
01297       }
01298       else
01299       {
01300         nsImapAction imapAction; 
01301         if (!fServerConnection.GetCurrentUrl())
01302           return;
01303         fServerConnection.GetCurrentUrl()->GetImapAction(&imapAction);
01304         nsXPIDLCString userDefinedFetchAttribute;
01305         fServerConnection.GetCurrentUrl()->GetCustomAttributeToFetch(getter_Copies(userDefinedFetchAttribute));
01306         if (imapAction == nsIImapUrl::nsImapUserDefinedFetchAttribute && !strcmp(userDefinedFetchAttribute.get(), fNextToken))
01307         {
01308           AdvanceToNextToken();
01309           char *fetchResult = CreateParenGroup();
01310           // look through the tokens until we find the closing ')'
01311           // we can have a result like the following:
01312           // ((A B) (C D) (E F))
01313           fServerConnection.GetCurrentUrl()->SetCustomAttributeResult(fetchResult);
01314           PR_Free(fetchResult);
01315           break;
01316         }
01317         else
01318           SetSyntaxError(PR_TRUE);
01319       }
01320       
01321         }
01322         
01323         if (ContinueParse())
01324         {
01325           if (CurrentResponseUID() && CurrentResponseUID() != nsMsgKey_None 
01326             && fCurrentLineContainedFlagInfo && fFlagState)
01327           {
01328             fFlagState->AddUidFlagPair(CurrentResponseUID(), fSavedFlagInfo, fFetchResponseIndex - 1);
01329             for (PRInt32 i = 0; i < fCustomFlags.Count(); i++)
01330               fFlagState->AddUidCustomFlagPair(CurrentResponseUID(), fCustomFlags.CStringAt(i)->get());
01331             fCustomFlags.Clear();
01332           }
01333           
01334           if (fFetchingAllFlags)
01335             fCurrentLineContainedFlagInfo = PR_FALSE;   // do not fire if in PostProcessEndOfLine          
01336           
01337           AdvanceToNextToken();    // eat the ')' ending token
01338                                                                       // should be at end of line
01339           if (bNeedEndMessageDownload)
01340           {
01341             if (ContinueParse())
01342             {
01343               // complete the message download
01344               fServerConnection.NormalMessageEndDownload();
01345             }
01346             else
01347               fServerConnection.AbortMessageDownLoad();
01348           }
01349           
01350         }
01351 }
01352 
01353 typedef enum _envelopeItemType
01354 {
01355        envelopeString,
01356        envelopeAddress
01357 } envelopeItemType;
01358 
01359 typedef struct 
01360 {
01361        const char * name;
01362        envelopeItemType type;
01363 } envelopeItem;
01364 
01365 // RFC3501:  envelope  = "(" env-date SP env-subject SP env-from SP
01366 //                       env-sender SP env-reply-to SP env-to SP env-cc SP
01367 //                       env-bcc SP env-in-reply-to SP env-message-id ")"
01368 //           env-date    = nstring
01369 //           env-subject = nstring
01370 //           env-from    = "(" 1*address ")" / nil
01371 //           env-sender  = "(" 1*address ")" / nil
01372 //           env-reply-to= "(" 1*address ")" / nil
01373 //           env-to      = "(" 1*address ")" / nil
01374 //           env-cc      = "(" 1*address ")" / nil
01375 //           env-bcc     = "(" 1*address ")" / nil
01376 //           env-in-reply-to = nstring
01377 //           env-message-id  = nstring
01378 
01379 static const envelopeItem EnvelopeTable[] =
01380 {
01381   {"Date", envelopeString},
01382   {"Subject", envelopeString},
01383   {"From", envelopeAddress},
01384   {"Sender", envelopeAddress},
01385   {"Reply-to", envelopeAddress},
01386   {"To", envelopeAddress},
01387   {"Cc", envelopeAddress},
01388   {"Bcc", envelopeAddress},
01389   {"In-reply-to", envelopeString},
01390   {"Message-id", envelopeString}
01391 };
01392 
01393 void nsImapServerResponseParser::envelope_data()
01394 {
01395   AdvanceToNextToken();
01396   fNextToken++; // eat '('
01397   for (int tableIndex = 0; tableIndex < (int)(sizeof(EnvelopeTable) / sizeof(EnvelopeTable[0])); tableIndex++)
01398   {
01399     PRBool headerNonNil = PR_TRUE;
01400     
01401     if (ContinueParse() && (*fNextToken != ')'))
01402     {
01403       nsCAutoString headerLine(EnvelopeTable[tableIndex].name);
01404       headerLine += ": ";
01405       if (EnvelopeTable[tableIndex].type == envelopeString)
01406       {
01407         nsXPIDLCString strValue;
01408         strValue.Adopt(CreateNilString());
01409         if (strValue)
01410         {
01411           headerLine.Append(strValue);
01412         }
01413         else
01414           headerNonNil = PR_FALSE;
01415       }
01416       else
01417       {
01418         nsCAutoString address;
01419         parse_address(address);
01420         headerLine += address;
01421         if (address.IsEmpty())
01422           headerNonNil = PR_FALSE;
01423       }
01424       if (headerNonNil)
01425         fServerConnection.HandleMessageDownLoadLine(headerLine.get(), PR_FALSE);
01426     }
01427     else
01428       break;
01429     // only fetch the next token if we aren't eating a parenthes
01430     if (ContinueParse() && (*fNextToken != ')') || tableIndex < (int)(sizeof(EnvelopeTable) / sizeof(EnvelopeTable[0])) - 1 )
01431       AdvanceToNextToken();
01432   }
01433   
01434   AdvanceToNextToken();
01435 }
01436 
01437 void nsImapServerResponseParser::xaolenvelope_data()
01438 {
01439   // eat the opening '('
01440   fNextToken++;
01441   
01442   if (ContinueParse() && (*fNextToken != ')'))
01443   {
01444     AdvanceToNextToken();
01445     fNextToken++; // eat '('
01446     nsXPIDLCString subject;
01447     subject.Adopt(CreateNilString());
01448     nsCAutoString subjectLine("Subject: ");
01449     subjectLine += subject;
01450     fServerConnection.HandleMessageDownLoadLine(subjectLine.get(), PR_FALSE);
01451     fNextToken++; // eat the next '('
01452     if (ContinueParse())
01453     {
01454       AdvanceToNextToken();
01455       if (ContinueParse())
01456       {
01457         nsCAutoString fromLine;
01458         if (!strcmp(GetSelectedMailboxName(), "Sent Items"))
01459         {
01460           // xaol envelope switches the From with the To, so we switch them back and
01461           // create a fake from line From: user@aol.com
01462           fromLine.Append("To: ");
01463           nsCAutoString fakeFromLine(NS_LITERAL_CSTRING("From: ") + nsDependentCString(fServerConnection.GetImapUserName()) + NS_LITERAL_CSTRING("@aol.com"));
01464           fServerConnection.HandleMessageDownLoadLine(fakeFromLine.get(), PR_FALSE);
01465         }
01466         else
01467         {
01468           fromLine.Append("From: ");
01469         }
01470         parse_address(fromLine);
01471         fServerConnection.HandleMessageDownLoadLine(fromLine.get(), PR_FALSE);
01472         if (ContinueParse())
01473         {
01474           AdvanceToNextToken();    // ge attachment size
01475           PRInt32 attachmentSize = atoi(fNextToken);
01476           if (attachmentSize != 0)
01477           {
01478             nsCAutoString attachmentLine("X-attachment-size: ");
01479             attachmentLine.AppendInt(attachmentSize);
01480             fServerConnection.HandleMessageDownLoadLine(attachmentLine.get(), PR_FALSE);
01481           }
01482         }
01483         if (ContinueParse())
01484         {
01485           AdvanceToNextToken();    // skip image size
01486           PRInt32 imageSize = atoi(fNextToken);
01487           if (imageSize != 0)
01488           {
01489             nsCAutoString imageLine("X-image-size: ");
01490             imageLine.AppendInt(imageSize);
01491             fServerConnection.HandleMessageDownLoadLine(imageLine.get(), PR_FALSE);
01492           }
01493         }
01494         if (ContinueParse())
01495           AdvanceToNextToken();    // skip )
01496       }
01497     }
01498   }
01499 }
01500 
01501 void nsImapServerResponseParser::parse_address(nsCAutoString &addressLine)
01502 {
01503   if (!nsCRT::strcmp(fNextToken, "NIL"))
01504     return;
01505   PRBool firstAddress = PR_TRUE;
01506   // should really look at chars here
01507   NS_ASSERTION(*fNextToken == '(', "address should start with '('");
01508   fNextToken++; // eat the next '('
01509   while (ContinueParse() && *fNextToken == '(')
01510   {
01511     NS_ASSERTION(*fNextToken == '(', "address should start with '('");
01512     fNextToken++; // eat the next '('
01513     
01514     if (!firstAddress)
01515       addressLine += ", ";
01516     
01517     firstAddress = PR_FALSE;
01518     char *personalName = CreateNilString();
01519     AdvanceToNextToken();
01520     char *atDomainList = CreateNilString();
01521     if (ContinueParse())
01522     {
01523       AdvanceToNextToken();
01524       char *mailboxName = CreateNilString();
01525       if (ContinueParse())
01526       {
01527         AdvanceToNextToken();
01528         char *hostName = CreateNilString();
01529         // our tokenizer doesn't handle "NIL)" quite like we
01530         // expect, so we need to check specially for this.
01531         if (hostName || *fNextToken != ')')
01532           AdvanceToNextToken();    // skip hostName
01533         addressLine += mailboxName;
01534         if (hostName)
01535         {
01536           addressLine += '@';
01537           addressLine += hostName;
01538           nsCRT::free(hostName);
01539         }
01540         if (personalName)
01541         {
01542           addressLine += " (";
01543           addressLine += personalName;
01544           addressLine += ')';
01545         }
01546       }
01547     }
01548     PR_Free(personalName);
01549     PR_Free(atDomainList);
01550     
01551     if (*fNextToken == ')')
01552       fNextToken++;
01553     // if the next token isn't a ')' for the address term,
01554     // then we must have another address pair left....so get the next
01555     // token and continue parsing in this loop...
01556     if ( *fNextToken == '\0' )
01557       AdvanceToNextToken();
01558     
01559   }
01560   if (*fNextToken == ')')
01561     fNextToken++;
01562   //   AdvanceToNextToken();       // skip "))"
01563 }
01564 
01565 void nsImapServerResponseParser::internal_date()
01566 {
01567   AdvanceToNextToken();
01568   if (ContinueParse())
01569   {
01570     nsCAutoString dateLine("Date: ");
01571     char *strValue = CreateNilString();
01572     if (strValue)
01573     {
01574       dateLine += strValue;
01575       nsCRT::free(strValue);
01576     }
01577     fServerConnection.HandleMessageDownLoadLine(dateLine.get(), PR_FALSE);
01578   }
01579   // advance the parser.
01580   AdvanceToNextToken();
01581 }
01582 
01583 void nsImapServerResponseParser::flags()
01584 {
01585   imapMessageFlagsType messageFlags = kNoImapMsgFlag;
01586   fCustomFlags.Clear();
01587 
01588   // clear the custom flags for this message
01589   // otherwise the old custom flags will stay around
01590   // see bug #191042
01591   if (fFlagState && CurrentResponseUID() != nsMsgKey_None)
01592     fFlagState->ClearCustomFlags(CurrentResponseUID());
01593 
01594   // eat the opening '('
01595   fNextToken++;
01596   while (ContinueParse() && (*fNextToken != ')'))
01597   {
01598     PRBool knownFlag = PR_FALSE;                                    
01599     if (*fNextToken == '\\')
01600     {
01601       switch (toupper(fNextToken[1])) {
01602       case 'S':
01603         if (!PL_strncasecmp(fNextToken, "\\Seen",5))
01604         {
01605           messageFlags |= kImapMsgSeenFlag;
01606           knownFlag = PR_TRUE;
01607         }
01608         break;
01609       case 'A':
01610         if (!PL_strncasecmp(fNextToken, "\\Answered",9))
01611         {
01612           messageFlags |= kImapMsgAnsweredFlag;
01613           knownFlag = PR_TRUE;
01614         }
01615         break;
01616       case 'F':
01617         if (!PL_strncasecmp(fNextToken, "\\Flagged",8))
01618         {
01619           messageFlags |= kImapMsgFlaggedFlag;
01620           knownFlag = PR_TRUE;
01621         }
01622         break;
01623       case 'D':
01624         if (!PL_strncasecmp(fNextToken, "\\Deleted",8))
01625         {
01626           messageFlags |= kImapMsgDeletedFlag;
01627           knownFlag = PR_TRUE;
01628         }
01629         else if (!PL_strncasecmp(fNextToken, "\\Draft",6))
01630         {
01631           messageFlags |= kImapMsgDraftFlag;
01632           knownFlag = PR_TRUE;
01633         }
01634         break;
01635       case 'R':
01636         if (!PL_strncasecmp(fNextToken, "\\Recent",7))
01637         {
01638           messageFlags |= kImapMsgRecentFlag;
01639           knownFlag = PR_TRUE;
01640         }
01641         break;
01642       default:
01643         break;
01644       }
01645     }
01646     else if (*fNextToken == '$')
01647     {
01648       switch (toupper(fNextToken[1])) {
01649       case 'M':
01650         if ((fSupportsUserDefinedFlags & (kImapMsgSupportUserFlag |
01651           kImapMsgSupportMDNSentFlag))
01652           && !PL_strncasecmp(fNextToken, "$MDNSent",8))
01653         {
01654           messageFlags |= kImapMsgMDNSentFlag;
01655           knownFlag = PR_TRUE;
01656         }
01657         break;
01658       case 'F':
01659         if ((fSupportsUserDefinedFlags & (kImapMsgSupportUserFlag |
01660           kImapMsgSupportForwardedFlag))
01661           && !PL_strncasecmp(fNextToken, "$Forwarded",10))
01662         {
01663           messageFlags |= kImapMsgForwardedFlag;
01664           knownFlag = PR_TRUE;
01665         }
01666         break;
01667       case 'L':
01668         if ((fSupportsUserDefinedFlags & (kImapMsgSupportUserFlag |
01669           kImapMsgLabelFlags))
01670           && !PL_strncasecmp(fNextToken, "$Label", 6))
01671         {
01672           PRInt32 labelValue = fNextToken[6];
01673           if (labelValue > '0')
01674           {
01675             // turn off any previous label flags
01676             messageFlags &= ~kImapMsgLabelFlags;
01677             // turn on this label flag
01678             messageFlags |= (labelValue - '0') << 9;
01679           }
01680           knownFlag = PR_TRUE;
01681         }
01682         break;
01683       default:
01684         break;
01685       }
01686     }
01687     if (!knownFlag && fFlagState)
01688     {
01689       nsCAutoString flag(fNextToken);
01690       PRInt32 parenIndex = flag.FindChar(')');
01691       if (parenIndex > 0)
01692         flag.Truncate(parenIndex);
01693       messageFlags |= kImapMsgCustomKeywordFlag;
01694       if (CurrentResponseUID() != nsMsgKey_None)
01695         fFlagState->AddUidCustomFlagPair(CurrentResponseUID(), flag.get());
01696       else
01697         fCustomFlags.AppendCString(flag);
01698     }
01699     if (PL_strcasestr(fNextToken, ")"))
01700     {
01701       // eat token chars until we get the ')'
01702       while (*fNextToken != ')')
01703         fNextToken++;
01704     }
01705     else
01706       AdvanceToNextToken();
01707   }
01708   
01709   if (ContinueParse())
01710     while(*fNextToken != ')')
01711       fNextToken++;
01712     
01713     fCurrentLineContainedFlagInfo = PR_TRUE;     // handled in PostProcessEndOfLine
01714     fSavedFlagInfo = messageFlags;
01715 }
01716 
01717 /* 
01718  resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
01719 */
01720 void nsImapServerResponseParser::resp_cond_state()
01721 {
01722   if ((!PL_strcasecmp(fNextToken, "NO") ||
01723     !PL_strcasecmp(fNextToken, "BAD") ) &&
01724     fProcessingTaggedResponse)
01725     
01726     fCurrentCommandFailed = PR_TRUE;
01727   
01728   AdvanceToNextToken();
01729   if (ContinueParse())
01730     resp_text();
01731 }
01732 
01733 /*
01734 resp_text       ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text)
01735 
01736   was changed to in order to enable a one symbol look ahead predictive
01737   parser.
01738   
01739     resp_text       ::= ["[" resp_text_code  SPACE] (text_mime2 / text)
01740 */
01741 void nsImapServerResponseParser::resp_text()
01742 {
01743   if (ContinueParse() && (*fNextToken == '['))
01744     resp_text_code();
01745               
01746   if (ContinueParse())
01747   {
01748     if (!PL_strcmp(fNextToken, "=?"))
01749       text_mime2();
01750     else
01751       text();
01752   }
01753 }
01754 /*
01755  text_mime2       ::= "=?" <charset> "?" <encoding> "?"
01756                                <encoded-text> "?="
01757                                ;; Syntax defined in [MIME-2]
01758 */
01759 void nsImapServerResponseParser::text_mime2()
01760 {
01761   skip_to_CRLF();
01762 }
01763 
01764 /*
01765  text            ::= 1*TEXT_CHAR
01766 
01767 */
01768 void nsImapServerResponseParser::text()
01769 {
01770   skip_to_CRLF();
01771 }
01772 
01773 void nsImapServerResponseParser::parse_folder_flags()
01774 {
01775   PRUint16 labelFlags = 0;
01776 
01777   do 
01778   {
01779     AdvanceToNextToken();
01780     if (*fNextToken == '(')
01781       fNextToken++;
01782     if (!PL_strncasecmp(fNextToken, "$MDNSent", 8))
01783       fSupportsUserDefinedFlags |= kImapMsgSupportMDNSentFlag;
01784     else if (!PL_strncasecmp(fNextToken, "$Forwarded", 10))
01785       fSupportsUserDefinedFlags |= kImapMsgSupportForwardedFlag;
01786     else if (!PL_strncasecmp(fNextToken, "\\Seen", 5))
01787       fSettablePermanentFlags |= kImapMsgSeenFlag;
01788     else if (!PL_strncasecmp(fNextToken, "\\Answered", 9))
01789       fSettablePermanentFlags |= kImapMsgAnsweredFlag;
01790     else if (!PL_strncasecmp(fNextToken, "\\Flagged", 8))
01791       fSettablePermanentFlags |= kImapMsgFlaggedFlag;
01792     else if (!PL_strncasecmp(fNextToken, "\\Deleted", 8))
01793       fSettablePermanentFlags |= kImapMsgDeletedFlag;
01794     else if (!PL_strncasecmp(fNextToken, "\\Draft", 6))
01795       fSettablePermanentFlags |= kImapMsgDraftFlag;
01796     else if (!PL_strncasecmp(fNextToken, "$Label1", 7))
01797       labelFlags |= 1;
01798     else if (!PL_strncasecmp(fNextToken, "$Label2", 7))
01799       labelFlags |= 2;
01800     else if (!PL_strncasecmp(fNextToken, "$Label3", 7))
01801       labelFlags |= 4;
01802     else if (!PL_strncasecmp(fNextToken, "$Label4", 7))
01803       labelFlags |= 8;
01804     else if (!PL_strncasecmp(fNextToken, "$Label5", 7))
01805       labelFlags |= 16;
01806     else if (!PL_strncasecmp(fNextToken, "\\*", 2))
01807     {
01808       fSupportsUserDefinedFlags |= kImapMsgSupportUserFlag;
01809       fSupportsUserDefinedFlags |= kImapMsgSupportForwardedFlag;
01810       fSupportsUserDefinedFlags |= kImapMsgSupportMDNSentFlag;
01811       fSupportsUserDefinedFlags |= kImapMsgLabelFlags;
01812     }
01813   } while (!fAtEndOfLine && ContinueParse());
01814 
01815   if (labelFlags == 31)
01816     fSupportsUserDefinedFlags |= kImapMsgLabelFlags;
01817 
01818   if (fFlagState)
01819     fFlagState->SetSupportedUserFlags(fSupportsUserDefinedFlags);
01820 }
01821 /*
01822  resp_text_code  ::= "ALERT" / "PARSE" /
01823                               "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
01824                               "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
01825                               "UIDVALIDITY" SPACE nz_number /
01826                               "UNSEEN" SPACE nz_number /
01827                               atom [SPACE 1*<any TEXT_CHAR except "]">]
01828                               
01829  was changed to in order to enable a one symbol look ahead predictive
01830  parser.
01831  
01832   resp_text_code  ::= ("ALERT" / "PARSE" /
01833                               "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
01834                               "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
01835                               "UIDVALIDITY" SPACE nz_number /
01836                               "UNSEEN" SPACE nz_number /
01837                               atom [SPACE 1*<any TEXT_CHAR except "]">] )
01838                       "]"
01839 
01840  
01841 */
01842 void nsImapServerResponseParser::resp_text_code()
01843 {
01844   // this is a special case way of advancing the token
01845   // strtok won't break up "[ALERT]" into separate tokens
01846   if (strlen(fNextToken) > 1)
01847     fNextToken++;
01848   else 
01849     AdvanceToNextToken();
01850   
01851   if (ContinueParse())
01852   {
01853     if (!PL_strcasecmp(fNextToken,"ALERT]"))
01854     {
01855       char *alertMsg = fCurrentTokenPlaceHolder; // advance past ALERT]
01856       if (alertMsg && *alertMsg && (!fLastAlert || PL_strcmp(fNextToken, fLastAlert)))
01857       {
01858         fServerConnection.AlertUserEvent(alertMsg);
01859         PR_Free(fLastAlert);
01860         fLastAlert = PL_strdup(alertMsg);
01861       }
01862       AdvanceToNextToken();
01863     }
01864     else if (!PL_strcasecmp(fNextToken,"PARSE]"))
01865     {
01866       // do nothing for now
01867       AdvanceToNextToken();
01868     }
01869     else if (!PL_strcasecmp(fNextToken,"NETSCAPE]"))
01870     {
01871       skip_to_CRLF();
01872     }
01873     else if (!PL_strcasecmp(fNextToken,"PERMANENTFLAGS"))
01874     {
01875       PRUint32 saveSettableFlags = fSettablePermanentFlags;
01876       fSupportsUserDefinedFlags = 0;             // assume no unless told
01877       fSettablePermanentFlags = 0;            // assume none, unless told otherwise.
01878       parse_folder_flags();
01879       // if the server tells us there are no permanent flags, we're
01880       // just going to pretend that the FLAGS response flags, if any, are
01881       // permanent in case the server is broken. This will allow us
01882       // to store delete and seen flag changes - if they're not permanent, 
01883       // they're not permanent, but at least we'll try to set them.
01884       if (!fSettablePermanentFlags)
01885         fSettablePermanentFlags = saveSettableFlags;
01886       fGotPermanentFlags = PR_TRUE;
01887     }
01888     else if (!PL_strcasecmp(fNextToken,"READ-ONLY]"))
01889     {
01890       fCurrentFolderReadOnly = PR_TRUE;
01891       AdvanceToNextToken();
01892     }
01893     else if (!PL_strcasecmp(fNextToken,"READ-WRITE]"))
01894     {
01895       fCurrentFolderReadOnly = PR_FALSE;
01896       AdvanceToNextToken();
01897     }
01898     else if (!PL_strcasecmp(fNextToken,"TRYCREATE]"))
01899     {
01900       // do nothing for now
01901       AdvanceToNextToken();
01902     }
01903     else if (!PL_strcasecmp(fNextToken,"UIDVALIDITY"))
01904     {
01905       AdvanceToNextToken();
01906       if (ContinueParse())
01907       {
01908         fFolderUIDValidity = atoi(fNextToken);
01909         fHighestRecordedUID = 0;
01910         AdvanceToNextToken();
01911       }
01912     }
01913     else if (!PL_strcasecmp(fNextToken,"UNSEEN"))
01914     {
01915       AdvanceToNextToken();
01916       if (ContinueParse())
01917       {
01918         fNumberOfUnseenMessages = atoi(fNextToken);
01919         AdvanceToNextToken();
01920       }
01921     }
01922     else if (!PL_strcasecmp(fNextToken, "APPENDUID"))
01923     {
01924       AdvanceToNextToken();
01925       if (ContinueParse())
01926       {
01927         // ** jt -- the returned uidvalidity is the destination folder
01928         // uidvalidity; don't use it for current folder
01929         // fFolderUIDValidity = atoi(fNextToken);
01930         // fHighestRecordedUID = 0; ??? this should be wrong
01931         AdvanceToNextToken();
01932         if (ContinueParse())
01933         {
01934           fCurrentResponseUID = atoi(fNextToken);
01935           AdvanceToNextToken();
01936         }
01937       }
01938     }
01939     else if (!PL_strcasecmp(fNextToken, "COPYUID"))
01940     {
01941       AdvanceToNextToken();
01942       if (ContinueParse())
01943       {
01944         // ** jt -- destination folder uidvalidity
01945         // fFolderUIDValidity = atoi(fNextToken);
01946         // original message set; ignore it
01947         AdvanceToNextToken();
01948         if (ContinueParse())
01949         {
01950           // the resulting message set; should be in the form of
01951           // either uid or uid1:uid2
01952           AdvanceToNextToken();
01953           // clear copy response uid
01954           fServerConnection.SetCopyResponseUid(fNextToken);
01955         }
01956         if (ContinueParse())
01957           AdvanceToNextToken();
01958       }
01959     }
01960     else      // just text
01961     {
01962       // do nothing but eat tokens until we see the ] or CRLF
01963       // we should see the ] but we don't want to go into an
01964       // endless loop if the CRLF is not there
01965       do 
01966       {
01967         AdvanceToNextToken();
01968       } while (!PL_strcasestr(fNextToken, "]") && !fAtEndOfLine 
01969                 && ContinueParse());
01970     }
01971   }
01972 }
01973 
01974 /*
01975  response_done   ::= response_tagged / response_fatal
01976  */
01977  void nsImapServerResponseParser::response_done()
01978  {
01979    if (ContinueParse())
01980    {
01981      if (!PL_strcmp(fCurrentCommandTag, fNextToken))
01982        response_tagged();
01983      else
01984        response_fatal();
01985    }
01986  }
01987  
01988  /*  response_tagged ::= tag SPACE resp_cond_state CRLF
01989  */
01990  void nsImapServerResponseParser::response_tagged()
01991  {
01992    // eat the tag
01993    AdvanceToNextToken();
01994    if (ContinueParse())
01995    {
01996      fProcessingTaggedResponse = PR_TRUE;
01997      resp_cond_state();
01998      if (ContinueParse())
01999      {
02000        if (!fAtEndOfLine)
02001          SetSyntaxError(PR_TRUE);
02002        else if (!fCurrentCommandFailed)
02003          ResetLexAnalyzer();
02004      }
02005    }
02006  }
02007  
02008  /* response_fatal  ::= "*" SPACE resp_cond_bye CRLF
02009  */
02010  void nsImapServerResponseParser::response_fatal()
02011  {
02012    // eat the "*"
02013    AdvanceToNextToken();
02014    if (ContinueParse())
02015      resp_cond_bye();
02016  }
02017 /*
02018 resp_cond_bye   ::= "BYE" SPACE resp_text
02019                               ;; Server will disconnect condition
02020                     */
02021 void nsImapServerResponseParser::resp_cond_bye()
02022 {
02023   SetConnected(PR_FALSE);
02024   fIMAPstate = kNonAuthenticated;
02025 }
02026 
02027 
02028 void nsImapServerResponseParser::msg_fetch_headers(const char *partNum)
02029 {
02030   if (GetFillingInShell())
02031   {
02032     char *headerData = CreateAstring();
02033     AdvanceToNextToken();
02034     m_shell->AdoptMessageHeaders(headerData, partNum);
02035   }
02036   else
02037   {
02038     msg_fetch_content(PR_FALSE, 0, MESSAGE_RFC822);
02039   }
02040 }
02041 
02042 
02043 /* nstring         ::= string / nil
02044 string          ::= quoted / literal
02045 nil             ::= "NIL"
02046 
02047 */
02048 void nsImapServerResponseParser::msg_fetch_content(PRBool chunk, PRInt32 origin, const char *content_type)
02049 {
02050   // setup the stream for downloading this message.
02051   // Don't do it if we are filling in a shell or downloading a part.
02052   // DO do it if we are downloading a whole message as a result of
02053   // an invalid shell trying to generate.
02054   if ((!chunk || (origin == 0)) && !GetDownloadingHeaders() &&
02055     (GetFillingInShell() ? m_shell->GetGeneratingWholeMessage() : PR_TRUE))
02056   {
02057     if (NS_FAILED(BeginMessageDownload(content_type)))
02058       return;
02059   }
02060   
02061   if (PL_strcasecmp(fNextToken, "NIL"))
02062   {
02063     if (*fNextToken == '"')
02064       fLastChunk = msg_fetch_quoted(chunk, origin);
02065     else
02066       fLastChunk = msg_fetch_literal(chunk, origin);
02067   }
02068   else
02069     AdvanceToNextToken();   // eat "NIL"
02070   
02071   if (fLastChunk && (GetFillingInShell() ? m_shell->GetGeneratingWholeMessage() : PR_TRUE))
02072   {
02073     // complete the message download
02074     if (ContinueParse())
02075     {
02076       if (fReceivedHeaderOrSizeForUID == CurrentResponseUID())
02077       {
02078         fServerConnection.NormalMessageEndDownload();
02079         fReceivedHeaderOrSizeForUID = nsMsgKey_None;
02080       }
02081       else
02082          fReceivedHeaderOrSizeForUID = CurrentResponseUID();
02083     }
02084     else
02085       fServerConnection.AbortMessageDownLoad();
02086   }
02087 }
02088 
02089 
02090 /*
02091 quoted          ::= <"> *QUOTED_CHAR <">
02092 
02093   QUOTED_CHAR     ::= <any TEXT_CHAR except quoted_specials> /
02094   "\" quoted_specials
02095   
02096     quoted_specials ::= <"> / "\"
02097 */
02098 
02099 PRBool nsImapServerResponseParser::msg_fetch_quoted(PRBool chunk, PRInt32 origin)
02100 {
02101   
02102 #ifdef DEBUG_chrisf
02103         PR_ASSERT(!chunk);
02104 #endif
02105          
02106          char *q = CreateQuoted();
02107          if (q)
02108          {
02109            fServerConnection.HandleMessageDownLoadLine(q, PR_FALSE, q);
02110            PR_Free(q);
02111          }
02112          
02113          AdvanceToNextToken();
02114          
02115          PRBool lastChunk = !chunk || ((origin + numberOfCharsInThisChunk) >= fTotalDownloadSize);
02116          return lastChunk;
02117 }
02118 /* msg_obsolete    ::= "COPY" / ("STORE" SPACE msg_fetch)
02119 ;; OBSOLETE untagged data responses */
02120 void nsImapServerResponseParser::msg_obsolete()
02121 {
02122   if (!PL_strcasecmp(fNextToken, "COPY"))
02123     AdvanceToNextToken();
02124   else if (!PL_strcasecmp(fNextToken, "STORE"))
02125   {
02126     AdvanceToNextToken();
02127     if (ContinueParse())
02128       msg_fetch();
02129   }
02130   else
02131     SetSyntaxError(PR_TRUE);
02132   
02133 }
02134 void nsImapServerResponseParser::capability_data()
02135 {
02136   fCapabilityFlag = kCapabilityDefined;
02137   do {
02138     AdvanceToNextToken();
02139     // for now we only care about AUTH=LOGIN
02140     if (fNextToken) {
02141       if(! PL_strcasecmp(fNextToken, "AUTH=LOGIN"))
02142         fCapabilityFlag |= kHasAuthLoginCapability;
02143       else if (! PL_strcasecmp(fNextToken, "AUTH=PLAIN"))
02144         fCapabilityFlag |= kHasAuthPlainCapability;
02145       else if (! PL_strcasecmp(fNextToken, "AUTH=CRAM-MD5"))
02146         fCapabilityFlag |= kHasCRAMCapability;
02147       else if (! PL_strcasecmp(fNextToken, "AUTH=NTLM"))
02148         fCapabilityFlag |= kHasAuthNTLMCapability;
02149       else if (! PL_strcasecmp(fNextToken, "AUTH=GSSAPI"))
02150         fCapabilityFlag |= kHasAuthGssApiCapability;
02151       else if (! PL_strcasecmp(fNextToken, "AUTH=MSN"))
02152         fCapabilityFlag |= kHasAuthMSNCapability;
02153       else if (! PL_strcasecmp(fNextToken, "STARTTLS"))
02154         fCapabilityFlag |= kHasStartTLSCapability;
02155       else if (! PL_strcasecmp(fNextToken, "LOGINDISABLED"))
02156         fCapabilityFlag |= kLoginDisabled;
02157       else if (! PL_strcasecmp(fNextToken, "X-NETSCAPE"))
02158         fCapabilityFlag |= kHasXNetscapeCapability;
02159       else if (! PL_strcasecmp(fNextToken, "XSENDER"))
02160         fCapabilityFlag |= kHasXSenderCapability;
02161       else if (! PL_strcasecmp(fNextToken, "IMAP4"))
02162         fCapabilityFlag |= kIMAP4Capability;
02163       else if (! PL_strcasecmp(fNextToken, "IMAP4rev1"))
02164         fCapabilityFlag |= kIMAP4rev1Capability;
02165       else if (! PL_strncasecmp(fNextToken, "IMAP4", 5))
02166         fCapabilityFlag |= kIMAP4other;
02167       else if (! PL_strcasecmp(fNextToken, "X-NO-ATOMIC-RENAME"))
02168         fCapabilityFlag |= kNoHierarchyRename;
02169       else if (! PL_strcasecmp(fNextToken, "X-NON-HIERARCHICAL-RENAME"))
02170         fCapabilityFlag |= kNoHierarchyRename;
02171       else if (! PL_strcasecmp(fNextToken, "NAMESPACE"))
02172         fCapabilityFlag |= kNamespaceCapability;
02173       else if (! PL_strcasecmp(fNextToken, "MAILBOXDATA"))
02174         fCapabilityFlag |= kMailboxDataCapability;
02175       else if (! PL_strcasecmp(fNextToken, "ACL"))
02176         fCapabilityFlag |= kACLCapability;
02177       else if (! PL_strcasecmp(fNextToken, "XSERVERINFO"))
02178         fCapabilityFlag |= kXServerInfoCapability;
02179       else if (! PL_strcasecmp(fNextToken, "UIDPLUS"))
02180         fCapabilityFlag |= kUidplusCapability;
02181       else if (! PL_strcasecmp(fNextToken, "LITERAL+"))
02182         fCapabilityFlag |= kLiteralPlusCapability;
02183       else if (! PL_strcasecmp(fNextToken, "XAOL-OPTION"))
02184         fCapabilityFlag |= kAOLImapCapability;
02185       else if (! PL_strcasecmp(fNextToken, "QUOTA"))
02186         fCapabilityFlag |= kQuotaCapability;
02187       else if (! PL_strcasecmp(fNextToken, "LANGUAGE"))
02188         fCapabilityFlag |= kHasLanguageCapability;
02189       else if (! PL_strcasecmp(fNextToken, "IDLE"))
02190         fCapabilityFlag |= kHasIdleCapability;
02191     }
02192   } while (fNextToken && 
02193                       !fAtEndOfLine &&
02194                          ContinueParse());
02195   
02196   if (fHostSessionList)
02197     fHostSessionList->SetCapabilityForHost(
02198     fServerConnection.GetImapServerKey(), 
02199     fCapabilityFlag);
02200   nsImapProtocol *navCon = &fServerConnection;
02201   NS_ASSERTION(navCon, "null imap protocol connection while parsing capability response"); // we should always have this
02202   if (navCon)
02203     navCon->CommitCapability();
02204   skip_to_CRLF();
02205 }
02206 
02207 void nsImapServerResponseParser::xmailboxinfo_data()
02208 {
02209   AdvanceToNextToken();
02210   if (!fNextToken)
02211     return;
02212   
02213   char *mailboxName = CreateAstring(); // PL_strdup(fNextToken);
02214   if (mailboxName)
02215   {
02216     do 
02217     {
02218       AdvanceToNextToken();
02219       if (fNextToken) 
02220       {
02221         if (!PL_strcmp("MANAGEURL", fNextToken))
02222         {
02223           AdvanceToNextToken();
02224           fFolderAdminUrl = CreateAstring();
02225         }
02226         else if (!PL_strcmp("POSTURL", fNextToken))
02227         {
02228           AdvanceToNextToken();
02229           // ignore this for now...
02230         }
02231       }
02232     } while (fNextToken && !fAtEndOfLine && ContinueParse());
02233   }
02234 }
02235 
02236 void nsImapServerResponseParser::xserverinfo_data()
02237 {
02238   do 
02239   {
02240     AdvanceToNextToken();
02241     if (!fNextToken)
02242       break;
02243     if (!PL_strcmp("MANAGEACCOUNTURL", fNextToken))
02244     {
02245       AdvanceToNextToken();
02246       fMailAccountUrl = CreateNilString();
02247     }
02248     else if (!PL_strcmp("MANAGELISTSURL", fNextToken))
02249     {
02250       AdvanceToNextToken();
02251       fManageListsUrl = CreateNilString();
02252     }
02253     else if (!PL_strcmp("MANAGEFILTERSURL", fNextToken))
02254     {
02255       AdvanceToNextToken();
02256       fManageFiltersUrl = CreateNilString();
02257     }
02258   } while (fNextToken && !fAtEndOfLine && ContinueParse());
02259 }
02260 
02261 void nsImapServerResponseParser::language_data()
02262 {
02263   // we may want to go out and store the language returned to us
02264   // by the language command in the host info session stuff. 
02265 
02266   // for now, just eat the language....
02267   do
02268   {
02269     // eat each language returned to us
02270     AdvanceToNextToken();
02271   } while (fNextToken && !fAtEndOfLine && ContinueParse());
02272 }
02273 
02274 // cram/auth response data ::= "+" SPACE challenge CRLF
02275 // the server expects more client data after issuing its challenge
02276 
02277 void nsImapServerResponseParser::authChallengeResponse_data()
02278 {
02279   AdvanceToNextToken();
02280   fAuthChallenge = nsCRT::strdup(fNextToken);
02281   fWaitingForMoreClientInput = PR_TRUE; 
02282   
02283   skip_to_CRLF();
02284 }
02285 
02286 
02287 void nsImapServerResponseParser::namespace_data()
02288 {
02289        EIMAPNamespaceType namespaceType = kPersonalNamespace;
02290        PRBool namespacesCommitted = PR_FALSE;
02291   const char* serverKey = fServerConnection.GetImapServerKey();
02292        while ((namespaceType != kUnknownNamespace) && ContinueParse())
02293        {
02294               AdvanceToNextToken();
02295               while (fAtEndOfLine && ContinueParse())
02296                      AdvanceToNextToken();
02297               if (!PL_strcasecmp(fNextToken,"NIL"))
02298               {
02299                      // No namespace for this type.
02300                      // Don't add anything to the Namespace object.
02301               }
02302               else if (fNextToken[0] == '(')
02303               {
02304                      // There may be multiple namespaces of the same type.
02305                      // Go through each of them and add them to our Namespace object.
02306 
02307                      fNextToken++;
02308                      while (fNextToken[0] == '(' && ContinueParse())
02309                      {
02310                             // we have another namespace for this namespace type
02311                             fNextToken++;
02312                             if (fNextToken[0] != '"')
02313                             {
02314                                    SetSyntaxError(PR_TRUE);
02315                             }
02316                             else
02317                             {
02318                                    char *namespacePrefix = CreateQuoted(PR_FALSE);
02319 
02320                                    AdvanceToNextToken();
02321                                    char *quotedDelimiter = fNextToken;
02322                                    char namespaceDelimiter = '\0';
02323 
02324                                    if (quotedDelimiter[0] == '"')
02325                                    {
02326                                           quotedDelimiter++;
02327                                           namespaceDelimiter = quotedDelimiter[0];
02328                                    }
02329                                    else if (!PL_strncasecmp(quotedDelimiter, "NIL", 3))
02330                                    {
02331                                           // NIL hierarchy delimiter.  Leave namespace delimiter nsnull.
02332                                    }
02333                                    else
02334                                    {
02335                                           // not quoted or NIL.
02336                                           SetSyntaxError(PR_TRUE);
02337                                    }
02338                                    if (ContinueParse())
02339                                    {
02340             // add code to parse the TRANSLATE attribute if it is present....
02341             // we'll also need to expand the name space code to take in the translated prefix name.
02342 
02343                                           nsIMAPNamespace *newNamespace = new nsIMAPNamespace(namespaceType, namespacePrefix, namespaceDelimiter, PR_FALSE);
02344                                           // add it to a temporary list in the host
02345                                           if (newNamespace && fHostSessionList)
02346                                                  fHostSessionList->AddNewNamespaceForHost(
02347                                 serverKey, newNamespace);
02348 
02349                                           skip_to_close_paren();      // Ignore any extension data
02350        
02351                                           PRBool endOfThisNamespaceType = (fNextToken[0] == ')');
02352                                           if (!endOfThisNamespaceType && fNextToken[0] != '(')    // no space between namespaces of the same type
02353                                           {
02354                                                  SetSyntaxError(PR_TRUE);
02355                                           }
02356                                    }
02357                                    PR_Free(namespacePrefix);
02358                             }
02359                      }
02360               }
02361               else
02362               {
02363                      SetSyntaxError(PR_TRUE);
02364               }
02365               switch (namespaceType)
02366               {
02367               case kPersonalNamespace:
02368                      namespaceType = kOtherUsersNamespace;
02369                      break;
02370               case kOtherUsersNamespace:
02371                      namespaceType = kPublicNamespace;
02372                      break;
02373               default:
02374                      namespaceType = kUnknownNamespace;
02375                      break;
02376               }
02377        }
02378        if (ContinueParse())
02379        {
02380               nsImapProtocol *navCon = &fServerConnection;
02381               NS_ASSERTION(navCon, "null protocol connection while parsing namespace");    // we should always have this
02382               if (navCon)
02383               {
02384                      navCon->CommitNamespacesForHostEvent();
02385                      namespacesCommitted = PR_TRUE;
02386               }
02387        }
02388        skip_to_CRLF();
02389 
02390        if (!namespacesCommitted && fHostSessionList)
02391        {
02392               PRBool success;
02393               fHostSessionList->FlushUncommittedNamespacesForHost(serverKey,
02394                                                             success);
02395        }
02396 }
02397 
02398 void nsImapServerResponseParser::myrights_data()
02399 {
02400   AdvanceToNextToken();
02401   if (ContinueParse() && !fAtEndOfLine)
02402   {
02403     char *mailboxName = CreateAstring(); // PL_strdup(fNextToken);
02404     if (mailboxName)
02405     {
02406       AdvanceToNextToken();
02407       if (ContinueParse())
02408       {
02409         char *myrights = CreateAstring(); // PL_strdup(fNextToken);
02410         if (myrights)
02411         {
02412           nsImapProtocol *navCon = &fServerConnection;
02413           NS_ASSERTION(navCon, "null connection parsing my rights");  // we should always have this
02414           if (navCon)
02415             navCon->AddFolderRightsForUser(mailboxName, nsnull /* means "me" */, myrights);
02416           PR_Free(myrights);
02417         }
02418         else
02419         {
02420           HandleMemoryFailure();
02421         }
02422         if (ContinueParse())
02423           AdvanceToNextToken();
02424       }
02425       PR_Free(mailboxName);
02426     }
02427     else
02428     {
02429       HandleMemoryFailure();
02430     }
02431   }
02432   else
02433   {
02434     SetSyntaxError(PR_TRUE);
02435   }
02436 }
02437 
02438 void nsImapServerResponseParser::acl_data()
02439 {
02440   AdvanceToNextToken();
02441   if (ContinueParse() && !fAtEndOfLine)
02442   {
02443     char *mailboxName = CreateAstring();  // PL_strdup(fNextToken);
02444     if (mailboxName && ContinueParse())
02445     {
02446       AdvanceToNextToken();
02447       while (ContinueParse() && !fAtEndOfLine)
02448       {
02449         char *userName = CreateAstring(); // PL_strdup(fNextToken);
02450         if (userName && ContinueParse())
02451         {
02452           AdvanceToNextToken();
02453           if (ContinueParse())
02454           {
02455             char *rights = CreateAstring(); // PL_strdup(fNextToken);
02456             if (rights)
02457             {
02458               fServerConnection.AddFolderRightsForUser(mailboxName, userName, rights);
02459               PR_Free(rights);
02460             }
02461             else
02462               HandleMemoryFailure();
02463             
02464             if (ContinueParse())
02465               AdvanceToNextToken();
02466           }
02467           PR_Free(userName);
02468         }
02469         else
02470           HandleMemoryFailure();
02471       }
02472       PR_Free(mailboxName);
02473     }
02474     else
02475       HandleMemoryFailure();
02476   }
02477 }
02478 
02479 
02480 void nsImapServerResponseParser::mime_data()
02481 {
02482   if (PL_strstr(fNextToken, "MIME"))
02483     mime_header_data();
02484   else
02485     mime_part_data();
02486 }
02487 
02488 // mime_header_data should not be streamed out;  rather, it should be
02489 // buffered in the nsIMAPBodyShell.
02490 // This is because we are still in the process of generating enough
02491 // information from the server (such as the MIME header's size) so that
02492 // we can construct the final output stream.
02493 void nsImapServerResponseParser::mime_header_data()
02494 {
02495   char *partNumber = PL_strdup(fNextToken);
02496   if (partNumber)
02497   {
02498     char *start = partNumber+5, *end = partNumber+5;    // 5 == nsCRT::strlen("BODY[")
02499     while (ContinueParse() && end && *end != 'M' && *end != 'm')
02500     {
02501       end++;
02502     }
02503     if (end && (*end == 'M' || *end == 'm'))
02504     {
02505       *(end-1) = 0;
02506       AdvanceToNextToken();
02507       char *mimeHeaderData = CreateAstring();    // is it really this simple?
02508       AdvanceToNextToken();
02509       if (m_shell)
02510       {
02511         m_shell->AdoptMimeHeader(start, mimeHeaderData);
02512       }
02513     }
02514     else
02515     {
02516       SetSyntaxError(PR_TRUE);
02517     }
02518     PR_Free(partNumber);    // partNumber is not adopted by the body shell.
02519   }
02520   else
02521   {
02522     HandleMemoryFailure();
02523   }
02524 }
02525 
02526 // Actual mime parts are filled in on demand (either from shell generation
02527 // or from explicit user download), so we need to stream these out.
02528 void nsImapServerResponseParser::mime_part_data()
02529 {
02530   char *checkOriginToken = PL_strdup(fNextToken);
02531   if (checkOriginToken)
02532   {
02533     PRUint32 origin = 0;
02534     PRBool originFound = PR_FALSE;
02535     char *whereStart = PL_strchr(checkOriginToken, '<');
02536     if (whereStart)
02537     {
02538       char *whereEnd = PL_strchr(whereStart, '>');
02539       if (whereEnd)
02540       {
02541         *whereEnd = 0;
02542         whereStart++;
02543         origin = atoi(whereStart);
02544         originFound = PR_TRUE;
02545       }
02546     }
02547     PR_Free(checkOriginToken);
02548     AdvanceToNextToken();
02549     msg_fetch_content(originFound, origin, MESSAGE_RFC822);    // keep content type as message/rfc822, even though the
02550     // MIME part might not be, because then libmime will
02551     // still handle and decode it.
02552   }
02553   else
02554     HandleMemoryFailure();
02555 }
02556 
02557 // parse FETCH BODYSTRUCTURE response, "a parenthesized list that describes
02558 // the [MIME-IMB] body structure of a message" [RFC 3501].
02559 void nsImapServerResponseParser::bodystructure_data()
02560 {
02561   AdvanceToNextToken();
02562   if (ContinueParse() && fNextToken && *fNextToken == '(')  // It has to start with an open paren.
02563   {
02564     // Turn the BODYSTRUCTURE response into a form that the nsIMAPBodypartMessage can be constructed from.
02565     nsIMAPBodypartMessage *message = new nsIMAPBodypartMessage(NULL, NULL, PR_TRUE,
02566                                                                nsCRT::strdup("message"), nsCRT::strdup("rfc822"),
02567                                                                NULL, NULL, NULL, 0);
02568     nsIMAPBodypart *body = bodystructure_part(PL_strdup("1"), message);
02569     if (body)
02570       message->SetBody(body);
02571     else
02572     {
02573       delete message;
02574       message = nsnull;
02575     }
02576     m_shell = new nsIMAPBodyShell(&fServerConnection, message, CurrentResponseUID(), GetSelectedMailboxName());
02577     // ignore syntax errors in parsing the body structure response. If there's an error
02578     // we'll just fall back to fetching the whole message.
02579     SetSyntaxError(PR_FALSE);
02580   }
02581   else
02582     SetSyntaxError(PR_TRUE);
02583 }
02584 
02585 // RFC3501:  body = "(" (body-type-1part / body-type-mpart) ")"
02586 nsIMAPBodypart *
02587 nsImapServerResponseParser::bodystructure_part(char *partNum, nsIMAPBodypart *parentPart)
02588 {
02589   // Check to see if this buffer is a leaf or container
02590   // (Look at second character - if an open paren, then it is a container)
02591   if (*fNextToken != '(')
02592   {
02593     NS_ASSERTION(PR_FALSE, "bodystructure_part must begin with '('");
02594     return NULL;
02595   }
02596   
02597   if (fNextToken[1] == '(')
02598     return bodystructure_multipart(partNum, parentPart);
02599   else
02600     return bodystructure_leaf(partNum, parentPart);
02601 }
02602 
02603 // RFC3501: body-type-1part = (body-type-basic / body-type-msg / body-type-text)
02604 //                            [SP body-ext-1part]
02605 nsIMAPBodypart *
02606 nsImapServerResponseParser::bodystructure_leaf(char *partNum, nsIMAPBodypart *parentPart) 
02607 {
02608   // historical note: this code was originally in nsIMAPBodypartLeaf::ParseIntoObjects()
02609   char *bodyType = nsnull, *bodySubType = nsnull, *bodyID = nsnull, *bodyDescription = nsnull, *bodyEncoding = nsnull;
02610   PRInt32 partLength = 0;
02611   PRBool isValid = PR_TRUE;
02612   
02613   // body type  ("application", "text", "image", etc.)
02614   if (ContinueParse())
02615   {
02616     fNextToken++; // eat the first '('
02617     bodyType = CreateNilString();
02618     if (ContinueParse())
02619       AdvanceToNextToken();
02620   }
02621   
02622   // body subtype  ("gif", "html", etc.)
02623   if (isValid && ContinueParse())
02624   {
02625     bodySubType = CreateNilString();
02626     if (ContinueParse())
02627       AdvanceToNextToken();
02628   }
02629   
02630   // body parameter: parenthesized list
02631   if (isValid && ContinueParse())
02632   {
02633     if (fNextToken[0] == '(')
02634     {
02635       fNextToken++;
02636       skip_to_close_paren();
02637     }
02638     else if (!PL_strcasecmp(fNextToken, "NIL"))
02639       AdvanceToNextToken();
02640   }
02641   
02642   // body id
02643   if (isValid && ContinueParse())
02644   {
02645     bodyID = CreateNilString();
02646     if (ContinueParse())
02647       AdvanceToNextToken();
02648   }
02649   
02650   // body description
02651   if (isValid && ContinueParse())
02652   {
02653     bodyDescription = CreateNilString();
02654     if (ContinueParse())
02655       AdvanceToNextToken();
02656   }
02657   
02658   // body encoding
02659   if (isValid && ContinueParse())
02660   {
02661     bodyEncoding = CreateNilString();
02662     if (ContinueParse())
02663       AdvanceToNextToken();
02664   }
02665   
02666   // body size
02667   if (isValid && ContinueParse())
02668   {
02669     char *bodySizeString = CreateAtom();
02670     if (!bodySizeString)
02671       isValid = PR_FALSE;
02672     else
02673     {
02674       partLength = atoi(bodySizeString);
02675       PR_Free(bodySizeString);
02676       if (ContinueParse())
02677         AdvanceToNextToken();
02678     }
02679   }
02680 
02681   if (!isValid || !ContinueParse())
02682   {
02683     PR_FREEIF(partNum);
02684     PR_FREEIF(bodyType);
02685     PR_FREEIF(bodySubType);
02686     PR_FREEIF(bodyID);
02687     PR_FREEIF(bodyDescription);
02688     PR_FREEIF(bodyEncoding);
02689   }
02690   else
02691   {
02692     if (PL_strcasecmp(bodyType, "message") || PL_strcasecmp(bodySubType, "rfc822"))
02693     {
02694       skip_to_close_paren();
02695       return new nsIMAPBodypartLeaf(partNum, parentPart, bodyType, bodySubType,
02696           bodyID, bodyDescription, bodyEncoding, partLength);
02697     }
02698     
02699     // This part is of type "message/rfc822"  (probably a forwarded message)
02700     nsIMAPBodypartMessage *message = new nsIMAPBodypartMessage(partNum, parentPart, PR_FALSE,
02701       bodyType, bodySubType, bodyID, bodyDescription, bodyEncoding, partLength);
02702 
02703     // there are three additional fields: envelope structure, bodystructure, and size in lines    
02704     // historical note: this code was originally in nsIMAPBodypartMessage::ParseIntoObjects()
02705 
02706     // envelope (ignored)
02707     if (*fNextToken == '(')
02708     {
02709       fNextToken++;
02710       skip_to_close_paren();
02711     }
02712     else
02713       isValid = PR_FALSE;
02714 
02715     // bodystructure
02716     if (isValid && ContinueParse())
02717     {
02718       if (*fNextToken != '(')
02719         isValid = PR_FALSE;
02720       else
02721       {
02722         char *bodyPartNum = PR_smprintf("%s.1", partNum);
02723         if (bodyPartNum)
02724         {
02725           nsIMAPBodypart *body = bodystructure_part(bodyPartNum, message);
02726           if (body)
02727             message->SetBody(body);
02728           else
02729             isValid = PR_FALSE;
02730         }
02731       }
02732     }
02733     
02734     // ignore "size in text lines"
02735 
02736     if (isValid && ContinueParse()) {
02737       skip_to_close_paren();
02738       return message;
02739     }
02740     delete message;
02741   }
02742 
02743   // parsing failed, just move to the end of the parentheses group
02744   if (ContinueParse())
02745     skip_to_close_paren();
02746   return nsnull;
02747 }
02748 
02749 
02750 // RFC3501:  body-type-mpart = 1*body SP media-subtype
02751 //                             [SP body-ext-mpart]
02752 nsIMAPBodypart *
02753 nsImapServerResponseParser::bodystructure_multipart(char *partNum, nsIMAPBodypart *parentPart) 
02754 {
02755   nsIMAPBodypartMultipart *multipart = new nsIMAPBodypartMultipart(partNum, parentPart);
02756   PRBool isValid = multipart->GetIsValid();
02757   // historical note: this code was originally in nsIMAPBodypartMultipart::ParseIntoObjects()
02758   if (ContinueParse())
02759   {
02760     fNextToken++; // eat the first '('
02761     // Parse list of children
02762     int childCount = 0;
02763     while (isValid && fNextToken[0] == '(' && ContinueParse())
02764     {
02765       childCount++;
02766       char *childPartNum = NULL;
02767       // note: the multipart constructor does some magic on partNumber
02768       if (PL_strcmp(multipart->GetPartNumberString(), "0")) // not top-level
02769         childPartNum = PR_smprintf("%s.%d", multipart->GetPartNumberString(), childCount);
02770       else // top-level
02771         childPartNum = PR_smprintf("%d", childCount);
02772       if (!childPartNum)
02773         isValid = PR_FALSE;
02774       else
02775       {
02776         nsIMAPBodypart *child = bodystructure_part(childPartNum, multipart);
02777         if (child)
02778           multipart->AppendPart(child);
02779         else
02780           isValid = PR_FALSE;
02781       }
02782     }
02783 
02784     // RFC3501:  media-subtype   = string
02785     // (multipart subtype: mixed, alternative, etc.)
02786     if (isValid && ContinueParse())
02787     {
02788       char *bodySubType = CreateNilString();
02789       multipart->SetBodySubType(bodySubType);
02790       if (ContinueParse())
02791         AdvanceToNextToken();
02792     }
02793 
02794     // extension data:
02795     // RFC3501:  body-ext-mpart = body-fld-param [SP body-fld-dsp [SP body-fld-lang
02796     //                            [SP body-fld-loc *(SP body-extension)]]]
02797     
02798     // body parameter parenthesized list (optional data), includes boundary parameter
02799     // RFC3501:  body-fld-param  = "(" string SP string *(SP string SP string) ")" / nil
02800     char *boundaryData = nsnull;
02801     if (isValid && ContinueParse() && *fNextToken == '(')
02802     {
02803       fNextToken++;
02804       while (ContinueParse() && *fNextToken != ')')
02805       {
02806         char *attribute = CreateNilString();
02807         if (ContinueParse())
02808           AdvanceToNextToken();
02809         if (ContinueParse() && !PL_strcasecmp(attribute, "BOUNDARY"))
02810         {
02811           char *boundary = CreateNilString();
02812           if (boundary)
02813             boundaryData = PR_smprintf("--%s", boundary);
02814           PR_FREEIF(boundary);
02815         }
02816         else if (ContinueParse())
02817         {
02818           char *value = CreateNilString();
02819           PR_FREEIF(value);
02820         }
02821         PR_FREEIF(attribute);
02822         if (ContinueParse())
02823           AdvanceToNextToken();
02824       }
02825       if (ContinueParse())
02826         fNextToken++;  // skip closing ')'
02827     }
02828     if (boundaryData)
02829       multipart->SetBoundaryData(boundaryData);
02830     else  
02831       isValid = PR_FALSE;   // Actually, we should probably generate a boundary here.
02832   }
02833 
02834   // always move to closing ')', even if part was not successfully read 
02835   if (ContinueParse())
02836     skip_to_close_paren();
02837 
02838   if (isValid)
02839     return multipart;
02840   delete multipart;
02841   return nsnull;
02842 }
02843 
02844 
02845 void nsImapServerResponseParser::quota_data()
02846 {
02847   nsCString quotaroot;
02848   if (!PL_strcasecmp(fNextToken, "QUOTAROOT"))
02849   {
02850     nsCString quotaroot; 
02851     AdvanceToNextToken();
02852     while (ContinueParse() && !fAtEndOfLine)
02853     {
02854       quotaroot.Adopt(CreateAstring());
02855       AdvanceToNextToken();
02856     }
02857   }
02858   else if(!PL_strcasecmp(fNextToken, "QUOTA"))
02859   {
02860     PRUint32 used, max;
02861     char *parengroup;
02862 
02863     AdvanceToNextToken();
02864     if (! fNextToken)
02865       SetSyntaxError(PR_TRUE);
02866     else
02867     {
02868       quotaroot.Adopt(CreateAstring());
02869 
02870       if(ContinueParse() && !fAtEndOfLine)
02871       {
02872         AdvanceToNextToken();
02873         if(fNextToken)
02874         {
02875           if(!PL_strcasecmp(fNextToken, "(STORAGE"))
02876           {
02877             parengroup = CreateParenGroup();
02878             if(parengroup && (PR_sscanf(parengroup, "(STORAGE %lu %lu)", &used, &max) == 2) )
02879             {
02880               fServerConnection.UpdateFolderQuotaData(quotaroot, used, max);
02881               skip_to_CRLF();
02882             }
02883             else
02884               SetSyntaxError(PR_TRUE);
02885 
02886             PR_Free(parengroup);
02887           }
02888           else
02889             // Ignore other limits, we just check STORAGE for now
02890             skip_to_CRLF();
02891         }
02892         else
02893           SetSyntaxError(PR_TRUE);
02894       }
02895       else
02896         HandleMemoryFailure();
02897     }
02898   }
02899   else
02900     SetSyntaxError(PR_TRUE);
02901 }
02902 
02903 PRBool nsImapServerResponseParser::GetFillingInShell()
02904 {
02905   return (m_shell != nsnull);
02906 }
02907 
02908 PRBool nsImapServerResponseParser::GetDownloadingHeaders()
02909 {
02910   return fDownloadingHeaders;
02911 }
02912 
02913 // Tells the server state parser to use a previously cached shell.
02914 void   nsImapServerResponseParser::UseCachedShell(nsIMAPBodyShell *cachedShell)
02915 {
02916        // We shouldn't already have another shell we're dealing with.
02917        if (m_shell && cachedShell)
02918        {
02919               PR_LOG(IMAP, PR_LOG_ALWAYS, ("PARSER: Shell Collision"));
02920               NS_ASSERTION(PR_FALSE, "shell collision");
02921        }
02922        m_shell = cachedShell;
02923 }
02924 
02925 
02926 void nsImapServerResponseParser::ResetCapabilityFlag() 
02927 {
02928     if (fHostSessionList)
02929     {
02930         fHostSessionList->SetCapabilityForHost(
02931             fServerConnection.GetImapServerKey(), kCapabilityUndefined);
02932     }
02933 }
02934 
02935 /*
02936  literal         ::= "{" number "}" CRLF *CHAR8
02937                               ;; Number represents the number of CHAR8 octets
02938 */
02939 // returns PR_TRUE if this is the last chunk and we should close the stream
02940 PRBool nsImapServerResponseParser::msg_fetch_literal(PRBool chunk, PRInt32 origin)
02941 {
02942   numberOfCharsInThisChunk = atoi(fNextToken + 1); // might be the whole message
02943   charsReadSoFar = 0;
02944   static PRBool lastCRLFwasCRCRLF = PR_FALSE;
02945   
02946   PRBool lastChunk = !chunk || (origin + numberOfCharsInThisChunk >= fTotalDownloadSize);
02947   
02948   nsImapAction imapAction; 
02949   if (!fServerConnection.GetCurrentUrl())
02950     return PR_TRUE;
02951   fServerConnection.GetCurrentUrl()->GetImapAction(&imapAction);
02952   if (!lastCRLFwasCRCRLF && 
02953     fServerConnection.GetIOTunnellingEnabled() && 
02954     (numberOfCharsInThisChunk > fServerConnection.GetTunnellingThreshold()) &&
02955     (imapAction != nsIImapUrl::nsImapOnlineToOfflineCopy) &&
02956     (imapAction != nsIImapUrl::nsImapOnlineToOfflineMove))
02957   {
02958     // One day maybe we'll make this smarter and know how to handle CR/LF boundaries across tunnels.
02959     // For now, we won't, even though it might not be too hard, because it is very rare and will add
02960     // some complexity.
02961     charsReadSoFar = fServerConnection.OpenTunnel(numberOfCharsInThisChunk);
02962   }
02963   
02964   // If we opened a tunnel, finish everything off here.  Otherwise, get everything here.
02965   // (just like before)
02966   
02967   while (ContinueParse() && !fServerConnection.DeathSignalReceived() && (charsReadSoFar < numberOfCharsInThisChunk))
02968   {
02969     AdvanceToNextLine();
02970     if (ContinueParse())
02971     {
02972       if (lastCRLFwasCRCRLF && (*fCurrentLine == nsCRT::CR))
02973       {
02974         char *usableCurrentLine = PL_strdup(fCurrentLine + 1);
02975         PR_Free(fCurrentLine);
02976         fCurrentLine = usableCurrentLine;
02977       }
02978       
02979       if (ContinueParse())
02980       {
02981         charsReadSoFar += strlen(fCurrentLine);
02982         if (!fDownloadingHeaders && fCurrentCommandIsSingleMessageFetch)
02983         {
02984           fServerConnection.ProgressEventFunctionUsingId(IMAP_DOWNLOADING_MESSAGE);
02985           if (fTotalDownloadSize > 0)
02986             fServerConnection.PercentProgressUpdateEvent(0,charsReadSoFar + origin, fTotalDownloadSize);
02987         }
02988         if (charsReadSoFar > numberOfCharsInThisChunk)
02989         {     // this is rare.  If this msg ends in the middle of a line then only display the actual message.
02990           char *displayEndOfLine = (fCurrentLine + strlen(fCurrentLine) - (charsReadSoFar - numberOfCharsInThisChunk));
02991           char saveit = *displayEndOfLine;
02992           *displayEndOfLine = 0;
02993           fServerConnection.HandleMessageDownLoadLine(fCurrentLine, !lastChunk);
02994           *displayEndOfLine = saveit;
02995           lastCRLFwasCRCRLF = (*(displayEndOfLine - 1) == nsCRT::CR);
02996         }
02997         else
02998         {
02999           lastCRLFwasCRCRLF = (*(fCurrentLine + strlen(fCurrentLine) - 1) == nsCRT::CR);
03000           fServerConnection.HandleMessageDownLoadLine(fCurrentLine, !lastChunk && (charsReadSoFar == numberOfCharsInThisChunk), fCurrentLine);
03001         }
03002       }
03003     }
03004   }
03005   
03006   // This would be a good thing to log.
03007   if (lastCRLFwasCRCRLF)
03008     PR_LOG(IMAP, PR_LOG_ALWAYS, ("PARSER: CR/LF fell on chunk boundary."));
03009   
03010   if (ContinueParse())
03011   {
03012     if (charsReadSoFar > numberOfCharsInThisChunk)
03013     {
03014       // move the lexical analyzer state to the end of this message because this message
03015       // fetch ends in the middle of this line.
03016       //fCurrentTokenPlaceHolder = fLineOfTokens + nsCRT::strlen(fCurrentLine) - (charsReadSoFar - numberOfCharsInThisChunk);
03017       AdvanceTokenizerStartingPoint(strlen(fCurrentLine) - (charsReadSoFar - numberOfCharsInThisChunk));
03018       AdvanceToNextToken();
03019     }
03020     else
03021     {
03022       skip_to_CRLF();
03023       AdvanceToNextToken();
03024     }
03025   }
03026   else
03027   {
03028     lastCRLFwasCRCRLF = PR_FALSE;
03029   }
03030   return lastChunk;
03031 }
03032 
03033 PRBool nsImapServerResponseParser::CurrentFolderReadOnly()
03034 {
03035   return fCurrentFolderReadOnly;
03036 }
03037 
03038 PRInt32       nsImapServerResponseParser::NumberOfMessages()
03039 {
03040   return fNumberOfExistingMessages;
03041 }
03042 
03043 PRInt32 nsImapServerResponseParser::NumberOfRecentMessages()
03044 {
03045   return fNumberOfRecentMessages;
03046 }
03047 
03048 PRInt32 nsImapServerResponseParser::NumberOfUnseenMessages()
03049 {
03050   return fNumberOfUnseenMessages;
03051 }
03052 
03053 PRInt32 nsImapServerResponseParser::FolderUID()
03054 {
03055   return fFolderUIDValidity;
03056 }
03057 
03058 void nsImapServerResponseParser::SetCurrentResponseUID(PRUint32 uid)
03059 {
03060   if (uid > 0)
03061     fCurrentResponseUID = uid;
03062 }
03063 
03064 PRUint32 nsImapServerResponseParser::CurrentResponseUID()
03065 {
03066   return fCurrentResponseUID;
03067 }
03068 
03069 PRUint32 nsImapServerResponseParser::HighestRecordedUID()
03070 {
03071   return fHighestRecordedUID;
03072 }
03073 
03074 PRBool nsImapServerResponseParser::IsNumericString(const char *string)
03075 {
03076   int i;
03077   for(i = 0; i < (int) PL_strlen(string); i++)
03078   {
03079     if (! isdigit(string[i]))
03080     {
03081       return PR_FALSE;
03082     }
03083   }
03084   
03085   return PR_TRUE;
03086 }
03087 
03088 
03089 nsImapMailboxSpec *nsImapServerResponseParser::CreateCurrentMailboxSpec(const char *mailboxName /* = nsnull */)
03090 {
03091   nsImapMailboxSpec *returnSpec = new nsImapMailboxSpec;
03092   if (!returnSpec)
03093   {
03094     HandleMemoryFailure();
03095     return  nsnull;
03096   }
03097   NS_ADDREF(returnSpec);
03098   const char *mailboxNameToConvert = (mailboxName) ? mailboxName : fSelectedMailboxName;
03099   if (mailboxNameToConvert)
03100   {
03101     const char *serverKey = fServerConnection.GetImapServerKey();
03102     nsIMAPNamespace *ns = nsnull;
03103     if (serverKey && fHostSessionList)
03104       fHostSessionList->GetNamespaceForMailboxForHost(serverKey, mailboxNameToConvert, ns);       // for
03105       // delimiter
03106     returnSpec->hierarchySeparator = (ns) ? ns->GetDelimiter(): '/';
03107     
03108   }
03109   
03110   returnSpec->folderSelected = !mailboxName; // if mailboxName is null, we're doing a Status
03111   returnSpec->folder_UIDVALIDITY = fFolderUIDValidity;
03112   returnSpec->number_of_messages = (mailboxName) ? fStatusExistingMessages : fNumberOfExistingMessages;
03113   returnSpec->number_of_unseen_messages = (mailboxName) ? fStatusUnseenMessages : fNumberOfUnseenMessages;
03114   returnSpec->number_of_recent_messages = (mailboxName) ? fStatusRecentMessages : fNumberOfRecentMessages;
03115   
03116   returnSpec->supportedUserFlags = fSupportsUserDefinedFlags;
03117 
03118   returnSpec->box_flags = kNoFlags;       // stub
03119   returnSpec->onlineVerified = PR_FALSE;  // we're fabricating this.  The flags aren't verified.
03120   returnSpec->allocatedPathName = strdup(mailboxNameToConvert);
03121   returnSpec->connection = &fServerConnection;
03122   if (returnSpec->connection)
03123   {
03124     nsIURI * aUrl = nsnull;
03125     nsresult rv = NS_OK;
03126     returnSpec->connection->GetCurrentUrl()->QueryInterface(NS_GET_IID(nsIURI), (void **) &aUrl);
03127     if (NS_SUCCEEDED(rv) && aUrl) 
03128     {
03129       nsCAutoString host;
03130       aUrl->GetHost(host);
03131       returnSpec->hostName = ToNewCString(host);
03132     }
03133     NS_IF_RELEASE(aUrl);
03134     
03135   }
03136   else
03137     returnSpec->hostName = nsnull;
03138 
03139   if (fFlagState)
03140     returnSpec->flagState = fFlagState; //copies flag state
03141   else
03142     returnSpec->flagState = nsnull;
03143   
03144   return returnSpec;
03145   
03146 }
03147 // zero stops a list recording of flags and causes the flags for
03148 // each individual message to be sent back to libmsg 
03149 void nsImapServerResponseParser::ResetFlagInfo(int numberOfInterestingMessages)
03150 {
03151   if (fFlagState)
03152     fFlagState->Reset(numberOfInterestingMessages);
03153 }
03154 
03155 
03156 PRBool nsImapServerResponseParser::GetLastFetchChunkReceived()
03157 {
03158   return fLastChunk;
03159 }
03160 
03161 void nsImapServerResponseParser::ClearLastFetchChunkReceived()
03162 {
03163   fLastChunk = PR_FALSE;
03164 }
03165 
03166 void nsImapServerResponseParser::SetHostSessionList(nsIImapHostSessionList*
03167                                                aHostSessionList)
03168 {
03169     NS_IF_RELEASE (fHostSessionList);
03170     fHostSessionList = aHostSessionList;
03171     NS_IF_ADDREF (fHostSessionList);
03172 }
03173 
03174 nsIImapHostSessionList*
03175 nsImapServerResponseParser::GetHostSessionList()
03176 {
03177     NS_IF_ADDREF(fHostSessionList);
03178     return fHostSessionList;
03179 }
03180 
03181 void nsImapServerResponseParser::SetSyntaxError(PRBool error, const char *msg)
03182 {
03183   nsIMAPGenericParser::SetSyntaxError(error, msg);
03184   if (error)
03185   {
03186     if (!fCurrentLine)
03187     {
03188       HandleMemoryFailure();
03189       fServerConnection.Log("PARSER", ("Internal Syntax Error: %s: <no line>"), msg);
03190     }
03191     else
03192     {
03193       if (!nsCRT::strcmp(fCurrentLine, CRLF))
03194         fServerConnection.Log("PARSER", "Internal Syntax Error: %s: <CRLF>", msg);
03195       else
03196       {
03197         if (msg)
03198           fServerConnection.Log("PARSER", "Internal Syntax Error: %s:", msg);
03199         fServerConnection.Log("PARSER", "Internal Syntax Error on line: %s", fCurrentLine);
03200       }      
03201     }
03202   }
03203 }
03204 
03205 nsresult nsImapServerResponseParser::BeginMessageDownload(const char *content_type)
03206 {
03207   // if we're downloading a message, assert that we know its size.
03208   NS_ASSERTION(fDownloadingHeaders || fSizeOfMostRecentMessage > 0, "most recent message has 0 or negative size");
03209   nsresult rv = fServerConnection.BeginMessageDownLoad(fSizeOfMostRecentMessage, 
03210     content_type);
03211   if (NS_FAILED(rv))
03212   {
03213     skip_to_CRLF();
03214     fServerConnection.PseudoInterrupt(PR_TRUE);
03215     fServerConnection.AbortMessageDownLoad();
03216   }
03217   return rv;
03218 }