1 21 package org.jsmtpd.plugins.deliveryServices; 22 23 import java.io.BufferedWriter ; 24 import java.io.ByteArrayOutputStream ; 25 import java.io.IOException ; 26 import java.io.OutputStreamWriter ; 27 import java.net.Inet4Address ; 28 import java.net.InetSocketAddress ; 29 import java.net.Socket ; 30 import java.net.SocketAddress ; 31 import java.util.ArrayList ; 32 import java.util.HashSet ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import java.util.Set ; 36 import java.util.Stack ; 37 38 import org.apache.commons.logging.Log; 39 import org.apache.commons.logging.LogFactory; 40 import org.jsmtpd.core.common.delivery.FatalDeliveryException; 41 import org.jsmtpd.core.common.delivery.TemporaryDeliveryException; 42 import org.jsmtpd.core.common.io.BareLFException; 43 import org.jsmtpd.core.common.io.InputSizeToBig; 44 import org.jsmtpd.core.common.io.commandStream.MultiLineCommandStreamParser; 45 import org.jsmtpd.core.mail.Email; 46 import org.jsmtpd.core.mail.Rcpt; 47 import org.jsmtpd.tools.Base64Helper; 48 49 57 public class SmtpSender { 58 59 62 private String smtpHost; 63 66 private Email e; 67 70 private List rcpts; 71 74 private Inet4Address server; 75 76 private Log log=LogFactory.getLog(SmtpSender.class); 77 80 private Socket sock = null; 81 82 85 private int serverPort; 86 87 90 private BufferedWriter wr = null; 91 92 private MultiLineCommandStreamParser csp; 93 94 private int connectionTimeout; 95 96 private String login; 97 private String password; 98 private String authMethod="none"; 99 private String helloCommand="HELO"; 100 108 public SmtpSender(String smtpHost, Email in, Inet4Address server, int serverPort, List rcpts, int connectionTimeout) { 109 this.smtpHost = smtpHost; 110 this.e = in; 111 this.server = server; 112 this.rcpts = rcpts; 113 this.connectionTimeout=connectionTimeout; 114 this.serverPort=serverPort; 115 } 116 127 public SmtpSender(String smtpHost, Email in, Inet4Address server, int serverPort, List rcpts, int connectionTimeout,String authMethod,String login, String password) { 128 this.smtpHost = smtpHost; 129 this.e = in; 130 this.server = server; 131 this.rcpts = rcpts; 132 this.connectionTimeout=connectionTimeout; 133 this.login=login; 134 this.password=password; 135 this.authMethod=authMethod; 136 this.serverPort=serverPort; 137 } 138 139 private void performAuth () throws TemporaryDeliveryException, FatalDeliveryException { 140 if ((authMethod==null)||(authMethod.equals("none"))) 141 return; 142 143 if (authMethod.equals("plain")) { 144 log.debug("Performing authentication ..."); 145 ByteArrayOutputStream bos = new ByteArrayOutputStream (); 146 try { 147 bos.write(login.getBytes()); 148 bos.write('\0'); 149 bos.write(password.getBytes()); 150 } catch (IOException e) { 151 } 152 153 String lpEncoded = Base64Helper.encode(bos.toByteArray()); 154 send("AUTH PLAIN "+lpEncoded); 155 String response = receive(); 156 log.debug("Auth replied: "+response); 157 if ((response!=null)&&(response.startsWith("235"))) { 158 log.debug("Authenticated as "+login); 159 return; 160 } else { 161 log.warn("Remote server rejected authentication: "+response); 162 throw new FatalDeliveryException("Remote host rejected authentication"); 163 } 164 } 165 166 } 167 private List <String > multilineReceive () throws TemporaryDeliveryException, FatalDeliveryException { 169 List <String > response = new ArrayList <String >(); 170 while (true) { 171 String buffer = receive(); 172 if ((buffer==null)||buffer.equals("")) 173 throw new TemporaryDeliveryException("Remote server issued a null response"); 174 175 response.add(buffer); 176 if (buffer.charAt(3)==' ') break; 178 if (buffer.charAt(3)!='-') throw new TemporaryDeliveryException("Server issued somthing I don't understaind."); 180 } 181 return response; 182 } 183 188 public void doDelivery() throws TemporaryDeliveryException, FatalDeliveryException { 189 Stack <Rcpt> successfullRcpt=new Stack <Rcpt>(); 190 int respCode; 191 String respString; 192 try { 193 init(); 194 } catch (IOException ioe) { 195 log.warn("Error connecting to " + server.toString()); 196 closeConnection(); 197 throw new TemporaryDeliveryException("Error connecting to " + server.toString(),ioe); 198 } 199 200 try { 201 List <String > responses = multilineReceive(); 203 if (responses.size()==0) { 204 log.warn("Error while chatting with server. No response(s)"); 205 throw new TemporaryDeliveryException("Error chatting with " + server.toString()+", I could not read a response to connection"); 206 } 207 if (parseResponse(responses.get(responses.size()-1)) != RESP_HELO_FIRST) { 208 log.warn("Error while chatting with server"); 209 throw new TemporaryDeliveryException("Error chatting with " + server.toString()+", I could not find a valid welcome response from server"); 210 } 211 212 213 214 221 222 233 234 send(helloCommand+" " + smtpHost); 235 239 240 responses = multilineReceive(); 241 if (responses.size()==0) { 242 log.warn("Error while chatting with server. No response(s)"); 243 throw new TemporaryDeliveryException("Error chatting with " + server.toString()+", server did not respond to my HELO command (no response)"); 244 } 245 if ((parseResponse(responses.get(responses.size()-1))!=RESP_OK)) { 246 log.warn("Temporary Error while chatting with server (" + server.getHostAddress() + ") for delivering mail " + e.getDiskName() 247 + ", server said: " + responses.get(responses.size())); 248 throw new TemporaryDeliveryException("Error chatting with " + server.toString()+", Error in HELO command (server did not sent me a ok response for helo command)"); 249 } 250 251 267 268 performAuth(); 269 270 281 if (e.getFrom().toString().equals("<>")) 282 send("MAIL FROM:" + e.getFrom() + ""); else 284 send("MAIL FROM:<" + e.getFrom() + ">"); 285 286 respString = receive(); 287 respCode = parseResponse(respString); 288 289 if (respCode != RESP_OK) { 290 if ((respCode == 451) || (respCode == 452)) { 291 log.warn("Temporary Error while chatting with server (" + server.getHostAddress() + ") for delivering mail " + e.getDiskName() 292 + ", command=MAIL FROM, server said: " + respString); 293 throw new TemporaryDeliveryException("Error chatting with " + server.toString()+", could not issue mail from command"); 294 } else { 295 log.warn("Fatal Error while chatting with server (" + server.getHostAddress() + ") for delivering mail " + e.getDiskName() 296 + ", command=MAIL FROM, server said: " + respString); 297 for (Iterator iter = rcpts.iterator(); iter.hasNext();) { 298 Rcpt element = (Rcpt) iter.next(); 299 element.setLastError(respString); 300 } 301 throw new FatalDeliveryException("Error while sending FROM to server "+server.toString()+", server replied "+respString); 302 } 303 } 304 305 325 Set <Rcpt> tempFailure = new HashSet <Rcpt>(); 326 for (Iterator iter = rcpts.iterator(); iter.hasNext();) { 327 Rcpt oneRcpt = (Rcpt) iter.next(); 328 send("RCPT TO:<" + oneRcpt.getEmailAddress().toString() + ">"); 329 respString = receive(); 330 respCode = parseResponse(respString); 331 if (respCode != RESP_OK) { 332 if ( (respCode == 450) || (respCode == 451) || (respCode == 452)) { 334 log.warn("RSMPT> Temporary Error while chatting with server (" + server.getHostAddress() + ") for delivering mail " + e.getDiskName() 335 + ", command=RCPT " + oneRcpt.getEmailAddress().toString() + ", server said: " + respString); 336 oneRcpt.setDelivered(Rcpt.STATUS_ERROR_NOT_FATAL); 337 oneRcpt.setLastError("Recipient rejected: "+respString); 338 tempFailure.add(oneRcpt); 339 } else { 340 log.warn("Fatal Error while chatting with server (" + server.getHostAddress() + ") for delivering mail " + e.getDiskName() 341 + ", command=RCPT " + oneRcpt.getEmailAddress().toString() + ", server said: " + respString); 342 oneRcpt.setDelivered(Rcpt.STATUS_ERROR_FATAL); 343 oneRcpt.setLastError("Remote server permanently rejected recipient : "+respString); 344 } 345 } else { 346 successfullRcpt.push(oneRcpt); } 348 } 349 350 if ((successfullRcpt.size()==0) && (tempFailure.size()==0) ){ 351 log.warn("all recipient(s) where rejected"); 352 throw new FatalDeliveryException("All recipient where rejected, and none is in temporary error."); 353 } 354 355 if ((successfullRcpt.size()==0) && (tempFailure.size()>0) ){ log.warn("There is no valid recipient this time. Abort chat"); 357 return; 358 } 359 360 376 377 send("DATA"); 378 respString=receive(); 379 if (parseResponse(respString) != RESP_DATA_OK) { 380 if (respString.startsWith("5")) { 381 log.warn("Fatal error while chatting with server during DATA"); 382 for (Iterator iter = rcpts.iterator(); iter.hasNext();) { 383 Rcpt element = (Rcpt) iter.next(); 384 element.setLastError(respString); 385 } 386 throw new FatalDeliveryException("Could not send data command to server, received a permanent error while sending data : "+respString); 387 } else { 388 log.warn("Temporary error while chatting with server during DATA"); 389 throw new TemporaryDeliveryException("Temporary error while sending mail data command : "+respString); 390 } 391 392 } 393 try { 394 sock.getOutputStream().write(e.getDataAsByte()); 395 send("\r\n."); } catch (IOException e2) { 397 log.warn("RSMPT> Error while chatting with server during DATA"); 398 throw new TemporaryDeliveryException("Error while transmitting data, IO Error: "+e2.getMessage()); 399 } 400 401 respString = receive(); 402 if (parseResponse(respString) != RESP_OK) { 403 if (respString.startsWith("5")) { log.warn("Fatal error while chatting with server while ending data, response = "+respString); 405 for (Iterator iter = rcpts.iterator(); iter.hasNext();) { 406 Rcpt element = (Rcpt) iter.next(); 407 element.setLastError(respString); 408 } 409 throw new FatalDeliveryException("Fatal Error send mail data: "+respString); 410 } else { 411 log.warn("Temporary error while chatting with server while ending data, response = "+respString); 412 throw new TemporaryDeliveryException("Temporary error sending mail data : "+respString); 413 } 414 } else { 415 while (!successfullRcpt.empty()){ 416 Rcpt tmp = (Rcpt)successfullRcpt.pop(); 417 tmp.setDelivered(Rcpt.STATUS_DELIVERED); 418 } 419 } 420 421 try { 422 send("QUIT"); 423 respString=receive(); 424 if (parseResponse(respString) != RESP_END) { 425 log.warn("Error while chatting with server during end connection, last response = "+respString); 426 } 428 } catch (TemporaryDeliveryException e1) { 429 log.error("Looks like remote server ended connection !(mail was accepted anyway)",e1); 430 } 431 } catch (TemporaryDeliveryException e) { 432 closeConnection(); 433 throw new TemporaryDeliveryException(e); 434 } catch (FatalDeliveryException e) { 435 closeConnection(); 436 throw new FatalDeliveryException(e); 437 } 438 closeConnection(); 439 } 440 441 445 private void closeConnection() { 446 try { 447 if (wr != null) 448 wr.close(); 449 if (sock != null) 450 sock.close(); 451 } catch (IOException e) {} 452 } 453 454 458 public void init() throws IOException { 459 sock = new Socket (); 460 sock.setSoTimeout(connectionTimeout*1000); 461 SocketAddress sockaddr = new InetSocketAddress (server, serverPort); 462 sock.connect(sockaddr); 463 csp = new MultiLineCommandStreamParser(sock.getInputStream(), 512, false); 464 wr = new BufferedWriter (new OutputStreamWriter (sock.getOutputStream())); 465 } 466 467 472 private void send(String msg) throws TemporaryDeliveryException { 473 477 String res = msg.replaceAll("[^\r]\n", "\r\n"); 478 try { 479 wr.write(res + "\r\n"); 480 wr.flush(); 481 log.debug("Sent: " + res.replaceAll("\r", "<CR>").replaceAll("\n", "<LF>") + "<CR><LF>"); 482 } catch (IOException e) { 483 log.error("I/O error while trying to send "+msg,e); 484 throw new TemporaryDeliveryException(e); 485 } 486 } 487 488 493 private String receive() throws TemporaryDeliveryException, FatalDeliveryException { 494 String rec; 495 try { 496 rec = csp.readLine(); 497 log.debug("Received: " + rec); 498 return rec; 499 } catch (InputSizeToBig e) { 500 log.error("RemoteSender connected to " + server.toString() + " received a response > 512 bytes."); 501 throw new FatalDeliveryException(); 502 } catch (IOException e) { 503 throw new TemporaryDeliveryException(); 504 } catch (BareLFException e) { 505 throw new TemporaryDeliveryException(); 506 } 507 } 508 509 private int parseResponse(String cmd) { 510 511 if (cmd==null) 512 return RESP_UNKW; 513 514 if (cmd.startsWith("220")) 515 return RESP_HELO_FIRST; 516 517 if (cmd.startsWith("250")) 518 return RESP_OK; 519 520 if (cmd.startsWith("354")) 521 return RESP_DATA_OK; 522 523 if (cmd.startsWith("221")) 524 return RESP_END; 525 526 if (cmd.length() > 4) 527 return Integer.parseInt(cmd.substring(0, 3)); 528 529 return RESP_UNKW; 530 } 531 532 private static final int RESP_HELO_FIRST = 0; private static final int RESP_OK = 1; 534 private static final int RESP_DATA_OK = 2; 535 private static final int RESP_END = 3; 536 private static final int RESP_UNKW = -1; 537 538 public void setLogin(String login) { 539 this.login = login; 540 } 541 542 public void setPassword(String password) { 543 this.password = password; 544 } 545 546 public void setAuthMethod(String authMethod) { 547 this.authMethod = authMethod; 548 } 549 550 public void setHelloCommand(String helloCommand) { 551 this.helloCommand = helloCommand; 552 } 553 554 555 } | Popular Tags |