Back to index

lightning-sunbird  0.9+nobinonly
nsIMAPGenericParser.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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  *   Hans-Andreas Engel <engel@physics.harvard.edu>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "msgCore.h"  // for pre-compiled headers
00040 
00041 #include "nsImapCore.h"
00042 #include "nsImapProtocol.h"
00043 #include "nsIMAPGenericParser.h"
00044 #include "nsString.h"
00045 #include "nsReadableUtils.h"
00046 
00048 
00049 
00050 nsIMAPGenericParser::nsIMAPGenericParser() :
00051 fNextToken(nsnull),
00052 fCurrentLine(nsnull),
00053 fLineOfTokens(nsnull),
00054 fStartOfLineOfTokens(nsnull),
00055 fCurrentTokenPlaceHolder(nsnull),
00056 fAtEndOfLine(PR_FALSE),
00057 fParserState(stateOK)
00058 {
00059 }
00060 
00061 nsIMAPGenericParser::~nsIMAPGenericParser()
00062 {
00063   PR_FREEIF( fCurrentLine );
00064   PR_FREEIF( fStartOfLineOfTokens);
00065 }
00066 
00067 void nsIMAPGenericParser::HandleMemoryFailure()
00068 {
00069   SetConnected(PR_FALSE);
00070 }
00071 
00072 void nsIMAPGenericParser::ResetLexAnalyzer()
00073 {
00074   PR_FREEIF( fCurrentLine );
00075   PR_FREEIF( fStartOfLineOfTokens );
00076   
00077   fCurrentLine = fNextToken = fLineOfTokens = fStartOfLineOfTokens = fCurrentTokenPlaceHolder = nsnull;
00078   fAtEndOfLine = PR_FALSE;
00079 }
00080 
00081 PRBool nsIMAPGenericParser::LastCommandSuccessful()
00082 {
00083   return fParserState == stateOK;
00084 }
00085 
00086 void nsIMAPGenericParser::SetSyntaxError(PRBool error, const char *msg)
00087 {
00088   if (error)
00089       fParserState |= stateSyntaxErrorFlag;
00090   else
00091       fParserState &= ~stateSyntaxErrorFlag;
00092   NS_ASSERTION(!error, "syntax error in generic parser");      
00093 }
00094 
00095 void nsIMAPGenericParser::SetConnected(PRBool connected)
00096 {
00097   if (connected)
00098       fParserState &= ~stateDisconnectedFlag;
00099   else
00100       fParserState |= stateDisconnectedFlag;
00101 }
00102 
00103 void nsIMAPGenericParser::skip_to_CRLF()
00104 {
00105   while (Connected() && !fAtEndOfLine)
00106     AdvanceToNextToken();
00107 }
00108 
00109 // fNextToken initially should point to
00110 // a string after the initial open paren ("(")
00111 // After this call, fNextToken points to the
00112 // first character after the matching close
00113 // paren.  Only call AdvanceToNextToken() to get the NEXT
00114 // token after the one returned in fNextToken.
00115 void nsIMAPGenericParser::skip_to_close_paren()
00116 {
00117   int numberOfCloseParensNeeded = 1;
00118   while (ContinueParse())
00119   {
00120     // go through fNextToken, account for nested parens
00121     char *loc;
00122     for (loc = fNextToken; loc && *loc; loc++)
00123     {
00124       if (*loc == '(')
00125         numberOfCloseParensNeeded++;
00126       else if (*loc == ')')
00127       {
00128         numberOfCloseParensNeeded--;
00129         if (numberOfCloseParensNeeded == 0)
00130         {
00131           fNextToken = loc + 1;
00132           if (!fNextToken || !*fNextToken)
00133             AdvanceToNextToken();
00134           return;
00135         }
00136       }
00137       else if (*loc == '{' || *loc == '"') {
00138         // quoted or literal  
00139         fNextToken = loc;
00140         char *a = CreateString();
00141         PR_FREEIF(a);
00142         break; // move to next token
00143       }
00144     }
00145     if (ContinueParse())
00146       AdvanceToNextToken();
00147   }
00148 }
00149 
00150 void nsIMAPGenericParser::AdvanceToNextToken()
00151 {
00152   if (!fCurrentLine || fAtEndOfLine)
00153     AdvanceToNextLine();
00154   if (Connected())
00155   {
00156     if (!fStartOfLineOfTokens)
00157     {
00158       // this is the first token of the line; setup tokenizer now
00159       fStartOfLineOfTokens = PL_strdup(fCurrentLine);
00160       if (!fStartOfLineOfTokens)
00161       {
00162         HandleMemoryFailure();
00163         return;
00164       }
00165       fLineOfTokens = fStartOfLineOfTokens;
00166       fCurrentTokenPlaceHolder = fStartOfLineOfTokens;
00167     }
00168     fNextToken = nsCRT::strtok(fCurrentTokenPlaceHolder, WHITESPACE, &fCurrentTokenPlaceHolder);
00169     if (!fNextToken)
00170     {
00171       fAtEndOfLine = PR_TRUE;
00172       fNextToken = CRLF;
00173     }
00174   }
00175 }
00176 
00177 void nsIMAPGenericParser::AdvanceToNextLine()
00178 {
00179   PR_FREEIF( fCurrentLine );
00180   PR_FREEIF( fStartOfLineOfTokens);
00181   
00182   PRBool ok = GetNextLineForParser(&fCurrentLine);
00183   if (!ok)
00184   {
00185     SetConnected(PR_FALSE);
00186     fStartOfLineOfTokens = nsnull;
00187     fLineOfTokens = nsnull;
00188     fCurrentTokenPlaceHolder = nsnull;
00189     fAtEndOfLine = PR_TRUE;
00190     fNextToken = CRLF;
00191   }
00192   else if (!fCurrentLine)
00193   {
00194     HandleMemoryFailure();
00195   }
00196   else
00197   {
00198      fNextToken = nsnull;
00199      // determine if there are any tokens (without calling AdvanceToNextToken);
00200      // otherwise we are already at end of line
00201      NS_ASSERTION(strlen(WHITESPACE) == 3, "assume 3 chars of whitespace");
00202      char *firstToken = fCurrentLine;
00203      while (*firstToken && (*firstToken == WHITESPACE[0] ||
00204             *firstToken == WHITESPACE[1] || *firstToken == WHITESPACE[2]))
00205        firstToken++;
00206      fAtEndOfLine = (*firstToken == '\0');
00207   }
00208 }
00209 
00210 // advances |fLineOfTokens| by |bytesToAdvance| bytes
00211 void nsIMAPGenericParser::AdvanceTokenizerStartingPoint(int32 bytesToAdvance)
00212 {
00213   NS_PRECONDITION(bytesToAdvance>=0, "bytesToAdvance must not be negative");
00214   if (!fStartOfLineOfTokens)
00215   {
00216     AdvanceToNextToken();  // the tokenizer was not yet initialized, do it now
00217     if (!fStartOfLineOfTokens)
00218       return;
00219   }
00220     
00221   if(!fStartOfLineOfTokens)
00222       return;
00223   // The last call to AdvanceToNextToken() cleared the token separator to '\0'
00224   // iff |fCurrentTokenPlaceHolder|.  We must recover this token separator now.
00225   if (fCurrentTokenPlaceHolder)
00226   {
00227     int endTokenOffset = fCurrentTokenPlaceHolder - fStartOfLineOfTokens - 1;
00228     if (endTokenOffset >= 0)
00229       fStartOfLineOfTokens[endTokenOffset] = fCurrentLine[endTokenOffset];
00230   }
00231 
00232   NS_ASSERTION(bytesToAdvance + (fLineOfTokens-fStartOfLineOfTokens) <=
00233     (int32)strlen(fCurrentLine), "cannot advance beyond end of fLineOfTokens");
00234   fLineOfTokens += bytesToAdvance;
00235   fCurrentTokenPlaceHolder = fLineOfTokens;
00236 }
00237 
00238 // RFC3501:  astring = 1*ASTRING-CHAR / string
00239 //           string  = quoted / literal
00240 // This function leaves us off with fCurrentTokenPlaceHolder immediately after
00241 // the end of the Astring.  Call AdvanceToNextToken() to get the token after it.
00242 char *nsIMAPGenericParser::CreateAstring()
00243 {
00244   if (*fNextToken == '{')
00245     return CreateLiteral();        // literal
00246   else if (*fNextToken == '"')
00247     return CreateQuoted();         // quoted
00248   else
00249     return CreateAtom(PR_TRUE); // atom
00250 }
00251 
00252 // Create an atom
00253 // This function does not advance the parser.
00254 // Call AdvanceToNextToken() to get the next token after the atom.
00255 // RFC3501:  atom            = 1*ATOM-CHAR
00256 //           ASTRING-CHAR    = ATOM-CHAR / resp-specials
00257 //           ATOM-CHAR       = <any CHAR except atom-specials>
00258 //           atom-specials   = "(" / ")" / "{" / SP / CTL / list-wildcards /
00259 //                             quoted-specials / resp-specials
00260 //           list-wildcards  = "%" / "*"
00261 //           quoted-specials = DQUOTE / "\"
00262 //           resp-specials   = "]"
00263 // "Characters are 7-bit US-ASCII unless otherwise specified." [RFC3501, 1.2.]
00264 char *nsIMAPGenericParser::CreateAtom(PRBool isAstring)
00265 {
00266   char *rv = PL_strdup(fNextToken);
00267   if (!rv)
00268   {
00269     HandleMemoryFailure();
00270     return nsnull;
00271   }
00272   // We wish to stop at the following characters (in decimal ascii)
00273   // 1-31 (CTL), 32 (SP), 34 '"', 37 '%', 40-42 "()*", 92 '\\', 123 '{'
00274   // also, ']' is only allowed in astrings
00275   char *last = rv;
00276   char c = *last;
00277   while ((c > 42 || c == 33 || c == 35 || c == 36 || c == 38 || c == 39)
00278          && c != '\\' && c != '{' && (isAstring || c != ']'))
00279      c = *++last;
00280   if (rv == last) {
00281      SetSyntaxError(PR_TRUE, "no atom characters found");
00282      PL_strfree(rv);
00283      return nsnull;
00284   }
00285   if (*last)
00286   {
00287     // not the whole token was consumed  
00288     *last = '\0';
00289     AdvanceTokenizerStartingPoint((fNextToken - fLineOfTokens) + (last-rv));
00290   }
00291   return rv;
00292 }
00293 
00294 // CreateNilString creates either NIL (reutrns NULL) or a string
00295 // Call with fNextToken pointing to the thing which we think is the nilstring.
00296 // This function leaves us off with fCurrentTokenPlaceHolder immediately after
00297 // the end of the string, if it is a string, or at the NIL.
00298 // Regardless of type, call AdvanceToNextToken() to get the token after it.
00299 char *nsIMAPGenericParser::CreateNilString()
00300 {
00301   if (!PL_strncasecmp(fNextToken, "NIL", 3))
00302   {
00303     if (strlen(fNextToken) != 3)
00304       fNextToken += 3;
00305     return NULL;
00306   }
00307   else
00308     return CreateString();
00309 }
00310 
00311 
00312 // Create a string, which can either be quoted or literal,
00313 // but not an atom.
00314 // This function leaves us off with fCurrentTokenPlaceHolder immediately after
00315 // the end of the String.  Call AdvanceToNextToken() to get the token after it.
00316 char *nsIMAPGenericParser::CreateString()
00317 {
00318   if (*fNextToken == '{')
00319   {
00320     char *rv = CreateLiteral();           // literal
00321     return (rv);
00322   }
00323   else if (*fNextToken == '"')
00324   {
00325     char *rv = CreateQuoted();            // quoted
00326     return (rv);
00327   }
00328   else
00329   {
00330     SetSyntaxError(PR_TRUE, "string does not start with '{' or '\"'");
00331     return NULL;
00332   }
00333 }
00334 
00335 // This function sets fCurrentTokenPlaceHolder immediately after the end of the
00336 // closing quote.  Call AdvanceToNextToken() to get the token after it.
00337 // QUOTED_CHAR     ::= <any TEXT_CHAR except quoted_specials> /
00338 //                     "\" quoted_specials
00339 // TEXT_CHAR       ::= <any CHAR except CR and LF>
00340 // quoted_specials ::= <"> / "\"
00341 // Note that according to RFC 1064 and RFC 2060, CRs and LFs are not allowed 
00342 // inside a quoted string.  It is sufficient to read from the current line only.
00343 char *nsIMAPGenericParser::CreateQuoted(PRBool /*skipToEnd*/)
00344 {
00345   char *currentChar = fCurrentLine + 
00346     (fNextToken - fStartOfLineOfTokens)
00347     + 1;      // one char past opening '"'
00348   
00349   int  charIndex = 0;
00350   int  escapeCharsCut = 0;
00351   PRBool closeQuoteFound = PR_FALSE;
00352   nsCString returnString(currentChar);
00353   
00354   while (returnString.CharAt(charIndex))
00355   {
00356     if (returnString.CharAt(charIndex) == '"')
00357     {
00358       // don't check to see if it was escaped, 
00359       // that was handled in the next clause
00360       closeQuoteFound = PR_TRUE;
00361       break;
00362     }
00363     else if (returnString.CharAt(charIndex) == '\\')
00364     {
00365       // eat the escape character
00366       returnString.Cut(charIndex, 1);
00367       // whatever the escaped character was, we want it
00368       charIndex++;
00369       
00370       // account for charIndex not reflecting the eat of the escape character
00371       escapeCharsCut++;
00372     }
00373     else
00374       charIndex++;
00375   }
00376   
00377   if (closeQuoteFound)
00378   {
00379     returnString.Truncate(charIndex);
00380     //if ((charIndex == 0) && skipToEnd)  // it's an empty string.  Why skip to end?
00381     // skip_to_CRLF();
00382     //else if (charIndex == strlen(fCurrentLine))       // should we have this?
00383     //AdvanceToNextLine();
00384     //else 
00385     if (charIndex < (int) (strlen(fNextToken) - 2))     // -2 because of the start and end quotes
00386     {
00387       // the quoted string was fully contained within fNextToken,
00388       // and there is text after the quote in fNextToken that we
00389       // still need
00390       //                    int charDiff = strlen(fNextToken) - charIndex - 1;
00391       //                    fCurrentTokenPlaceHolder -= charDiff;
00392       //                    if (!nsCRT::strcmp(fCurrentTokenPlaceHolder, CRLF))
00393       //                           fAtEndOfLine = PR_TRUE;
00394       AdvanceTokenizerStartingPoint ((fNextToken - fLineOfTokens) + returnString.Length() + escapeCharsCut + 2);
00395     }
00396     else
00397     {
00398       fCurrentTokenPlaceHolder += escapeCharsCut + charIndex + 1 - strlen(fNextToken);
00399       if (!*fCurrentTokenPlaceHolder)
00400         *fCurrentTokenPlaceHolder = ' ';  // put the token delimiter back
00401                                                 /*      if (!nsCRT::strcmp(fNextToken, CRLF))
00402                                                 fAtEndOfLine = PR_TRUE;
00403       */
00404     }
00405   }
00406   else
00407     SetSyntaxError(PR_TRUE, "no closing '\"' found in quoted");
00408   
00409   return ToNewCString(returnString);
00410 }
00411 
00412 
00413 // This function leaves us off with fCurrentTokenPlaceHolder immediately after
00414 // the end of the literal string.  Call AdvanceToNextToken() to get the token
00415 // after the literal string.
00416 char *nsIMAPGenericParser::CreateLiteral()
00417 {
00418   int32 numberOfCharsInMessage = atoi(fNextToken + 1);
00419   int32 charsReadSoFar = 0, currentLineLength = 0;
00420   int32 bytesToCopy = 0;
00421   
00422   uint32 numBytes = numberOfCharsInMessage + 1;
00423   NS_ASSERTION(numBytes, "overflow!");
00424   if (!numBytes)
00425     return nsnull;
00426   
00427   char *returnString = (char *) PR_Malloc(numBytes);
00428     if (!returnString)
00429         return nsnull;
00430  
00431     *(returnString + numberOfCharsInMessage) = 0; // Null terminate it first
00432     
00433     PRBool terminatedLine = PR_FALSE;
00434         if (fCurrentTokenPlaceHolder &&
00435           *fCurrentTokenPlaceHolder == nsCRT::LF &&
00436           *(fCurrentTokenPlaceHolder+1))
00437         {
00438           // This is a static buffer, with a CRLF between the literal size ({91}) and
00439           // the string itself
00440           fCurrentTokenPlaceHolder++;
00441         }
00442         else
00443         {
00444           // We have to read the next line from AdvanceToNextLine().
00445           terminatedLine = PR_TRUE;
00446         }
00447     while (ContinueParse() && (charsReadSoFar < numberOfCharsInMessage))
00448     {
00449       if(terminatedLine)
00450         AdvanceToNextLine();
00451 
00452       if (ContinueParse())
00453       {
00454         currentLineLength = strlen(terminatedLine ? fCurrentLine : fCurrentTokenPlaceHolder);
00455         bytesToCopy = (currentLineLength > numberOfCharsInMessage - charsReadSoFar ?
00456           numberOfCharsInMessage - charsReadSoFar : currentLineLength);
00457         NS_ASSERTION (bytesToCopy, "0 length literal?");
00458         
00459         memcpy(returnString + charsReadSoFar, terminatedLine ? fCurrentLine : fCurrentTokenPlaceHolder, bytesToCopy); 
00460         charsReadSoFar += bytesToCopy;
00461       }
00462       if (charsReadSoFar < numberOfCharsInMessage) // read the next line
00463           terminatedLine = PR_TRUE;
00464     }
00465     
00466     if (ContinueParse())
00467     {
00468       if (bytesToCopy == 0)
00469       {
00470         // the loop above was never executed, we just move to the next line
00471         if (terminatedLine)
00472           AdvanceToNextLine();
00473       }
00474       else if (currentLineLength == bytesToCopy)
00475       {
00476           // We have consumed the entire line.
00477           // Consider the input  "A1 {4}\r\nL2\r\n A3\r\n" which is read
00478           // line-by-line.  Reading 3 Astrings, this should result in 
00479           // "A1", "L2\r\n", and "A3".  Note that this confuses the parser, 
00480           // since the second line is "L2\r\n" where the "\r\n" is part of the
00481           // literal.  Hence, the 'full' imap line was not read in yet after the
00482           // second line of input (which is where we are now).  We now read the
00483           // next line to ensure that the next call to AdvanceToNextToken()
00484           // would lead to fNextToken=="A3" in our example.
00485           // Note that setting fAtEndOfLine=PR_TRUE is wrong here, since the "\r\n"
00486           // were just some characters from the literal; fAtEndOfLine would
00487           // give a misleading result.
00488           AdvanceToNextLine();
00489       }
00490       else
00491       {
00492         // Move fCurrentTokenPlaceHolder
00493         if (terminatedLine)
00494           AdvanceTokenizerStartingPoint (bytesToCopy);
00495         else
00496           AdvanceTokenizerStartingPoint ( bytesToCopy + 
00497           strlen(fNextToken) + 
00498           2 /* CRLF */ +
00499           (fNextToken - fLineOfTokens)
00500           );
00501       }       
00502     }
00503   
00504   return returnString;
00505 }
00506 
00507 
00508 // Call this to create a buffer containing all characters within
00509 // a given set of parentheses.
00510 // Call this with fNextToken[0]=='(', that is, the open paren
00511 // of the group.
00512 // It will allocate and return all characters up to and including the corresponding
00513 // closing paren, and leave the parser in the right place afterwards.
00514 char *nsIMAPGenericParser::CreateParenGroup()
00515 {
00516 #ifdef DEBUG_bienvenu
00517   NS_ASSERTION(fNextToken[0] == '(', "we don't have a paren group!");
00518 #endif
00519   
00520   int numOpenParens = 1;
00521   
00522   // build up a buffer with the paren group.
00523   // start with an initial chunk, expand later if necessary
00524   nsCString buf;
00525   nsCString returnString;
00526   int bytesUsed = 0;
00527   
00528   // count the number of parens in the current token
00529   int count, tokenLen = strlen(fNextToken);
00530   for (count = 1; (count < tokenLen) && (numOpenParens > 0); count++)
00531   {
00532     if (fNextToken[count] == '(')
00533       numOpenParens++;
00534     else if (fNextToken[count] == ')')
00535       numOpenParens--;
00536   }
00537   
00538   if ((numOpenParens > 0) && ContinueParse())
00539   {
00540     // Copy that first token from before
00541     returnString =fNextToken;
00542     returnString.Append(" ");      // space that got stripped off the token
00543     
00544     PRBool extractReset = PR_TRUE;
00545     while (extractReset && ContinueParse())
00546     {
00547       extractReset = PR_FALSE;
00548       // Go through the current line and look for the last close paren.
00549       // We're not trying to parse it just yet, just separate it out.
00550       int len = strlen(fCurrentTokenPlaceHolder);
00551       for (count = 0; (count < len) && (numOpenParens > 0) && !extractReset; count++)
00552       {
00553         if (*fCurrentTokenPlaceHolder == '{')
00554         {
00555           AdvanceToNextToken();
00556           NS_ASSERTION(fNextToken, "out of memory?or invalid syntax");
00557           if (fNextToken)
00558           {
00559             tokenLen = strlen(fNextToken);
00560             if (fNextToken[tokenLen-1] == '}')
00561             {
00562               // ok, we're looking at a literal string here
00563               
00564               // first, flush buf
00565               if (bytesUsed > 0)
00566               {
00567                 buf.Truncate(bytesUsed);
00568                 returnString.Append(buf);
00569                 buf.Truncate();
00570                 bytesUsed = 0;
00571               }
00572               
00573               returnString.Append(fNextToken);   // append the {xx} to the buffer
00574               returnString.Append(CRLF);                // append a CRLF to the buffer
00575               char *lit = CreateLiteral();
00576               NS_ASSERTION(lit, "syntax error or out of memory");
00577               if (lit)
00578               {
00579                 returnString.Append(lit);
00580                 //fCurrentTokenPlaceHolder += nsCRT::strlen(lit);
00581                 //AdvanceTokenizerStartingPoint(nsCRT::strlen(lit));
00582                 //AdvanceToNextToken();
00583                 extractReset = PR_TRUE;
00584                 PR_Free(lit);
00585               }
00586             }
00587             else
00588             {
00589 #ifdef DEBUG_bienvenu
00590               NS_ASSERTION(PR_FALSE, "syntax error creating paren group");   // maybe not an error, but definitely a rare condition
00591 #endif
00592             }
00593           }
00594         }
00595         else if (*fCurrentTokenPlaceHolder == '"')
00596         {
00597           // We're looking at a quoted string here.
00598           // Ignore the characters within it.
00599           
00600           // first, flush buf
00601           if (bytesUsed > 0)
00602           {
00603             buf.Truncate(bytesUsed);
00604             returnString.Append(buf);
00605             buf.Truncate();
00606             bytesUsed = 0;
00607           }
00608           
00609           AdvanceToNextToken();
00610           NS_ASSERTION(fNextToken, "syntax error or out of memory creating paren group");
00611           if (fNextToken)
00612           {
00613             char *q = CreateQuoted();
00614             NS_ASSERTION(q, "syntax error or out of memory creating paren group");
00615             if (q)
00616             {
00617               returnString.Append("\"");
00618               returnString.Append(q);
00619               returnString.Append("\"");
00620               extractReset = PR_TRUE;
00621               PR_Free(q);
00622             }
00623           }
00624         }
00625         else if (*fCurrentTokenPlaceHolder == '(')
00626           numOpenParens++;
00627         else if (*fCurrentTokenPlaceHolder == ')')
00628           numOpenParens--;
00629         
00630         
00631         if (!extractReset)
00632         {
00633           // append this character to the buffer
00634           buf += *fCurrentTokenPlaceHolder;
00635           
00636           //.SetCharAt(*fCurrentTokenPlaceHolder, bytesUsed);
00637           bytesUsed++;
00638           fCurrentTokenPlaceHolder++;
00639         }
00640       }
00641     }
00642   }
00643   else if ((numOpenParens == 0) && ContinueParse())
00644   {
00645     // the whole paren group response was finished in a single token
00646     buf.Append(fNextToken);
00647   }
00648   
00649   
00650   if (numOpenParens != 0 || !ContinueParse())
00651   {
00652     SetSyntaxError(PR_TRUE, "closing ')' not found in paren group");
00653     returnString.SetLength(0);
00654   }
00655   else
00656   {
00657     // flush buf the final time
00658     if (bytesUsed > 0)
00659     {
00660       buf.Truncate(bytesUsed);
00661       returnString.Append(buf);
00662       buf.Truncate();
00663     }
00664     AdvanceToNextToken();
00665   }
00666   
00667   return ToNewCString(returnString);
00668 }
00669