/[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.1 by dog, Sun Oct 19 08:51:37 2003 UTC revision 1.2 by dog, Sun Oct 19 16:16:50 2003 UTC
# Line 52  public class IMAPResponseTokenizer imple Line 52  public class IMAPResponseTokenizer imple
52    
53    private byte[] buffer = null;    private byte[] buffer = null;
54    
55    private static final int BUFFER_SIZE = 4096; // TODO review    private static final int BUFFER_SIZE = 4096;  // TODO review
56    private static final String DEFAULT_ENCODING = "US-ASCII";    private static final String DEFAULT_ENCODING = "US-ASCII";
57      
58    private static final int STATE_TAG = 0;    private static final int STATE_TAG = 0;
59    private static final int STATE_COUNT = 1;    private static final int STATE_COUNT = 1;
60    private static final int STATE_ID = 2;    private static final int STATE_ID = 2;
# Line 69  public class IMAPResponseTokenizer imple Line 69  public class IMAPResponseTokenizer imple
69     * Constructor.     * Constructor.
70     * @param in the server socket input stream     * @param in the server socket input stream
71     */     */
72    public IMAPResponseTokenizer(InputStream in) {    public IMAPResponseTokenizer(InputStream in)
73      {
74      this.in = in;      this.in = in;
75    }    }
76    
# Line 78  public class IMAPResponseTokenizer imple Line 79  public class IMAPResponseTokenizer imple
79     * cache. If moreNeeded is specified, always performs a read to append     * cache. If moreNeeded is specified, always performs a read to append
80     * more to the cache.     * more to the cache.
81     */     */
82    byte[] read(boolean moreNeeded)    byte[] read(boolean moreNeeded) throws IOException
     throws IOException  
83    {    {
84      if (buffer!=null && !moreNeeded && buffer.length>0)      if (buffer != null && !moreNeeded && buffer.length > 0)
85        return buffer;        return buffer;
86      int max = in.available();      int max = in.available();
87      if (max<1)      if (max < 1)
88        max = BUFFER_SIZE;          max = BUFFER_SIZE;
89      byte[] tmp = new byte[max];        byte[] tmp = new byte[max];
90      int len = 0;      int len = 0;
91      while (len==0)      while (len == 0)
92        len = in.read(tmp, 0, max);        len = in.read(tmp, 0, max);
93      if (len==-1)      if (len == -1)
94        return null; // EOF        return null;              // EOF
95      int blen = (buffer==null) ? 0 : buffer.length;      int blen = (buffer == null) ? 0 : buffer.length;
96      byte[] uni = new byte[blen+len];        byte[] uni = new byte[blen + len];
97      if (blen!=0)      if (blen != 0)
98        System.arraycopy(buffer, 0, uni, 0, blen);          System.arraycopy(buffer, 0, uni, 0, blen);
99      System.arraycopy(tmp, 0, uni, blen, len);        System.arraycopy(tmp, 0, uni, blen, len);
100      buffer = uni;        buffer = uni;
101      return buffer;        return buffer;
102    }    }
103    
104    /*    /*
105     * Invalidates the byte cache up to the specified index.     * Invalidates the byte cache up to the specified index.
106     */     */
107    void mark(int index) {    void mark(int index)
108      {
109      int len = buffer.length;      int len = buffer.length;
110      int start = index + 1;      int start = index + 1;
111      if (start<len)      if (start < len)
112      {      {
113        int n = (len-start);        int n = (len - start);
114        byte[] tmp = new byte[n];        byte[]tmp = new byte[n];
115        System.arraycopy(buffer, start, tmp, 0, n);        System.arraycopy(buffer, start, tmp, 0, n);
116        buffer = tmp;        buffer = tmp;
117      }      }
# Line 121  public class IMAPResponseTokenizer imple Line 122  public class IMAPResponseTokenizer imple
122    /**    /**
123     * Returns the next IMAPResponse.     * Returns the next IMAPResponse.
124     */     */
125    public IMAPResponse next()    public IMAPResponse next() throws IOException
     throws IOException  
126    {    {
127      // Perform read      // Perform read
128      byte[] buf = read(false);      byte[] buf = read(false);
129      if (buf==null)      if (buf == null)
130        return null; // pass EOF back up the chain        return null;              // pass EOF back up the chain
131      int len = buf.length;      int len = buf.length;
132        
133      IMAPResponse response = new IMAPResponse();      IMAPResponse response = new IMAPResponse();
134      ByteArrayOutputStream genericSink = new ByteArrayOutputStream();      ByteArrayOutputStream genericSink = new ByteArrayOutputStream();
135      ByteArrayOutputStream literalSink = null;      ByteArrayOutputStream literalSink = null;
# Line 138  public class IMAPResponseTokenizer imple Line 138  public class IMAPResponseTokenizer imple
138      int state = STATE_TAG;      int state = STATE_TAG;
139      boolean inQuote = false;      boolean inQuote = false;
140      boolean inContent = false;      boolean inContent = false;
141      for (int i=0; i<len; i++)      for (int i = 0; i < len; i++)
142      {      {
143        byte b = buf[i];        byte b = buf[i];
144        switch (state)        switch (state)
145        {        {
146          case STATE_TAG: // expect tag        case STATE_TAG:          // expect tag
147            if (i==0 && b==0x2a) // untagged          if (i == 0 && b == 0x2a)        // untagged
148              response.tag = IMAPResponse.UNTAGGED;            response.tag = IMAPResponse.UNTAGGED;
149            else if (i==0 && b==0x2b) // continuation          else if (i == 0 && b == 0x2b)   // continuation
150              response.tag = IMAPResponse.CONTINUATION;            response.tag = IMAPResponse.CONTINUATION;
151            else if (b==0x20) // delimiter          else if (b == 0x20)     // delimiter
152            {          {
153              if (response.tag==null)            if (response.tag == null)
154              {            {
155                byte[] tb = genericSink.toByteArray();              byte[]tb = genericSink.toByteArray();
156                response.tag = new String(tb, DEFAULT_ENCODING);              response.tag = new String(tb, DEFAULT_ENCODING);
157              }            }
158              genericSink.reset();            genericSink.reset();
159              if (response.isContinuation())            if (response.isContinuation())
160                state = STATE_TEXT;              state = STATE_TEXT;
161              else            else
162                state = STATE_COUNT;              state = STATE_COUNT;
163            }          }
164            else // tag literal          else                    // tag literal
165              genericSink.write(b);            genericSink.write(b);
166            break;          break;
167          case STATE_COUNT: // expect count or id        case STATE_COUNT:        // expect count or id
168            if (b<0x30 || b>0x39)          if (b < 0x30 || b > 0x39)
169              state = STATE_ID;            state = STATE_ID;
170            if (b==0x20) // delimiter          if (b == 0x20)          // delimiter
171            {
172              byte[]cb = genericSink.toByteArray();
173              genericSink.reset();
174              String cs = new String(cb, DEFAULT_ENCODING);
175              try
176              {
177                response.count = Integer.parseInt(cs);
178              }
179              catch(NumberFormatException e)
180              {
181                throw new ProtocolException("Expecting number: " + cs);
182              }
183              state = STATE_ID;
184            }
185            else
186              genericSink.write(b);
187            break;
188          case STATE_ID:           // expect id
189            if (b == 0x20)          // delimiter
190            {
191              byte[]ib = genericSink.toByteArray();
192              genericSink.reset();
193              response.id = new String(ib, DEFAULT_ENCODING).intern();
194              state = STATE_MAYBE_CODE;
195            }
196            else if (b == 0x0a)     // EOL
197            {
198              byte[]ib = genericSink.toByteArray();
199              genericSink.reset();
200              response.id = new String(ib, DEFAULT_ENCODING).intern();
201              state = STATE_TAG;
202              // mark bytes read
203              mark(i);
204              return response;
205            }
206            else if (b != 0x0d)     // id literal
207              genericSink.write(b);
208            break;
209          case STATE_MAYBE_CODE:   // expect code or text
210            if (b == 0x28 || b == 0x5b)
211            {
212              List top = new ArrayList();
213              response.code = top;
214              context.push(top);
215              state = STATE_CODE;
216            }
217            else
218            {
219              if (response.id == FETCH)
220            {            {
221              byte[] cb = genericSink.toByteArray();              // We can't have text here so it must be the beginning of
222                // FETCH FLAGS. Go back to ID state.
223              genericSink.reset();              genericSink.reset();
224              String cs = new String(cb, DEFAULT_ENCODING);              byte[]fetchBytes = new byte[]
             try  
             {  
               response.count = Integer.parseInt(cs);  
             }  
             catch (NumberFormatException e)  
225              {              {
226                throw new ProtocolException("Expecting number: "+cs);              0x46, 0x45, 0x54, 0x43, 0x48, 0x20};
227              }              genericSink.write(fetchBytes);
228                genericSink.write(b);
229              state = STATE_ID;              state = STATE_ID;
230            }            }
231            else            else if (response.id == STATUS)
             genericSink.write(b);  
           break;  
         case STATE_ID: // expect id  
           if (b==0x20) // delimiter  
232            {            {
233              byte[] ib = genericSink.toByteArray();              // We are in the mailbox name part of the STATUS response
234              genericSink.reset();              genericSink.write(b);
235              response.id = new String(ib, DEFAULT_ENCODING).intern();              state = STATE_STATUS;
             state = STATE_MAYBE_CODE;  
236            }            }
237            else if (b==0x0a) // EOL            else
238            {            {
             byte[] ib = genericSink.toByteArray();  
             genericSink.reset();  
             response.id = new String(ib, DEFAULT_ENCODING).intern();  
             state = STATE_TAG;  
             // mark bytes read  
             mark(i);  
             return response;  
           }  
           else if (b!=0x0d) // id literal  
239              genericSink.write(b);              genericSink.write(b);
240            break;              state = STATE_TEXT;
         case STATE_MAYBE_CODE: // expect code or text  
           if (b==0x28 || b==0x5b)  
           {  
             List top = new ArrayList();  
             response.code = top;  
             context.push(top);  
             state = STATE_CODE;  
241            }            }
242            else          }
243            break;
244          case STATE_STATUS:
245            if (b == 0x20)          // delimiter
246            {
247              response.mailbox = genericSink.toString();
248              genericSink.reset();
249              state = STATE_MAYBE_CODE;
250            }
251            else
252              genericSink.write(b);
253            break;
254          case STATE_CODE:         // response code inside parentheses
255            if (b == 0x22)          // quote delimiter
256            {
257              inQuote = !inQuote;
258            }
259            else if (inQuote)
260            {
261              genericSink.write(b);
262            }
263            else
264            {
265              if (b == 0x28 || b == 0x5b)   // start parenthesis/bracket
266            {            {
267              if (response.id==FETCH)              List parent = (List) context.peek();
268                List top = new ArrayList();
269                if (genericSink.size() > 0)
270              {              {
271                // We can't have text here so it must be the beginning of                byte[]tb = genericSink.toByteArray();
272                // FETCH FLAGS. Go back to ID state.                String token = new String(tb, DEFAULT_ENCODING).intern();
273                  Pair pair = new Pair(token, top);
274                  parent.add(pair);
275                genericSink.reset();                genericSink.reset();
               byte[] fetchBytes =  
                 new byte[]{0x46, 0x45, 0x54, 0x43, 0x48, 0x20};  
               genericSink.write(fetchBytes);  
               genericSink.write(b);  
               state = STATE_ID;  
             }  
             else if (response.id==STATUS)  
             {  
               // We are in the mailbox name part of the STATUS response  
               genericSink.write(b);  
               state = STATE_STATUS;  
276              }              }
277              else              else
278              {              {
279                genericSink.write(b);                parent.add(top);
               state = STATE_TEXT;  
280              }              }
281                context.push(top);
282            }            }
283            break;            else if (b == 0x29 || b == 0x5d)      // end parenthesis/bracket
         case STATE_STATUS:  
           if (b==0x20) // delimiter  
           {  
             response.mailbox = genericSink.toString();  
             genericSink.reset();  
             state = STATE_MAYBE_CODE;  
           }  
           else  
             genericSink.write(b);  
           break;  
         case STATE_CODE: // response code inside parentheses  
           if (b==0x22) // quote delimiter  
284            {            {
285              inQuote = !inQuote;              List top = (List) context.pop();
286                // flush genericSink
287                if (genericSink.size() > 0)
288                {
289                  byte[]tb = genericSink.toByteArray();
290                  String token = new String(tb, DEFAULT_ENCODING).intern();
291                  top.add(token);
292                  genericSink.reset();
293                }
294            }            }
295            else if (inQuote)            else if (b == 0x7b)   // literal length
296            {            {
297              genericSink.write(b);              genericSink.reset();
298                state = STATE_LITERAL_LENGTH;
299            }            }
300            else            else if (b == 0x20)   // token delimiter
301            {            {
302              if (b==0x28 || b==0x5b) // start parenthesis/bracket              if (context.size() == 0)    // end state
303              {                state = STATE_TEXT;
304                List parent = (List)context.peek();              else                // add token
               List top = new ArrayList();  
               if (genericSink.size()>0)  
               {  
                 byte[] tb = genericSink.toByteArray();  
                 String token = new String(tb, DEFAULT_ENCODING).intern();  
                 Pair pair = new Pair(token, top);  
                 parent.add(pair);  
                 genericSink.reset();  
               }  
               else  
               {  
                 parent.add(top);  
               }  
               context.push(top);  
             }  
             else if (b==0x29 || b==0x5d) // end parenthesis/bracket  
305              {              {
306                List top = (List)context.pop();                List top = (List) context.peek();
307                // flush genericSink                // flush genericSink
308                if (genericSink.size()>0)                if (genericSink.size() > 0)
309                {                {
310                  byte[] tb = genericSink.toByteArray();                  byte[]tb = genericSink.toByteArray();
311                  String token = new String(tb, DEFAULT_ENCODING).intern();                  String token = new String(tb, DEFAULT_ENCODING).intern();
312                  top.add(token);                  top.add(token);
313                  genericSink.reset();                  genericSink.reset();
314                }                }
315              }              }
             else if (b==0x7b) // literal length  
             {  
               genericSink.reset();  
               state = STATE_LITERAL_LENGTH;  
             }  
             else if (b==0x20) // token delimiter  
             {  
               if (context.size()==0) // end state  
                 state = STATE_TEXT;  
               else // add token  
               {  
                 List top = (List)context.peek();  
                 // flush genericSink  
                 if (genericSink.size()>0)  
                 {  
                   byte[] tb = genericSink.toByteArray();  
                   String token = new String(tb, DEFAULT_ENCODING).intern();  
                   top.add(token);  
                   genericSink.reset();  
                 }  
               }  
             }  
             else if (b==0x0a) // EOL  
             {  
               state = STATE_TAG;  
               // mark bytes read  
               mark(i);  
               return response;  
             }  
             else if (b!=0x0d) // ignore CR  
               genericSink.write(b);  
           }  
           break;  
         case STATE_LITERAL_LENGTH:  
           if (b==0x7d) // end literal length  
           {  
             byte[] cb = genericSink.toByteArray();  
             genericSink.reset();  
             String cs = new String(cb, DEFAULT_ENCODING);  
             try  
             {  
               literalLength = Integer.parseInt(cs);  
             }  
             catch (NumberFormatException e)  
             {  
               throw new ProtocolException("Expecting number: "+cs);  
             }  
           }  
           else if (b==0x0a) // start literal  
           {  
             state = STATE_LITERAL;  
             literalSink = new ByteArrayOutputStream();  
             literalCount = 0;  
           }  
           else if (b!=0x0d) // ignore CR  
             genericSink.write(b);  
           break;  
         case STATE_LITERAL:  
           if (literalCount>=literalLength)  
           {  
             List top = (List)context.peek();  
             byte[] literal = literalSink.toByteArray();  
             top.add(literal);  
             literalSink = null;  
             inContent = false;  
             state = STATE_CODE;  
316            }            }
317            else            else if (b == 0x0a)   // EOL
318            {            {
             literalSink.write(b);  
             literalCount++;  
           }  
           break;  
         case STATE_TEXT: // human-readable text  
           if (b==0x0a) // delimiter  
           {  
             byte[] tb = genericSink.toByteArray();  
             genericSink.reset();  
             response.text = new String(tb, DEFAULT_ENCODING);  
319              state = STATE_TAG;              state = STATE_TAG;
320              // mark bytes read              // mark bytes read
321              mark(i);              mark(i);
322              return response;              return response;
323            }            }
324            else if (b!=0x0d) // ignore CR            else if (b != 0x0d)   // ignore CR
325              genericSink.write(b);              genericSink.write(b);
326            break;          }
327            break;
328          case STATE_LITERAL_LENGTH:
329            if (b == 0x7d)          // end literal length
330            {
331              byte[]cb = genericSink.toByteArray();
332              genericSink.reset();
333              String cs = new String(cb, DEFAULT_ENCODING);
334              try
335              {
336                literalLength = Integer.parseInt(cs);
337              }
338              catch(NumberFormatException e)
339              {
340                throw new ProtocolException("Expecting number: " + cs);
341              }
342            }
343            else if (b == 0x0a)     // start literal
344            {
345              state = STATE_LITERAL;
346              literalSink = new ByteArrayOutputStream();
347              literalCount = 0;
348            }
349            else if (b != 0x0d)     // ignore CR
350              genericSink.write(b);
351            break;
352          case STATE_LITERAL:
353            if (literalCount >= literalLength)
354            {
355              List top = (List) context.peek();
356              byte[]literal = literalSink.toByteArray();
357              top.add(literal);
358              literalSink = null;
359              inContent = false;
360              state = STATE_CODE;
361            }
362            else
363            {
364              literalSink.write(b);
365              literalCount++;
366            }
367            break;
368          case STATE_TEXT:         // human-readable text
369            if (b == 0x0a)          // delimiter
370            {
371              byte[]tb = genericSink.toByteArray();
372              genericSink.reset();
373              response.text = new String(tb, DEFAULT_ENCODING);
374              state = STATE_TAG;
375              // mark bytes read
376              mark(i);
377              return response;
378            }
379            else if (b != 0x0d)     // ignore CR
380              genericSink.write(b);
381            break;
382        }        }
383      }      }
384      // get more bytes      // get more bytes

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2

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