/[classpath]/inetlib/source/gnu/inet/imap/IMAPResponseTokenizer.java
ViewVC logotype

Diff of /inetlib/source/gnu/inet/imap/IMAPResponseTokenizer.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.5 by dog, Thu Oct 21 15:21:55 2004 UTC revision 1.6 by dog, Thu Nov 25 22:15:05 2004 UTC
# Line 1  Line 1 
1  /*  /*
2   * $Id$   * IMAPResponseTokenizer.java
3   * Copyright (C) 2003 The Free Software Foundation   * Copyright (C) 2003 The Free Software Foundation
4   *   *
5   * This file is part of GNU inetlib, a library.   * This file is part of GNU inetlib, a library.
# Line 51  import java.util.Stack; Line 51  import java.util.Stack;
51   * protocol server responses into IMAPResponse tokens.   * protocol server responses into IMAPResponse tokens.
52   *   *
53   * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>   * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  * @version $Revision$ $Date$  
54   */   */
55  public class IMAPResponseTokenizer implements IMAPConstants  public class IMAPResponseTokenizer
56      implements IMAPConstants
57  {  {
58    
59    /**    /**
# Line 80  public class IMAPResponseTokenizer imple Line 80  public class IMAPResponseTokenizer imple
80     * Constructor.     * Constructor.
81     * @param in the server socket input stream     * @param in the server socket input stream
82     */     */
83    public IMAPResponseTokenizer (InputStream in)    public IMAPResponseTokenizer(InputStream in)
84      {    {
85        this.in = in;      this.in = in;
86      }    }
87    
88    /*    /*
89     * Reads bytes, from the underlying stream if necessary, or from the     * Reads bytes, from the underlying stream if necessary, or from the
90     * cache. If moreNeeded is specified, always performs a read to append     * cache. If moreNeeded is specified, always performs a read to append
91     * more to the cache.     * more to the cache.
92     */     */
93    byte[] read (boolean moreNeeded) throws IOException    byte[] read(boolean moreNeeded)
94      {      throws IOException
95        if (buffer != null && !moreNeeded && buffer.length > 0)    {
96          {      if (buffer != null && !moreNeeded && buffer.length > 0)
97            return buffer;        {
98          }          return buffer;
99        int max = in.available ();        }
100        if (max < 1)      int max = in.available();
101          {      if (max < 1)
102            max = BUFFER_SIZE;        {
103          }          max = BUFFER_SIZE;
104        byte[] tmp = new byte[max];        }
105        int len = 0;      byte[] tmp = new byte[max];
106        while (len == 0)      int len = 0;
107          {      while (len == 0)
108            len = in.read (tmp, 0, max);        {
109          }          len = in.read(tmp, 0, max);
110        if (len == -1)        }
111          {      if (len == -1)
112            return null;              // EOF        {
113          }          return null;              // EOF
114        int blen = (buffer == null) ? 0 : buffer.length;        }
115        byte[] uni = new byte[blen + len];      int blen =(buffer == null) ? 0 : buffer.length;
116        if (blen != 0)      byte[] uni = new byte[blen + len];
117          {      if (blen != 0)
118            System.arraycopy (buffer, 0, uni, 0, blen);        {
119          }          System.arraycopy(buffer, 0, uni, 0, blen);
120        System.arraycopy (tmp, 0, uni, blen, len);        }
121        buffer = uni;      System.arraycopy(tmp, 0, uni, blen, len);
122        return buffer;      buffer = uni;
123      }      return buffer;
124      }
125    
126    /*    /*
127     * Invalidates the byte cache up to the specified index.     * Invalidates the byte cache up to the specified index.
128     */     */
129    void mark (int index)    void mark(int index)
130      {    {
131        int len = buffer.length;      int len = buffer.length;
132        int start = index + 1;      int start = index + 1;
133        if (start < len)      if (start < len)
134          {        {
135            int n = (len - start);          int n =(len - start);
136            byte[] tmp = new byte[n];          byte[] tmp = new byte[n];
137            System.arraycopy (buffer, start, tmp, 0, n);          System.arraycopy(buffer, start, tmp, 0, n);
138            buffer = tmp;          buffer = tmp;
139          }        }
140        else      else
141          {        {
142            buffer = null;          buffer = null;
143          }        }
144      }    }
145    
146    /**    /**
147     * Returns the next IMAPResponse.     * Returns the next IMAPResponse.
148     */     */
149    public IMAPResponse next () throws IOException    public IMAPResponse next()
150      {      throws IOException
151        // Perform read    {
152        byte[] buf = read (false);      // Perform read
153        if (buf == null)      byte[] buf = read(false);
154          {      if (buf == null)
155            return null;              // pass EOF back up the chain        {
156          }          return null;              // pass EOF back up the chain
157        int len = buf.length;        }
158        int len = buf.length;
159        IMAPResponse response = new IMAPResponse ();      
160        ByteArrayOutputStream genericSink = new ByteArrayOutputStream ();      IMAPResponse response = new IMAPResponse();
161        ByteArrayOutputStream literalSink = null;      ByteArrayOutputStream genericSink = new ByteArrayOutputStream();
162        int literalCount = 0, literalLength = -1;      ByteArrayOutputStream literalSink = null;
163        Stack context = new Stack ();      int literalCount = 0, literalLength = -1;
164        int state = STATE_TAG;      Stack context = new Stack();
165        boolean inQuote = false;      int state = STATE_TAG;
166        boolean inContent = false;      boolean inQuote = false;
167        for (int i = 0; i < len; i++)      boolean inContent = false;
168          {      for (int i = 0; i < len; i++)
169            byte b = buf[i];        {
170            switch (state)          byte b = buf[i];
171              {          switch (state)
172              case STATE_TAG:          // expect tag            {
173                if (i == 0 && b == 0x2a)        // untagged            case STATE_TAG:          // expect tag
174                  {              if (i == 0 && b == 0x2a)        // untagged
175                    response.tag = IMAPResponse.UNTAGGED;                {
176                  }                  response.tag = IMAPResponse.UNTAGGED;
177                else if (i == 0 && b == 0x2b)   // continuation                }
178                  {              else if (i == 0 && b == 0x2b)   // continuation
179                    response.tag = IMAPResponse.CONTINUATION;                {
180                  }                  response.tag = IMAPResponse.CONTINUATION;
181                else if (b == 0x20)     // delimiter                }
182                  {              else if (b == 0x20)     // delimiter
183                    if (response.tag == null)                {
184                      {                  if (response.tag == null)
185                        byte[] tb = genericSink.toByteArray ();                    {
186                        response.tag = new String (tb, DEFAULT_ENCODING);                      byte[] tb = genericSink.toByteArray();
187                      }                      response.tag = new String(tb, DEFAULT_ENCODING);
188                    genericSink.reset ();                    }
189                    if (response.isContinuation ())                  genericSink.reset();
190                      {                  if (response.isContinuation())
191                        state = STATE_TEXT;                    {
192                      }                      state = STATE_TEXT;
193                    else                    }
194                      {                  else
195                        state = STATE_COUNT;                    {
196                      }                      state = STATE_COUNT;
197                  }                    }
198                else                    // tag literal                }
199                  {              else                    // tag literal
200                    genericSink.write (b);                {
201                  }                  genericSink.write(b);
202                break;                }
203              case STATE_COUNT:        // expect count or id              break;
204                if (b < 0x30 || b > 0x39)            case STATE_COUNT:        // expect count or id
205                  {              if (b < 0x30 || b > 0x39)
206                    state = STATE_ID;                {
207                  }                  state = STATE_ID;
208                if (b == 0x20)          // delimiter                }
209                  {              if (b == 0x20)          // delimiter
210                    byte[] cb = genericSink.toByteArray ();                {
211                    genericSink.reset ();                  byte[] cb = genericSink.toByteArray();
212                    String cs = new String (cb, DEFAULT_ENCODING);                  genericSink.reset();
213                    try                  String cs = new String(cb, DEFAULT_ENCODING);
214                      {                  try
215                        response.count = Integer.parseInt (cs);                    {
216                      }                      response.count = Integer.parseInt(cs);
217                    catch (NumberFormatException e)                    }
218                      {                  catch (NumberFormatException e)
219                        throw new ProtocolException ("Expecting number: " + cs);                    {
220                      }                      throw new ProtocolException("Expecting number: " + cs);
221                    state = STATE_ID;                    }
222                  }                  state = STATE_ID;
223                else                }
224                  {              else
225                    genericSink.write (b);                {
226                  }                  genericSink.write(b);
227                break;                }
228              case STATE_ID:           // expect id              break;
229                if (b == 0x20)          // delimiter            case STATE_ID:           // expect id
230                  {              if (b == 0x20)          // delimiter
231                    byte[] ib = genericSink.toByteArray ();                {
232                    genericSink.reset ();                  byte[] ib = genericSink.toByteArray();
233                    response.id = new String (ib, DEFAULT_ENCODING).intern ();                  genericSink.reset();
234                    state = STATE_MAYBE_CODE;                  response.id = new String(ib, DEFAULT_ENCODING).intern();
235                  }                  state = STATE_MAYBE_CODE;
236                else if (b == 0x0a)     // EOL                }
237                  {              else if (b == 0x0a)     // EOL
238                    byte[] ib = genericSink.toByteArray ();                {
239                    genericSink.reset ();                  byte[] ib = genericSink.toByteArray();
240                    response.id = new String (ib, DEFAULT_ENCODING).intern ();                  genericSink.reset();
241                    state = STATE_TAG;                  response.id = new String(ib, DEFAULT_ENCODING).intern();
242                    // mark bytes read                  state = STATE_TAG;
243                    mark (i);                  // mark bytes read
244                    return response;                  mark(i);
245                  }                  return response;
246                else if (b != 0x0d)     // id literal                }
247                  {              else if (b != 0x0d)     // id literal
248                    genericSink.write (b);                {
249                  }                  genericSink.write(b);
250                break;                }
251              case STATE_MAYBE_CODE:   // expect code or text              break;
252                if (b == 0x28 || b == 0x5b)            case STATE_MAYBE_CODE:   // expect code or text
253                  {              if (b == 0x28 || b == 0x5b)
254                    List top = new ArrayList ();                {
255                    response.code = top;                  List top = new ArrayList();
256                    context.push (top);                  response.code = top;
257                    state = STATE_CODE;                  context.push(top);
258                  }                  state = STATE_CODE;
259                else                }
260                  {              else
261                    if (response.id == FETCH)                {
262                      {                  if (response.id == FETCH)
263                        // We can't have text here so it must be the beginning of                    {
264                        // FETCH FLAGS. Go back to ID state.                      // We can't have text here so it must be the beginning of
265                        genericSink.reset ();                      // FETCH FLAGS. Go back to ID state.
266                        byte[] fetchBytes =                      genericSink.reset();
267                          new byte[] { 0x46, 0x45, 0x54, 0x43, 0x48, 0x20};                      byte[] fetchBytes =
268                        genericSink.write (fetchBytes);                        new byte[] { 0x46, 0x45, 0x54, 0x43, 0x48, 0x20};
269                        genericSink.write (b);                      genericSink.write(fetchBytes);
270                        state = STATE_ID;                      genericSink.write(b);
271                      }                      state = STATE_ID;
272                    else if (response.id == STATUS)                    }
273                      {                  else if (response.id == STATUS)
274                        // We are in the mailbox name part of the STATUS response                    {
275                        genericSink.write (b);                      // We are in the mailbox name part of the STATUS response
276                        state = STATE_STATUS;                      genericSink.write(b);
277                      }                      state = STATE_STATUS;
278                    else                    }
279                      {                  else
280                        genericSink.write (b);                    {
281                        state = STATE_TEXT;                      genericSink.write(b);
282                      }                      state = STATE_TEXT;
283                  }                    }
284                break;                }
285              case STATE_STATUS:              break;
286                if (b == 0x20)          // delimiter            case STATE_STATUS:
287                  {              if (b == 0x20)          // delimiter
288                    response.mailbox = genericSink.toString ();                {
289                    genericSink.reset ();                  response.mailbox = genericSink.toString();
290                    state = STATE_MAYBE_CODE;                  genericSink.reset();
291                  }                  state = STATE_MAYBE_CODE;
292                else                }
293                  {              else
294                    genericSink.write (b);                {
295                  }                  genericSink.write(b);
296                break;                }
297              case STATE_CODE:         // response code inside parentheses              break;
298                if (b == 0x22)          // quote delimiter            case STATE_CODE:         // response code inside parentheses
299                  {              if (b == 0x22)          // quote delimiter
300                    inQuote = !inQuote;                {
301                  }                  inQuote = !inQuote;
302                else if (inQuote)                }
303                  {              else if (inQuote)
304                    genericSink.write (b);                {
305                  }                  genericSink.write(b);
306                else                }
307                  {              else
308                    if (b == 0x28 || b == 0x5b)   // start parenthesis/bracket                {
309                      {                  if (b == 0x28 || b == 0x5b)   // start parenthesis/bracket
310                        List parent = (List) context.peek ();                    {
311                        List top = new ArrayList ();                      List parent =(List) context.peek();
312                        if (genericSink.size () > 0)                      List top = new ArrayList();
313                          {                      if (genericSink.size() > 0)
314                            byte[] tb = genericSink.toByteArray ();                        {
315                            String token =                          byte[] tb = genericSink.toByteArray();
316                              new String (tb, DEFAULT_ENCODING).intern ();                          String token =
317                            Pair pair = new Pair (token, top);                            new String(tb, DEFAULT_ENCODING).intern();
318                            parent.add (pair);                          Pair pair = new Pair(token, top);
319                            genericSink.reset ();                          parent.add(pair);
320                          }                          genericSink.reset();
321                        else                        }
322                          {                      else
323                            parent.add (top);                        {
324                          }                          parent.add(top);
325                        context.push (top);                        }
326                      }                      context.push(top);
327                    else if (b == 0x29 || b == 0x5d)      // end parenthesis/bracket                    }
328                      {                  else if (b == 0x29 || b == 0x5d)      // end parenthesis/bracket
329                        List top = (List) context.pop ();                    {
330                        // flush genericSink                      List top =(List) context.pop();
331                        if (genericSink.size () > 0)                      // flush genericSink
332                          {                      if (genericSink.size() > 0)
333                            byte[] tb = genericSink.toByteArray ();                        {
334                            String token =                          byte[] tb = genericSink.toByteArray();
335                              new String (tb, DEFAULT_ENCODING).intern ();                          String token =
336                            top.add (token);                            new String(tb, DEFAULT_ENCODING).intern();
337                            genericSink.reset ();                          top.add(token);
338                          }                          genericSink.reset();
339                      }                        }
340                    else if (b == 0x7b)   // literal length                    }
341                      {                  else if (b == 0x7b)   // literal length
342                        genericSink.reset ();                    {
343                        state = STATE_LITERAL_LENGTH;                      genericSink.reset();
344                      }                      state = STATE_LITERAL_LENGTH;
345                    else if (b == 0x20)   // token delimiter                    }
346                      {                  else if (b == 0x20)   // token delimiter
347                        if (context.size () == 0)    // end state                    {
348                          {                      if (context.size() == 0)    // end state
349                            state = STATE_TEXT;                        {
350                          }                          state = STATE_TEXT;
351                        else                // add token                        }
352                          {                      else                // add token
353                            List top = (List) context.peek ();                        {
354                            // flush genericSink                          List top =(List) context.peek();
355                            if (genericSink.size () > 0)                          // flush genericSink
356                              {                          if (genericSink.size() > 0)
357                                byte[] tb = genericSink.toByteArray ();                            {
358                                String token =                              byte[] tb = genericSink.toByteArray();
359                                  new String (tb, DEFAULT_ENCODING).intern ();                              String token =
360                                top.add (token);                                new String(tb, DEFAULT_ENCODING).intern();
361                                genericSink.reset ();                              top.add(token);
362                              }                              genericSink.reset();
363                          }                            }
364                      }                        }
365                    else if (b == 0x0a)   // EOL                    }
366                      {                  else if (b == 0x0a)   // EOL
367                        state = STATE_TAG;                    {
368                        // mark bytes read                      state = STATE_TAG;
369                        mark (i);                      // mark bytes read
370                        return response;                      mark(i);
371                      }                      return response;
372                    else if (b != 0x0d)   // ignore CR                    }
373                      {                  else if (b != 0x0d)   // ignore CR
374                        genericSink.write (b);                    {
375                      }                      genericSink.write(b);
376                  }                    }
377                break;                }
378              case STATE_LITERAL_LENGTH:              break;
379                if (b == 0x7d)          // end literal length            case STATE_LITERAL_LENGTH:
380                  {              if (b == 0x7d)          // end literal length
381                    byte[] cb = genericSink.toByteArray ();                {
382                    genericSink.reset ();                  byte[] cb = genericSink.toByteArray();
383                    String cs = new String (cb, DEFAULT_ENCODING);                  genericSink.reset();
384                    try                  String cs = new String(cb, DEFAULT_ENCODING);
385                      {                  try
386                        literalLength = Integer.parseInt (cs);                    {
387                      }                      literalLength = Integer.parseInt(cs);
388                    catch (NumberFormatException e)                    }
389                      {                  catch (NumberFormatException e)
390                        throw new ProtocolException ("Expecting number: " + cs);                    {
391                      }                      throw new ProtocolException("Expecting number: " + cs);
392                  }                    }
393                else if (b == 0x0a)     // start literal                }
394                  {              else if (b == 0x0a)     // start literal
395                    state = STATE_LITERAL;                {
396                    literalSink = new ByteArrayOutputStream ();                  state = STATE_LITERAL;
397                    literalCount = 0;                  literalSink = new ByteArrayOutputStream();
398                  }                  literalCount = 0;
399                else if (b != 0x0d)     // ignore CR                }
400                  {              else if (b != 0x0d)     // ignore CR
401                    genericSink.write (b);                {
402                  }                  genericSink.write(b);
403                break;                }
404              case STATE_LITERAL:              break;
405                if (literalCount >= literalLength)            case STATE_LITERAL:
406                  {              if (literalCount >= literalLength)
407                    List top = (List) context.peek ();                {
408                    byte[] literal = literalSink.toByteArray ();                  List top =(List) context.peek();
409                    top.add (literal);                  byte[] literal = literalSink.toByteArray();
410                    literalSink = null;                  top.add(literal);
411                    inContent = false;                  literalSink = null;
412                    state = STATE_CODE;                  inContent = false;
413                  }                  state = STATE_CODE;
414                else                }
415                  {              else
416                    literalSink.write (b);                {
417                    literalCount++;                  literalSink.write(b);
418                  }                  literalCount++;
419                break;                }
420              case STATE_TEXT:         // human-readable text              break;
421                if (b == 0x0a)          // delimiter            case STATE_TEXT:         // human-readable text
422                  {              if (b == 0x0a)          // delimiter
423                    byte[] tb = genericSink.toByteArray ();                {
424                    genericSink.reset ();                  byte[] tb = genericSink.toByteArray();
425                    response.text = new String (tb, DEFAULT_ENCODING);                  genericSink.reset();
426                    state = STATE_TAG;                  response.text = new String(tb, DEFAULT_ENCODING);
427                    // mark bytes read                  state = STATE_TAG;
428                    mark (i);                  // mark bytes read
429                    return response;                  mark(i);
430                  }                  return response;
431                else if (b != 0x0d)     // ignore CR                }
432                  {              else if (b != 0x0d)     // ignore CR
433                    genericSink.write (b);                {
434                  }                  genericSink.write(b);
435                break;                }
436              }              break;
437          }            }
438        // get more bytes        }
439        buf = read (true);      // get more bytes
440        return next ();      buf = read(true);
441      }      return next();
442      }
443      
444  }  }
445    

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.6

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26