1:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48:
49:
55: public class IMAPResponseTokenizer
56: implements IMAPConstants
57: {
58:
59:
62: protected InputStream in;
63:
64: private byte[] buffer = null;
65:
66: private static final int BUFFER_SIZE = 4096;
67: private static final String DEFAULT_ENCODING = "US-ASCII";
68:
69: private static final int STATE_TAG = 0;
70: private static final int STATE_COUNT = 1;
71: private static final int STATE_ID = 2;
72: private static final int STATE_MAYBE_CODE = 3;
73: private static final int STATE_CODE = 4;
74: private static final int STATE_LITERAL_LENGTH = 5;
75: private static final int STATE_LITERAL = 6;
76: private static final int STATE_TEXT = 7;
77: private static final int STATE_STATUS = 8;
78:
79:
83: public IMAPResponseTokenizer(InputStream in)
84: {
85: this.in = in;
86: }
87:
88:
93: byte[] read(boolean moreNeeded)
94: throws IOException
95: {
96: if (buffer != null && !moreNeeded && buffer.length > 0)
97: {
98: return buffer;
99: }
100: int max = in.available();
101: if (max < 1)
102: {
103: max = BUFFER_SIZE;
104: }
105: byte[] tmp = new byte[max];
106: int len = 0;
107: while (len == 0)
108: {
109: len = in.read(tmp, 0, max);
110: }
111: if (len == -1)
112: {
113: return null;
114: }
115: int blen =(buffer == null) ? 0 : buffer.length;
116: byte[] uni = new byte[blen + len];
117: if (blen != 0)
118: {
119: System.arraycopy(buffer, 0, uni, 0, blen);
120: }
121: System.arraycopy(tmp, 0, uni, blen, len);
122: buffer = uni;
123: return buffer;
124: }
125:
126:
129: void mark(int index)
130: {
131: int len = buffer.length;
132: int start = index + 1;
133: if (start < len)
134: {
135: int n =(len - start);
136: byte[] tmp = new byte[n];
137: System.arraycopy(buffer, start, tmp, 0, n);
138: buffer = tmp;
139: }
140: else
141: {
142: buffer = null;
143: }
144: }
145:
146:
149: public IMAPResponse next()
150: throws IOException
151: {
152:
153: byte[] buf = read(false);
154: if (buf == null)
155: {
156: return null;
157: }
158: int len = buf.length;
159:
160: IMAPResponse response = new IMAPResponse();
161: ByteArrayOutputStream genericSink = new ByteArrayOutputStream();
162: ByteArrayOutputStream literalSink = null;
163: int literalCount = 0, literalLength = -1;
164: Stack context = new Stack();
165: int state = STATE_TAG;
166: boolean inQuote = false;
167: boolean inContent = false;
168: for (int i = 0; i < len; i++)
169: {
170: byte b = buf[i];
171: switch (state)
172: {
173: case STATE_TAG:
174: if (i == 0 && b == 0x2a)
175: {
176: response.tag = IMAPResponse.UNTAGGED;
177: }
178: else if (i == 0 && b == 0x2b)
179: {
180: response.tag = IMAPResponse.CONTINUATION;
181: }
182: else if (b == 0x20)
183: {
184: if (response.tag == null)
185: {
186: byte[] tb = genericSink.toByteArray();
187: response.tag = new String(tb, DEFAULT_ENCODING);
188: }
189: genericSink.reset();
190: if (response.isContinuation())
191: {
192: state = STATE_TEXT;
193: }
194: else
195: {
196: state = STATE_COUNT;
197: }
198: }
199: else
200: {
201: genericSink.write(b);
202: }
203: break;
204: case STATE_COUNT:
205: if (b < 0x30 || b > 0x39)
206: {
207: state = STATE_ID;
208: }
209: if (b == 0x20)
210: {
211: byte[] cb = genericSink.toByteArray();
212: genericSink.reset();
213: String cs = new String(cb, DEFAULT_ENCODING);
214: try
215: {
216: response.count = Integer.parseInt(cs);
217: }
218: catch (NumberFormatException e)
219: {
220: throw new ProtocolException("Expecting number: " + cs);
221: }
222: state = STATE_ID;
223: }
224: else
225: {
226: genericSink.write(b);
227: }
228: break;
229: case STATE_ID:
230: if (b == 0x20)
231: {
232: byte[] ib = genericSink.toByteArray();
233: genericSink.reset();
234: response.id = new String(ib, DEFAULT_ENCODING).intern();
235: state = STATE_MAYBE_CODE;
236: }
237: else if (b == 0x0a)
238: {
239: byte[] ib = genericSink.toByteArray();
240: genericSink.reset();
241: response.id = new String(ib, DEFAULT_ENCODING).intern();
242: state = STATE_TAG;
243:
244: mark(i);
245: return response;
246: }
247: else if (b != 0x0d)
248: {
249: genericSink.write(b);
250: }
251: break;
252: case STATE_MAYBE_CODE:
253: if (b == 0x28 || b == 0x5b)
254: {
255: List top = new ArrayList();
256: response.code = top;
257: context.push(top);
258: state = STATE_CODE;
259: }
260: else
261: {
262: if (response.id == FETCH)
263: {
264:
265:
266: genericSink.reset();
267: byte[] fetchBytes =
268: new byte[] { 0x46, 0x45, 0x54, 0x43, 0x48, 0x20};
269: genericSink.write(fetchBytes);
270: genericSink.write(b);
271: state = STATE_ID;
272: }
273: else if (response.id == STATUS)
274: {
275:
276: genericSink.write(b);
277: state = STATE_STATUS;
278: }
279: else
280: {
281: genericSink.write(b);
282: state = STATE_TEXT;
283: }
284: }
285: break;
286: case STATE_STATUS:
287: if (b == 0x20)
288: {
289: response.mailbox = genericSink.toString();
290: genericSink.reset();
291: state = STATE_MAYBE_CODE;
292: }
293: else
294: {
295: genericSink.write(b);
296: }
297: break;
298: case STATE_CODE:
299: if (b == 0x22)
300: {
301: inQuote = !inQuote;
302: }
303: else if (inQuote)
304: {
305: genericSink.write(b);
306: }
307: else
308: {
309: if (b == 0x28 || b == 0x5b)
310: {
311: List parent =(List) context.peek();
312: List top = new ArrayList();
313: if (genericSink.size() > 0)
314: {
315: byte[] tb = genericSink.toByteArray();
316: String token =
317: new String(tb, DEFAULT_ENCODING).intern();
318: Pair pair = new Pair(token, top);
319: parent.add(pair);
320: genericSink.reset();
321: }
322: else
323: {
324: parent.add(top);
325: }
326: context.push(top);
327: }
328: else if (b == 0x29 || b == 0x5d)
329: {
330: List top =(List) context.pop();
331:
332: if (genericSink.size() > 0)
333: {
334: byte[] tb = genericSink.toByteArray();
335: String token =
336: new String(tb, DEFAULT_ENCODING).intern();
337: top.add(token);
338: genericSink.reset();
339: }
340: }
341: else if (b == 0x7b)
342: {
343: genericSink.reset();
344: state = STATE_LITERAL_LENGTH;
345: }
346: else if (b == 0x20)
347: {
348: if (context.size() == 0)
349: {
350: state = STATE_TEXT;
351: }
352: else
353: {
354: List top =(List) context.peek();
355:
356: if (genericSink.size() > 0)
357: {
358: byte[] tb = genericSink.toByteArray();
359: String token =
360: new String(tb, DEFAULT_ENCODING).intern();
361: top.add(token);
362: genericSink.reset();
363: }
364: }
365: }
366: else if (b == 0x0a)
367: {
368: state = STATE_TAG;
369:
370: mark(i);
371: return response;
372: }
373: else if (b != 0x0d)
374: {
375: genericSink.write(b);
376: }
377: }
378: break;
379: case STATE_LITERAL_LENGTH:
380: if (b == 0x7d)
381: {
382: byte[] cb = genericSink.toByteArray();
383: genericSink.reset();
384: String cs = new String(cb, DEFAULT_ENCODING);
385: try
386: {
387: literalLength = Integer.parseInt(cs);
388: }
389: catch (NumberFormatException e)
390: {
391: throw new ProtocolException("Expecting number: " + cs);
392: }
393: }
394: else if (b == 0x0a)
395: {
396: state = STATE_LITERAL;
397: literalSink = new ByteArrayOutputStream();
398: literalCount = 0;
399: }
400: else if (b != 0x0d)
401: {
402: genericSink.write(b);
403: }
404: break;
405: case STATE_LITERAL:
406: if (literalCount >= literalLength)
407: {
408: List top =(List) context.peek();
409: byte[] literal = literalSink.toByteArray();
410: top.add(literal);
411: literalSink = null;
412: inContent = false;
413: state = STATE_CODE;
414: }
415: else
416: {
417: literalSink.write(b);
418: literalCount++;
419: }
420: break;
421: case STATE_TEXT:
422: if (b == 0x0a)
423: {
424: byte[] tb = genericSink.toByteArray();
425: genericSink.reset();
426: response.text = new String(tb, DEFAULT_ENCODING);
427: state = STATE_TAG;
428:
429: mark(i);
430: return response;
431: }
432: else if (b != 0x0d)
433: {
434: genericSink.write(b);
435: }
436: break;
437: }
438: }
439:
440: buf = read(true);
441: return next();
442: }
443:
444: }