/*
* Copyright (C) 2010 The Android Open Source Project
*
* 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 com.android.tradefed.util;
import com.android.ddmlib.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* A helper class to send an email. Note that this class is NOT PLATFORM
* INDEPENDENT. It will likely fail on Windows, and possibly on Mac OS X as
* well. It will fail on any machine where The binary pointed at by the
* {@code mailer} constant doesn't exist.
*/
public class Email implements IEmail {
private static final String LOG_TAG = "Email";
private static final String[] mailer = { "/usr/sbin/sendmail", "-t", "-i" };
static final String CRLF = "\r\n";
// 将一个string字符串和string集合拼接成一个长字符串
private static String join(Collection<String> list, String sep) {
StringBuilder builder = new StringBuilder();
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
String element = iter.next();
builder.append(element);
if (iter.hasNext()) {
builder.append(sep);
}
}
return builder.toString();
}
/**
* A helper method to use ProcessBuilder to create a new process. This can't
* use {@link com.android.tradefed.util.IRunUtil} because that class doesn't
* provide a way to pass data to the stdin of the spawned process, which is
* the usage paradigm for most commandline mailers such as mailx and
* sendmail.
* <p/>
* Exposed for mocking
*
* @param cmd
* The {@link String[]} to pass to the {@link ProcessBuilder}
* constructor
* @return The {@link Process} returned from from
* {@link ProcessBuilder#start()}
* @throws IOException
* if sending email failed in a synchronously-detectable way
*/
Process run(String[] cmd) throws IOException {
//创建操作系统进程
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
return pb.start();
}
/**
* 增加文件头
* A small helper function that adds the specified header to the header list
* only if the value is non-null
*/
private void addHeader(List<String> headers, String name, String value) {
if (name == null || value == null)
return;
headers.add(String.format("%s: %s", name, value));
}
/**
* A small helper function that adds the specified header to the header list
* only if the value is non-null
*/
private void addHeaders(List<String> headers, String name,
Collection<String> values) {
if (name == null || values == null)
return;
if (values.isEmpty())
return;
final String strValues = join(values, ",");
headers.add(String.format("%s: %s", name, strValues));
}
/**
* 由于1.5的局限性,继承于接口的方法上面不能写@Overrider,只能用
* {@inheritDoc}代替,意思同@Overrider一样,但是1.5后可以用@Override标注
*
* 该方法发送邮件,只需传入一个Message对象即可
*/
@Override
public void send(Message msg) throws IllegalArgumentException, IOException {
// Sanity checks
if (msg.getTo() == null) {
throw new IllegalArgumentException(
"Message is missing a destination");
} else if (msg.getSubject() == null) {
throw new IllegalArgumentException("Message is missing a subject");
} else if (msg.getBody() == null) {
throw new IllegalArgumentException("Message is missing a body");
}
// Sender, Recipients, CC, BCC, Subject are all set with appropriate
// email headers
final ArrayList<String> headers = new ArrayList<String>();
final String[] mailCmd;
if (msg.getSender() != null) {
addHeader(headers, "From", msg.getSender());
// Envelope Sender (will receive any errors related to the email)
int cmdLen = mailer.length + 2;
mailCmd = Arrays.copyOf(mailer, cmdLen);
mailCmd[cmdLen - 2] = "-f";
mailCmd[cmdLen - 1] = msg.getSender();
} else {
mailCmd = mailer;
}
addHeaders(headers, "To", msg.getTo());
addHeaders(headers, "Cc", msg.getCc());
addHeaders(headers, "Bcc", msg.getBcc());
addHeader(headers, "Content-type", msg.getContentType());
addHeader(headers, "Subject", msg.getSubject());
final StringBuilder fullMsg = new StringBuilder();
fullMsg.append(join(headers, CRLF));
fullMsg.append(CRLF);
fullMsg.append(CRLF);
fullMsg.append(msg.getBody());
Log.d(LOG_TAG,
String.format("About to send email with command: %s",
Arrays.toString(mailCmd)));
Process mailerProc = run(mailCmd);
BufferedOutputStream mailerStdin = new BufferedOutputStream(
mailerProc.getOutputStream());
/*
* There is no such thing as a "character" in the land of the shell;
* there are only bytes. Here, we convert the body from a Java string
* (consisting of characters) to a byte array encoding each character
* with UTF-8. Each character will be represented as between one and
* four bytes apiece.
*/
mailerStdin.write(fullMsg.toString().getBytes("UTF-8"));
mailerStdin.flush();
mailerStdin.close();
int retValue;
try {
retValue = mailerProc.waitFor();
} catch (InterruptedException e) {
// ignore, but set retValue to something bogus
retValue = -12345;
}
if (retValue != 0) {
Log.e(LOG_TAG, String.format(
"Mailer finished with non-zero return value: %d", retValue));
BufferedInputStream mailerStdout = new BufferedInputStream(
mailerProc.getInputStream());
StringBuilder stdout = new StringBuilder();
int theByte;
while ((theByte = mailerStdout.read()) != -1) {
stdout.append((char) theByte);
}
Log.e(LOG_TAG, "Mailer output was: " + stdout.toString());
} else {
Log.v(LOG_TAG, "Mailer returned successfully.");
}
}
}