KickJava   Java API By Example, From Geeks To Geeks.

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


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.IOException JavaDoc;
39 import java.io.InputStream JavaDoc;
40 import java.io.OutputStream JavaDoc;
41 import java.io.PrintStream JavaDoc;
42 import java.net.Socket JavaDoc;
43 import java.net.SocketException JavaDoc;
44 import java.nio.ByteBuffer JavaDoc;
45 import java.nio.charset.Charset JavaDoc;
46 import java.util.ArrayList JavaDoc;
47 import java.util.Arrays JavaDoc;
48 import java.util.Iterator JavaDoc;
49 import java.util.LinkedList JavaDoc;
50 import java.util.List JavaDoc;
51
52 import javax.net.ssl.SSLException;
53 import javax.net.ssl.SSLSocket;
54
55 import org.columba.ristretto.auth.AuthenticationException;
56 import org.columba.ristretto.auth.AuthenticationFactory;
57 import org.columba.ristretto.auth.AuthenticationMechanism;
58 import org.columba.ristretto.auth.AuthenticationServer;
59 import org.columba.ristretto.auth.NoSuchAuthenticationException;
60 import org.columba.ristretto.coder.Base64;
61 import org.columba.ristretto.config.RistrettoConfig;
62 import org.columba.ristretto.imap.parser.AppendInfoParser;
63 import org.columba.ristretto.imap.parser.CopyInfoParser;
64 import org.columba.ristretto.imap.parser.FlagsParser;
65 import org.columba.ristretto.imap.parser.IMAPHeaderParser;
66 import org.columba.ristretto.imap.parser.ListInfoParser;
67 import org.columba.ristretto.imap.parser.MailboxInfoParser;
68 import org.columba.ristretto.imap.parser.MailboxStatusParser;
69 import org.columba.ristretto.imap.parser.MimeTreeParser;
70 import org.columba.ristretto.imap.parser.NamespaceParser;
71 import org.columba.ristretto.imap.parser.NumberListParser;
72 import org.columba.ristretto.imap.parser.QuotaInfoParser;
73 import org.columba.ristretto.imap.parser.StringListParser;
74 import org.columba.ristretto.imap.parser.UIDParser;
75 import org.columba.ristretto.log.LogInputStream;
76 import org.columba.ristretto.log.LogOutputStream;
77 import org.columba.ristretto.log.RistrettoLogger;
78 import org.columba.ristretto.message.MailboxInfo;
79 import org.columba.ristretto.message.MimeTree;
80 import org.columba.ristretto.parser.ParserException;
81 import org.columba.ristretto.ssl.RistrettoSSLSocketFactory;
82
83 /**
84  * IMAP Protocol implementation.
85  *
86  * @author Timo Stich <tstich@users.sourceforge.net>
87  */

88 public class IMAPProtocol implements AuthenticationServer {
89     /**
90      * @deprecated Use NOT_CONNECTED instead
91      *
92      */

93     public static final int LOGOUT = 0;
94     
95     /**
96      * State of the Protocol.
97      *
98      */

99     public static final int NOT_CONNECTED = 0;
100     
101     /**
102      * State of the Protocol.
103      *
104      */

105     public static final int NON_AUTHENTICATED = 1;
106
107     /**
108      * State of the Protocol.
109      *
110      */

111     public static final int AUTHENTICATED = 2;
112
113     /**
114      * State of the Protocol.
115      *
116      */

117     public static final int SELECTED = 3;
118
119     /**
120      * The default IMAP port is 143.
121      */

122     public static final int DEFAULT_PORT = 143;
123
124     /**
125      * The default IMAPS port is 993.
126      */

127     public static final int DEFAULT_SSL_PORT = 993;
128
129     private TagFactory tagFactory;
130
131     protected String JavaDoc host;
132
133     protected int port;
134
135     private IMAPInputStream in;
136
137     private OutputStream JavaDoc out;
138
139     private Socket JavaDoc socket;
140
141     private int state;
142
143     private String JavaDoc selectedMailbox;
144
145     private Object JavaDoc lock;
146
147     private ArrayList JavaDoc listeners;
148
149     /**
150      * Constructs the IMAPProtocol2.
151      *
152      * @param host
153      * Address of the IMAP Server
154      * @param port
155      * Port to connect to
156      */

157     public IMAPProtocol(String JavaDoc host, int port) {
158         this.host = host;
159         this.port = port;
160
161         tagFactory = new TagFactory();
162
163         state = NOT_CONNECTED;
164         listeners = new ArrayList JavaDoc();
165     }
166
167     /**
168      * Opens a socket to the IMAP server.
169      *
170      * @throws IOException
171      * @throws IMAPException
172      */

173     public void openPort() throws IOException JavaDoc, IMAPException {
174         socket = new Socket JavaDoc(host, port);
175         socket.setSoTimeout(RistrettoConfig.getInstance().getTimeout());
176
177         createStreams();
178
179         IMAPResponse response;
180         try {
181             response = in.readResponse();
182         } catch (IOException JavaDoc e) {
183             state = NOT_CONNECTED;
184             throw e;
185         }
186         // Answer can be OK, PRE-AUTH or BYE
187

188         if (response.isBYE())
189             throw new IMAPException(response);
190
191         if (response.isOK())
192             state = NON_AUTHENTICATED;
193         else
194             // must be a PRE-AUTH response -> we are already authenticated
195
state = AUTHENTICATED;
196     }
197
198     /**
199      * Opens a SSL socket to the IMAP server.
200      *
201      *
202      * @throws IOException
203      * @throws SSLException
204      * @throws IMAPException
205      */

206     public void openSSLPort() throws IOException JavaDoc, SSLException, IMAPException {
207         socket = RistrettoSSLSocketFactory.getInstance().createSocket(host,
208                 port);
209         socket.setSoTimeout(RistrettoConfig.getInstance().getTimeout());
210
211         // handshake (which cyper algorithms are used?)
212
((SSLSocket) socket).startHandshake();
213
214         createStreams();
215
216         IMAPResponse response;
217         try {
218             response = in.readResponse();
219         } catch (IOException JavaDoc e) {
220             state = NOT_CONNECTED;
221             throw e;
222         }
223         // Answer can be OK, PRE-AUTH or BYE
224

225         if (response.isBYE())
226             throw new IMAPException(response);
227
228         if (response.isOK())
229             state = NON_AUTHENTICATED;
230         else
231             // must be a PRE-AUTH response -> we are already authenticated
232
state = AUTHENTICATED;
233     }
234
235     // Commands in ALL STATES
236

237     /**
238      * Gets the capabilities of the IMAP server.
239      *
240      * @return the capabilities of the server
241      * @throws IOException
242      * @throws IMAPException
243      *
244      */

245     public String JavaDoc[] capability() throws IOException JavaDoc, IMAPException {
246         checkState(NON_AUTHENTICATED);
247         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(),
248                 "CAPABILITY");
249
250         IMAPResponse[] responses = communicate(command);
251         ArrayList JavaDoc result = new ArrayList JavaDoc();
252         for (int i = 0; i < responses.length - 1; i++) {
253             if (responses[i].getResponseSubType().equals("CAPABILITY")) {
254                 result.addAll(Arrays.asList(StringListParser.parse(responses[0]
255                         .getResponseMessage())));
256             } else {
257                 handleResponse(responses[i]);
258             }
259         }
260
261         // Check last response for command success
262
if (!responses[responses.length - 1].isOK())
263             throwException(responses[responses.length - 1]);
264
265         return (String JavaDoc[]) result.toArray(new String JavaDoc[0]);
266     }
267
268     /**
269      * Sends the NOOP command to the server. This can be used to poll for new
270      * messages. To do this register as an #IMAPListener with this protocol.
271      *
272      * @throws IOException
273      * @throws IMAPException
274      */

