package mireka.filter.proxy; import java.io.IOException; import java.io.InputStream; import mireka.address.Recipient; import mireka.smtp.SendException; import mireka.smtp.client.BackendServer; import mireka.smtp.client.SmtpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.subethamail.smtp.RejectException; import org.subethamail.smtp.TooMuchDataException; import org.subethamail.smtp.client.SMTPException; import org.subethamail.smtp.client.SmartClient; /** * ClientWithProxyErrorHandling decorates a SmartClient with exception handling * suitable for a proxy: it converts backend error responses and IO errors to * SMTP errors which are valid from the viewpoint of the original sender. */ class ClientWithProxyErrorHandling { private final Logger logger = LoggerFactory .getLogger(ClientWithProxyErrorHandling.class); private final BackendServer backend; private final SmartClient smartClient; public ClientWithProxyErrorHandling(BackendServer backend) throws BackendRejectException, RejectException { this.backend = backend; this.smartClient = connect(); } private SmartClient connect() throws BackendRejectException, RejectException { SmtpClient client; try { client = backend.createClient(); } catch (SendException e) { throw new RejectException(e.errorStatus().getSmtpReplyCode(), e.errorStatus().getMessage()); } try { client.connect(); } catch (SMTPException e) { throw new BackendRejectException(e, " - Backend rejected connection"); } catch (IOException e) { logger.error( "Error while communicating with " + backend.toString(), e); throw new RejectException(451, "Local error in processing."); } return client; } public void from(String from) throws BackendRejectException, RejectException { try { smartClient.from(from); } catch (SMTPException e) { throw new BackendRejectException(e, " - Backend rejected sender"); } catch (IOException e) { throw new RejectException(451, e.getMessage()); } } public void recipient(Recipient recipient) throws RejectException, BackendRejectException { String destinationMailbox = null; try { destinationMailbox = recipient.sourceRouteStripped(); smartClient.to(destinationMailbox); } catch (SMTPException e) { throw new BackendRejectException(e, " - Backend rejected recipient"); } catch (IOException e) { throw new RejectException(451, e.getMessage()); } } public void data(InputStream data) throws RejectException, TooMuchDataException, IOException { this.sendDataStart(); this.sendDataStream(data); this.sendDataEnd(); } /** * Start the DATA command on backend server. */ private void sendDataStart() throws RejectException, BackendRejectException { try { smartClient.dataStart(); } catch (SMTPException e) { throw new BackendRejectException(e, " - Backend rejected DATA"); } catch (IOException e) { throw new RejectException(451, e.getMessage()); } } private void sendDataStream(InputStream data) throws IOException, RejectException { byte[] buffer = new byte[8192]; int numRead; while ((numRead = data.read(buffer)) > 0) { try { smartClient.dataWrite(buffer, numRead); } catch (IOException e) { throw new RejectException(451, e.getMessage()); } } } /** * Complete the data session on all connected targets. Shut down and remove * targets that fail. */ private void sendDataEnd() throws RejectException, BackendRejectException { try { smartClient.dataEnd(); } catch (SMTPException e) { throw new BackendRejectException(e, " - Backend server rejected at end of data"); } catch (IOException e) { throw new RejectException(451, e.getMessage()); } } public void quit() { smartClient.quit(); } }