KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > ristretto > imap > IMAPInputStream


1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is Ristretto Mail API.
15  *
16  * The Initial Developers of the Original Code are
17  * Timo Stich and Frederik Dietz.
18  * Portions created by the Initial Developers are Copyright (C) 2004
19  * All Rights Reserved.
20  *
21  * Contributor(s):
22  *
23  * Alternatively, the contents of this file may be used under the terms of
24  * either the GNU General Public License Version 2 or later (the "GPL"), or
25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26  * in which case the provisions of the GPL or the LGPL are applicable instead
27  * of those above. If you wish to allow use of your version of this file only
28  * under the terms of either the GPL or the LGPL, and not to allow others to
29  * use your version of this file under the terms of the MPL, indicate your
30  * decision by deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL or the LGPL. If you do not delete
32  * the provisions above, a recipient may use your version of this file under
33  * the terms of any one of the MPL, the GPL or the LGPL.
34  *
35  * ***** END LICENSE BLOCK ***** */

36 package org.columba.ristretto.imap;
37
38 import java.io.ByteArrayInputStream JavaDoc;
39 import java.io.FilterInputStream JavaDoc;
40 import java.io.IOException JavaDoc;
41 import java.io.InputStream JavaDoc;
42 import java.util.LinkedList JavaDoc;
43 import java.util.regex.Matcher JavaDoc;
44 import java.util.regex.Pattern JavaDoc;
45
46 import org.columba.ristretto.concurrency.Mutex;
47 import org.columba.ristretto.imap.parser.IMAPResponseParser;
48 import org.columba.ristretto.imap.parser.MessageAttributeParser;
49 import org.columba.ristretto.io.AsyncInputStream;
50 import org.columba.ristretto.io.ConnectionDroppedException;
51 import org.columba.ristretto.io.TempSourceFactory;
52 import org.columba.ristretto.message.Attributes;
53 import org.columba.ristretto.parser.ParserException;
54
55 /**
56  *
57  * InputStream to read IMAPReponses from an IMAP server. It handles all IMAP
58  * specific features like literals in an transparent way.
59  *
60  * @author tstich <tstich@users.sourceforge.net>
61  */

