/* * This source is part of the * _____ ___ ____ * __ / / _ \/ _ | / __/___ _______ _ * / // / , _/ __ |/ _/_/ _ \/ __/ _ `/ * \___/_/|_/_/ |_/_/ (_)___/_/ \_, / * /___/ * repository. * * Copyright (C) 2015 Carmen Alvarez (c@rmen.ca) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ca.rmen.android.networkmonitor.app.email; import android.util.Base64; import org.apache.commons.net.PrintCommandListener; import org.apache.commons.net.smtp.AuthenticatingSMTPClient; import org.apache.commons.net.smtp.SMTPClient; import org.apache.commons.net.smtp.SMTPReply; import org.apache.commons.net.smtp.SimpleSMTPHeader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.Collection; import java.util.Set; import java.util.UUID; import ca.rmen.android.networkmonitor.util.IoUtil; /** * Sends a mail to a recipient or recipients, including (or not) some file attachments. * This class attempts to have the fewest dependencies possible on Android or this project. */ class Emailer { private static final String ENCODING = "UTF-8"; private static final int SMTP_TIMEOUT_MS = 15000; /** * Sends an e-mail in UTF-8 encoding. * * @param protocol this has been tested with "TLS", "SSL", and null. * @param attachments optional attachments to include in the mail. * @param debug if true, details about the smtp commands will be logged to stdout. */ static void sendEmail(String protocol, String server, int port, String user, String password, String from, String[] recipients, String subject, String body, Set<File> attachments, boolean debug) throws Exception { // Set up the mail connectivity final AuthenticatingSMTPClient client; if (protocol == null) client = new AuthenticatingSMTPClient(); else client = new AuthenticatingSMTPClient(protocol); if (debug) client.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out), true)); client.setDefaultTimeout(SMTP_TIMEOUT_MS); client.setCharset(Charset.forName(ENCODING)); client.connect(server, port); checkReply(client); client.helo("[" + client.getLocalAddress().getHostAddress() + "]"); checkReply(client); if ("TLS".equals(protocol)) { if (!client.execTLS()) { checkReply(client); throw new RuntimeException("Could not start tls"); } } client.auth(AuthenticatingSMTPClient.AUTH_METHOD.LOGIN, user, password); checkReply(client); // Set up the mail participants client.setSender(from); checkReply(client); for (String recipient : recipients) { client.addRecipient(recipient); checkReply(client); } // Set up the mail content Writer writer = client.sendMessageData(); SimpleSMTPHeader header = new SimpleSMTPHeader(from, recipients[0], subject); for (int i = 1; i < recipients.length; i++) header.addCC(recipients[i]); // Just plain text mail: no attachments if (attachments == null || attachments.isEmpty()) { header.addHeaderField("Content-Type", "text/plain; charset=" + ENCODING); writer.write(header.toString()); writer.write(body); } // Mail with attachments else { String boundary = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 28); header.addHeaderField("Content-Type", "multipart/mixed; boundary=" + boundary); writer.write(header.toString()); // Write the main text message writer.write("--" + boundary + "\n"); writer.write("Content-Type: text/plain; charset=" + ENCODING + "\n\n"); writer.write(body); writer.write("\n"); // Write the attachments appendAttachments(writer, boundary, attachments); writer.write("--" + boundary + "--\n\n"); } writer.close(); if (!client.completePendingCommand()) { throw new RuntimeException("Could not send mail"); } client.logout(); client.disconnect(); } /** * Append the given attachments to the message which is being written by the given writer. * * @param boundary separates each file attachment */ private static void appendAttachments(Writer writer, String boundary, Collection<File> attachments) throws IOException { for (File attachment : attachments) { ByteArrayOutputStream fileOs = new ByteArrayOutputStream((int) attachment.length()); FileInputStream fileIs = new FileInputStream(attachment); try { IoUtil.copy(fileIs, fileOs); } finally { IoUtil.closeSilently(fileIs, fileOs); } final String mimeType = attachment.getName().substring(attachment.getName().indexOf(".") + 1); writer.write("--" + boundary + "\n"); writer.write("Content-Type: application/" + mimeType + "; name=\"" + attachment.getName() + "\"\n"); writer.write("Content-Disposition: attachment; filename=\"" + attachment.getName() + "\"\n"); writer.write("Content-Transfer-Encoding: base64\n\n"); String encodedFile = Base64.encodeToString(fileOs.toByteArray(), Base64.DEFAULT); writer.write(encodedFile); writer.write("\n"); } } // http://blog.dahanne.net/2013/06/17/sending-a-mail-in-java-and-android-with-apache-commons-net/ private static void checkReply(SMTPClient sc) throws IOException { if (SMTPReply.isNegativeTransient(sc.getReplyCode())) { throw new RuntimeException("Transient SMTP error " + sc.getReply() + sc.getReplyString()); } else if (SMTPReply.isNegativePermanent(sc.getReplyCode())) { throw new RuntimeException("Permanent SMTP error " + sc.getReply() + sc.getReplyString()); } } }