275     public void noop() throws IOException JavaDoc, IMAPException {
276         checkState(NON_AUTHENTICATED);
277
278         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "NOOP");
279         IMAPResponse[] responses = communicate(command);
280         // handle any responses that do not directly
281
// relate to the command
282
for (int i = 0; i < responses.length - 1; i++) {
283             handleResponse(responses[i]);
284         }
285
286         // Check last response for command success
287
if (!responses[responses.length - 1].isOK())
288             throwException(responses[responses.length - 1]);
289     }
290
291     /**
292      * @param responses
293      * @throws IMAPException
294      */

295     private void throwException(IMAPResponse response) throws IMAPException {
296         if (response.isBYE()) {
297             handleResponse(response);
298         } else {
299             throw new IMAPException(response);
300         }
301     }
302
303     /**
304      * Close the socket to the server.
305      *
306      * @throws IOException
307      * @throws IMAPException
308      */

309     public void logout() throws IOException JavaDoc, IMAPException {
310         checkState(NON_AUTHENTICATED);
311         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "LOGOUT");
312
313         IMAPResponse[] responses = communicate(command);
314         // handle any responses that do not directly
315
// relate to the command
316
for (int i = 0; i < responses.length; i++) {
317             if (!responses[i].isBYE()) {
318                 handleResponse(responses[i]);
319             }
320         }
321
322         state = NOT_CONNECTED;
323         // Close the streams
324
in.close();
325         in = null;
326         out.close();
327         out = null;
328     }
329
330     // Commands in NON-AUTHENTICATES state
331
/**
332      * Sends the TLS command and switches to a SSL encrypted connection. <br>
333      * Protocol has to be in NON_AUTHENTICATED state.
334      *
335      * @see #getState()
336      *
337      * @throws IOException
338      * @throws SSLException
339      * @throws IMAPException
340      */

341     public void startTLS() throws IOException JavaDoc, SSLException, IMAPException {
342         checkState(NON_AUTHENTICATED);
343
344         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "STARTTLS");
345         IMAPResponse[] responses = communicate(command);
346         // handle any responses that do not directly
347
// relate to the command
348
for (int i = 0; i < responses.length - 1; i++) {
349             handleResponse(responses[i]);
350         }
351
352         // Check last response for command success
353
if (!responses[responses.length - 1].isOK())
354             throwException(responses[responses.length - 1]);
355
356         socket = RistrettoSSLSocketFactory.getInstance().createSocket(socket,
357                 host, port, true);
358
359         // handshake (which cyper algorithms are used?)
360
((SSLSocket) socket).startHandshake();
361
362         createStreams();
363     }
364
365     /**
366      * Authenticate with the specified SASL method. The supported
367      * methods from the server can be found out with the #capability()
368      * command.
369      * <br>Protocol has to be in NON_AUTHENTICATED state.
370      * @see #getState()
371      *
372      * @param method the SASL method
373      * @param user
374      * @param password
375      * @throws IOException
376      * @throws IMAPException
377      * @throws AuthenticationException
378      */

379     public void authenticate(String JavaDoc method, String JavaDoc user, char[] password)
380             throws IOException JavaDoc, IMAPException, AuthenticationException {
381         checkState(NON_AUTHENTICATED);
382         String JavaDoc tag = tagFactory.nextTag();
383
384         try {
385             AuthenticationMechanism auth = AuthenticationFactory.getInstance()
386                     .getAuthentication(method);
387             IMAPCommand command = new IMAPCommand(tag, "AUTHENTICATE",
388                     new Object JavaDoc[] { method });
389             
390         
391             try{
392                 command.writeToStream(in, out);
393             } catch (IOException JavaDoc e) {
394                 state = NOT_CONNECTED;
395                 throw e;
396             }
397
398             auth.authenticate(this, user, password);
399         } catch (NoSuchAuthenticationException e) {
400             throw new IMAPException(e);
401         }
402         
403         IMAPResponse response;
404         
405         try {
406             response = in.readResponse();
407         } catch (SocketException JavaDoc e) {
408             state = NOT_CONNECTED;
409             throw e;
410         }
411
412         // handle any responses that do not directly
413
// relate to the command
414
while (!response.isTagged()) {
415             handleResponse(response);
416             try {
417                 response = in.readResponse();
418             } catch (SocketException JavaDoc e) {
419                 state = NOT_CONNECTED;
420                 throw e;
421             }
422         }
423
424         if (!response.isOK())
425             throw new IMAPException(response);
426         state = AUTHENTICATED;
427     }
428
429     
430     /**
431      * Sends the LOGIN command to authenticate with
432      * the server.
433      * <br>Protocol has to be in NON_AUTHENTICATED state.
434      * @see #getState()
435      *
436      * @param user
437      * @param password
438      * @throws IOException
439      * @throws IMAPException
440      */

441     public void login(String JavaDoc user, char[] password) throws IOException JavaDoc,
442             IMAPException {
443         checkState(NON_AUTHENTICATED);
444         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "LOGIN",
445                 new Object JavaDoc[] { user, new String JavaDoc(password) });
446         
447         IMAPResponse[] responses = communicate(command);
448
449         // handle any responses that do not directly
450
// relate to the command
451
for (int i = 0; i < responses.length - 1; i++) {
452             handleResponse(responses[i]);
453         }
454
455         // Check last response for command success
456
if (!responses[responses.length - 1].isOK())
457             throwException(responses[responses.length - 1]);
458         state = AUTHENTICATED;
459     }
460
461     // Commands in AUTHENTICATED state
462

463     /**
464      * Select a mailbox. To get a list of all subscribed
465      * mailboxes use the #lsub(String, String) command.
466      *
467      * <br>Protocol has to be in AUTHENTICATED state.
468      * @see #getState()
469      *
470      * @param mailbox the mailbox to select
471      * @return the MailboxInfo of the selected mailbox
472      * @throws IOException
473      * @throws IMAPException
474      */

475     public MailboxInfo select(String JavaDoc mailbox) throws IOException JavaDoc, IMAPException {
476         return selectCore(mailbox, "SELECT");
477     }
478
479     private MailboxInfo selectCore(String JavaDoc mailbox, String JavaDoc s)
480             throws IMAPException, IOException JavaDoc {
481         checkState(AUTHENTICATED);
482         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), s,
483                 new Object JavaDoc[] { MailboxNameUTF7Converter.encode(mailbox) });
484
485         IMAPResponse[] responses = communicate(command);
486
487         MailboxInfo result = new MailboxInfo();
488
489         for (int i = 0; i < responses.length - 1; i++) {
490             if (responses[i].getResponseType() == IMAPResponse.RESPONSE_MAILBOX_DATA
491                     || responses[i].isOK()) {
492                 try {
493                     result = MailboxInfoParser.parse(responses[i], result);
494                 } catch (ParserException e1) {
495                     throw new IMAPException(e1);
496                 }
497             } else {
498                 handleResponse(responses[i]);
499             }
500         }
501
502         // Check last response for command success
503
if (!responses[responses.length - 1].isOK()) {
504             state = AUTHENTICATED;
505             throwException(responses[responses.length - 1]);
506         }
507
508         // Parse the last response to find
509
try {
510             result = MailboxInfoParser.parse(responses[responses.length - 1],
511                     result);
512         } catch (ParserException e1) {
513             throw new IMAPException(e1);
514         }
515
516         state = SELECTED;
517         selectedMailbox = mailbox;
518
519         return result;
520     }
521
522     /**
523      * Sends the EXAMINE command to get the mailbox info.
524      * <br>Protocol has to be in AUTHENTICATED state.
525      * @see #getState()
526      *
527      * @param mailbox
528      * @return the MailboxInfo of the examined mailbox.
529      * @throws IOException
530      * @throws IMAPException
531      */

