| 1 22 23 package gnu.mail.providers.imap4; 24 25 import java.io.ByteArrayOutputStream ; 26 import java.io.InputStream ; 27 import java.io.IOException ; 28 import java.net.ProtocolException ; 29 import java.util.ArrayList ; 30 import java.util.List ; 31 import java.util.Stack ; 32 33 40 public class IMAPResponseTokenizer implements IMAPConstants 41 { 42 43 46 protected InputStream in; 47 48 private byte[] buffer; 49 50 private static final int BUFFER_SIZE = 4096; private static final String DEFAULT_ENCODING = "US-ASCII"; 52 53 private static final int STATE_TAG = 0; 54 private static final int STATE_COUNT = 1; 55 private static final int STATE_ID = 2; 56 private static final int STATE_MAYBE_CODE = 3; 57 private static final int STATE_CODE = 4; 58 private static final int STATE_CONTENT_LENGTH = 5; 59 private static final int STATE_CONTENT = 6; 60 private static final int STATE_TEXT = 7; 61 private static final int STATE_STATUS = 8; 62 63 67 public IMAPResponseTokenizer(InputStream in) { 68 this.in = in; 69 } 70 71 76 byte[] read(boolean moreNeeded) 77 throws IOException  78 { 79 if (buffer!=null && !moreNeeded && buffer.length>0) 80 return buffer; 81 int max = in.available(); 82 if (max<1) 83 max = BUFFER_SIZE; 84 byte[] tmp = new byte[max]; 85 int len = 0; 86 while (len==0) 87 len = in.read(tmp, 0, max); 88 if (len==-1) 89 return null; int blen = (buffer==null) ? 0 : buffer.length; 91 byte[] uni = new byte[blen+len]; 92 if (blen!=0) 93 System.arraycopy(buffer, 0, uni, 0, blen); 94 System.arraycopy(tmp, 0, uni, blen, len); 95 buffer = uni; 96 return buffer; 97 } 98 99 102 void mark(int index) { 103 int len = buffer.length; 104 int start = index + 1; 105 if (start<len) 106 { 107 int n = (len-start); 108 byte[] tmp = new byte[n]; 109 System.arraycopy(buffer, start, tmp, 0, n); 110 buffer = tmp; 111 } 112 else 113 buffer = null; 114 } 115 116 119 public IMAPResponse next() 120 throws IOException  121 { 122 byte[] buf = read(false); 124 if (buf==null) 125 return null; int len = buf.length; 127 128 IMAPResponse response = new IMAPResponse(); 129 ByteArrayOutputStream genericSink = new ByteArrayOutputStream (); 130 ByteArrayOutputStream contentSink = null; 131 int contentCount = 0, contentLength = -1; 132 Stack context = new Stack (); 133 int state = STATE_TAG; 134 boolean inQuote = false; 135 boolean inContent = false; 136 for (int i=0; i<len; i++) 137 { 138 byte b = buf[i]; 139 switch (state) 140 { 141 case STATE_TAG: if (i==0 && b==0x2a) response.tag = IMAPResponse.UNTAGGED; 144 else if (i==0 && b==0x2b) response.tag = IMAPResponse.CONTINUATION; 146 else if (b==0x20) { 148 if (response.tag==null) 149 { 150 byte[] tb = genericSink.toByteArray(); 151 response.tag = new String (tb, DEFAULT_ENCODING); 152 } 153 genericSink.reset(); 154 if (response.isContinuation()) 155 state = STATE_TEXT; 156 else 157 state = STATE_COUNT; 158 } 159 else genericSink.write(b); 161 break; 162 case STATE_COUNT: if (b<0x30 || b>0x39) 164 state = STATE_ID; 165 if (b==0x20) { 167 byte[] cb = genericSink.toByteArray(); 168 genericSink.reset(); 169 String cs = new String (cb, DEFAULT_ENCODING); 170 try 171 { 172 response.count = Integer.parseInt(cs); 173 } 174 catch (NumberFormatException e) 175 { 176 throw new ProtocolException ("Expecting number: "+cs); 177 } 178 state = STATE_ID; 179 } 180 else 181 genericSink.write(b); 182 break; 183 case STATE_ID: if (b==0x20) { 186 byte[] ib = genericSink.toByteArray(); 187 genericSink.reset(); 188 response.id = new String (ib, DEFAULT_ENCODING).intern(); 189 state = STATE_MAYBE_CODE; 190 } 191 else if (b==0x0a) { 193 byte[] ib = genericSink.toByteArray(); 194 genericSink.reset(); 195 response.id = new String (ib, DEFAULT_ENCODING).intern(); 196 state = STATE_TAG; 197 mark(i); 199 return response; 200 } 201 else if (b!=0x0d) genericSink.write(b); 203 break; 204 case STATE_MAYBE_CODE: if (b==0x28 || b==0x5b) 206 { 207 List top = new ArrayList (); 208 context.push(top); 210 state = STATE_CODE; 211 } 212 else 213 { 214 if (response.id==FETCH) 215 { 216 genericSink.reset(); 219 byte[] fetchBytes = 220 new byte[]{0x46, 0x45, 0x54, 0x43, 0x48, 0x20}; 221 genericSink.write(fetchBytes); 222 genericSink.write(b); 223 state = STATE_ID; 224 } 225 else if (response.id==STATUS) 226 { 227 genericSink.write(b); 229 state = STATE_STATUS; 230 } 231 else 232 { 233 genericSink.write(b); 234 state = STATE_TEXT; 235 } 236 } 237 break; 238 case STATE_STATUS: 239 if (b==0x20) { 241 response.mailbox = genericSink.toString(); 242 genericSink.reset(); 243 state = STATE_MAYBE_CODE; 244 } 245 else 246 genericSink.write(b); 247 break; 248 case STATE_CODE: if (b==0x22) { 251 inQuote = !inQuote; 252 genericSink.write(b); 253 } 254 else if (inQuote) 255 { 256 genericSink.write(b); 257 } 258 else 259 { 260 if (b==0x28 || b==0x5b) { 262 List top = new ArrayList (); 263 List parent = (List )context.peek(); 264 parent.add(top); 265 context.push(top); 266 } 267 else if (b==0x29 || b==0x5d) { 269 List top = (List )context.pop(); 270 byte[] tb = genericSink.toByteArray(); 272 if (tb.length>0) 273 { 274 genericSink.reset(); 275 String token = new String (tb, DEFAULT_ENCODING).intern(); 276 top.add(token); 277 } 278 if (context.size()==0) response.code = top; 280 } 281 else if (b==0x7b) { 283 genericSink.reset(); 285 state = STATE_CONTENT_LENGTH; 286 } 287 else if (b==0x20) { 289 if (context.size()==0) state = STATE_TEXT; 291 else { 293 List top = (List )context.peek(); 294 byte[] tb = genericSink.toByteArray(); 296 if (tb.length>0) 297 { 298 genericSink.reset(); 299 String token = new String (tb, DEFAULT_ENCODING).intern(); 300 top.add(token); 301 } 302 } 303 } 304 else if (b==0x0a) { 306 state = STATE_TAG; 307 mark(i); 309 return response; 310 } 311 else if (b!=0x0d) genericSink.write(b); 313 } 314 break; 315 case STATE_CONTENT_LENGTH: 316 if (b==0x7d) { 318 byte[] cb = genericSink.toByteArray(); 319 genericSink.reset(); 320 String cs = new String (cb, DEFAULT_ENCODING); 321 try 322 { 323 contentLength = Integer.parseInt(cs); 324 } 325 catch (NumberFormatException e) 326 { 327 throw new ProtocolException ("Expecting number: "+cs); 328 } 329 state = STATE_CONTENT; 330 contentSink = new ByteArrayOutputStream (); 331 contentCount = 0; 332 } 333 else 334 genericSink.write(b); 335 break; 336 case STATE_CONTENT: 337 if (contentCount>contentLength) 338 { 339 response.content = contentSink.toByteArray(); 340 contentSink = null; 341 inContent = false; 342 state = STATE_CODE; 343 } 344 else 345 { 346 if (!inContent && b!=0x20 && b!=0x0d && b!=0x0a) 347 inContent = true; 348 if (inContent) 349 contentSink.write(b); 350 contentCount++; 351 } 352 break; 353 case STATE_TEXT: if (b==0x0a) { 356 byte[] tb = genericSink.toByteArray(); 357 genericSink.reset(); 358 response.text = new String (tb, DEFAULT_ENCODING); 359 state = STATE_TAG; 360 mark(i); 362 return response; 363 } 364 else if (b!=0x0d) genericSink.write(b); 366 break; 367 } 368 } 369 buf = read(true); 371 return next(); 372 } 373 374 } 375 | Popular Tags |