View Javadoc
1 /* 2 * @(#)NetworkClient.java 1.33 01/12/03 3 * 4 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 5 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 6 */ 7 package org.masukomi.aspirin.core; 8 9 import sun.io.CharToByteConverter; 10 11 import java.io.*; 12 13 import java.net.InetAddress; 14 import java.net.InetSocketAddress; 15 import java.net.Socket; 16 17 //import java.net.URL; 18 import java.net.UnknownHostException; 19 20 import java.util.Arrays; 21 import java.util.Vector; 22 23 24 /*** 25 * This is the base class for network clients. 26 * 27 * @version 1.33, 12/03/01 28 * @author Jonathan Payne 29 */ 30 public class SMTPClient { 31 //~ Static fields/initializers --------------------------------------------- 32 33 /*** DOCUMENT ME! */ 34 static final boolean debug = false; 35 36 /*** DOCUMENT ME! */ 37 protected static int defaultSoTimeout; 38 39 /*** DOCUMENT ME! */ 40 protected static int defaultConnectTimeout; 41 42 /* Name of encoding to use for output */ 43 44 /*** DOCUMENT ME! */ 45 protected static String encoding; 46 47 static { 48 Integer tm = (Integer) java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { 49 public Object run() { 50 return (Integer.getInteger( 51 "sun.net.client.defaultReadTimeout")); 52 } 53 }); 54 55 if (tm == null) { 56 defaultSoTimeout = -1; 57 } else { 58 defaultSoTimeout = tm.intValue(); 59 } 60 61 tm = (Integer) java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { 62 public Object run() { 63 return (Integer.getInteger( 64 "sun.net.client.defaultConnectTimeout")); 65 } 66 }); 67 68 if (tm == null) { 69 defaultConnectTimeout = -1; 70 } else { 71 defaultConnectTimeout = tm.intValue(); 72 } 73 74 encoding = (String) java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction( 75 "file.encoding", "ISO8859_1")); 76 77 try { 78 if (!isASCIISuperset(encoding)) { 79 encoding = "ISO8859_1"; 80 } 81 } catch (Exception e) { 82 encoding = "ISO8859_1"; 83 } 84 } 85 86 //~ Instance fields -------------------------------------------------------- 87 88 /*** Buffered stream for reading replies from server. */ 89 public InputStream serverInput; 90 91 /*** Stream for printing to the server. */ 92 public PrintStream serverOutput; 93 94 /*** Socket for communicating with server. */ 95 protected Socket serverSocket = null; 96 97 /*** Array of strings (usually 1 entry) for the last reply 98 from the server. */ 99 protected Vector serverResponse = new Vector(1); 100 101 /*** code for last reply */ 102 protected int lastReplyCode; 103 104 /*** DOCUMENT ME! */ 105 SmtpPrintStream message; 106 107 /*** DOCUMENT ME! */ 108 String mailhost; 109 110 //~ Constructors ----------------------------------------------------------- 111 112 /*** Create connection with host <i>host</i> on port <i>port</i> */ 113 public SMTPClient(String host, int port) throws IOException { 114 openServer(host, port); 115 } 116 117 /*** New SMTP client connected to host <i>host</i>. */ 118 public SMTPClient(String host) throws IOException { 119 super(); 120 121 if (host != null) { 122 try { 123 openServer(host); 124 mailhost = host; 125 126 return; 127 } catch (Exception e) { 128 } 129 } 130 131 try { 132 String s; 133 mailhost = (String) java.security.AccessController.doPrivileged(new sun.security.action.GetPropertyAction( 134 "mail.host")); 135 136 if (mailhost != null) { 137 openServer(mailhost); 138 139 return; 140 } 141 } catch (Exception e) { 142 } 143 144 try { 145 mailhost = "localhost"; 146 openServer(mailhost); 147 } catch (Exception e) { 148 mailhost = "mailhost"; 149 openServer(mailhost); 150 } 151 } 152 153 /*** Create an uninitialized SMTP client. */ 154 public SMTPClient() throws IOException { 155 this(null); 156 } 157 158 //~ Methods ---------------------------------------------------------------- 159 160 /*** 161 * DOCUMENT ME! 162 * 163 * @return DOCUMENT ME! 164 */ 165 public String getMailHost() { 166 return mailhost; 167 } 168 169 /*** converts the server response into a string. */ 170 public String getResponseString() { 171 return (String) serverResponse.elementAt(0); 172 } 173 174 /*** Returns all server response strings. */ 175 public Vector getResponseStrings() { 176 return serverResponse; 177 } 178 179 /*** 180 * issue the QUIT command to the SMTP server and close the connection. 181 */ 182 public void closeServer() throws IOException { 183 if (serverIsOpen()) { 184 closeMessage(); 185 issueCommand("QUIT\r\n", 221); 186 serverSocket.close(); 187 serverSocket = null; 188 serverInput = null; 189 serverOutput = null; 190 } 191 } 192 193 /*** 194 * DOCUMENT ME! 195 * 196 * @param s DOCUMENT ME! 197 * 198 * @throws IOException DOCUMENT ME! 199 */ 200 public void from(String s) throws IOException { 201 if (s.startsWith("<")) { 202 issueCommand("mail from: " + s + "\r\n", 250); 203 } else { 204 issueCommand("mail from: <" + s + ">\r\n", 250); 205 } 206 } 207 208 /*** Open a connection to the server. */ 209 public void openServer(String server, int port) 210 throws IOException, UnknownHostException { 211 if (serverSocket != null) { 212 closeServer(); 213 } 214 215 serverSocket = doConnect(server, port); 216 217 try { 218 serverOutput = new PrintStream(new BufferedOutputStream( 219 serverSocket.getOutputStream()), true, encoding); 220 } catch (UnsupportedEncodingException e) { 221 throw new InternalError(encoding + "encoding not found"); 222 } 223 224 serverInput = new BufferedInputStream(serverSocket.getInputStream()); 225 } 226 227 /*** 228 * Pulls the response from the server and returns the code as a 229 * number. Returns -1 on failure. 230 */ 231 public int readServerResponse() throws IOException { 232 StringBuffer replyBuf = new StringBuffer(32); 233 int c; 234 int continuingCode = -1; 235 int code; 236 String response; 237 238 serverResponse.setSize(0); 239 240 while (true) { 241 while ((c = serverInput.read()) != -1) { 242 if (c == '\r') { 243 if ((c = serverInput.read()) != '\n') { 244 replyBuf.append('\r'); 245 } 246 } 247 248 replyBuf.append((char) c); 249 250 if (c == '\n') { 251 break; 252 } 253 } 254 255 response = replyBuf.toString(); 256 replyBuf.setLength(0); 257 258 if (debug) { 259 System.out.print(response); 260 } 261 262 if (response.length() == 0) { 263 code = -1; 264 } else { 265 try { 266 code = Integer.parseInt(response.substring(0, 3)); 267 } catch (NumberFormatException e) { 268 code = -1; 269 } catch (StringIndexOutOfBoundsException e) { 270 /* this line doesn't contain a response code, so 271 we just completely ignore it */ 272 continue; 273 } 274 } 275 276 serverResponse.addElement(response); 277 278 if (continuingCode != -1) { 279 /* we've seen a XXX- sequence */ 280 if ((code != continuingCode) 281 || ((response.length() >= 4) 282 && (response.charAt(3) == '-'))) { 283 continue; 284 } else { 285 /* seen the end of code sequence */ 286 continuingCode = -1; 287 288 break; 289 } 290 } else if ((response.length() >= 4) && (response.charAt(3) == '-')) { 291 continuingCode = code; 292 293 continue; 294 } else { 295 break; 296 } 297 } 298 299 return lastReplyCode = code; 300 } 301 302 /*** Sends command <i>cmd</i> to the server. */ 303 public void sendServer(String cmd) { 304 serverOutput.print(cmd); 305 306 if (debug) { 307 System.out.print("Sending: " + cmd); 308 } 309 } 310 311 /*** Return server connection status */ 312 public boolean serverIsOpen() { 313 return serverSocket != null; 314 } 315 316 /*** 317 * DOCUMENT ME! 318 * 319 * @return DOCUMENT ME! 320 * 321 * @throws IOException DOCUMENT ME! 322 * @throws InternalError DOCUMENT ME! 323 */ 324 public PrintStream startMessage() throws IOException { 325 issueCommand("data\r\n", 354); 326 327 try { 328 message = new SmtpPrintStream(serverOutput, this); 329 } catch (UnsupportedEncodingException e) { 330 throw new InternalError(encoding + " encoding not found"); 331 } 332 333 return message; 334 } 335 336 /*** 337 * DOCUMENT ME! 338 * 339 * @param s DOCUMENT ME! 340 * 341 * @throws IOException DOCUMENT ME! 342 */ 343 public void to(String s) throws IOException { 344 int st = 0; 345 int limit = s.length(); 346 int pos = 0; 347 int lastnonsp = 0; 348 int parendepth = 0; 349 boolean ignore = false; 350 351 while (pos < limit) { 352 int c = s.charAt(pos); 353 354 if (parendepth > 0) { 355 if (c == '(') { 356 parendepth++; 357 } else if (c == ')') { 358 parendepth--; 359 } 360 361 if (parendepth == 0) { 362 if (lastnonsp > st) { 363 ignore = true; 364 } else { 365 st = pos + 1; 366 } 367 } 368 } else if (c == '(') { 369 parendepth++; 370 } else if (c == '<') { 371 st = lastnonsp = pos + 1; 372 } else if (c == '>') { 373 ignore = true; 374 } else if (c == ',') { 375 if (lastnonsp > st) { 376 toCanonical(s.substring(st, lastnonsp)); 377 } 378 379 st = pos + 1; 380 ignore = false; 381 } else { 382 if ((c > ' ') && !ignore) { 383 lastnonsp = pos + 1; 384 } else if (st == pos) { 385 st++; 386 } 387 } 388 389 pos++; 390 } 391 392 if (lastnonsp > st) { 393 toCanonical(s.substring(st, lastnonsp)); 394 } 395 } 396 397 /*** 398 * DOCUMENT ME! 399 * 400 * @return DOCUMENT ME! 401 * 402 * @throws IOException DOCUMENT ME! 403 */ 404 protected InetAddress getLocalAddress() throws IOException { 405 if (serverSocket == null) { 406 throw new IOException("not connected"); 407 } 408 409 return serverSocket.getLocalAddress(); 410 } 411 412 /*** 413 * Return a socket connected to the server, with any 414 * appropriate options pre-established 415 */ 416 protected Socket doConnect(String server, int port) 417 throws IOException, UnknownHostException { 418 Socket s = new Socket(); 419 420 if (defaultConnectTimeout > 0) { 421 s.connect(new InetSocketAddress(server, port), defaultConnectTimeout); 422 } else { 423 s.connect(new InetSocketAddress(server, port)); 424 } 425 426 if (defaultSoTimeout > 0) { 427 s.setSoTimeout(defaultSoTimeout); 428 } 429 430 return s; 431 } 432 433 /*** 434 * DOCUMENT ME! 435 * 436 * @return DOCUMENT ME! 437 */ 438 String getEncoding() { 439 return encoding; 440 } 441 442 /*** 443 * DOCUMENT ME! 444 * 445 * @throws IOException DOCUMENT ME! 446 */ 447 void closeMessage() throws IOException { 448 if (message != null) { 449 message.close(); 450 } 451 } 452 453 /*** 454 * DOCUMENT ME! 455 * 456 * @param cmd DOCUMENT ME! 457 * @param expect DOCUMENT ME! 458 * 459 * @throws IOException DOCUMENT ME! 460 * @throws SmtpProtocolException DOCUMENT ME! 461 */ 462 void issueCommand(String cmd, int expect) throws IOException { 463 464 sendServer(cmd); 465 466 int reply; 467 468 while ((reply = readServerResponse()) != expect) { 469 if (reply != 220) { 470 throw new SmtpProtocolException(getResponseString()); 471 } 472 } 473 } 474 475 /*** 476 * Test the named character encoding to verify that it converts ASCII 477 * characters correctly. We have to use an ASCII based encoding, or else 478 * the NetworkClients will not work correctly in EBCDIC based systems. 479 * However, we cannot just use ASCII or ISO8859_1 universally, because in 480 * Asian locales, non-ASCII characters may be embedded in otherwise 481 * ASCII based protocols (eg. HTTP). The specifications (RFC2616, 2398) 482 * are a little ambiguous in this matter. For instance, RFC2398 [part 2.1] 483 * says that the HTTP request URI should be escaped using a defined 484 * mechanism, but there is no way to specify in the escaped string what 485 * the original character set is. It is not correct to assume that 486 * UTF-8 is always used (as in URLs in HTML 4.0). For this reason, 487 * until the specifications are updated to deal with this issue more 488 * comprehensively, and more importantly, HTTP servers are known to 489 * support these mechanisms, we will maintain the current behavior 490 * where it is possible to send non-ASCII characters in their original 491 * unescaped form. 492 */ 493 private static boolean isASCIISuperset(String encoding) 494 throws Exception { 495 String chkS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" 496 + "abcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,"; 497 498 // Expected byte sequence for string above 499 byte[] chkB = { 500 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 501 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 502 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 503 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 45, 504 95, 46, 33, 126, 42, 39, 40, 41, 59, 47, 63, 58, 64, 38, 61, 43, 36, 505 44 506 }; 507 508 CharToByteConverter ctob = CharToByteConverter.getConverter(encoding); 509 byte[] b = ctob.convertAll(chkS.toCharArray()); 510 511 return Arrays.equals(b, chkB); 512 } 513 514 /*** open a SMTP connection to host <i>host</i>. */ 515 private void openServer(String host) throws IOException { 516 mailhost = host; 517 openServer(mailhost, 25); 518 issueCommand("helo " + InetAddress.getLocalHost().getHostName() 519 + "\r\n", 250); 520 } 521 522 /*** 523 * DOCUMENT ME! 524 * 525 * @param s DOCUMENT ME! 526 * 527 * @throws IOException DOCUMENT ME! 528 */ 529 private void toCanonical(String s) throws IOException { 530 if (s.startsWith("<")) { 531 issueCommand("rcpt to: " + s + "\r\n", 250); 532 } else { 533 issueCommand("rcpt to: <" + s + ">\r\n", 250); 534 } 535 } 536 } 537 538 539 /*** 540 * DOCUMENT ME! 541 * 542 * @author $author$ 543 * @version $Revision: 1.1 $ 544 */ 545 class SmtpPrintStream extends java.io.PrintStream { 546 //~ Instance fields -------------------------------------------------------- 547 548 /*** DOCUMENT ME! */ 549 private SMTPClient target; 550 551 /*** DOCUMENT ME! */ 552 private int lastc = '\n'; 553 554 //~ Constructors ----------------------------------------------------------- 555 556 /*** 557 * Creates a new SmtpPrintStream object. 558 * 559 * @param fos DOCUMENT ME! 560 * @param cl DOCUMENT ME! 561 * 562 * @throws UnsupportedEncodingException DOCUMENT ME! 563 */ 564 SmtpPrintStream(OutputStream fos, SMTPClient cl) 565 throws UnsupportedEncodingException { 566 super(fos, false, cl.getEncoding()); 567 target = cl; 568 } 569 570 //~ Methods ---------------------------------------------------------------- 571 572 /*** 573 * DOCUMENT ME! 574 */ 575 public void close() { 576 if (target == null) { 577 return; 578 } 579 580 if (lastc != '\n') { 581 write('\n'); 582 } 583 584 try { 585 target.issueCommand(".\r\n", 250); 586 target.message = null; 587 out = null; 588 target = null; 589 } catch (IOException e) { 590 } 591 } 592 593 /*** 594 * DOCUMENT ME! 595 * 596 * @param s DOCUMENT ME! 597 */ 598 public void print(String s) { 599 int len = s.length(); 600 601 for (int i = 0; i < len; i++) { 602 write(s.charAt(i)); 603 } 604 } 605 606 /*** 607 * DOCUMENT ME! 608 * 609 * @param b DOCUMENT ME! 610 */ 611 public void write(int b) { 612 try { 613 // quote a dot at the beginning of a line 614 if ((lastc == '\n') && (b == '.')) { 615 out.write('.'); 616 } 617 618 // translate NL to CRLF 619 if ((b == '\n') && (lastc != '\r')) { 620 out.write('\r'); 621 } 622 623 out.write(b); 624 lastc = b; 625 } catch (IOException e) { 626 } 627 } 628 629 /*** 630 * DOCUMENT ME! 631 * 632 * @param b DOCUMENT ME! 633 * @param off DOCUMENT ME! 634 * @param len DOCUMENT ME! 635 */ 636 public void write(byte[] b, int off, int len) { 637 try { 638 int lc = lastc; 639 640 while (--len >= 0) { 641 int c = b[off++]; 642 643 // quote a dot at the beginning of a line 644 if ((lc == '\n') && (c == '.')) { 645 out.write('.'); 646 } 647 648 // translate NL to CRLF 649 if ((c == '\n') && (lc != '\r')) { 650 out.write('\r'); 651 } 652 653 out.write(c); 654 lc = c; 655 } 656 657 lastc = lc; 658 } catch (IOException e) { 659 } 660 } 661 } 662

This page was automatically generated by Maven