532     public MailboxInfo examine(String JavaDoc mailbox) throws IOException JavaDoc,
533             IMAPException {
534         return selectCore(mailbox, "EXAMINE");
535     }
536
537     /**
538      * Creates a new mailbox. Be sure to also #subscribe(String)
539      * to the mailbox before selecting it.
540      * <br>Protocol has to be in AUTHENTICATED state.
541      * @see #getState()
542      *
543      * @param mailbox the mailbox to create
544      * @throws IOException
545      * @throws IMAPException
546      */

547     public void create(String JavaDoc mailbox) throws IOException JavaDoc, IMAPException {
548         checkState(AUTHENTICATED);
549         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "CREATE",
550                 new Object JavaDoc[] { MailboxNameUTF7Converter.encode(mailbox) });
551
552         IMAPResponse[] responses = communicate(command);
553         // handle any responses that do not directly
554
// relate to the command
555
for (int i = 0; i < responses.length - 1; i++) {
556             handleResponse(responses[i]);
557         }
558
559         // Check last response for command success
560
if (!responses[responses.length - 1].isOK())
561             throwException(responses[responses.length - 1]);
562     }
563
564     /**
565      * Deletes the mailbox. Most likely you also want to
566      * #unsubscribe(String).
567      * <br>Protocol has to be in AUTHENTICATED state.
568      * @see #getState()
569      *
570      * @param mailbox the mailbox to delete
571      * @throws IOException
572      * @throws IMAPException
573      */

574     public void delete(String JavaDoc mailbox) throws IOException JavaDoc, IMAPException {
575         checkState(AUTHENTICATED);
576         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "DELETE",
577                 new Object JavaDoc[] { MailboxNameUTF7Converter.encode(mailbox) });
578
579         IMAPResponse[] responses = communicate(command);
580         // handle any responses that do not directly
581
// relate to the command
582
for (int i = 0; i < responses.length - 1; i++) {
583             handleResponse(responses[i]);
584         }
585
586         // Check last response for command success
587
if (!responses[responses.length - 1].isOK())
588             throwException(responses[responses.length - 1]);
589     }
590
591     
592     /**
593      * Gets the QuotaInfo of the mailbox. Works only if the
594      * server supports QUOTA capability.
595      * <br>Protocol has to be in AUTHENTICATED state.
596      * @see #getState()
597      *
598      * @param mailbox
599      * @return the QutaInfo of this mailbox.
600      * @throws IOException
601      * @throws IMAPException
602      */

603     public QuotaInfo getQuota(String JavaDoc mailbox) throws IOException JavaDoc, IMAPException {
604         checkState(AUTHENTICATED);
605         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(),
606                 "GETQUOTAROOT", new Object JavaDoc[] { MailboxNameUTF7Converter
607                         .encode(mailbox) });
608
609         IMAPResponse[] responses = communicate(command);
610         QuotaInfo result = null;
611         for (int i = 0; i < responses.length - 1; i++) {
612             if (responses[i].getResponseType() == IMAPResponse.RESPONSE_MAILBOX_DATA) {
613                 try {
614                     result = QuotaInfoParser.parse(responses[i], result);
615                 } catch (ParserException e) {
616                     throw new IMAPException(e);
617                 }
618             } else {
619                 handleResponse(responses[i]);
620             }
621         }
622
623         // Check last response for command success
624
if (!responses[responses.length - 1].isOK())
625             throwException(responses[responses.length - 1]);
626
627         return result;
628     }
629
630     /**
631      * Renames the mailbox. Most likely you also want
632      * to #subscribe(String) and #unsubscribe(String).
633      * <br>Protocol has to be in AUTHENTICATED state.
634      * @see #getState()
635      *
636      * @param oldname
637      * @param newname
638      * @throws IOException
639      * @throws IMAPException
640      */

641     public void rename(String JavaDoc oldname, String JavaDoc newname) throws IOException JavaDoc,
642             IMAPException {
643         checkState(AUTHENTICATED);
644         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "RENAME",
645                 new Object JavaDoc[] { MailboxNameUTF7Converter.encode(oldname),
646                         MailboxNameUTF7Converter.encode(newname) });
647
648         IMAPResponse[] responses = communicate(command);
649         // handle any responses that do not directly
650
// relate to the command
651
for (int i = 0; i < responses.length - 1; i++) {
652             handleResponse(responses[i]);
653         }
654
655         // Check last response for command success
656
if (!responses[responses.length - 1].isOK())
657             throwException(responses[responses.length - 1]);
658     }
659
660     /**
661      * Subscribes to a mailbox. Only subscribed mailboxes
662      * can be selected.
663      * <br>Protocol has to be in AUTHENTICATED state.
664      * @see #getState()
665      *
666      * @see #unsubscribe(String)
667      * @param mailbox
668      * @throws IOException
669      * @throws IMAPException
670      */

671     public void subscribe(String JavaDoc mailbox) throws IOException JavaDoc, IMAPException {
672         checkState(AUTHENTICATED);
673         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(),
674                 "SUBSCRIBE", new Object JavaDoc[] { MailboxNameUTF7Converter
675                         .encode(mailbox) });
676
677         IMAPResponse[] responses = communicate(command);
678         // handle any responses that do not directly
679
// relate to the command
680
for (int i = 0; i < responses.length - 1; i++) {
681             handleResponse(responses[i]);
682         }
683
684         // Check last response for command success
685
if (!responses[responses.length - 1].isOK())
686             throwException(responses[responses.length - 1]);
687     }
688
689     /**
690      * Unsubscribes a subscribed mailbox.
691      * <br>Protocol has to be in AUTHENTICATED state.
692      * @see #getState()
693      *
694      * @see #subscribe(String)
695      *
696      * @param mailbox
697      * @throws IOException
698      * @throws IMAPException
699      */

700     public void unsubscribe(String JavaDoc mailbox) throws IOException JavaDoc, IMAPException {
701         checkState(AUTHENTICATED);
702         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(),
703                 "UNSUBSCRIBE", new Object JavaDoc[] { MailboxNameUTF7Converter
704                         .encode(mailbox) });
705
706         IMAPResponse[] responses = communicate(command);
707         // handle any responses that do not directly
708
// relate to the command
709
for (int i = 0; i < responses.length - 1; i++) {
710             handleResponse(responses[i]);
711         }
712
713         // Check last response for command success
714
if (!responses[responses.length - 1].isOK())
715             throwException(responses[responses.length - 1]);
716     }
717
718     
719     /**
720      * Lists available mailboxes on the server.
721      * You have to subscribe to a mailbox before you can
722      * select it.
723      * <br>Protocol has to be in AUTHENTICATED state.
724      * @see #getState()
725      *
726      *
727      * @param reference
728      * @param mailbox
729      * @return the ListInfo of the listed mailboxes
730      * @throws IOException
731      * @throws IMAPException
732      */

733     public ListInfo[] list(String JavaDoc reference, String JavaDoc mailbox)
734             throws IOException JavaDoc, IMAPException {
735         return listCore(reference, mailbox, "LIST");
736     }
737
738     /**
739      * @param reference
740      * @param mailbox
741      * @return @throws
742      * IMAPException
743      * @throws IOException
744      */

745     private ListInfo[] listCore(String JavaDoc reference, String JavaDoc mailbox, String JavaDoc s)
746             throws IMAPException, IOException JavaDoc {
747         checkState(AUTHENTICATED);
748         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), s,
749                 new Object JavaDoc[] { MailboxNameUTF7Converter.encode(reference),
750                         MailboxNameUTF7Converter.encode(mailbox) });
751
752         IMAPResponse[] responses = communicate(command);
753         // Check last response for command success
754
if (!responses[responses.length - 1].isOK())
755             throwException(responses[responses.length - 1]);
756
757         List JavaDoc result = new ArrayList JavaDoc();
758
759         for (int i = 0; i < responses.length - 1; i++) {
760             if (responses[i].getResponseSubType().equals(s)) {
761                 try {
762                     result.add(ListInfoParser.parse(responses[i]));
763                 } catch (ParserException e) {
764                     throw new IMAPException(e);
765                 }
766             } else {
767                 handleResponse(responses[i]);
768             }
769         }
770
771         return (ListInfo[]) result.toArray(new ListInfo[0]);
772     }
773
774     /**
775      * Lists all subscribed mailboxes.
776      * <br>Protocol has to be in AUTHENTICATED state.
777      * @see #getState()
778      *
779      * @param reference
780      * @param mailbox
781      * @return the ListInfo of the subscribed mailboxes
782      * @throws IOException
783      * @throws IMAPException
784      */

