1 36 package org.columba.ristretto.imap; 37 38 import java.io.IOException ; 39 import java.io.InputStream ; 40 import java.io.OutputStream ; 41 import java.io.PrintStream ; 42 import java.net.Socket ; 43 import java.net.SocketException ; 44 import java.nio.ByteBuffer ; 45 import java.nio.charset.Charset ; 46 import java.util.ArrayList ; 47 import java.util.Arrays ; 48 import java.util.Iterator ; 49 import java.util.LinkedList ; 50 import java.util.List ; 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 88 public class IMAPProtocol implements AuthenticationServer { 89 93 public static final int LOGOUT = 0; 94 95 99 public static final int NOT_CONNECTED = 0; 100 101 105 public static final int NON_AUTHENTICATED = 1; 106 107 111 public static final int AUTHENTICATED = 2; 112 113 117 public static final int SELECTED = 3; 118 119 122 public static final int DEFAULT_PORT = 143; 123 124 127 public static final int DEFAULT_SSL_PORT = 993; 128 129 private TagFactory tagFactory; 130 131 protected String host; 132 133 protected int port; 134 135 private IMAPInputStream in; 136 137 private OutputStream out; 138 139 private Socket socket; 140 141 private int state; 142 143 private String selectedMailbox; 144 145 private Object lock; 146 147 private ArrayList listeners; 148 149 157 public IMAPProtocol(String 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 (); 165 } 166 167 173 public void openPort() throws IOException , IMAPException { 174 socket = new Socket (host, port); 175 socket.setSoTimeout(RistrettoConfig.getInstance().getTimeout()); 176 177 createStreams(); 178 179 IMAPResponse response; 180 try { 181 response = in.readResponse(); 182 } catch (IOException e) { 183 state = NOT_CONNECTED; 184 throw e; 185 } 186 188 if (response.isBYE()) 189 throw new IMAPException(response); 190 191 if (response.isOK()) 192 state = NON_AUTHENTICATED; 193 else 194 state = AUTHENTICATED; 196 } 197 198 206 public void openSSLPort() throws IOException , SSLException, IMAPException { 207 socket = RistrettoSSLSocketFactory.getInstance().createSocket(host, 208 port); 209 socket.setSoTimeout(RistrettoConfig.getInstance().getTimeout()); 210 211 ((SSLSocket) socket).startHandshake(); 213 214 createStreams(); 215 216 IMAPResponse response; 217 try { 218 response = in.readResponse(); 219 } catch (IOException e) { 220 state = NOT_CONNECTED; 221 throw e; 222 } 223 225 if (response.isBYE()) 226 throw new IMAPException(response); 227 228 if (response.isOK()) 229 state = NON_AUTHENTICATED; 230 else 231 state = AUTHENTICATED; 233 } 234 235 237 245 public String [] capability() throws IOException , IMAPException { 246 checkState(NON_AUTHENTICATED); 247 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), 248 "CAPABILITY"); 249 250 IMAPResponse[] responses = communicate(command); 251 ArrayList result = new ArrayList (); 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 if (!responses[responses.length - 1].isOK()) 263 throwException(responses[responses.length - 1]); 264 265 return (String []) result.toArray(new String [0]); 266 } 267 268 275 public void noop() throws IOException , IMAPException { 276 checkState(NON_AUTHENTICATED); 277 278 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "NOOP"); 279 IMAPResponse[] responses = communicate(command); 280 for (int i = 0; i < responses.length - 1; i++) { 283 handleResponse(responses[i]); 284 } 285 286 if (!responses[responses.length - 1].isOK()) 288 throwException(responses[responses.length - 1]); 289 } 290 291 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 309 public void logout() throws IOException , IMAPException { 310 checkState(NON_AUTHENTICATED); 311 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "LOGOUT"); 312 313 IMAPResponse[] responses = communicate(command); 314 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 in.close(); 325 in = null; 326 out.close(); 327 out = null; 328 } 329 330 341 public void startTLS() throws IOException , SSLException, IMAPException { 342 checkState(NON_AUTHENTICATED); 343 344 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "STARTTLS"); 345 IMAPResponse[] responses = communicate(command); 346 for (int i = 0; i < responses.length - 1; i++) { 349 handleResponse(responses[i]); 350 } 351 352 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 ((SSLSocket) socket).startHandshake(); 361 362 createStreams(); 363 } 364 365 379 public void authenticate(String method, String user, char[] password) 380 throws IOException , IMAPException, AuthenticationException { 381 checkState(NON_AUTHENTICATED); 382 String tag = tagFactory.nextTag(); 383 384 try { 385 AuthenticationMechanism auth = AuthenticationFactory.getInstance() 386 .getAuthentication(method); 387 IMAPCommand command = new IMAPCommand(tag, "AUTHENTICATE", 388 new Object [] { method }); 389 390 391 try{ 392 command.writeToStream(in, out); 393 } catch (IOException 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 e) { 408 state = NOT_CONNECTED; 409 throw e; 410 } 411 412 while (!response.isTagged()) { 415 handleResponse(response); 416 try { 417 response = in.readResponse(); 418 } catch (SocketException 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 441 public void login(String user, char[] password) throws IOException , 442 IMAPException { 443 checkState(NON_AUTHENTICATED); 444 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "LOGIN", 445 new Object [] { user, new String (password) }); 446 447 IMAPResponse[] responses = communicate(command); 448 449 for (int i = 0; i < responses.length - 1; i++) { 452 handleResponse(responses[i]); 453 } 454 455 if (!responses[responses.length - 1].isOK()) 457 throwException(responses[responses.length - 1]); 458 state = AUTHENTICATED; 459 } 460 461 463 475 public MailboxInfo select(String mailbox) throws IOException , IMAPException { 476 return selectCore(mailbox, "SELECT"); 477 } 478 479 private MailboxInfo selectCore(String mailbox, String s) 480 throws IMAPException, IOException { 481 checkState(AUTHENTICATED); 482 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), s, 483 new Object [] { 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 if (!responses[responses.length - 1].isOK()) { 504 state = AUTHENTICATED; 505 throwException(responses[responses.length - 1]); 506 } 507 508 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 532 public MailboxInfo examine(String mailbox) throws IOException , 533 IMAPException { 534 return selectCore(mailbox, "EXAMINE"); 535 } 536 537 547 public void create(String mailbox) throws IOException , IMAPException { 548 checkState(AUTHENTICATED); 549 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "CREATE", 550 new Object [] { MailboxNameUTF7Converter.encode(mailbox) }); 551 552 IMAPResponse[] responses = communicate(command); 553 for (int i = 0; i < responses.length - 1; i++) { 556 handleResponse(responses[i]); 557 } 558 559 if (!responses[responses.length - 1].isOK()) 561 throwException(responses[responses.length - 1]); 562 } 563 564 574 public void delete(String mailbox) throws IOException , IMAPException { 575 checkState(AUTHENTICATED); 576 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "DELETE", 577 new Object [] { MailboxNameUTF7Converter.encode(mailbox) }); 578 579 IMAPResponse[] responses = communicate(command); 580 for (int i = 0; i < responses.length - 1; i++) { 583 handleResponse(responses[i]); 584 } 585 586 if (!responses[responses.length - 1].isOK()) 588 throwException(responses[responses.length - 1]); 589 } 590 591 592 603 public QuotaInfo getQuota(String mailbox) throws IOException , IMAPException { 604 checkState(AUTHENTICATED); 605 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), 606 "GETQUOTAROOT", new Object [] { 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 if (!responses[responses.length - 1].isOK()) 625 throwException(responses[responses.length - 1]); 626 627 return result; 628 } 629 630 641 public void rename(String oldname, String newname) throws IOException , 642 IMAPException { 643 checkState(AUTHENTICATED); 644 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "RENAME", 645 new Object [] { MailboxNameUTF7Converter.encode(oldname), 646 MailboxNameUTF7Converter.encode(newname) }); 647 648 IMAPResponse[] responses = communicate(command); 649 for (int i = 0; i < responses.length - 1; i++) { 652 handleResponse(responses[i]); 653 } 654 655 if (!responses[responses.length - 1].isOK()) 657 throwException(responses[responses.length - 1]); 658 } 659 660 671 public void subscribe(String mailbox) throws IOException , IMAPException { 672 checkState(AUTHENTICATED); 673 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), 674 "SUBSCRIBE", new Object [] { MailboxNameUTF7Converter 675 .encode(mailbox) }); 676 677 IMAPResponse[] responses = communicate(command); 678 for (int i = 0; i < responses.length - 1; i++) { 681 handleResponse(responses[i]); 682 } 683 684 if (!responses[responses.length - 1].isOK()) 686 throwException(responses[responses.length - 1]); 687 } 688 689 700 public void unsubscribe(String mailbox) throws IOException , IMAPException { 701 checkState(AUTHENTICATED); 702 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), 703 "UNSUBSCRIBE", new Object [] { MailboxNameUTF7Converter 704 .encode(mailbox) }); 705 706 IMAPResponse[] responses = communicate(command); 707 for (int i = 0; i < responses.length - 1; i++) { 710 handleResponse(responses[i]); 711 } 712 713 if (!responses[responses.length - 1].isOK()) 715 throwException(responses[responses.length - 1]); 716 } 717 718 719 733 public ListInfo[] list(String reference, String mailbox) 734 throws IOException , IMAPException { 735 return listCore(reference, mailbox, "LIST"); 736 } 737 738 745 private ListInfo[] listCore(String reference, String mailbox, String s) 746 throws IMAPException, IOException { 747 checkState(AUTHENTICATED); 748 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), s, 749 new Object [] { MailboxNameUTF7Converter.encode(reference), 750 MailboxNameUTF7Converter.encode(mailbox) }); 751 752 IMAPResponse[] responses = communicate(command); 753 if (!responses[responses.length - 1].isOK()) 755 throwException(responses[responses.length - 1]); 756 757 List result = new ArrayList (); 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 785 public ListInfo[] lsub(String reference, String mailbox) 786 throws IOException , IMAPException { 787 return listCore(reference, mailbox, "LSUB"); 788 } 789 790 802 public MailboxStatus status(String mailbox, String [] statusItems) 803 throws IOException , IMAPException { 804 checkState(AUTHENTICATED); 805 806 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "STATUS", 807 new Object [] { MailboxNameUTF7Converter.encode(mailbox), 808 statusItems }); 809 810 IMAPResponse[] responses = communicate(command); 811 812 MailboxStatus result = null; 813 814 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 if (!responses[responses.length - 1].isOK()) 829 throwException(responses[responses.length - 1]); 830 831 return result; 832 } 833 834 852 public AppendInfo append(String mailbox, InputStream message, 853 Object [] optargs) throws IOException , IMAPException { 854 checkState(AUTHENTICATED); 855 856 List args = new LinkedList (); 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 for (int i = 0; i < responses.length - 1; i++) { 871 handleResponse(responses[i]); 872 } 873 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 903 public AppendInfo appendPlus(String mailbox, InputStream message, 904 Object [] optargs) throws IOException , IMAPException { 905 return append(mailbox, message, optargs); 906 } 907 908 921 public AppendInfo append(String mailbox, InputStream message) 922 throws IOException , IMAPException { 923 return append(mailbox, message, null); 924 } 925 926 928 929 930 938 public void check() throws IOException , IMAPException { 939 checkState(SELECTED); 940 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "CHECK"); 941 IMAPResponse[] responses = communicate(command); 942 for (int i = 0; i < responses.length - 1; i++) { 945 handleResponse(responses[i]); 946 } 947 if (!responses[responses.length - 1].isOK()) 949 throwException(responses[responses.length - 1]); 950 } 951 952 961 public void close() throws IOException , IMAPException { 962 checkState(SELECTED); 963 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "CLOSE"); 964 IMAPResponse[] responses = communicate(command); 965 for (int i = 0; i < responses.length - 1; i++) { 968 handleResponse(responses[i]); 969 } 970 if (!responses[responses.length - 1].isOK()) 972 throwException(responses[responses.length - 1]); 973 state = AUTHENTICATED; 974 } 975 976 985 public int[] expunge() throws IOException , IMAPException { 986 checkState(SELECTED); 987 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "EXPUNGE"); 988 IMAPResponse[] responses = communicate(command); 989 if (!responses[responses.length - 1].isOK()) 991 throwException(responses[responses.length - 1]); 992 993 List expunged = new ArrayList (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 (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 )expunged.get(i)).intValue(); 1006 } 1007 1008 return expungedArray; 1009 } 1010 1011 1012 1013 1024 public Integer [] search(Charset charset, SearchKey[] search) 1025 throws IOException , IMAPException { 1026 return searchCore("SEARCH", charset, search); 1027 } 1028 1029 1040 public Integer [] uidSearch(Charset charset, SearchKey[] search) 1041 throws IOException , IMAPException { 1042 return searchCore("UID SEARCH", charset, search); 1043 } 1044 1045 1052 private Integer [] searchCore(String c, Charset charset, SearchKey[] search) 1053 throws IMAPException, IOException { 1054 IMAPCommand command; 1055 checkState(SELECTED); 1056 1057 List args = new LinkedList (); 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 [] 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 if (!responses[responses.length - 1].isOK()) 1085 throwException(responses[responses.length - 1]); 1086 1087 return result; 1088 } 1089 1090 1100 public Integer [] search(SearchKey[] search) throws IOException , 1101 IMAPException { 1102 return searchCore("SEARCH", null, search); 1103 } 1104 1105 1115 public Integer [] uidSearch(SearchKey[] search) throws IOException , 1116 IMAPException { 1117 return searchCore("UID SEARCH", null, search); 1118 } 1119 1120 1128 public IMAPFlags[] fetchFlags(SequenceSet set) throws IOException , 1129 IMAPException { 1130 checkState(SELECTED); 1131 1132 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "FETCH", 1133 new Object [] { set, new String [] { "FLAGS", "UID" } }); 1134 1135 IMAPResponse[] responses = communicate(command); 1136 1137 ArrayList result = new ArrayList (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 if (!responses[responses.length - 1].isOK()) 1148 throwException(responses[responses.length - 1]); 1149 1150 return (IMAPFlags[]) result.toArray(new IMAPFlags[0]); 1151 } 1152 1153 1161 public Integer [] fetchUid(SequenceSet set) throws IOException , 1162 IMAPException { 1163 checkState(SELECTED); 1164 1165 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), "FETCH", 1166 new Object [] { set, "UID" }); 1167 1168 IMAPResponse[] responses = communicate(command); 1169 1170 ArrayList result = new ArrayList (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 if (!responses[responses.length - 1].isOK()) 1186 throwException(responses[responses.length - 1]); 1187 1188 return (Integer []) result.toArray(new Integer [0]); 1189 } 1190 1191 private IMAPHeader[] fetchHeaderFieldsCore(String c, SequenceSet set, 1192 String [] fields) throws IOException , IMAPException { 1193 checkState(SELECTED); 1194 1195 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c, 1196 new Object [] { 1197 set, 1198 new Section("RFC822.SIZE BODY.PEEK", new Object [] { 1199 "HEADER.FIELDS", fields }) }); 1200 1201 IMAPResponse[] responses = communicate(command); 1202 1203 ArrayList result = new ArrayList (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 if (!responses[responses.length - 1].isOK()) 1220 throwException(responses[responses.length - 1]); 1221 1222 return (IMAPHeader[]) result.toArray(new IMAPHeader[0]); 1223 } 1224 1225 1234 public IMAPHeader[] fetchHeaderFields(SequenceSet set, String [] fields) 1235 throws IOException , IMAPException { 1236 return fetchHeaderFieldsCore("FETCH", set, fields); 1237 } 1238 1239 1248 public IMAPHeader[] uidFetchHeaderFields(SequenceSet set, String [] fields) 1249 throws IOException , IMAPException { 1250 return fetchHeaderFieldsCore("UID FETCH", set, fields); 1251 } 1252 1253 private IMAPHeader[] fetchHeaderCore(String c, SequenceSet set) 1254 throws IOException , IMAPException { 1255 checkState(SELECTED); 1256 1257 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c, 1258 new Object [] { 1259 set, 1260 new Section("RFC822.SIZE BODY.PEEK", 1261 new Object [] { "HEADER" }) }); 1262 1263 IMAPResponse[] responses = communicate(command); 1264 ArrayList result = new ArrayList (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 if (!responses[responses.length - 1].isOK()) 1280 throwException(responses[responses.length - 1]); 1281 1282 return (IMAPHeader[]) result.toArray(new IMAPHeader[0]); 1283 } 1284 1285 1293 public IMAPHeader[] fetchHeader(SequenceSet set) throws IOException , 1294 IMAPException { 1295 return fetchHeaderCore("FETCH", set); 1296 } 1297 1298 1306 public IMAPHeader[] uidFetchHeader(SequenceSet set) throws IOException , 1307 IMAPException { 1308 return fetchHeaderCore("UID FETCH", set); 1309 } 1310 1311 private InputStream fetchBodyCore(String c, int id, Integer [] address) 1312 throws IOException , IMAPException { 1313 checkState(SELECTED); 1314 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c, 1315 new Object [] { Integer.toString(id), 1316 new Section("BODY.PEEK", new Object [] { address }) }); 1317 1318 try { 1319 command.writeToStream(in, out); 1320 } catch (IOException e) { 1321 state = NOT_CONNECTED; 1322 throw e; 1323 } 1324 1325 return in.readBodyNonBlocking(); 1326 } 1327 1328 1337 public InputStream fetchBody(int index, Integer [] address) throws IOException , 1338 IMAPException { 1339 return fetchBodyCore("FETCH", index, address); 1340 } 1341 1342 1351 public InputStream uidFetchBody(int uid, Integer [] address) 1352 throws IOException , IMAPException { 1353 return fetchBodyCore("UID FETCH", uid, address); 1354 } 1355 1356 private MimeTree fetchBodystructureCore(String c, int id) 1357 throws IOException , IMAPException { 1358 checkState(SELECTED); 1359 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c, 1360 new Object [] { 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 1391 public MimeTree fetchBodystructure(int index) throws IOException , 1392 IMAPException { 1393 return fetchBodystructureCore("FETCH", index); 1394 } 1395 1396 1404 public MimeTree uidFetchBodystructure(int uid) throws IOException , 1405 IMAPException { 1406 return fetchBodystructureCore("UID FETCH", uid); 1407 } 1408 1409 private InputStream fetchMessageCore(String c, int id) throws IOException , 1410 IMAPException { 1411 checkState(SELECTED); 1412 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c, 1413 new Object [] { Integer.toString(id), 1414 "BODY.PEEK[]".toCharArray() }); 1415 1416 try { 1417 command.writeToStream(in, out); 1418 } catch (IOException e) { 1419 state = NOT_CONNECTED; 1420 throw e; 1421 } 1422 1423 try { 1424 return in.readBodyNonBlocking(); 1425 } catch (IOException e) { 1426 state = NOT_CONNECTED; 1427 throw e; 1428 } 1429 } 1430 1431 1439 public InputStream fetchMessage(int index) throws IOException , IMAPException { 1440 return fetchMessageCore("FETCH", index); 1441 } 1442 1443 1451 public InputStream uidFetchMessage(int uid) throws IOException , 1452 IMAPException { 1453 return fetchMessageCore("UID FETCH", uid); 1454 } 1455 1456 private InputStream fetchMimeHeaderSourceCore(String c, int id, 1457 Integer [] address) throws IOException , IMAPException { 1458 checkState(SELECTED); 1459 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c, 1460 new Object [] { 1461 Integer.toString(id), 1462 new Section("BODY.PEEK", new Object [] { address, 1463 ".MIME" }) }); 1464 1465 try { 1466 command.writeToStream(in, out); 1467 } catch (IOException e) { 1468 state = NOT_CONNECTED; 1469 throw e; 1470 } 1471 1472 return in.readBodyNonBlocking(); 1473 } 1474 1475 1484 public InputStream fetchMimeHeaderSource(int index, Integer [] address) 1485 throws IOException , IMAPException { 1486 return fetchMimeHeaderSourceCore("FETCH", index, address); 1487 } 1488 1489 1498 public InputStream uidFetchMimeHeaderSource(int uid, Integer [] address) 1499 throws IOException , IMAPException { 1500 return fetchMimeHeaderSourceCore("UID FETCH", uid, address); 1501 } 1502 1503 private CopyInfo copyCore(String c, SequenceSet set, String mailbox) 1504 throws IOException , IMAPException { 1505 checkState(SELECTED); 1506 1507 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c, 1508 new Object [] { set, MailboxNameUTF7Converter.encode(mailbox) }); 1509 1510 IMAPResponse[] responses = communicate(command); 1511 for (int i = 0; i < responses.length - 1; i++) { 1514 handleResponse(responses[i]); 1515 } 1516 1517 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 1545 public CopyInfo copy(SequenceSet set, String mailbox) throws IOException , 1546 IMAPException { 1547 return copyCore("COPY", set, mailbox); 1548 } 1549 1550 1561public CopyInfo uidCopy(SequenceSet set, String mailbox) throws IOException , 1562 IMAPException { 1563 return copyCore("UID COPY", set, mailbox); 1564 } 1565 1566 private IMAPFlags[] storeCore(String c, SequenceSet set, boolean type, 1567 IMAPFlags flags) throws IOException , IMAPException { 1568 checkState(SELECTED); 1569 1570 String flagType = type ? "+FLAGS.SILENT" : "-FLAGS.SILENT"; 1571 1572 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), c, 1573 new Object [] { set, flagType, flags }); 1574 1575 IMAPResponse[] responses = communicate(command); 1576 1577 ArrayList result = new ArrayList (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 } else { 1584 handleResponse(responses[i]); 1585 } 1586 } 1587 1588 if (!responses[responses.length - 1].isOK()) 1590 throwException(responses[responses.length - 1]); 1591 1592 return (IMAPFlags[]) result.toArray(new IMAPFlags[0]); 1593 } 1594 1595 1604 public void store(SequenceSet set, boolean type, IMAPFlags flags) 1605 throws IOException , IMAPException { 1606 storeCore("STORE", set, type, flags); 1607 } 1608 1609 1618 public void uidStore(SequenceSet set, boolean type, IMAPFlags flags) 1619 throws IOException , IMAPException { 1620 storeCore("UID STORE", set, type, flags); 1621 } 1622 1623 1630 public NamespaceCollection namespace() throws IOException , IMAPException { 1631 checkState(AUTHENTICATED); 1632 IMAPCommand command = new IMAPCommand(tagFactory.nextTag(), 1633 "NAMESPACE", new Object [] {}); 1634 1635 IMAPResponse[] responses = communicate(command); 1636 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 , IMAPException { 1662 1663 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 e1) { 1672 state = NOT_CONNECTED; 1673 throw e1; 1674 } 1675 1676 List responses = new LinkedList (); 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 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 { 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 (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 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 e) { 1744 state = NOT_CONNECTED; 1745 } 1746 } 1747 } catch (IOException e) { 1748 1749 } catch (IMAPException e) { 1750 } 1751 } 1752 } 1753 1754 1759 public void addIMAPListener(IMAPListener listener) { 1760 listeners.add(listener); 1761 } 1762 1763 1768 public void removeIMAPListener(IMAPListener listener) { 1769 listeners.remove(listener); 1770 } 1771 1772 void handleResponse(IMAPResponse response) throws IMAPException { 1773 Iterator it; 1774 if (response.isBYE()) { 1775 state = NOT_CONNECTED; 1776 try { 1778 in.close(); 1779 in = null; 1780 out.close(); 1781 out = null; 1782 } catch (IOException e) { 1783 } 1785 throw new IMAPDisconnectedException(response); 1786 } 1787 1788 if (response.getResponseSubType().equals("EXISTS")) { 1789 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 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 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 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 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 it = listeners.iterator(); 1842 while (it.hasNext()) { 1843 ((IMAPListener) it.next()).parseError(response 1844 .getResponseMessage()); 1845 } 1846 } 1847 } 1848 } 1849 1850 1853 public byte[] authReceive() throws AuthenticationException, IOException { 1854 try { 1855 IMAPResponse response; 1856 try { 1857 response = in.readResponse(); 1858 } catch (IOException 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 1876 public void authSend(byte[] call) throws IOException { 1877 out.write(Base64.encode(ByteBuffer.wrap(call), false).toString() 1878 .getBytes()); 1879 out.write('\r'); 1880 out.write('\n'); 1881 } 1882 1883 1886 public String getHostName() { 1887 return host; 1888 } 1889 1890 1893 public String getService() { 1894 return "imap"; 1895 } 1896 1897 1900 public String getSelectedMailbox() { 1901 return selectedMailbox; 1902 } 1903} | Popular Tags |