use of com.google.gerrit.entities.EmailHeader in project gerrit by GerritCodeReview.
the class SmtpEmailSender method render.
private void render(Writer out, Map<String, EmailHeader> callerHeaders, String textBody, @Nullable String htmlBody) throws IOException, EmailException {
final Map<String, EmailHeader> hdrs = new LinkedHashMap<>(callerHeaders);
setMissingHeader(hdrs, "MIME-Version", "1.0");
setMissingHeader(hdrs, "Content-Transfer-Encoding", "8bit");
setMissingHeader(hdrs, "Content-Disposition", "inline");
setMissingHeader(hdrs, "User-Agent", "Gerrit/" + Version.getVersion());
if (importance != null) {
setMissingHeader(hdrs, "Importance", importance);
}
if (expiryDays > 0) {
Instant expiry = Instant.ofEpochMilli(TimeUtil.nowMs() + expiryDays * 24 * 60 * 60 * 1000L);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss Z").withZone(ZoneId.systemDefault());
setMissingHeader(hdrs, "Expiry-Date", fmt.format(expiry));
}
String encodedBody;
if (htmlBody == null) {
setMissingHeader(hdrs, "Content-Type", "text/plain; charset=UTF-8");
encodedBody = textBody;
} else {
String boundary = generateMultipartBoundary(textBody, htmlBody);
setMissingHeader(hdrs, "Content-Type", "multipart/alternative; boundary=\"" + boundary + "\"; charset=UTF-8");
encodedBody = buildMultipartBody(boundary, textBody, htmlBody);
}
try (Writer w = new BufferedWriter(out)) {
for (Map.Entry<String, EmailHeader> h : hdrs.entrySet()) {
if (!h.getValue().isEmpty()) {
w.write(h.getKey());
w.write(": ");
h.getValue().write(w);
w.write("\r\n");
}
}
w.write("\r\n");
w.write(encodedBody);
w.flush();
}
}
use of com.google.gerrit.entities.EmailHeader in project gerrit by GerritCodeReview.
the class MailSenderIT method headerString.
private String headerString(Map<String, EmailHeader> headers, String name) {
EmailHeader header = headers.get(name);
assertThat(header).isInstanceOf(StringEmailHeader.class);
return ((StringEmailHeader) header).getString();
}
use of com.google.gerrit.entities.EmailHeader in project gerrit by GerritCodeReview.
the class OutgoingEmail method sendImpl.
private void sendImpl() throws EmailException {
if (!args.emailSender.isEnabled()) {
// Server has explicitly disabled email sending.
//
logger.atFine().log("Not sending '%s': Email sending is disabled by server config", messageClass);
return;
}
if (!notify.shouldNotify()) {
logger.atFine().log("Not sending '%s': Notify handling is NONE", messageClass);
return;
}
init();
if (messageId == null) {
throw new IllegalStateException("All emails must have a messageId");
}
format();
appendText(textTemplate("Footer"));
if (useHtml()) {
appendHtml(soyHtmlTemplate("FooterHtml"));
}
Set<Address> smtpRcptToPlaintextOnly = new HashSet<>();
if (shouldSendMessage()) {
if (fromId != null) {
Optional<AccountState> fromUser = args.accountCache.get(fromId);
if (fromUser.isPresent()) {
GeneralPreferencesInfo senderPrefs = fromUser.get().generalPreferences();
CurrentUser user = args.currentUserProvider.get();
boolean isImpersonating = user.isIdentifiedUser() && user.isImpersonating();
if (isImpersonating && user.getAccountId() != fromId) {
// set up correctly.
throw new EmailException(String.format("User %s is sending email from %s, while acting on behalf of %s", user.asIdentifiedUser().getRealUser().getAccountId(), fromId, user.getAccountId()));
}
if (senderPrefs != null && senderPrefs.getEmailStrategy() == CC_ON_OWN_COMMENTS) {
// Include the sender in email if they enabled email notifications on their own
// comments.
//
logger.atFine().log("CC email sender %s because the email strategy of this user is %s", fromUser.get().account().id(), CC_ON_OWN_COMMENTS);
add(RecipientType.CC, fromId);
} else if (isImpersonating) {
// If we are impersonating a user, make sure they receive a CC of
// this message regardless of email strategy, unless email notifications are explicitly
// disabled for this user. This way they can always review and audit what we sent
// on their behalf to others.
logger.atFine().log("CC email sender %s because the email is sent on behalf of and email notifications" + " are enabled for this user.", fromUser.get().account().id());
add(RecipientType.CC, fromId);
} else if (!notify.accounts().containsValue(fromId) && rcptTo.remove(fromId)) {
// If they don't want a copy, but we queued one up anyway,
// drop them from the recipient lists, but only if the user is not being impersonated.
//
logger.atFine().log("Not CCing email sender %s because the email strategy of this user is not %s but" + " %s", fromUser.get().account().id(), CC_ON_OWN_COMMENTS, senderPrefs != null ? senderPrefs.getEmailStrategy() : null);
removeUser(fromUser.get().account());
}
}
}
// In addition, check if users only want to receive plaintext email.
for (Account.Id id : rcptTo) {
Optional<AccountState> thisUser = args.accountCache.get(id);
if (thisUser.isPresent()) {
Account thisUserAccount = thisUser.get().account();
GeneralPreferencesInfo prefs = thisUser.get().generalPreferences();
if (prefs == null || prefs.getEmailStrategy() == DISABLED) {
logger.atFine().log("Not emailing account %s because user has set email strategy to %s", id, DISABLED);
removeUser(thisUserAccount);
} else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) {
logger.atFine().log("Removing account %s from HTML email because user prefers plain text emails", id);
removeUser(thisUserAccount);
smtpRcptToPlaintextOnly.add(Address.create(thisUserAccount.fullName(), thisUserAccount.preferredEmail()));
}
}
if (smtpRcptTo.isEmpty() && smtpRcptToPlaintextOnly.isEmpty()) {
logger.atFine().log("Not sending '%s': No SMTP recipients", messageClass);
return;
}
}
// inbound email replies.
if (!headers.containsKey(FieldName.REPLY_TO)) {
StringJoiner j = new StringJoiner(", ");
if (fromId != null) {
Address address = toAddress(fromId);
if (address != null) {
j.add(address.email());
}
}
// For users who prefer plaintext, this comes at the cost of not being
// listed in the multipart To and Cc headers. We work around this by adding
// all users to the Reply-To address in both the plaintext and multipart
// email. We should exclude any BCC addresses from reply-to, because they should be
// invisible to other recipients.
Sets.difference(Sets.union(smtpRcptTo, smtpRcptToPlaintextOnly), smtpBccRcptTo).stream().forEach(a -> j.add(a.email()));
setHeader(FieldName.REPLY_TO, j.toString());
}
String textPart = textBody.toString();
OutgoingEmailValidationListener.Args va = new OutgoingEmailValidationListener.Args();
va.messageClass = messageClass;
va.smtpFromAddress = smtpFromAddress;
va.smtpRcptTo = smtpRcptTo;
va.headers = headers;
va.body = textPart;
if (useHtml()) {
va.htmlBody = htmlBody.toString();
} else {
va.htmlBody = null;
}
Set<Address> intersection = Sets.intersection(va.smtpRcptTo, smtpRcptToPlaintextOnly);
if (!intersection.isEmpty()) {
logger.atSevere().log("Email '%s' will be sent twice to %s", messageClass, intersection);
}
if (!va.smtpRcptTo.isEmpty()) {
// Send multipart message
addMessageId(va, "-HTML");
if (!validateEmail(va))
return;
logger.atFine().log("Sending multipart '%s' from %s to %s", messageClass, va.smtpFromAddress, va.smtpRcptTo);
args.emailSender.send(va.smtpFromAddress, va.smtpRcptTo, va.headers, va.body, va.htmlBody);
}
if (!smtpRcptToPlaintextOnly.isEmpty()) {
addMessageId(va, "-PLAIN");
// Send plaintext message
Map<String, EmailHeader> shallowCopy = new HashMap<>();
shallowCopy.putAll(headers);
// Remove To and Cc
shallowCopy.remove(FieldName.TO);
shallowCopy.remove(FieldName.CC);
for (Address a : smtpRcptToPlaintextOnly) {
// Add new To
EmailHeader.AddressList to = new EmailHeader.AddressList();
to.add(a);
shallowCopy.put(FieldName.TO, to);
}
if (!validateEmail(va))
return;
logger.atFine().log("Sending plaintext '%s' from %s to %s", messageClass, va.smtpFromAddress, smtpRcptToPlaintextOnly);
args.emailSender.send(va.smtpFromAddress, smtpRcptToPlaintextOnly, shallowCopy, va.body);
}
}
}
Aggregations