785     public ListInfo[] lsub(String JavaDoc reference, String JavaDoc mailbox)
786             throws IOException JavaDoc, IMAPException {
787         return listCore(reference, mailbox, "LSUB");
788     }
789
790     /**
791      * Sends the STATUS command to get the status
792      * of a mailbox.
793      * <br>Protocol has to be in AUTHENTICATED state.
794      * @see #getState()
795      *
796      * @param mailbox
797      * @param statusItems
798      * @return the MailboxStatus of the mailbox
799      * @throws IOException
800      * @throws IMAPException
801      */

802     public MailboxStatus status(String JavaDoc mailbox, String JavaDoc[] statusItems)
803             throws IOException JavaDoc, IMAPException {
804         checkState(AUTHENTICATED);
805
806         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "STATUS",
807                 new Object JavaDoc[] { MailboxNameUTF7Converter.encode(mailbox),
808                         statusItems });
809
810         IMAPResponse[] responses = communicate(command);
811
812         MailboxStatus result = null;
813
814         // handle any responses that do not directly
815
// relate to the command
816
for (int i = 0; i < responses.length - 1; i++) {
817             if (responses[i].getResponseSubType().equals("STATUS")) {
818                 try {
819                     result = MailboxStatusParser.parse(responses[i]);
820                 } catch (ParserException e) {
821                     throw new IMAPException(responses[i]);
822                 }
823             } else {
824                 handleResponse(responses[i]);
825             }
826         }
827         // Check last response for command success
828
if (!responses[responses.length - 1].isOK())
829             throwException(responses[responses.length - 1]);
830
831         return result;
832     }
833
834     /**
835      * @param mailbox
836      * @param message
837      * @param optargs
838      * Can be IMAPFlags and IMAPDate. If you use the IMAPFlags be
839      * sure that only the System Flags are set. Otherwise a error
840      * from the Server will occur.
841      *
842      * If the server has the UIDPLUS (RFC2359) capability the new UID of the
843      * appended message is returned.
844      * <br>Protocol has to be in AUTENTICATED state.
845      * @see #getState()
846
847      * @return the AppendInfo if UIDPLUS is supported, <code>null</code> otherwise
848      *
849      * @throws IOException
850      * @throws IMAPException
851      */

852     public AppendInfo append(String JavaDoc mailbox, InputStream JavaDoc message,
853             Object JavaDoc[] optargs) throws IOException JavaDoc, IMAPException {
854         checkState(AUTHENTICATED);
855
856         List JavaDoc args = new LinkedList JavaDoc();
857         args.add(MailboxNameUTF7Converter.encode(mailbox));
858         if (optargs != null) {
859             for (int i = 0; i < optargs.length; i++) {
860                 args.add(optargs[i]);
861             }
862         }
863         args.add(message);
864
865         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "APPEND",
866                 args.toArray());
867         IMAPResponse[] responses = communicate(command);
868         // handle any responses that do not directly
869
// relate to the command
870
for (int i = 0; i < responses.length - 1; i++) {
871             handleResponse(responses[i]);
872         }
873         // Check last response for command success
874
if (!responses[responses.length - 1].isOK())
875             throwException(responses[responses.length - 1]);
876
877         AppendInfo result = null;
878         if (responses[responses.length - 1].getResponseTextCode() != null) {
879             try {
880                 result = AppendInfoParser
881                         .parse(responses[responses.length - 1]);
882             } catch (ParserException e1) {
883                 throw new IMAPException(e1);
884             }
885         }
886
887         return result;
888     }
889
890     /**
891      * @deprecated Use #append(String, InputStream, Object[]) instead
892      *
893      * @param mailbox
894      * @param message
895      * @param optargs
896      *
897      * add APPENDUID response code returned when UIDPLUS capability is present.
898      * @return the AppendInfo
899      *
900      * @throws IOException
901      * @throws IMAPException
902      */

903     public AppendInfo appendPlus(String JavaDoc mailbox, InputStream JavaDoc message,
904             Object JavaDoc[] optargs) throws IOException JavaDoc, IMAPException {
905         return append(mailbox, message, optargs);
906     }
907
908     /**
909      * @param mailbox
910      * @param message
911      *
912      * If the server has the UIDPLUS (RFC2359) capability the new UID of the
913      * appended message is returned.
914      * <br>Protocol has to be in AUTHENTICATED state.
915      * @see #getState()
916      * @return the AppendInfo if UIDPLUS is supported, <code>null</code> otherwise
917      *
918      * @throws IOException
919      * @throws IMAPException
920      */

921     public AppendInfo append(String JavaDoc mailbox, InputStream JavaDoc message)
922             throws IOException JavaDoc, IMAPException {
923         return append(mailbox, message, null);
924     }
925
926     // Selected State
927

928     
929     
930     /**
931      * Sends the CHECK command to the server.
932      *
933      * <br>Protocol has to be in SELECTED state.
934      * @see #getState()
935      * @throws IOException
936      * @throws IMAPException
937      */

938     public void check() throws IOException JavaDoc, IMAPException {
939         checkState(SELECTED);
940         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "CHECK");
941         IMAPResponse[] responses = communicate(command);
942         // handle any responses that do not directly
943
// relate to the command
944
for (int i = 0; i < responses.length - 1; i++) {
945             handleResponse(responses[i]);
946         }
947         // Check last response for command success
948
if (!responses[responses.length - 1].isOK())
949             throwException(responses[responses.length - 1]);
950     }
951
952     /**
953      * Closes the selected mailbox.
954      *
955      * <br>Protocol has to be in SELECTED state.
956      * @see #getState()
957      *
958      * @throws IOException
959      * @throws IMAPException
960      */

961     public void close() throws IOException JavaDoc, IMAPException {
962         checkState(SELECTED);
963         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "CLOSE");
964         IMAPResponse[] responses = communicate(command);
965         // handle any responses that do not directly
966
// relate to the command
967
for (int i = 0; i < responses.length - 1; i++) {
968             handleResponse(responses[i]);
969         }
970         // Check last response for command success
971
if (!responses[responses.length - 1].isOK())
972             throwException(responses[responses.length - 1]);
973         state = AUTHENTICATED;
974     }
975
976     /**
977      * Expunges the selected mailbox.
978      * <br>Protocol has to be in SELECTED state.
979      * @see #getState()
980      *
981      * @return the indices of the expunged messages
982      * @throws IOException
983      * @throws IMAPException
984      */

985     public int[] expunge() throws IOException JavaDoc, IMAPException {
986         checkState(SELECTED);
987         IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "EXPUNGE");
988         IMAPResponse[] responses = communicate(command);
989         // Check last response for command success
990
if (!responses[responses.length - 1].isOK())
991             throwException(responses[responses.length - 1]);
992
993         List JavaDoc expunged = new ArrayList JavaDoc(responses.length - 1);
994         
995         for (int i = 0; i < responses.length - 1; i++) {
996             if (responses[i].getResponseSubType().equals("EXPUNGE")) {
997                 expunged.add( new Integer JavaDoc(responses[i].getPreNumber()) );
998             } else {
999                 handleResponse(responses[i]);
1000            }
1001        }
1002
1003        int[] expungedArray = new int[expunged.size()];
1004        for( int i=0; i<expunged.size(); i++) {
1005            expungedArray[i] = ((Integer JavaDoc)expunged.get(i)).intValue();
1006        }
1007        
1008        return expungedArray;
1009    }
1010
1011
1012    
1013    /**
1014     * Search the selected mailbox.
1015     * <br>Protocol has to be in SELECTED state.
1016     * @see #getState()
1017     *
1018     * @param charset the charset of the search parameters
1019     * @param search
1020     * @return the indices of the found messages
1021     * @throws IOException
1022     * @throws IMAPException
1023     */

