use of com.google.gerrit.exceptions.EmailException in project gerrit by GerritCodeReview.
the class ChangeEmail method init.
/**
* Setup the message headers and envelope (TO, CC, BCC).
*/
@Override
protected void init() throws EmailException {
if (args.projectCache != null) {
projectState = args.projectCache.get(change.getProject()).orElse(null);
} else {
projectState = null;
}
if (patchSet == null) {
try {
patchSet = changeData.currentPatchSet();
} catch (StorageException err) {
patchSet = null;
}
}
if (patchSet != null) {
setHeader(MailHeader.PATCH_SET.fieldName(), patchSet.number() + "");
if (patchSetInfo == null) {
try {
patchSetInfo = args.patchSetInfoFactory.get(changeData.notes(), patchSet.id());
} catch (PatchSetInfoNotAvailableException | StorageException err) {
patchSetInfo = null;
}
}
}
authors = getAuthors();
try {
stars = changeData.stars();
} catch (StorageException e) {
throw new EmailException("Failed to load stars for change " + change.getChangeId(), e);
}
super.init();
if (timestamp != null) {
setHeader(FieldName.DATE, timestamp);
}
setChangeSubjectHeader();
setHeader(MailHeader.CHANGE_ID.fieldName(), "" + change.getKey().get());
setHeader(MailHeader.CHANGE_NUMBER.fieldName(), "" + change.getChangeId());
setHeader(MailHeader.PROJECT.fieldName(), "" + change.getProject());
setChangeUrlHeader();
setCommitIdHeader();
if (notify.handling().compareTo(NotifyHandling.OWNER_REVIEWERS) >= 0) {
try {
addByEmail(RecipientType.CC, changeData.reviewersByEmail().byState(ReviewerStateInternal.CC));
addByEmail(RecipientType.CC, changeData.reviewersByEmail().byState(ReviewerStateInternal.REVIEWER));
} catch (StorageException e) {
throw new EmailException("Failed to add unregistered CCs " + change.getChangeId(), e);
}
}
}
use of com.google.gerrit.exceptions.EmailException 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);
}
}
}
use of com.google.gerrit.exceptions.EmailException in project gerrit by GerritCodeReview.
the class SmtpEmailSender method send.
@Override
public void send(final Address from, Collection<Address> rcpt, final Map<String, EmailHeader> callerHeaders, String textBody, @Nullable String htmlBody) throws EmailException {
if (!isEnabled()) {
throw new EmailException("Sending email is disabled");
}
StringBuilder rejected = new StringBuilder();
try {
final SMTPClient client = open();
try {
if (!client.setSender(from.email())) {
throw new EmailException("Server " + smtpHost + " rejected from address " + from.email());
}
/* Do not prevent the email from being sent to "good" users simply
* because some users get rejected. If not, a single rejected
* project watcher could prevent email for most actions on a project
* from being sent to any user! Instead, queue up the errors, and
* throw an exception after sending the email to get the rejected
* error(s) logged.
*/
for (Address addr : rcpt) {
if (!client.addRecipient(addr.email())) {
String error = client.getReplyString();
rejected.append("Server ").append(smtpHost).append(" rejected recipient ").append(addr).append(": ").append(error);
}
}
try (Writer messageDataWriter = client.sendMessageData()) {
if (messageDataWriter == null) {
/* Include rejected recipient error messages here to not lose that
* information. That piece of the puzzle is vital if zero recipients
* are accepted and the server consequently rejects the DATA command.
*/
throw new EmailException(rejected.append("Server ").append(smtpHost).append(" rejected DATA command: ").append(client.getReplyString()).toString());
}
render(messageDataWriter, callerHeaders, textBody, htmlBody);
if (!client.completePendingCommand()) {
throw new EmailException("Server " + smtpHost + " rejected message body: " + client.getReplyString());
}
client.logout();
if (rejected.length() > 0) {
throw new EmailException(rejected.toString());
}
}
} finally {
client.disconnect();
}
} catch (IOException e) {
throw new EmailException("Cannot send outgoing email", e);
}
}
use of com.google.gerrit.exceptions.EmailException in project gerrit by GerritCodeReview.
the class SmtpEmailSender method open.
private SMTPClient open() throws EmailException {
final AuthSMTPClient client = new AuthSMTPClient(smtpEncryption == Encryption.SSL, sslVerify);
client.setConnectTimeout(connectTimeout);
try {
client.connect(smtpHost, smtpPort);
int replyCode = client.getReplyCode();
String replyString = client.getReplyString();
if (!SMTPReply.isPositiveCompletion(replyCode)) {
throw new EmailException(String.format("SMTP server rejected connection: %d: %s", replyCode, replyString));
}
if (!client.login()) {
throw new EmailException("SMTP server rejected HELO/EHLO greeting: " + replyString);
}
if (smtpEncryption == Encryption.TLS) {
if (!client.execTLS()) {
throw new EmailException("SMTP server does not support TLS");
}
if (!client.login()) {
throw new EmailException("SMTP server rejected login: " + replyString);
}
}
if (smtpUser != null && !client.auth(smtpUser, smtpPass)) {
throw new EmailException("SMTP server rejected auth: " + replyString);
}
return client;
} catch (IOException | EmailException e) {
if (client.isConnected()) {
try {
client.disconnect();
} catch (IOException e2) {
// Ignored
}
}
if (e instanceof EmailException) {
throw (EmailException) e;
}
throw new EmailException(e.getMessage(), e);
}
}
use of com.google.gerrit.exceptions.EmailException in project gerrit by GerritCodeReview.
the class PutHttpPassword method apply.
@UsedAt(UsedAt.Project.PLUGIN_SERVICEUSER)
public Response<String> apply(IdentifiedUser user, String newPassword) throws ResourceNotFoundException, ResourceConflictException, IOException, ConfigInvalidException {
String userName = user.getUserName().orElseThrow(() -> new ResourceConflictException("username must be set"));
Optional<ExternalId> optionalExtId = externalIds.get(externalIdKeyFactory.create(SCHEME_USERNAME, userName));
ExternalId extId = optionalExtId.orElseThrow(ResourceNotFoundException::new);
accountsUpdateProvider.get().update("Set HTTP Password via API", extId.accountId(), u -> u.updateExternalId(externalIdFactory.createWithPassword(extId.key(), extId.accountId(), extId.email(), newPassword)));
try {
httpPasswordUpdateSenderFactory.create(user, newPassword == null ? "deleted" : "added or updated").send();
} catch (EmailException e) {
logger.atSevere().withCause(e).log("Cannot send HttpPassword update message to %s", user.getAccount().preferredEmail());
}
return Strings.isNullOrEmpty(newPassword) ? Response.none() : Response.ok(newPassword);
}
Aggregations