62 public class IMAPInputStream extends FilterInputStream JavaDoc {
63
64     private static final Pattern JavaDoc literalPattern = Pattern
65             .compile("\\{(\\d+)\\}$");
66
67     private IMAPProtocol protocol;
68
69     private Matcher JavaDoc literalMatcher;
70
71     private Mutex mutex;
72
73     private StringBuffer JavaDoc lineBuffer;
74
75     private LinkedList JavaDoc observerList;
76
77     /**
78      * Constructs the IMAPInputStream.
79      *
80      * @param in
81      * the socket InputStream.
82      * @param protocol
83      * the associated protocol class.
84      */

85     public IMAPInputStream(InputStream JavaDoc in, IMAPProtocol protocol) {
86         super(in);
87         literalMatcher = literalPattern.matcher("");
88         lineBuffer = new StringBuffer JavaDoc();
89
90         observerList = new LinkedList JavaDoc();
91         mutex = new Mutex();
92
93         this.protocol = protocol;
94     }
95
96     /**
97      * Reads a body part from the asynchronously from the InputStream. This is
98      * done using the IMAPDownloadThread.
99      *
100      * @see IMAPDownloadThread
101      *
102      * @return the InputStream from the downloaded part
103      * @throws IOException
104      * @throws IMAPException
105      */

106     public InputStream JavaDoc readBodyNonBlocking() throws IOException JavaDoc, IMAPException {
107         mutex.lock();
108         boolean dontrelease = false;
109         try {
110             // do basic parsing to find out what type the answer is
111
IMAPResponse response;
112             try {
113                 readLineInBuffer();
114                 response = IMAPResponseParser.parse(lineBuffer);
115             } catch (ParserException e) {
116                 throw new IMAPException(e);
117             }
118
119             // Error Response
120
if (response.isTagged() && (response.isBAD() || response.isNO())) {
121                 mutex.release();
122                 throw new IMAPException(response);
123             }
124
125             // Some unsolicited Responses which must be processed
126
while (!response.isTagged()
127                     && !response.getResponseSubType().equals("FETCH")
128                     && response.getResponseMessage().indexOf("BODY") == -1) {
129                 protocol.handleResponse(response);
130
131                 try {
132                     readLineInBuffer();
133                     response = IMAPResponseParser.parse(lineBuffer);
134                 } catch (ParserException e) {
135                     mutex.release();
136                     throw new IMAPException(e);
137                 }
138             }
139
140             // Error Response
141
if (response.isTagged() && (response.isBAD() || response.isNO())) {
142                 mutex.release();
143                 throw new IMAPException(response);
144             }
145
146             // is there a literal in the answer
147
literalMatcher.reset(response.getResponseMessage());
148
149             // This is what we care about
150
if (literalMatcher.find()) {
151
152                 // read literal from inputstream
153
int size = Integer.parseInt(literalMatcher.group(1));
154
155                 AsyncInputStream result = IMAPDownloadThread.asyncDownload(
156                         this, size, mutex);
157                 dontrelease = true;
158                 return result;
159             } else {
160                 // first parse the message attributes
161
Attributes attributes = MessageAttributeParser.parse(response
162                         .getResponseMessage());
163                 String JavaDoc body = (String JavaDoc) attributes.get("BODY");
164                 if (body == null) {
165                     mutex.release();
166                     return new ByteArrayInputStream JavaDoc(new byte[0]);
167                 }
168
169                 if (body.length() > 0 && body.charAt(0) == '\"') {
170                     body = body.substring(1, body.length() - 1);
171                 }
172
173                 try {
174                     // Read OK Response
175
readLineInBuffer();
176                 } catch (IOException JavaDoc e1) {
177                     mutex.release();
178                     throw e1;
179                 }
180
181                 return new ByteArrayInputStream JavaDoc(body != null ? body
182                         .getBytes("US-ASCII") : new byte[0]);
183             }
184         } finally {
185             if (dontrelease == false) {
186                 mutex.release();
187             }
188         }
189     }
190
191     /**
192      * Reads a response from the InputStream.
193      *
194      * @return the read reponse
195      * @throws IOException
196      * @throws IMAPException
197      */

198     public IMAPResponse readResponse() throws IOException JavaDoc, IMAPException {
199         IMAPResponse response;
200         mutex.lock();
201         try {
202             // do basic parsing to find out what type the answer is
203

204             try {
205                 readLineInBuffer();
206                 response = IMAPResponseParser.parse(lineBuffer);
207             } catch (ParserException e) {
208                 throw new IMAPException(e);
209             }
210             // is there a literal in the answer
211
literalMatcher.reset(response.getResponseMessage());
212             int literalIndex = 0;
213             while (literalMatcher.find()) {
214                 // read literal from inputstream
215
int literalSize = Integer.parseInt(literalMatcher.group(1));
216
217                 // Cleanup literals to make the {N} to increase for every
218
// literal
219
response.setResponseMessage(response.getResponseMessage()
220                         .substring(0, literalMatcher.start())
221                         + '{' + (literalIndex++) + '}');
222
223                 // assign literal to response
224
response.addLiteral(TempSourceFactory.createTempSource(this,
225                         literalSize));
226
227                 // read rest in response and remove the trailing CRLF
228
readLineInBuffer();
229                 String JavaDoc restresponse = lineBuffer.toString();
230                 restresponse = restresponse.substring(0,
231                         restresponse.length() - 2);
232                 response.appendResponseText(restresponse);
233
234                 // Could there be another Literal?
235
if (restresponse.length() > 3) {
236                     literalMatcher.reset(response.getResponseMessage());
237                 }
238             }
239
240         } finally {
241             mutex.release();
242         }
243         return response;
244     }
245
246     private void readLineInBuffer() throws IOException JavaDoc {
247         // Clear the buffer
248
lineBuffer.delete(0, lineBuffer.length());
249
250         int read = in.read();
251         // read until CRLF
252
while (read != '\r' && read != -1) {
253             lineBuffer.append((char) read);
254             read = in.read();
255         }
256         lineBuffer.append((char) read);
257
258         // read the LF
259
read = in.read();
260         if (read != '\n')
261             throw new ConnectionDroppedException();
262         lineBuffer.append((char) read);
263     }
264
265     /**
266      * Checks if the is a unread response waiting to be read. This can e.g.
267      * occur if the IMAP server sends a BYE response before closing the socket
268      * because of a idle timeout.
269      *
270      * @return <code>true</code> there is response waiting to be read.
271      * @throws IOException
272      */

273     public boolean hasUnsolicitedReponse() throws IOException JavaDoc {
274         boolean result;
275
276         mutex.lock();
277         try {
278             result = in.available() > 0;
279         } finally {
280             mutex.release();
281         }
282         return result;
283     }
284 }
285
Popular Tags