1024    public Integer JavaDoc[] search(Charset JavaDoc charset, SearchKey[] search)
1025            throws IOException JavaDoc, IMAPException {
1026        return searchCore("SEARCH", charset, search);
1027    }
1028
1029    /**
1030     * Search the selected mailbox.
1031     * <br>Protocol has to be in SELECTED state.
1032     * @see #getState()
1033     *
1034     * @param charset the charset of the search parameters
1035     * @param search
1036     * @return the UIDs of the found messages
1037     * @throws IOException
1038     * @throws IMAPException
1039     */

1040    public Integer JavaDoc[] uidSearch(Charset JavaDoc charset, SearchKey[] search)
1041            throws IOException JavaDoc, IMAPException {
1042        return searchCore("UID SEARCH", charset, search);
1043    }
1044
1045    /**
1046     * @param charset
1047     * @param search
1048     * @return @throws
1049     * IMAPException
1050     * @throws IOException
1051     */

1052    private Integer JavaDoc[] searchCore(String JavaDoc c, Charset JavaDoc charset, SearchKey[] search)
1053            throws IMAPException, IOException JavaDoc {
1054        IMAPCommand command;
1055        checkState(SELECTED);
1056
1057        List JavaDoc args = new LinkedList JavaDoc();
1058        if (charset != null) {
1059            args.add("CHARSET");
1060            args.add(charset);
1061        }
1062        for (int i = 0; i < search.length; i++) {
1063            args.add(search[i]);
1064        }
1065
1066        if (charset == null) {
1067            command = new IMAPCommand(tagFactory.nextTag(), c, args.toArray());
1068        } else {
1069            command = new IMAPCommand(tagFactory.nextTag(), c, args.toArray(),
1070                    charset);
1071        }
1072
1073        Integer JavaDoc[] result = null;
1074        IMAPResponse[] responses = communicate(command);
1075        for (int i = 0; i < responses.length - 1; i++) {
1076            if (responses[i].getResponseSubType().equals("SEARCH")) {
1077                result = NumberListParser.parse(responses[i]);
1078            } else {
1079                handleResponse(responses[i]);
1080            }
1081        }
1082
1083        // Check last response for command success
1084
if (!responses[responses.length - 1].isOK())
1085            throwException(responses[responses.length - 1]);
1086
1087        return result;
1088    }
1089
1090    /**
1091     * Search the selected mailbox.
1092     * <br>Protocol has to be in SELECTED state.
1093     * @see #getState()
1094     *
1095     * @param search
1096     * @return the indices of the found messages
1097     * @throws IOException
1098     * @throws IMAPException
1099     */

1100    public Integer JavaDoc[] search(SearchKey[] search) throws IOException JavaDoc,
1101            IMAPException {
1102        return searchCore("SEARCH", null, search);
1103    }
1104
1105    /**
1106     * Search the selected mailbox.
1107     * <br>Protocol has to be in SELECTED state.
1108     * @see #getState()
1109     *
1110     * @param search
1111     * @return the UIDs of the found messages
1112     * @throws IOException
1113     * @throws IMAPException
1114     */

1115    public Integer JavaDoc[] uidSearch(SearchKey[] search) throws IOException JavaDoc,
1116            IMAPException {
1117        return searchCore("UID SEARCH", null, search);
1118    }
1119
1120    /**
1121     * Fetch the Flags of the specified messages.
1122     *
1123     * @param set the indices of the messages
1124     * @return the Flags of the messages
1125     * @throws IOException
1126     * @throws IMAPException
1127     */

1128    public IMAPFlags[] fetchFlags(SequenceSet set) throws IOException JavaDoc,
1129            IMAPException {
1130        checkState(SELECTED);
1131
1132        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "FETCH",
1133                new Object JavaDoc[] { set, new String JavaDoc[] { "FLAGS", "UID" } });
1134
1135        IMAPResponse[] responses = communicate(command);
1136
1137        ArrayList JavaDoc result = new ArrayList JavaDoc(responses.length - 1);
1138        for (int i = 0; i < responses.length - 1; i++) {
1139            if (responses[i].getResponseSubType().equals("FETCH")) {
1140                result.add(FlagsParser.parse(responses[i]));
1141            } else {
1142                handleResponse(responses[i]);
1143            }
1144        }
1145
1146        // Check last response for command success
1147
if (!responses[responses.length - 1].isOK())
1148            throwException(responses[responses.length - 1]);
1149
1150        return (IMAPFlags[]) result.toArray(new IMAPFlags[0]);
1151    }
1152
1153    /**
1154     * Fetch the Flags of the specified messages.
1155     *
1156     * @param set the UIDs of the messages
1157     * @return the Flags of the messages
1158     * @throws IOException
1159     * @throws IMAPException
1160     */

1161    public Integer JavaDoc[] fetchUid(SequenceSet set) throws IOException JavaDoc,
1162            IMAPException {
1163        checkState(SELECTED);
1164
1165        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "FETCH",
1166                new Object JavaDoc[] { set, "UID" });
1167
1168        IMAPResponse[] responses = communicate(command);
1169
1170        ArrayList JavaDoc result = new ArrayList JavaDoc(responses.length - 1);
1171
1172        try {
1173            for (int i = 0; i < responses.length - 1; i++) {
1174                if (responses[i].getResponseSubType().equals("FETCH")) {
1175                    result.add(UIDParser.parse(responses[i]));
1176                } else {
1177                    handleResponse(responses[i]);
1178                }
1179            }
1180        } catch (ParserException e) {
1181            throw new IMAPException(e);
1182        }
1183
1184        // Check last response for command success
1185
if (!responses[responses.length - 1].isOK())
1186            throwException(responses[responses.length - 1]);
1187
1188        return (Integer JavaDoc[]) result.toArray(new Integer JavaDoc[0]);
1189    }
1190
1191    private IMAPHeader[] fetchHeaderFieldsCore(String JavaDoc c, SequenceSet set,
1192            String JavaDoc[] fields) throws IOException JavaDoc, IMAPException {
1193        checkState(SELECTED);
1194
1195        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c,
1196                new Object JavaDoc[] {
1197                        set,
1198                        new Section("RFC822.SIZE BODY.PEEK", new Object JavaDoc[] {
1199                                "HEADER.FIELDS", fields }) });
1200
1201        IMAPResponse[] responses = communicate(command);
1202
1203        ArrayList JavaDoc result = new ArrayList JavaDoc(responses.length - 1);
1204
1205        try {
1206            for (int i = 0; i < responses.length - 1; i++) {
1207                if (responses[i].getResponseSubType().equals("FETCH")
1208                        && responses[i].getResponseMessage().indexOf("BODY") != -1) {
1209                    result.add(IMAPHeaderParser.parse(responses[i]));
1210                } else {
1211                    handleResponse(responses[i]);
1212                }
1213            }
1214        } catch (ParserException e) {
1215            throw new IMAPException(e);
1216        }
1217
1218        // Check last response for command success
1219
if (!responses[responses.length - 1].isOK())
1220            throwException(responses[responses.length - 1]);
1221
1222        return (IMAPHeader[]) result.toArray(new IMAPHeader[0]);
1223    }
1224
1225    /**
1226     * Fetch the headerfields (e.g. Subject, From, etc) of the specified messages.
1227     *
1228     * @param set the indices of the messages
1229     * @param fields the fields to retrieve
1230     * @return the Headers of the messages
1231     * @throws IOException
1232     * @throws IMAPException
1233     */

