KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > web > webmail > MboxStream


1 /*
2  * Copyright (c) 1998-2001 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.web.webmail;
30
31 import com.caucho.util.ByteBuffer;
32 import com.caucho.util.CharBuffer;
33 import com.caucho.vfs.ReadStream;
34 import com.caucho.vfs.StreamImpl;
35
36 import java.io.IOException JavaDoc;
37 import java.util.HashMap JavaDoc;
38 import java.util.Iterator JavaDoc;
39
40 /*
41  * A stream for reading mbox files.
42  *
43  * <p/>Each call to openRead() will return the next part and return null
44  * when complete.
45  *
46  * <pre><code>
47  * MboxStream mbox = new MboxStream(rawIs);
48  * ReadStream is;
49  *
50  * while ((is = multi.openRead()) != null) {
51  * // read from is as a normal stream
52  * }
53  */

54 public class MboxStream extends StreamImpl {
55   private byte []boundaryBuffer = "From ".getBytes();
56   private int boundaryLength = 5;
57   
58   private ByteBuffer peekBuffer = new ByteBuffer();
59   private byte []peek;
60   private int peekOffset;
61   private int peekLength;
62
63   private byte []dummyBuffer = new byte[32];
64   
65   private ReadStream is;
66   private ReadStream readStream;
67   private boolean isPartDone;
68   private boolean isDone;
69   private HashMap JavaDoc headers = new HashMap JavaDoc();
70   private CharBuffer line = new CharBuffer();
71
72   private String JavaDoc defaultEncoding;
73
74   public MboxStream()
75     throws IOException JavaDoc
76   {
77   }
78
79   public MboxStream(ReadStream is)
80     throws IOException JavaDoc
81   {
82     this();
83
84     init(is);
85   }
86
87   /**
88    * Returns the default encoding.
89    */

90   public String JavaDoc getEncoding()
91   {
92     return defaultEncoding;
93   }
94
95   /**
96    * Sets the default encoding.
97    */

98   public void setEncoding(String JavaDoc encoding)
99   {
100     this.defaultEncoding = encoding;
101   }
102
103   /**
104    * Initialize the multipart stream with a given boundary. The boundary
105    * passed to <code>init</code> will have "--" prefixed.
106    *
107    * @param is the underlying stream
108    * @param headerBoundary the multipart/mime boundary.
109    */

110   public void init(ReadStream is)
111     throws IOException JavaDoc
112   {
113     this.is = is;
114     
115     peekBuffer.setLength(boundaryLength + 5);
116     peek = peekBuffer.getBuffer();
117     peekOffset = 0;
118     peekLength = 0;
119     peek[peekLength++] = (byte) '\n';
120
121     isPartDone = false;
122     isDone = false;
123     
124     while (read(dummyBuffer, 0, dummyBuffer.length) >= 0) {
125     }
126     
127     isPartDone = true;
128   }
129
130   /**
131    * Opens the next message of the mbox stream for reading. Returns
132    * null when the last message is read.
133    */

134   public ReadStream openRead()
135     throws IOException JavaDoc
136   {
137     if (isDone)
138       return null;
139     else if (readStream == null)
140       readStream = new ReadStream(this, null);
141     else if (! isPartDone) {
142       int len;
143       while ((len = read(dummyBuffer, 0, dummyBuffer.length)) >= 0) {
144       }
145
146       if (isDone)
147         return null;
148     }
149     
150     readStream.init(this, null);
151
152     isPartDone = false;
153     
154     if (scanHeaders()) {
155       String JavaDoc contentType = (String JavaDoc) getAttribute("content-type");
156       
157       String JavaDoc charset = getAttributePart(contentType, "charset");
158       
159       if (charset != null)
160         readStream.setEncoding(charset);
161       else if (defaultEncoding != null)
162         readStream.setEncoding(defaultEncoding);
163       
164       return readStream;
165     }
166     else {
167       isDone = true;
168       readStream.close();
169       return null;
170     }
171   }
172
173   /**
174    * Returns a read attribute from the multipart mime.
175    */

176   public Object JavaDoc getAttribute(String JavaDoc key)
177   {
178     return headers.get(key.toLowerCase());
179   }
180
181   /**
182    * Returns the headers from the mime.
183    */

184   public Iterator JavaDoc getAttributeNames()
185   {
186     return headers.keySet().iterator();
187   }
188
189   /**
190    * Scans the mime headers. The mime headers are in standard mail/http
191    * header format: "key: value".
192    */

193   private boolean scanHeaders()
194     throws IOException JavaDoc
195   {
196     int ch = read() ;
197
198     headers.clear();
199     while (ch > 0 && ch != '\n' && ch != '\r') {
200       line.clear();
201
202       line.append((char) ch);
203       for (ch = read();
204            ch >= 0 && ch != '\n' && ch != '\r';
205            ch = read()) {
206         line.append((char) ch);
207       }
208
209       if (ch == '\r') {
210         if ((ch = read()) == '\n')
211           ch = read();
212       } else if (ch == '\n')
213         ch = read();
214
215       int i = 0;
216       for (; i < line.length() && line.charAt(i) != ':'; i++) {
217       }
218
219       String JavaDoc key = null;
220       String JavaDoc value = null;
221       if (i < line.length()) {
222         key = line.substring(0, i).trim().toLowerCase();
223         value = line.substring(i + 1).trim();
224
225         headers.put(key, value);
226       }
227     }
228     
229     if (ch == '\r') {
230       if ((ch = read()) != '\n') {
231         peek[0] = (byte) ch;
232         peekOffset = 0;
233         peekLength = 1;
234       }
235     }
236
237     return true;
238   }
239   
240   public boolean canRead()
241   {
242     return true;
243   }
244
245   /**
246    * Reads from the multipart mime buffer.
247    */

248   public int read(byte []buffer, int offset, int length) throws IOException JavaDoc
249   {
250     int b = -1;
251
252     if (isPartDone)
253       return -1;
254
255     int i = 0;
256     while (i < length && (b = read()) >= 0) {
257       boolean hasCr = false;
258
259       if (b == '\r') {
260         hasCr = true;
261         b = read();
262
263         // XXX: Macintosh?
264
if (b != '\n') {
265           buffer[offset + i++] = (byte) '\r';
266           peek[0] = (byte) b;
267           peekOffset = 0;
268           peekLength = 1;
269           continue;
270         }
271       }
272       else if (b != '\n') {
273         buffer[offset + i++] = (byte) b;
274         continue;
275       }
276
277       int j;
278       for (j = 0;
279            j < boundaryLength && (b = read()) >= 0 && boundaryBuffer[j] == b;
280            j++) {
281       }
282
283       if (j == boundaryLength) {
284         isPartDone = true;
285
286         while ((b = read()) >= 0 && b != '\r' && b != '\n') {
287         }
288         
289         return 1;
290       }
291
292       peekLength = 0;
293       if (hasCr && i + 1 < length) {
294         buffer[offset + i++] = (byte) '\r';
295         buffer[offset + i++] = (byte) '\n';
296       }
297       else if (hasCr) {
298         buffer[offset + i++] = (byte) '\r';
299         peek[peekLength++] = (byte) '\n';
300       }
301       else {
302         buffer[offset + i++] = (byte) '\n';
303       }
304
305       int k = 0;
306       while (k < j && i + 1 < length)
307         buffer[offset + i++] = boundaryBuffer[k++];
308
309       while (k < j)
310         peek[peekLength++] = boundaryBuffer[k++];
311
312       peek[peekLength++] = (byte) b;
313       peekOffset = 0;
314     }
315
316     if (i <= 0) {
317       isPartDone = true;
318       if (b < 0)
319         isDone = true;
320       return -1;
321     }
322     else {
323       return i;
324     }
325   }
326
327   /**
328    * Read the next byte from the peek or from the underlying stream.
329    */

330   private int read()
331     throws IOException JavaDoc
332   {
333     if (peekOffset < peekLength)
334       return peek[peekOffset++] & 0xff;
335     else
336       return is.read();
337   }
338
339   private static String JavaDoc getAttributePart(String JavaDoc attr, String JavaDoc name)
340   {
341     if (attr == null)
342       return null;
343     
344     int length = attr.length();
345     int i = attr.indexOf(name);
346     if (i < 0)
347       return null;
348
349     for (i += name.length(); i < length && attr.charAt(i) != '='; i++) {
350     }
351     
352     for (i++; i < length && attr.charAt(i) == ' '; i++) {
353     }
354
355     CharBuffer value = CharBuffer.allocate();
356     if (i < length && attr.charAt(i) == '\'') {
357       for (i++; i < length && attr.charAt(i) != '\''; i++)
358         value.append(attr.charAt(i));
359     }
360     else if (i < length && attr.charAt(i) == '"') {
361       for (i++; i < length && attr.charAt(i) != '"'; i++)
362         value.append(attr.charAt(i));
363     }
364     else if (i < length) {
365       char ch;
366       for (; i < length && (ch = attr.charAt(i)) != ' ' && ch != ';'; i++)
367         value.append(ch);
368     }
369
370     return value.close();
371   }
372 }
373
Popular Tags