1 36 package org.columba.ristretto.smtp; 37 38 import java.io.IOException ; 39 import java.io.InputStream ; 40 import java.io.OutputStream ; 41 import java.net.InetAddress ; 42 import java.net.Socket ; 43 import java.net.SocketException ; 44 import java.nio.ByteBuffer ; 45 import java.util.LinkedList ; 46 import java.util.logging.Logger ; 47 48 import javax.net.ssl.SSLException; 49 import javax.net.ssl.SSLSocket; 50 51 import org.columba.ristretto.auth.AuthenticationException; 52 import org.columba.ristretto.auth.AuthenticationFactory; 53 import org.columba.ristretto.auth.AuthenticationMechanism; 54 import org.columba.ristretto.auth.AuthenticationServer; 55 import org.columba.ristretto.auth.NoSuchAuthenticationException; 56 import org.columba.ristretto.coder.Base64; 57 import org.columba.ristretto.config.RistrettoConfig; 58 import org.columba.ristretto.log.LogInputStream; 59 import org.columba.ristretto.log.LogOutputStream; 60 import org.columba.ristretto.log.RistrettoLogger; 61 import org.columba.ristretto.message.Address; 62 import org.columba.ristretto.parser.AddressParser; 63 import org.columba.ristretto.parser.ParserException; 64 import org.columba.ristretto.ssl.RistrettoSSLSocketFactory; 65 66 71 public class SMTPProtocol implements AuthenticationServer { 72 73 74 private static final Logger LOG = Logger 75 .getLogger("org.columba.ristretto.smtp"); 76 77 private static final byte[] STOPWORD = { '\r', '\n', '.', '\r', '\n' }; 78 79 private static final int DEFAULTPORT = 25; 80 81 84 public static final int NO_CONNECTION = 0; 85 86 89 public static final int NOT_CONNECTED = 0; 90 93 public static final int PLAIN = 1; 94 97 public static final int AUTHORIZED = 2; 98 99 100 103 public static final int TO = 0; 104 107 public static final int CC = 1; 108 109 private String host; 110 111 private int port; 112 113 private Socket socket; 114 115 protected SMTPInputStream in; 116 117 protected OutputStream out; 118 119 private int state; 120 121 129 public SMTPProtocol(String host, int port) { 130 this.host = host; 131 this.port = port; 132 } 133 134 141 public SMTPProtocol(String host) { 142 this(host, DEFAULTPORT); 143 } 144 145 152 public String openPort() throws IOException , SMTPException { 153 try { 154 socket = new Socket (host, port); 155 socket.setSoTimeout(RistrettoConfig.getInstance().getTimeout()); 156 157 createStreams(); 158 159 SMTPResponse response = readSingleLineResponse(); 160 if (response.isERR()) 161 throw new SMTPException(response.getMessage()); 162 String domain = response.getDomain(); 163 164 if (response.isHasSuccessor()) { 166 response = readSingleLineResponse(); 167 168 while (response.isHasSuccessor() && response.isOK()) { 169 response = readSingleLineResponse(); 170 } 171 } 172 173 state = PLAIN; 174 175 return domain; 176 } catch (SocketException e) { 177 e.printStackTrace(); 178 179 if (state != NOT_CONNECTED) 182 throw e; 183 else 184 return ""; 185 } 186 } 187 188 private void createStreams() throws IOException { 189 if (RistrettoLogger.logStream != null) { 190 in = new SMTPInputStream(new LogInputStream( 191 socket.getInputStream(), RistrettoLogger.logStream)); 192 out = new LogOutputStream(socket.getOutputStream(), 193 RistrettoLogger.logStream); 194 } else { 195 in = new SMTPInputStream(socket.getInputStream()); 196 out = socket.getOutputStream(); 197 198 } 199 } 200 201 208 public void startTLS() throws IOException , SSLException, SMTPException { 209 try { 210 sendCommand("STARTTLS", null); 211 212 SMTPResponse response = readSingleLineResponse(); 213 if (response.isERR()) 214 throw new SMTPException(response.getMessage()); 215 216 socket = RistrettoSSLSocketFactory.getInstance().createSocket( 217 socket, host, port, true); 218 219 ((SSLSocket) socket).startHandshake(); 221 222 createStreams(); 223 } catch (SocketException e) { 224 if (state != NOT_CONNECTED) 227 throw e; 228 } 229 230 } 231 232 protected void sendCommand(String command, String [] parameters) 233 throws IOException { 234 try { 235 out.write(command.getBytes()); 237 238 if (parameters != null) { 240 for (int i = 0; i < parameters.length; i++) { 241 out.write(' '); 242 out.write(parameters[i].getBytes()); 243 } 244 } 245 246 out.write('\r'); 248 out.write('\n'); 249 250 out.flush(); 252 } catch (IOException e) { 253 state = NOT_CONNECTED; 254 throw e; 255 } 256 } 257 258 271 public String [] ehlo(InetAddress domain) throws IOException , SMTPException { 272 ensureState(PLAIN); 273 try { 274 275 LinkedList capas = new LinkedList (); 276 String ipaddress = domain.getHostAddress(); 277 278 sendCommand("EHLO", new String [] { "[" + ipaddress + "]" }); 279 280 SMTPResponse response = readSingleLineResponse(); 282 if (response.isERR()) { 283 throw new SMTPException(response.getMessage()); 284 } 285 286 if (response.isHasSuccessor()) { 287 response = readSingleLineResponse(); 288 289 while (response.isHasSuccessor() && response.isOK()) { 290 capas.add(response.getMessage()); 291 response = readSingleLineResponse(); 292 } 293 capas.add(response.getMessage()); 294 } 295 296 return (String []) capas.toArray(new String [] {}); 297 298 } catch (SocketException e) { 299 if (state != NOT_CONNECTED) 302 throw e; 303 304 else 305 return new String [0]; 306 } 307 308 } 309 310 320 public void helo(InetAddress domain) throws IOException , SMTPException { 321 ensureState(PLAIN); 322 try { 323 String ipaddress = domain.getHostAddress(); 324 325 sendCommand("HELO", new String [] { "[" + ipaddress + "]" }); 326 327 SMTPResponse response = readSingleLineResponse(); 328 if (response.isERR()) 329 throw new SMTPException(response); 330 } catch (SocketException e) { 331 if (state != NOT_CONNECTED) 334 throw e; 335 } 336 337 } 338 339 359 public void auth(String algorithm, String user, char[] password) 360 throws IOException , SMTPException, AuthenticationException { 361 ensureState(PLAIN); 362 try { 363 try { 364 AuthenticationMechanism auth = AuthenticationFactory 365 .getInstance().getAuthentication(algorithm); 366 sendCommand("AUTH", new String [] { algorithm }); 367 368 auth.authenticate(this, user, password); 369 } catch (NoSuchAuthenticationException e) { 370 throw new SMTPException(e); 371 } 372 373 SMTPResponse response = readSingleLineResponse(); 374 if (response.isERR()) 375 throw new SMTPException(response); 376 377 state = AUTHORIZED; 378 } catch (SocketException e) { 379 if (state != NOT_CONNECTED) 382 throw e; 383 } 384 385 } 386 387 399 public void mail(Address from) throws IOException , SMTPException { 400 ensureState(PLAIN); 401 try { 402 sendCommand("MAIL", new String [] { "FROM:" 403 + from.getCanonicalMailAddress() }); 404 405 SMTPResponse response = readSingleLineResponse(); 406 if (response.isERR()) 407 throw new SMTPException(response); 408 } catch (SocketException e) { 409 if (state != NOT_CONNECTED) 412 throw e; 413 } 414 415 } 416 417 430 public void rcpt(Address address) throws IOException , SMTPException { 431 try { 432 sendCommand("RCPT", new String [] { "TO:" 433 + address.getCanonicalMailAddress() }); 434 435 SMTPResponse response = readSingleLineResponse(); 436 if (response.isERR()) 437 throw new SMTPException(response); 438 } catch (SocketException e) { 439 if (state != NOT_CONNECTED) 442 throw e; 443 } 444 445 } 446 447 464 public void rcpt(int type, Address address) throws IOException , 465 SMTPException { 466 try { 467 switch (type) { 468 case TO: { 469 sendCommand("RCPT", new String [] { "TO:" 470 + address.getCanonicalMailAddress() }); 471 break; 472 } 473 474 case CC: { 475 sendCommand("RCPT", new String [] { "CC:" 476 + address.getCanonicalMailAddress() }); 477 break; 478 } 479 } 480 481 SMTPResponse response = readSingleLineResponse(); 482 if (response.isERR()) 483 throw new SMTPException(response); 484 } catch (SocketException e) { 485 if (state != NOT_CONNECTED) 488 throw e; 489 } 490 491 } 492 493 505 public void data(InputStream data) throws IOException , SMTPException { 506 ensureState(PLAIN); 507 508 try { 509 sendCommand("DATA", null); 510 511 SMTPResponse response = readSingleLineResponse(); 512 if (response.getCode() == 354) { 513 try { 514 copyStream(new StopWordSafeInputStream(data), out); 515 out.write(STOPWORD); 516 out.flush(); 517 } catch (IOException e) { 518 state = NOT_CONNECTED; 519 throw e; 520 } 521 } else { 522 throw new SMTPException(response); 523 } 524 525 response = readSingleLineResponse(); 526 if (response.isERR()) 527 throw new SMTPException(response); 528 } catch (SocketException e) { 529 if (state != NOT_CONNECTED) 532 throw e; 533 } 534 } 535 536 542 public void quit() throws IOException , SMTPException { 543 try { 544 sendCommand("QUIT", null); 545 546 socket.close(); 547 in = null; 548 out = null; 549 socket = null; 550 551 state = NOT_CONNECTED; 552 } catch (SocketException e) { 553 if (state != NOT_CONNECTED) 556 throw e; 557 } 558 } 559 560 566 public void reset() throws IOException , SMTPException { 567 try { 568 sendCommand("RSET", null); 569 570 SMTPResponse response = readSingleLineResponse(); 571 if (response.isERR()) 572 throw new SMTPException(response); 573 } catch (SocketException e) { 574 if (state != NOT_CONNECTED) 577 throw e; 578 } 579 580 } 581 582 590 public void verify(String address) throws IOException , SMTPException { 591 try { 592 sendCommand("VRFY", new String [] { address }); 593 594 SMTPResponse response = readSingleLineResponse(); 595 if (response.isERR()) 596 throw new SMTPException(response); 597 } catch (SocketException e) { 598 if (state != NOT_CONNECTED) 601 throw e; 602 } 603 604 } 605 606 615 public Address [] expand(Address mailinglist) throws IOException , 616 SMTPException { 617 ensureState(PLAIN); 618 try { 619 LinkedList addresses = new LinkedList (); 620 sendCommand("VRFY", new String [] { mailinglist 621 .getCanonicalMailAddress() }); 622 623 SMTPResponse response = readSingleLineResponse(); 625 if (response.isERR()) { 626 throw new SMTPException(response); 627 } 628 629 if (response.isHasSuccessor()) { 630 response = readSingleLineResponse(); 631 632 while (response.isHasSuccessor() && response.isOK()) { 633 try { 634 addresses.add(AddressParser.parseAddress(response 635 .getMessage())); 636 } catch (ParserException e) { 637 LOG.severe(e.getLocalizedMessage()); 638 } 639 response = readSingleLineResponse(); 640 } 641 try { 642 addresses.add(AddressParser.parseAddress(response 643 .getMessage())); 644 } catch (ParserException e) { 645 LOG.severe(e.getMessage()); 646 } 647 } 648 649 return (Address []) addresses.toArray(new Address [] {}); 650 } catch (SocketException e) { 651 if (state != NOT_CONNECTED) 654 throw e; 655 656 else 657 return new Address [0]; 658 } 659 } 660 661 667 public void noop() throws IOException , SMTPException { 668 ensureState(PLAIN); 669 try { 670 sendCommand("NOOP", null); 671 SMTPResponse response = readSingleLineResponse(); 672 if (response.isERR()) 673 throw new SMTPException(response.getMessage()); 674 } catch (SocketException e) { 675 if (state != NOT_CONNECTED) 678 throw e; 679 } 680 681 } 682 683 private void copyStream(InputStream in, OutputStream out) 684 throws IOException { 685 byte[] buffer = new byte[10240]; 686 int copied = 0; 687 int read; 688 689 read = in.read(buffer); 690 while (read != -1) { 691 out.write(buffer, 0, read); 692 copied += read; 693 read = in.read(buffer); 694 } 695 } 696 697 700 public byte[] authReceive() throws AuthenticationException, IOException { 701 702 try { 703 SMTPResponse response = in.readSingleLineResponse(); 704 705 if (response.isOK()) { 706 if (response.getMessage() != null) { 707 return Base64.decodeToArray(response.getMessage()); 708 } else { 709 return new byte[0]; 710 } 711 712 } else { 713 throw new AuthenticationException(new SMTPException(response)); 714 } 715 } catch (SMTPException e) { 716 throw new AuthenticationException(e); 717 } 718 } 719 720 723 public void authSend(byte[] call) throws IOException { 724 sendCommand(Base64.encode(ByteBuffer.wrap(call), false).toString(), 725 null); 726 } 727 728 731 public int getState() { 732 return state; 733 } 734 735 738 public String getHostName() { 739 return host; 740 } 741 742 745 public String getService() { 746 return "smtp"; 747 } 748 749 755 public void dropConnection() throws IOException { 756 if (state != NOT_CONNECTED) { 757 state = NOT_CONNECTED; 758 759 socket.close(); 760 in = null; 761 out = null; 762 socket = null; 763 } 764 } 765 766 private void ensureState(int s) throws SMTPException { 767 if (state < s) 768 throw new SMTPException("Wrong state!"); 769 } 770 771 protected SMTPResponse readSingleLineResponse() throws IOException , SMTPException{ 772 try { 773 return in.readSingleLineResponse(); 774 } catch (IOException e) { 775 state = NOT_CONNECTED; 776 throw e; 777 } 778 } 779 780 } | Popular Tags |