1234    public IMAPHeader[] fetchHeaderFields(SequenceSet set, String JavaDoc[] fields)
1235            throws IOException JavaDoc, IMAPException {
1236        return fetchHeaderFieldsCore("FETCH", set, fields);
1237    }
1238
1239    /**
1240     * Fetch the headerfields (e.g. Subject, From, etc) of the specified messages.
1241     *
1242     * @param set the UIDs of the messages
1243     * @param fields the fields to retrieve
1244     * @return the Headers of the messages
1245     * @throws IOException
1246     * @throws IMAPException
1247     */

1248    public IMAPHeader[] uidFetchHeaderFields(SequenceSet set, String JavaDoc[] fields)
1249            throws IOException JavaDoc, IMAPException {
1250        return fetchHeaderFieldsCore("UID FETCH", set, fields);
1251    }
1252
1253    private IMAPHeader[] fetchHeaderCore(String JavaDoc c, SequenceSet set)
1254            throws IOException JavaDoc, IMAPException {
1255        checkState(SELECTED);
1256
1257        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c,
1258                new Object JavaDoc[] {
1259                        set,
1260                        new Section("RFC822.SIZE BODY.PEEK",
1261                                new Object JavaDoc[] { "HEADER" }) });
1262
1263        IMAPResponse[] responses = communicate(command);
1264        ArrayList JavaDoc result = new ArrayList JavaDoc(responses.length - 1);
1265
1266        try {
1267            for (int i = 0; i < responses.length - 1; i++) {
1268                if (responses[i].getResponseSubType().equals("FETCH") && responses[i].getResponseMessage().indexOf("BODY") != -1) {
1269                    result.add(IMAPHeaderParser.parse(responses[i]));
1270                } else {
1271                    handleResponse(responses[i]);
1272                }
1273            }
1274        } catch (ParserException e) {
1275            throw new IMAPException(e);
1276        }
1277
1278        // Check last response for command success
1279
if (!responses[responses.length - 1].isOK())
1280            throwException(responses[responses.length - 1]);
1281
1282        return (IMAPHeader[]) result.toArray(new IMAPHeader[0]);
1283    }
1284
1285    /**
1286     * Fetch the complete headers of the specified messages.
1287     *
1288     * @param set the indices of the messages
1289     * @return the Headers of the messages
1290     * @throws IOException
1291     * @throws IMAPException
1292     */

1293    public IMAPHeader[] fetchHeader(SequenceSet set) throws IOException JavaDoc,
1294            IMAPException {
1295        return fetchHeaderCore("FETCH", set);
1296    }
1297
1298    /**
1299     * Fetch the complete headers of the specified messages.
1300     *
1301     * @param set the UIDs of the messages
1302     * @return the Headers of the messages
1303     * @throws IOException
1304     * @throws IMAPException
1305     */

1306    public IMAPHeader[] uidFetchHeader(SequenceSet set) throws IOException JavaDoc,
1307            IMAPException {
1308        return fetchHeaderCore("UID FETCH", set);
1309    }
1310
1311    private InputStream JavaDoc fetchBodyCore(String JavaDoc c, int id, Integer JavaDoc[] address)
1312            throws IOException JavaDoc, IMAPException {
1313        checkState(SELECTED);
1314        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c,
1315                new Object JavaDoc[] { Integer.toString(id),
1316                        new Section("BODY.PEEK", new Object JavaDoc[] { address }) });
1317
1318        try {
1319            command.writeToStream(in, out);
1320        } catch (IOException JavaDoc e) {
1321            state = NOT_CONNECTED;
1322            throw e;
1323        }
1324
1325        return in.readBodyNonBlocking();
1326    }
1327
1328    /**
1329     * Fetch the specified body of the message.
1330     *
1331     * @param index the index of the message
1332     * @param address the address of the body
1333     * @return the InputStream of the body
1334     * @throws IOException
1335     * @throws IMAPException
1336     */

1337    public InputStream JavaDoc fetchBody(int index, Integer JavaDoc[] address) throws IOException JavaDoc,
1338            IMAPException {
1339        return fetchBodyCore("FETCH", index, address);
1340    }
1341
1342    /**
1343     * Fetch the specified body of the message.
1344     *
1345     * @param uid the UID of the message
1346     * @param address the address of the body
1347     * @return the InputStream of the body
1348     * @throws IOException
1349     * @throws IMAPException
1350     */

1351    public InputStream JavaDoc uidFetchBody(int uid, Integer JavaDoc[] address)
1352            throws IOException JavaDoc, IMAPException {
1353        return fetchBodyCore("UID FETCH", uid, address);
1354    }
1355
1356    private MimeTree fetchBodystructureCore(String JavaDoc c, int id)
1357            throws IOException JavaDoc, IMAPException {
1358        checkState(SELECTED);
1359        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c,
1360                new Object JavaDoc[] { Integer.toString(id), "BODYSTRUCTURE" });
1361
1362        IMAPResponse[] responses = communicate(command);
1363
1364        MimeTree result = null;
1365        for (int i = 0; i < responses.length - 1; i++) {
1366            if (responses[i].getResponseSubType().equals("FETCH") && responses[i].getResponseMessage().indexOf("BODYSTRUCTURE") != -1) {
1367                try {
1368                    result = MimeTreeParser.parse(responses[i]);
1369                } catch (ParserException e) {
1370                    throw new IMAPException(e);
1371                }
1372            } else {
1373                handleResponse(responses[i]);
1374            }
1375
1376        }
1377        if (!responses[responses.length - 1].isOK())
1378            throwException(responses[responses.length - 1]);
1379
1380        return result;
1381    }
1382
1383    /**
1384     * Fetch the Bodystructure of the message.
1385     *
1386     * @param index the index of the message
1387     * @return the Bodystructure of the message
1388     * @throws IOException
1389     * @throws IMAPException
1390     */

1391    public MimeTree fetchBodystructure(int index) throws IOException JavaDoc,
1392            IMAPException {
1393        return fetchBodystructureCore("FETCH", index);
1394    }
1395
1396    /**
1397     * Fetch the Bodystructure of the message.
1398     *
1399     * @param uid the UID of the message
1400     * @return the Bodystructure of the message
1401     * @throws IOException
1402     * @throws IMAPException
1403     */

1404    public MimeTree uidFetchBodystructure(int uid) throws IOException JavaDoc,
1405            IMAPException {
1406        return fetchBodystructureCore("UID FETCH", uid);
1407    }
1408
1409    private InputStream JavaDoc fetchMessageCore(String JavaDoc c, int id) throws IOException JavaDoc,
1410            IMAPException {
1411        checkState(SELECTED);
1412        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c,
1413                new Object JavaDoc[] { Integer.toString(id),
1414                        "BODY.PEEK[]".toCharArray() });
1415
1416        try {
1417        command.writeToStream(in, out);
1418        } catch (IOException JavaDoc e) {
1419            state = NOT_CONNECTED;
1420            throw e;
1421        }
1422
1423        try {
1424            return in.readBodyNonBlocking();
1425        } catch (IOException JavaDoc e) {
1426            state = NOT_CONNECTED;
1427            throw e;
1428        }
1429    }
1430
1431    /**
1432     * Fetch the message source.
1433     *
1434     * @param index the index of the message
1435     * @return the InputStream of the message source
1436     * @throws IOException
1437     * @throws IMAPException
1438     */

1439    public InputStream JavaDoc fetchMessage(int index) throws IOException JavaDoc, IMAPException {
1440        return fetchMessageCore("FETCH", index);
1441    }
1442
1443    /**
1444     * Fetch the message source.
1445     *
1446     * @param uid the UID of the message
1447     * @return the InputStream of the message source
1448     * @throws IOException
1449     * @throws IMAPException
1450     */

