1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.geronimo.javamail.store.imap.connection;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.io.PrintStream;
24 import java.io.UnsupportedEncodingException;
25 import java.io.Writer;
26 import java.net.InetAddress;
27 import java.net.Socket;
28 import java.net.SocketException;
29 import java.net.UnknownHostException;
30 import java.util.ArrayList;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.StringTokenizer;
37
38 import javax.mail.Address;
39 import javax.mail.AuthenticationFailedException;
40 import javax.mail.FetchProfile;
41 import javax.mail.Flags;
42 import javax.mail.Folder;
43 import javax.mail.Message;
44 import javax.mail.MessagingException;
45 import javax.mail.MethodNotSupportedException;
46 import javax.mail.Quota;
47 import javax.mail.Session;
48 import javax.mail.UIDFolder;
49 import javax.mail.URLName;
50
51 import javax.mail.internet.InternetHeaders;
52
53 import javax.mail.search.SearchTerm;
54
55 import org.apache.geronimo.javamail.authentication.AuthenticatorFactory;
56 import org.apache.geronimo.javamail.authentication.ClientAuthenticator;
57 import org.apache.geronimo.javamail.authentication.LoginAuthenticator;
58 import org.apache.geronimo.javamail.authentication.PlainAuthenticator;
59 import org.apache.geronimo.javamail.store.imap.ACL;
60 import org.apache.geronimo.javamail.store.imap.Rights;
61
62 import org.apache.geronimo.javamail.util.CommandFailedException;
63 import org.apache.geronimo.javamail.util.InvalidCommandException;
64 import org.apache.geronimo.javamail.util.MailConnection;
65 import org.apache.geronimo.javamail.util.ProtocolProperties;
66 import org.apache.geronimo.javamail.util.TraceInputStream;
67 import org.apache.geronimo.javamail.util.TraceOutputStream;
68 import org.apache.geronimo.mail.util.Base64;
69
70
71
72
73
74
75
76
77
78
79
80
81 public class IMAPConnection extends MailConnection {
82
83 protected static final String CAPABILITY_LOGIN_DISABLED = "LOGINDISABLED";
84
85
86
87 protected IMAPConnectionPool pool;
88
89
90 protected IMAPResponseStream reader;
91
92
93 protected long lastAccess = 0;
94
95 protected LinkedList responseHandlers = new LinkedList();
96
97 protected List queuedResponses = new LinkedList();
98
99
100 protected boolean closed = false;
101
102
103
104
105
106
107
108
109 public IMAPConnection(ProtocolProperties props, IMAPConnectionPool pool) {
110 super(props);
111 this.pool = pool;
112 }
113
114
115
116
117
118
119
120 public boolean protocolConnect(String host, int port, String authid, String realm, String username, String password) throws MessagingException {
121 this.serverHost = host;
122 this.serverPort = port;
123 this.realm = realm;
124 this.authid = authid;
125 this.username = username;
126 this.password = password;
127
128 boolean preAuthorized = false;
129
130 try {
131
132 getConnection();
133
134
135
136 getCapability();
137
138 if (extractResponse("PREAUTH") != null) {
139 preAuthorized = true;
140 }
141
142
143 processPendingResponses();
144
145
146
147 if (!sslConnection && props.getBooleanProperty(MAIL_STARTTLS_ENABLE, false) && hasCapability(CAPABILITY_STARTTLS)) {
148
149
150
151
152 sendSimpleCommand("STARTTLS");
153
154
155 getConnectedTLSSocket();
156
157
158 reader = new IMAPResponseStream(inputStream);
159
160
161
162
163 getCapability();
164
165 if (extractResponse("PREAUTH") != null) {
166 preAuthorized = true;
167 }
168 }
169
170
171 if (preAuthorized) {
172 return true;
173 }
174
175
176 return login();
177 } catch (IOException e) {
178 if (debug) {
179 debugOut("I/O exception establishing connection", e);
180 }
181 throw new MessagingException("Connection error", e);
182 }
183 finally {
184
185 processPendingResponses();
186 }
187 }
188
189
190
191
192 protected void updateLastAccess() {
193 lastAccess = System.currentTimeMillis();
194 }
195
196
197
198
199
200
201
202
203
204
205 public boolean isStale(long timeout) {
206 return (System.currentTimeMillis() - lastAccess) > timeout;
207 }
208
209
210
211
212
213
214
215
216 public void close() throws MessagingException {
217
218 if (socket == null) {
219 return;
220 }
221 try {
222
223 logout();
224 } finally {
225
226
227 closeServerConnection();
228
229 reader = null;
230 }
231 }
232
233
234
235
236
237
238
239
240 protected void getConnection() throws IOException, MessagingException
241 {
242
243
244 super.getConnection();
245
246 reader = new IMAPResponseStream(inputStream);
247
248
249 updateLastAccess();
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 public void sendSimpleCommand(String data) throws MessagingException {
266
267 IMAPCommand command = new IMAPCommand(data);
268 sendSimpleCommand(command);
269 }
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284 public void sendSimpleCommand(IMAPCommand data) throws MessagingException {
285
286
287 sendCommand(data);
288 }
289
290
291
292
293
294
295
296
297
298
299 public IMAPTaggedResponse sendCommand(String data) throws MessagingException {
300 IMAPCommand command = new IMAPCommand(data);
301 return sendCommand(command);
302 }
303
304
305
306
307
308
309
310
311
312
313
314 public synchronized IMAPTaggedResponse sendCommand(IMAPCommand data) throws MessagingException {
315
316 checkConnected();
317 try {
318
319 data.writeTo(outputStream, this);
320 outputStream.flush();
321
322 updateLastAccess();
323
324 return receiveResponse();
325 } catch (IOException e) {
326 throw new MessagingException(e.toString(), e);
327 }
328 }
329
330
331
332
333
334
335
336
337
338
339
340 public IMAPTaggedResponse sendLine(String data) throws MessagingException {
341 return sendLine(data.getBytes());
342 }
343
344
345
346
347
348
349
350
351
352
353
354 public IMAPTaggedResponse sendLine(byte[] data) throws MessagingException {
355 return sendLine(data, 0, data.length);
356 }
357
358
359
360
361
362
363
364
365
366
367
368
369
370 public synchronized IMAPTaggedResponse sendLine(byte[] data, int offset, int length) throws MessagingException {
371
372 checkConnected();
373
374 try {
375 outputStream.write(data, offset, length);
376 outputStream.write(CR);
377 outputStream.write(LF);
378 outputStream.flush();
379
380 updateLastAccess();
381 return receiveResponse();
382 } catch (IOException e) {
383 throw new MessagingException(e.toString(), e);
384 }
385 }
386
387
388
389
390
391
392
393 public IMAPTaggedResponse receiveResponse() throws MessagingException {
394 while (true) {
395
396 IMAPResponse response = reader.readResponse();
397
398
399 if (response instanceof IMAPTaggedResponse) {
400
401 updateLastAccess();
402 IMAPTaggedResponse tagged = (IMAPTaggedResponse)response;
403
404
405 if (tagged.isBAD()) {
406 throw new InvalidCommandException("Unexpected command IMAP command error");
407 }
408 else if (tagged.isNO()) {
409 throw new CommandFailedException("Unexpected error executing IMAP command");
410 }
411 return tagged;
412 }
413 else {
414
415
416
417
418 queuePendingResponse((IMAPUntaggedResponse)response);
419 }
420 }
421 }
422
423
424
425
426
427 public void getCapability() throws MessagingException {
428 sendCommand("CAPABILITY");
429
430 IMAPCapabilityResponse response = (IMAPCapabilityResponse)extractResponse("CAPABILITY");
431 capabilities = response.getCapabilities();
432 authentications = response.getAuthentications();
433 }
434
435
436
437
438 public void logout() throws MessagingException {
439
440
441 sendCommand("LOGOUT");
442 }
443
444
445
446
447
448
449 public void closeMailbox() throws MessagingException {
450
451
452 sendCommand("CLOSE");
453 }
454
455
456
457
458
459
460
461
462 protected boolean login() throws MessagingException
463 {
464
465
466
467 if (username == null || password == null) {
468 return false;
469 }
470
471
472 if (props.getBooleanProperty(MAIL_SASL_ENABLE, false)) {
473
474
475
476 if (processSaslAuthentication()) {
477 return true;
478 }
479 }
480
481
482 if (!props.getBooleanProperty(MAIL_PLAIN_DISABLE, false) && supportsMechanism(AUTHENTICATION_PLAIN)) {
483 return processPlainAuthentication();
484 }
485
486
487 if (!props.getBooleanProperty(MAIL_LOGIN_DISABLE, false) && supportsMechanism(AUTHENTICATION_LOGIN)) {
488
489 return processLoginAuthentication();
490 }
491
492
493
494 if (!hasCapability(CAPABILITY_LOGIN_DISABLED)) {
495 return processLogin();
496 }
497
498 throw new MessagingException("No supported LOGIN methods enabled");
499 }
500
501
502
503
504
505
506
507
508
509 protected boolean processSaslAuthentication() throws MessagingException {
510
511 ClientAuthenticator authenticator = getSaslAuthenticator();
512 if (authenticator == null) {
513 return false;
514 }
515
516
517 return processLogin(authenticator);
518 }
519
520 protected ClientAuthenticator getSaslAuthenticator() {
521 return AuthenticatorFactory.getAuthenticator(props, selectSaslMechanisms(), serverHost, username, password, authid, realm);
522 }
523
524
525
526
527
528
529
530 protected boolean processPlainAuthentication() throws MessagingException {
531
532 return processLogin(new PlainAuthenticator(username, password));
533 }
534
535
536
537
538
539
540
541
542 protected boolean processLoginAuthentication() throws MessagingException {
543
544 return processLogin(new LoginAuthenticator(username, password));
545 }
546
547
548
549
550
551
552
553
554 protected boolean processLogin() throws MessagingException {
555
556 IMAPCommand command = new IMAPCommand("LOGIN");
557 command.appendAtom(username);
558 command.appendAtom(password);
559
560
561 try {
562 sendCommand(command);
563 } catch (CommandFailedException e) {
564
565 return false;
566 }
567
568 return true;
569 }
570
571
572
573
574
575
576
577
578
579
580
581
582 protected synchronized boolean processLogin(ClientAuthenticator authenticator) throws MessagingException {
583 if (debug) {
584 debugOut("Authenticating for user: " + username + " using " + authenticator.getMechanismName());
585 }
586
587 IMAPCommand command = new IMAPCommand("AUTHENTICATE");
588
589 command.appendAtom(authenticator.getMechanismName());
590
591
592 try {
593 IMAPTaggedResponse response = sendCommand(command);
594
595
596
597 while (true) {
598
599 if (response.isContinuation()) {
600
601 byte[] challenge = response.decodeChallengeResponse();
602
603
604 response = sendLine(Base64.encode(authenticator.evaluateChallenge(challenge)));
605 }
606 else {
607
608
609 return true;
610 }
611 }
612 } catch (CommandFailedException e ) {
613
614
615
616 return false;
617 }
618 }
619
620
621
622
623
624
625
626 public String getHost() {
627 return serverHost;
628 }
629
630
631
632
633
634
635
636 public synchronized void addResponseHandler(IMAPUntaggedResponseHandler h) {
637 responseHandlers.add(h);
638 }
639
640
641
642
643
644
645
646 public synchronized void removeResponseHandler(IMAPUntaggedResponseHandler h) {
647 responseHandlers.remove(h);
648 }
649
650
651
652
653
654
655
656 public synchronized void queuePendingResponse(IMAPUntaggedResponse response) {
657 queuedResponses.add(response);
658 }
659
660
661
662
663
664
665 public void processPendingResponses() throws MessagingException {
666 List pendingResponses = null;
667 List handlerList = null;
668
669 synchronized(this) {
670 if (queuedResponses.isEmpty()) {
671 return;
672 }
673 pendingResponses = queuedResponses;
674 queuedResponses = new LinkedList();
675
676
677 handlerList = (List)responseHandlers.clone();
678 }
679
680 for (int i = 0; i < pendingResponses.size(); i++) {
681 IMAPUntaggedResponse response = (IMAPUntaggedResponse)pendingResponses.get(i);
682 for (int j = 0; j < handlerList.size(); j++) {
683
684
685
686 IMAPUntaggedResponseHandler h = (IMAPUntaggedResponseHandler)handlerList.get(j);
687 if (h.handleResponse(response)) {
688 break;
689 }
690 }
691 }
692 }
693
694
695
696
697
698
699
700
701
702
703 public IMAPUntaggedResponse extractResponse(String type) {
704 Iterator i = queuedResponses.iterator();
705 while (i.hasNext()) {
706 IMAPUntaggedResponse response = (IMAPUntaggedResponse)i.next();
707
708 if (response.isKeyword(type)) {
709 i.remove();
710 return response;
711 }
712 }
713 return null;
714 }
715
716
717
718
719
720
721
722
723
724
725 public List extractResponses(String type) {
726 List responses = new ArrayList();
727
728 Iterator i = queuedResponses.iterator();
729 while (i.hasNext()) {
730 IMAPUntaggedResponse response = (IMAPUntaggedResponse)i.next();
731
732 if (response.isKeyword(type)) {
733 i.remove();
734 responses.add(response);
735 }
736 }
737 return responses;
738 }
739
740
741
742
743
744
745
746
747
748
749
750 public List extractFetchResponses(int sequenceNumber) {
751 List responses = new ArrayList();
752
753 Iterator i = queuedResponses.iterator();
754 while (i.hasNext()) {
755 IMAPUntaggedResponse response = (IMAPUntaggedResponse)i.next();
756
757 if (response.isKeyword("FETCH")) {
758 IMAPFetchResponse fetch = (IMAPFetchResponse)response;
759
760 if (fetch.sequenceNumber == sequenceNumber) {
761
762 i.remove();
763 responses.add(response);
764 }
765 }
766 }
767 return responses;
768 }
769
770
771
772
773
774
775
776
777
778
779
780
781
782 protected IMAPFetchDataItem extractFetchDataItem(long sequenceNumber, int type)
783 {
784 Iterator i = queuedResponses.iterator();
785 while (i.hasNext()) {
786 IMAPUntaggedResponse response = (IMAPUntaggedResponse)i.next();
787
788 if (response.isKeyword("FETCH")) {
789 IMAPFetchResponse fetch = (IMAPFetchResponse)response;
790
791 if (fetch.sequenceNumber == sequenceNumber) {
792
793 IMAPFetchDataItem item = fetch.getDataItem(type);
794 if (item != null) {
795
796
797 i.remove();
798 return item;
799 }
800 }
801 }
802 }
803
804 return null;
805 }
806
807
808
809
810
811
812
813
814
815
816 protected List extractFetchDataItems(int type)
817 {
818 Iterator i = queuedResponses.iterator();
819 List items = new ArrayList();
820
821 while (i.hasNext()) {
822 IMAPUntaggedResponse response = (IMAPUntaggedResponse)i.next();
823
824 if (response.isKeyword("FETCH")) {
825 IMAPFetchResponse fetch = (IMAPFetchResponse)response;
826
827 IMAPFetchDataItem item = fetch.getDataItem(type);
828 if (item != null) {
829
830
831 i.remove();
832
833
834
835 items.add(fetch);
836 }
837 }
838 }
839
840 return items;
841 }
842
843
844
845
846
847
848 public void updateMailboxStatus() throws MessagingException {
849 sendSimpleCommand("NOOP");
850 }
851
852
853
854
855
856
857
858
859
860
861
862
863 public synchronized boolean isAlive(long timeout) {
864 long lastUsed = System.currentTimeMillis() - lastAccess;
865 if (lastUsed < timeout) {
866 return true;
867 }
868
869 try {
870 sendSimpleCommand("NOOP");
871 return true;
872 } catch (MessagingException e) {
873
874
875 }
876 return false;
877 }
878
879
880
881
882
883
884
885
886
887 public synchronized List fetchEnvelope(int sequenceNumber) throws MessagingException {
888 IMAPCommand command = new IMAPCommand("FETCH");
889 command.appendInteger(sequenceNumber);
890 command.startList();
891 command.appendAtom("ENVELOPE INTERNALDATE RFC822.SIZE");
892 command.endList();
893
894
895 sendCommand(command);
896
897
898 return extractFetchResponses(sequenceNumber);
899 }
900
901
902
903
904
905
906
907
908
909 public synchronized IMAPBodyStructure fetchBodyStructure(int sequenceNumber) throws MessagingException {
910 IMAPCommand command = new IMAPCommand("FETCH");
911 command.appendInteger(sequenceNumber);
912 command.startList();
913 command.appendAtom("BODYSTRUCTURE");
914 command.endList();
915
916
917 sendCommand(command);
918
919 IMAPBodyStructure bodyStructure = (IMAPBodyStructure)extractFetchDataItem(sequenceNumber, IMAPFetchDataItem.BODYSTRUCTURE);
920
921 if (bodyStructure == null) {
922 throw new MessagingException("No BODYSTRUCTURE information received from IMAP server");
923 }
924
925 return bodyStructure;
926 }
927
928
929
930
931
932
933
934
935
936
937 public synchronized InternetHeaders fetchHeaders(int sequenceNumber, String part) throws MessagingException {
938 IMAPCommand command = new IMAPCommand("FETCH");
939 command.appendInteger(sequenceNumber);
940 command.startList();
941 command.appendAtom("BODY.PEEK");
942 command.appendBodySection(part, "HEADER");
943 command.endList();
944
945
946 sendCommand(command);
947 IMAPInternetHeader header = (IMAPInternetHeader)extractFetchDataItem(sequenceNumber, IMAPFetchDataItem.HEADER);
948
949 if (header == null) {
950 throw new MessagingException("No HEADER information received from IMAP server");
951 }
952
953 return header.headers;
954 }
955
956
957
958
959
960
961
962
963
964
965 public synchronized IMAPMessageText fetchText(int sequenceNumber) throws MessagingException {
966 IMAPCommand command = new IMAPCommand("FETCH");
967 command.appendInteger(sequenceNumber);
968 command.startList();
969 command.appendAtom("BODY.PEEK");
970 command.appendBodySection("TEXT");
971 command.endList();
972
973
974 sendCommand(command);
975 IMAPMessageText text = (IMAPMessageText)extractFetchDataItem(sequenceNumber, IMAPFetchDataItem.TEXT);
976
977 if (text == null) {
978 throw new MessagingException("No TEXT information received from IMAP server");
979 }
980
981 return text;
982 }
983
984
985
986
987
988
989
990
991
992
993 public synchronized IMAPMessageText fetchBodyPartText(int sequenceNumber, String section) throws MessagingException {
994 IMAPCommand command = new IMAPCommand("FETCH");
995 command.appendInteger(sequenceNumber);
996 command.startList();
997 command.appendAtom("BODY.PEEK");
998 command.appendBodySection(section, "TEXT");
999 command.endList();
1000
1001
1002 sendCommand(command);
1003 IMAPMessageText text = (IMAPMessageText)extractFetchDataItem(sequenceNumber, IMAPFetchDataItem.TEXT);
1004
1005 if (text == null) {
1006 throw new MessagingException("No TEXT information received from IMAP server");
1007 }
1008
1009 return text;
1010 }
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026 public synchronized IMAPBody fetchBody(int sequenceNumber, String section) throws MessagingException {
1027 IMAPCommand command = new IMAPCommand("FETCH");
1028 command.appendInteger(sequenceNumber);
1029 command.startList();
1030 command.appendAtom("BODY.PEEK");
1031
1032
1033 command.appendBodySection(section, null);
1034 command.endList();
1035
1036
1037 sendCommand(command);
1038 IMAPBody body = (IMAPBody)extractFetchDataItem(sequenceNumber, IMAPFetchDataItem.BODY);
1039
1040 if (body == null) {
1041 throw new MessagingException("No BODY information received from IMAP server");
1042 }
1043
1044 return body;
1045 }
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058 public byte[] fetchContent(int sequenceNumber) throws MessagingException {
1059
1060 IMAPMessageText text = fetchText(sequenceNumber);
1061 return text.getContent();
1062 }
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075 public byte[] fetchContent(int sequenceNumber, String section) throws MessagingException {
1076 if (section == null) {
1077 IMAPMessageText text = fetchText(sequenceNumber);
1078 return text.getContent();
1079 } else {
1080 IMAPBody body = fetchBody(sequenceNumber, section);
1081 return body.getContent();
1082 }
1083 }
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095 public synchronized List list(String mailbox, String pattern) throws MessagingException {
1096 IMAPCommand command = new IMAPCommand("LIST");
1097
1098
1099 command.appendEncodedString(mailbox);
1100 command.appendEncodedString(pattern);
1101
1102 sendCommand(command);
1103
1104
1105 return extractResponses("LIST");
1106 }
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118 public List listSubscribed(String mailbox, String pattern) throws MessagingException {
1119 IMAPCommand command = new IMAPCommand("LSUB");
1120
1121
1122 command.appendEncodedString(mailbox);
1123 command.appendEncodedString(pattern);
1124
1125 sendCommand(command);
1126
1127 return extractResponses("LSUB");
1128 }
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138 public void subscribe(String mailbox) throws MessagingException {
1139 IMAPCommand command = new IMAPCommand("SUBSCRIBE");
1140
1141 command.appendEncodedString(mailbox);
1142
1143
1144 sendSimpleCommand(command);
1145 }
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155 public void unsubscribe(String mailbox) throws MessagingException {
1156 IMAPCommand command = new IMAPCommand("UNSUBSCRIBE");
1157
1158 command.appendEncodedString(mailbox);
1159
1160
1161 sendSimpleCommand(command);
1162 }
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172 public void createMailbox(String mailbox) throws MessagingException {
1173 IMAPCommand command = new IMAPCommand("CREATE");
1174
1175 command.appendEncodedString(mailbox);
1176
1177
1178 sendSimpleCommand(command);
1179 }
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189 public void deleteMailbox(String mailbox) throws MessagingException {
1190 IMAPCommand command = new IMAPCommand("DELETE");
1191
1192 command.appendEncodedString(mailbox);
1193
1194
1195 sendSimpleCommand(command);
1196 }
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206 public void renameMailbox(String oldName, String newName) throws MessagingException {
1207 IMAPCommand command = new IMAPCommand("RENAME");
1208
1209 command.appendEncodedString(oldName);
1210 command.appendEncodedString(newName);
1211
1212
1213 sendSimpleCommand(command);
1214 }
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225 public synchronized IMAPMailboxStatus getMailboxStatus(String mailbox) throws MessagingException {
1226 IMAPCommand command = new IMAPCommand("STATUS");
1227
1228
1229 command.appendEncodedString(mailbox);
1230
1231 command.append(" (MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)");
1232
1233 sendCommand(command);
1234
1235
1236 IMAPMailboxStatus status = new IMAPMailboxStatus();
1237 status.mergeSizeResponses(extractResponses("EXISTS"));
1238 status.mergeSizeResponses(extractResponses("RECENT"));
1239 status.mergeOkResponses(extractResponses("UIDNEXT"));
1240 status.mergeOkResponses(extractResponses("UIDVALIDITY"));
1241 status.mergeOkResponses(extractResponses("UNSEEN"));
1242 status.mergeStatus((IMAPStatusResponse)extractResponse("STATUS"));
1243 status.mergeStatus((IMAPPermanentFlagsResponse)extractResponse("PERMANENTFLAGS"));
1244
1245 return status;
1246 }
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260 public synchronized IMAPMailboxStatus openMailbox(String mailbox, boolean readOnly) throws MessagingException {
1261 IMAPCommand command = new IMAPCommand();
1262
1263
1264
1265 if (readOnly) {
1266 command.appendAtom("EXAMINE");
1267 }
1268 else {
1269 command.appendAtom("SELECT");
1270 }
1271
1272
1273 command.appendEncodedString(mailbox);
1274
1275
1276 IMAPTaggedResponse response = sendCommand(command);
1277
1278 IMAPMailboxStatus status = new IMAPMailboxStatus();
1279
1280 status.mode = readOnly ? Folder.READ_ONLY : Folder.READ_WRITE;
1281
1282
1283
1284 if (response.hasStatus("READ-ONLY")) {
1285 status.mode = Folder.READ_ONLY;
1286 }
1287
1288
1289 status.mergeFlags((IMAPFlagsResponse)extractResponse("FLAGS"));
1290 status.mergeStatus((IMAPSizeResponse)extractResponse("EXISTS"));
1291 status.mergeStatus((IMAPSizeResponse)extractResponse("RECENT"));
1292 status.mergeStatus((IMAPOkResponse)extractResponse("UIDVALIDITY"));
1293 status.mergeStatus((IMAPOkResponse)extractResponse("UNSEEN"));
1294 status.mergeStatus((IMAPPermanentFlagsResponse)extractResponse("PERMANENTFLAGS"));
1295
1296 return status;
1297 }
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310 public synchronized List expungeMailbox() throws MessagingException {
1311
1312 sendCommand("EXPUNGE");
1313
1314 return extractResponses("EXPUNGED");
1315 }
1316
1317 public int[] searchMailbox(SearchTerm term) throws MessagingException {
1318 return searchMailbox("ALL", term);
1319 }
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332 public int[] searchMailbox(String messages, SearchTerm term) throws MessagingException {
1333
1334 String charset = null;
1335
1336 if (IMAPCommand.checkSearchEncoding(term)) {
1337
1338
1339
1340
1341 charset = "UTF-8";
1342 }
1343
1344 return searchMailbox(messages, term, charset);
1345 }
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359 public synchronized int[] searchMailbox(String messages, SearchTerm term, String charset) throws MessagingException {
1360 IMAPCommand command = new IMAPCommand("SEARCH");
1361
1362
1363 if (charset != null) {
1364 command.appendAtom("CHARSET");
1365 command.appendAtom(charset);
1366 }
1367
1368
1369
1370
1371 command.appendSearchTerm(term, charset);
1372
1373 command.appendAtom(messages);
1374
1375
1376 sendCommand(command);
1377
1378
1379 IMAPSearchResponse hits = (IMAPSearchResponse)extractResponse("SEARCH");
1380
1381 return hits.messageNumbers;
1382 }
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398 public void appendMessage(String mailbox, Date messageDate, Flags messageFlags, byte[] messageData) throws MessagingException {
1399 IMAPCommand command = new IMAPCommand("APPEND");
1400
1401
1402 command.appendEncodedString(mailbox);
1403
1404 if (messageFlags != null) {
1405
1406
1407 messageFlags.remove(Flags.Flag.RECENT);
1408
1409 command.appendFlags(messageFlags);
1410 }
1411
1412 if (messageDate != null) {
1413 command.appendDate(messageDate);
1414 }
1415
1416
1417 command.appendLiteral(messageData);
1418
1419
1420 sendSimpleCommand(command);
1421 }
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432 public synchronized Flags fetchFlags(int sequenceNumber) throws MessagingException {
1433
1434 sendCommand("FETCH " + String.valueOf(sequenceNumber) + " (FLAGS)");
1435
1436 IMAPFlags flags = (IMAPFlags)extractFetchDataItem(sequenceNumber, IMAPFetchDataItem.FLAGS);
1437 return flags.flags;
1438 }
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451 public synchronized List setFlags(String messageSet, Flags flags, boolean set) throws MessagingException {
1452 IMAPCommand command = new IMAPCommand("STORE");
1453 command.appendAtom(messageSet);
1454
1455 if (set) {
1456 command.appendAtom("+FLAGS");
1457 }
1458 else {
1459 command.appendAtom("-FLAGS");
1460 }
1461
1462
1463 command.appendFlags(flags);
1464
1465
1466 sendCommand(command);
1467
1468
1469 return extractFetchDataItems(IMAPFetchDataItem.FLAGS);
1470 }
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483 public synchronized Flags setFlags(int sequenceNumber, Flags flags, boolean set) throws MessagingException {
1484 IMAPCommand command = new IMAPCommand("STORE");
1485 command.appendInteger(sequenceNumber);
1486
1487 if (set) {
1488 command.appendAtom("+FLAGS");
1489 }
1490 else {
1491 command.appendAtom("-FLAGS");
1492 }
1493
1494
1495 command.appendFlags(flags);
1496
1497
1498 sendCommand(command);
1499
1500 IMAPFlags flagResponse = (IMAPFlags)extractFetchDataItem(sequenceNumber, IMAPFetchDataItem.FLAGS);
1501 return flagResponse.flags;
1502 }
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513 public void copyMessages(String messageSet, String target) throws MessagingException {
1514 IMAPCommand command = new IMAPCommand("COPY");
1515
1516 command.appendAtom(messageSet);
1517
1518 command.appendEncodedString(target);
1519
1520
1521 sendSimpleCommand(command);
1522 }
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532 public synchronized IMAPUid getSequenceNumberForUid(long uid) throws MessagingException {
1533 IMAPCommand command = new IMAPCommand("UID FETCH");
1534 command.appendLong(uid);
1535 command.appendAtom("(UID)");
1536
1537
1538
1539
1540
1541
1542
1543
1544 sendCommand(command);
1545
1546 List responses = extractResponses("FETCH");
1547
1548
1549
1550 for (int i = 0; i < responses.size(); i++) {
1551 IMAPFetchResponse response = (IMAPFetchResponse)responses.get(i);
1552 IMAPUid item = (IMAPUid)response.getDataItem(IMAPFetchDataItem.UID);
1553
1554
1555
1556 if (item != null && item.uid == uid) {
1557 return item;
1558 }
1559
1560 queuePendingResponse(response);
1561 }
1562
1563 return null;
1564 }
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576 public synchronized List getSequenceNumbersForUids(long start, long end) throws MessagingException {
1577 IMAPCommand command = new IMAPCommand("UID FETCH");
1578
1579
1580 command.appendLong(start);
1581 command.append(":");
1582
1583
1584 if (end != UIDFolder.LASTUID) {
1585 command.appendLong(end);
1586 }
1587 else {
1588 command.append("*");
1589 }
1590 command.appendAtom("(UID)");
1591
1592
1593
1594
1595
1596
1597
1598
1599 sendCommand(command);
1600
1601 List responses = extractResponses("FETCH");
1602
1603 List uids = new ArrayList((int)(end - start + 1));
1604
1605
1606
1607 for (int i = 0; i < responses.size(); i++) {
1608 IMAPFetchResponse response = (IMAPFetchResponse)responses.get(i);
1609 IMAPUid item = (IMAPUid)response.getDataItem(IMAPFetchDataItem.UID);
1610
1611
1612
1613 if (item != null) {
1614 uids.add(item);
1615 }
1616 else {
1617
1618 queuePendingResponse(response);
1619 }
1620 }
1621
1622 return uids;
1623 }
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634 public synchronized IMAPUid getUidForSequenceNumber(int sequenceNumber) throws MessagingException {
1635 IMAPCommand command = new IMAPCommand("FETCH");
1636 command.appendInteger(sequenceNumber);
1637 command.appendAtom("(UID)");
1638
1639
1640
1641
1642 sendCommand(command);
1643
1644
1645 return (IMAPUid)extractFetchDataItem(sequenceNumber, IMAPFetchDataItem.UID);
1646 }
1647
1648
1649
1650
1651
1652
1653
1654
1655 public synchronized IMAPNamespaceResponse getNamespaces() throws MessagingException {
1656
1657
1658 if (!hasCapability("NAMESPACE")) {
1659 return new IMAPNamespaceResponse();
1660 }
1661
1662 sendCommand("NAMESPACE");
1663
1664
1665
1666 return (IMAPNamespaceResponse)extractResponse("NAMESPACE");
1667 }
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681 public synchronized List fetch(String messageSet, FetchProfile profile) throws MessagingException {
1682 IMAPCommand command = new IMAPCommand("FETCH");
1683 command.appendAtom(messageSet);
1684
1685 command.appendFetchProfile(profile);
1686
1687
1688
1689 sendCommand(command);
1690
1691
1692 return extractResponses("FETCH");
1693 }
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705 public synchronized void setACLRights(String mailbox, ACL acl) throws MessagingException {
1706 IMAPCommand command = new IMAPCommand("SETACL");
1707 command.appendEncodedString(mailbox);
1708
1709 command.appendACL(acl);
1710
1711 sendSimpleCommand(command);
1712 }
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723 public synchronized void addACLRights(String mailbox, ACL acl) throws MessagingException {
1724 if (!hasCapability("ACL")) {
1725 throw new MethodNotSupportedException("ACL not available from this IMAP server");
1726 }
1727 IMAPCommand command = new IMAPCommand("SETACL");
1728 command.appendEncodedString(mailbox);
1729
1730 command.appendACL(acl, "+");
1731
1732 sendSimpleCommand(command);
1733 }
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744 public synchronized void removeACLRights(String mailbox, ACL acl) throws MessagingException {
1745 if (!hasCapability("ACL")) {
1746 throw new MethodNotSupportedException("ACL not available from this IMAP server");
1747 }
1748 IMAPCommand command = new IMAPCommand("SETACL");
1749 command.appendEncodedString(mailbox);
1750
1751 command.appendACL(acl, "-");
1752
1753 sendSimpleCommand(command);
1754 }
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766 public synchronized ACL[] getACLRights(String mailbox) throws MessagingException {
1767 if (!hasCapability("ACL")) {
1768 throw new MethodNotSupportedException("ACL not available from this IMAP server");
1769 }
1770 IMAPCommand command = new IMAPCommand("GETACL");
1771 command.appendEncodedString(mailbox);
1772
1773
1774 sendCommand(command);
1775
1776 IMAPACLResponse response = (IMAPACLResponse)extractResponse("ACL");
1777 return response.acls;
1778 }
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789 public synchronized Rights getMyRights(String mailbox) throws MessagingException {
1790 if (!hasCapability("ACL")) {
1791 throw new MethodNotSupportedException("ACL not available from this IMAP server");
1792 }
1793 IMAPCommand command = new IMAPCommand("MYRIGHTS");
1794 command.appendEncodedString(mailbox);
1795
1796
1797 sendCommand(command);
1798
1799 IMAPMyRightsResponse response = (IMAPMyRightsResponse)extractResponse("MYRIGHTS");
1800 return response.rights;
1801 }
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814 public synchronized Rights[] listACLRights(String mailbox, String name) throws MessagingException {
1815 if (!hasCapability("ACL")) {
1816 throw new MethodNotSupportedException("ACL not available from this IMAP server");
1817 }
1818 IMAPCommand command = new IMAPCommand("LISTRIGHTS");
1819 command.appendEncodedString(mailbox);
1820 command.appendString(name);
1821
1822
1823 sendCommand(command);
1824
1825 IMAPListRightsResponse response = (IMAPListRightsResponse)extractResponse("LISTRIGHTS");
1826 return response.rights;
1827 }
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839 public synchronized void deleteACL(String mailbox, String name) throws MessagingException {
1840 if (!hasCapability("ACL")) {
1841 throw new MethodNotSupportedException("ACL not available from this IMAP server");
1842 }
1843 IMAPCommand command = new IMAPCommand("DELETEACL");
1844 command.appendEncodedString(mailbox);
1845 command.appendString(name);
1846
1847
1848 sendSimpleCommand(command);
1849 }
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860 public synchronized Quota[] fetchQuotaRoot(String mailbox) throws MessagingException {
1861 if (!hasCapability("QUOTA")) {
1862 throw new MethodNotSupportedException("QUOTA not available from this IMAP server");
1863 }
1864 IMAPCommand command = new IMAPCommand("GETQUOTAROOT");
1865 command.appendEncodedString(mailbox);
1866
1867
1868
1869 sendCommand(command);
1870
1871 extractResponse("QUOTAROOT");
1872
1873
1874 List responses = extractResponses("QUOTA");
1875
1876
1877 Quota[] quotas = new Quota[responses.size()];
1878 for (int i = 0; i < quotas.length; i++) {
1879 IMAPQuotaResponse q = (IMAPQuotaResponse)responses.get(i);
1880 quotas[i] = q.quota;
1881 }
1882
1883 return quotas;
1884 }
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894 public synchronized Quota[] fetchQuota(String root) throws MessagingException {
1895 if (!hasCapability("QUOTA")) {
1896 throw new MethodNotSupportedException("QUOTA not available from this IMAP server");
1897 }
1898 IMAPCommand command = new IMAPCommand("GETQUOTA");
1899 command.appendString(root);
1900
1901
1902
1903 sendCommand(command);
1904
1905
1906 List responses = extractResponses("QUOTA");
1907
1908
1909 Quota[] quotas = new Quota[responses.size()];
1910 for (int i = 0; i < quotas.length; i++) {
1911 IMAPQuotaResponse q = (IMAPQuotaResponse)responses.get(i);
1912 quotas[i] = q.quota;
1913 }
1914
1915 return quotas;
1916 }
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926 public synchronized void setQuota(Quota quota) throws MessagingException {
1927 if (!hasCapability("QUOTA")) {
1928 throw new MethodNotSupportedException("QUOTA not available from this IMAP server");
1929 }
1930 IMAPCommand command = new IMAPCommand("GETQUOTA");
1931
1932 command.appendQuota(quota);
1933
1934
1935
1936 sendCommand(command);
1937
1938 extractResponses("QUOTA");
1939 }
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949 public boolean hasCapability(String capability) {
1950 if (capabilities == null) {
1951 return false;
1952 }
1953 return capabilities.containsKey(capability);
1954 }
1955
1956
1957
1958
1959
1960
1961 public void setClosed() {
1962 closed = true;
1963 }
1964
1965
1966
1967
1968
1969
1970 public boolean isClosed() {
1971 return closed;
1972 }
1973 }
1974