1451    public InputStream JavaDoc uidFetchMessage(int uid) throws IOException JavaDoc,
1452            IMAPException {
1453        return fetchMessageCore("UID FETCH", uid);
1454    }
1455
1456    private InputStream JavaDoc fetchMimeHeaderSourceCore(String JavaDoc c, int id,
1457            Integer JavaDoc[] address) throws IOException JavaDoc, IMAPException {
1458        checkState(SELECTED);
1459        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c,
1460                new Object JavaDoc[] {
1461                        Integer.toString(id),
1462                        new Section("BODY.PEEK", new Object JavaDoc[] { address,
1463                                ".MIME" }) });
1464
1465        try {
1466            command.writeToStream(in, out);
1467        } catch (IOException JavaDoc e) {
1468            state = NOT_CONNECTED;
1469            throw e;
1470        }
1471
1472        return in.readBodyNonBlocking();
1473    }
1474
1475    /**
1476     * Fetch the source of the MIME Header of the specified MIME part.
1477     *
1478     * @param index the index of the message
1479     * @param address the address of the MIME part
1480     * @return the InputStream of the MIME Header source
1481     * @throws IOException
1482     * @throws IMAPException
1483     */

1484    public InputStream JavaDoc fetchMimeHeaderSource(int index, Integer JavaDoc[] address)
1485            throws IOException JavaDoc, IMAPException {
1486        return fetchMimeHeaderSourceCore("FETCH", index, address);
1487    }
1488
1489    /**
1490     * Fetch the source of the MIME Header of the specified MIME part.
1491     *
1492     * @param uid the UID of the message
1493     * @param address the address of the MIME part
1494     * @return the InputStream of the MIME Header source
1495     * @throws IOException
1496     * @throws IMAPException
1497     */

1498    public InputStream JavaDoc uidFetchMimeHeaderSource(int uid, Integer JavaDoc[] address)
1499            throws IOException JavaDoc, IMAPException {
1500        return fetchMimeHeaderSourceCore("UID FETCH", uid, address);
1501    }
1502
1503    private CopyInfo copyCore(String JavaDoc c, SequenceSet set, String JavaDoc mailbox)
1504            throws IOException JavaDoc, IMAPException {
1505        checkState(SELECTED);
1506
1507        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c,
1508                new Object JavaDoc[] { set, MailboxNameUTF7Converter.encode(mailbox) });
1509
1510        IMAPResponse[] responses = communicate(command);
1511        // handle any responses that do not directly
1512
// relate to the command
1513
for (int i = 0; i < responses.length - 1; i++) {
1514            handleResponse(responses[i]);
1515        }
1516
1517        // Check last response for command success
1518
if (!responses[responses.length - 1].isOK())
1519            throwException(responses[responses.length - 1]);
1520
1521        CopyInfo result = null;
1522        if (responses[responses.length - 1].getResponseTextCode() != null) {
1523            try {
1524                result = CopyInfoParser
1525                        .parse(responses[responses.length - 1]);
1526            } catch (ParserException e1) {
1527                throw new IMAPException(e1);
1528            }
1529        }
1530    
1531        return result;
1532    }
1533
1534    /**
1535     * Copy messages from the selected mailbox to a destination mailbox.
1536     * If the server has the UIDPLUS capability, the new uids can be found
1537     * in the CopyInfo.
1538     *
1539     * @param set the indices of the messages to copy
1540     * @param mailbox the destination mailbox
1541     * @return the CopyInfo if UIDPLUS is supported, <code>null</code> else
1542     * @throws IOException
1543     * @throws IMAPException
1544     */

1545    public CopyInfo copy(SequenceSet set, String JavaDoc mailbox) throws IOException JavaDoc,
1546            IMAPException {
1547        return copyCore("COPY", set, mailbox);
1548    }
1549
1550    /**
1551     * Copy messages from the selected mailbox to a destination mailbox.
1552     * If the server has the UIDPLUS capability, the new uids can be found
1553     * in the CopyInfo.
1554     *
1555     * @param set the UIDs of the messages to copy
1556     * @param mailbox the destination mailbox
1557     * @return the CopyInfo if UIDPLUS is supported, <code>null</code> else
1558     * @throws IOException
1559     * @throws IMAPException
1560     */

1561public CopyInfo uidCopy(SequenceSet set, String JavaDoc mailbox) throws IOException JavaDoc,
1562            IMAPException {
1563        return copyCore("UID COPY", set, mailbox);
1564    }
1565
1566    private IMAPFlags[] storeCore(String JavaDoc c, SequenceSet set, boolean type,
1567            IMAPFlags flags) throws IOException JavaDoc, IMAPException {
1568        checkState(SELECTED);
1569
1570        String JavaDoc flagType = type ? "+FLAGS.SILENT" : "-FLAGS.SILENT";
1571
1572        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c,
1573                new Object JavaDoc[] { set, flagType, flags });
1574
1575        IMAPResponse[] responses = communicate(command);
1576
1577        ArrayList JavaDoc result = new ArrayList JavaDoc(responses.length - 1);
1578        for (int i = 0; i < responses.length - 1; i++) {
1579            if (responses[i].getResponseSubType().equals("FLAGS")) {
1580                result.add(FlagsParser.parse(responses[i]));
1581            } else if (responses[i].getResponseTextCode() != null && responses[i].getResponseTextCode().getType() == ResponseTextCode.PERMANENTFLAGS){
1582                //additional info -> irrelevant
1583
} else {
1584                handleResponse(responses[i]);
1585            }
1586        }
1587
1588        // Check last response for command success
1589
if (!responses[responses.length - 1].isOK())
1590            throwException(responses[responses.length - 1]);
1591
1592        return (IMAPFlags[]) result.toArray(new IMAPFlags[0]);
1593    }
1594
1595    /**
1596     * Store the Flags with the specified messages.
1597     *
1598     * @param set the indices of the messages
1599     * @param type <code>true</code> for a silent store
1600     * @param flags the Flags to store
1601     * @throws IOException
1602     * @throws IMAPException
1603     */

1604    public void store(SequenceSet set, boolean type, IMAPFlags flags)
1605            throws IOException JavaDoc, IMAPException {
1606        storeCore("STORE", set, type, flags);
1607    }
1608
1609    /**
1610     * Store the Flags with the specified messages.
1611     *
1612     * @param set the UIDs of the messages
1613     * @param type <code>true</code> for a silent store
1614     * @param flags the Flags to store
1615     * @throws IOException
1616     * @throws IMAPException
1617     */

1618    public void uidStore(SequenceSet set, boolean type, IMAPFlags flags)
1619            throws IOException JavaDoc, IMAPException {
1620        storeCore("UID STORE", set, type, flags);
1621    }
1622
1623    /**
1624     * Gets the defined namespaces.
1625     *
1626     * @return the NamespaceCollection
1627     * @throws IOException
1628     * @throws IMAPException
1629     */

1630    public NamespaceCollection namespace() throws IOException JavaDoc, IMAPException {
1631        checkState(AUTHENTICATED);
1632        IMAPCommand command = new IMAPCommand(tagFactory.nextTag(),
1633                "NAMESPACE", new Object JavaDoc[] {});
1634
1635        IMAPResponse[] responses = communicate(command);
1636        // Check last response for command success
1637
if (!responses[responses.length - 1].isOK())
1638            throwException(responses[responses.length - 1]);
1639
1640        NamespaceCollection result = null;
1641
1642        for (int i = 0; i < responses.length - 1; i++) {
1643            if (responses[i].getResponseSubType().equals("NAMESPACE")) {
1644                try {
1645                    responses[i].getResponseMessage();
1646
1647                    result = NamespaceParser.parse(responses[i]);
1648                } catch (ParserException e) {
1649                    throw new IMAPException(e);
1650                }
1651            } else {
1652                handleResponse(responses[i]);
1653            }
1654        }
1655
1656        return result;
1657
1658    }
1659
1660    private synchronized IMAPResponse[] communicate(IMAPCommand command)
1661            throws IOException JavaDoc, IMAPException {
1662
1663        // Check for the length of the command if enabled
1664
if (RistrettoConfig.getInstance().isCheckCommandLineLength()
1665                && command.estimateLength() > 1000) {
1666            throw new CommmandTooLongException(command);
1667        }
1668
1669        try {
1670            command.writeToStream(in, out);
1671        } catch (IOException JavaDoc e1) {
1672            state = NOT_CONNECTED;
1673            throw e1;
1674        }
1675
1676        List JavaDoc responses = new LinkedList JavaDoc();
1677        IMAPResponse response;
1678
1679        try {
1680            response = in.readResponse();
1681            while (!response.isTagged() && !response.isBYE()) {
1682                responses.add(response);
1683                response = in.readResponse();
1684            }
1685        } catch (IOException JavaDoc e) {
1686            state = NOT_CONNECTED;
1687            throw e;
1688        }
1689
1690        if (response.isTagged()) {
1691            if (!response.getTag().equals(command.getTag()))
1692                throw new IMAPException("Tag mismatch" + response.getSource()
1693                        + ". Expected " + command.getTag() + " but is "
1694                        + response.getTag());
1695        }
1696
1697        responses.add(response);
1698
1699        return (IMAPResponse[]) responses.toArray(new IMAPResponse[] {});
1700    }
1701
1702    private void createStreams() throws IOException JavaDoc {
1703        if (RistrettoLogger.logStream != null) {
1704            in = new IMAPInputStream(new LogInputStream(
1705                    socket.getInputStream(), RistrettoLogger.logStream), this);
1706
1707            out = new LogOutputStream(socket.getOutputStream(),
1708                    RistrettoLogger.logStream);
1709        } else {
1710            in = new IMAPInputStream(socket.getInputStream(), this);
1711
1712            out = new PrintStream JavaDoc(socket.getOutputStream(), true);
1713        }
1714    }
1715
1716    private void checkState(int state) throws IMAPException {
1717        if (getState() < state) {
1718            if (getState() == NOT_CONNECTED)
1719                throw new IMAPDisconnectedException();
1720            else
1721                throw new IMAPException("Wrong state for command");
1722        }
1723
1724    }
1725
1726    /**
1727     * @return Returns the state.
1728     */

1729    public int getState() {
1730        processUnsolicitedResponses();
1731
1732        return state;
1733    }
1734
1735    private void processUnsolicitedResponses() {
1736        if (state > NOT_CONNECTED) {
1737            try {
1738                while (in.hasUnsolicitedReponse()) {
1739                    IMAPResponse response;
1740                    try {
1741                        response = in.readResponse();
1742                        handleResponse(response);
1743                    } catch (IOException JavaDoc e) {
1744                        state = NOT_CONNECTED;
1745                    }
1746                }
1747            } catch (IOException JavaDoc e) {
1748
1749            } catch (IMAPException e) {
1750            }
1751        }
1752    }
1753
1754    /**
1755     * Adds an IMAPListener to the protocol.
1756     *
1757     * @param listener
1758     */

1759    public void addIMAPListener(IMAPListener listener) {
1760        listeners.add(listener);
1761    }
1762
1763    /**
1764     * Remove the listener from the protocol.
1765     *
1766     * @param listener
1767     */

1768    public void removeIMAPListener(IMAPListener listener) {
1769        listeners.remove(listener);
1770    }
1771
1772    void handleResponse(IMAPResponse response) throws IMAPException {
1773        Iterator JavaDoc it;
1774        if (response.isBYE()) {
1775            state = NOT_CONNECTED;
1776            // Close the streams
1777
try {
1778                in.close();
1779                in = null;
1780                out.close();
1781                out = null;
1782            } catch (IOException JavaDoc e) {
1783                //should never happen
1784
}
1785            throw new IMAPDisconnectedException(response);
1786        }
1787
1788        if (response.getResponseSubType().equals("EXISTS")) {
1789            // Exists
1790
it = listeners.iterator();
1791            while (it.hasNext()) {
1792                ((IMAPListener) it.next()).existsChanged(getSelectedMailbox(),
1793                        response.getPreNumber());
1794            }
1795        }
1796
1797        if (response.getResponseSubType().equals("FLAGS")) {
1798            IMAPFlags changedFlags = FlagsParser.parse(response);
1799            if (changedFlags.getIndex() != -1) {
1800                // Maybe Flags update
1801
it = listeners.iterator();
1802                while (it.hasNext()) {
1803                    ((IMAPListener) it.next()).flagsChanged(
1804                            getSelectedMailbox(), changedFlags);
1805                }
1806            }
1807        }
1808
1809        if (response.getResponseSubType().equals("FETCH")
1810                && response.getResponseMessage().indexOf("FLAGS") != -1) {
1811            // Flags update
1812
it = listeners.iterator();
1813            while (it.hasNext()) {
1814                ((IMAPListener) it.next()).flagsChanged(getSelectedMailbox(),
1815                        FlagsParser.parse(response));
1816            }
1817        }
1818
1819        if (response.getResponseSubType().equals("RECENT")) {
1820            // Recent changed
1821
it = listeners.iterator();
1822            while (it.hasNext()) {
1823                ((IMAPListener) it.next()).recentChanged(getSelectedMailbox(),
1824                        response.getPreNumber());
1825            }
1826        }
1827
1828        if ((response.isNO() || response.isBAD() || response.isOK())
1829                && response.getResponseTextCode() != null) {
1830            if (response.getResponseTextCode().equals("ALERT")) {
1831                // Alert message
1832
it = listeners.iterator();
1833                while (it.hasNext()) {
1834                    ((IMAPListener) it.next()).alertMessage(response
1835                            .getResponseMessage());
1836                }
1837            }
1838
1839            if (response.getResponseTextCode().equals("PARSE")) {
1840                // Header-Parser error
1841
it = listeners.iterator();
1842                while (it.hasNext()) {
1843                    ((IMAPListener) it.next()).parseError(response
1844                            .getResponseMessage());
1845                }
1846            }
1847        }
1848    }
1849
1850    /**
1851     * @see org.columba.ristretto.auth.AuthenticationServer#authReceive()
1852     */

1853    public byte[] authReceive() throws AuthenticationException, IOException JavaDoc {
1854        try {
1855            IMAPResponse response;
1856            try {
1857                response = in.readResponse();
1858            } catch (IOException JavaDoc e) {
1859                state = NOT_CONNECTED;
1860                throw e;
1861            }
1862            
1863            if (response.getResponseType() == IMAPResponse.RESPONSE_CONTINUATION) {
1864                return Base64.decodeToArray(response.getResponseMessage());
1865            } else {
1866                throw new AuthenticationException(new IMAPException(response));
1867            }
1868        } catch (IMAPException e) {
1869            throw new AuthenticationException(e);
1870        }
1871    }
1872
1873    /**
1874     * @see org.columba.ristretto.auth.AuthenticationServer#authSend(byte[])
1875     */

1876    public void authSend(byte[] call) throws IOException JavaDoc {
1877        out.write(Base64.encode(ByteBuffer.wrap(call), false).toString()
1878                .getBytes());
1879        out.write('\r');
1880        out.write('\n');
1881    }
1882
1883    /**
1884     * @see org.columba.ristretto.auth.AuthenticationServer#getHostName()
1885     */

1886    public String JavaDoc getHostName() {
1887        return host;
1888    }
1889
1890    /**
1891     * @see org.columba.ristretto.auth.AuthenticationServer#getService()
1892     */

1893    public String JavaDoc getService() {
1894        return "imap";
1895    }
1896
1897    /**
1898     * @return Returns the selectedMailbox.
1899     */

1900    public String JavaDoc getSelectedMailbox() {
1901        return selectedMailbox;
1902    }
1903}
Popular Tags