Search in sources :

Example 6 with EmailException

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);
        }
    }
}
Also used : EmailException(com.google.gerrit.exceptions.EmailException) PatchSetInfoNotAvailableException(com.google.gerrit.server.patch.PatchSetInfoNotAvailableException) StorageException(com.google.gerrit.exceptions.StorageException)

Example 7 with EmailException

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);
        }
    }
}
Also used : Account(com.google.gerrit.entities.Account) Address(com.google.gerrit.entities.Address) CurrentUser(com.google.gerrit.server.CurrentUser) EmailHeader(com.google.gerrit.entities.EmailHeader) StringEmailHeader(com.google.gerrit.entities.EmailHeader.StringEmailHeader) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) AccountState(com.google.gerrit.server.account.AccountState) OutgoingEmailValidationListener(com.google.gerrit.server.validators.OutgoingEmailValidationListener) AddressList(com.google.gerrit.entities.EmailHeader.AddressList) EmailException(com.google.gerrit.exceptions.EmailException) GeneralPreferencesInfo(com.google.gerrit.extensions.client.GeneralPreferencesInfo) AddressList(com.google.gerrit.entities.EmailHeader.AddressList) StringJoiner(java.util.StringJoiner) HashSet(java.util.HashSet)

Example 8 with EmailException

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);
    }
}
Also used : AuthSMTPClient(org.apache.commons.net.smtp.AuthSMTPClient) SMTPClient(org.apache.commons.net.smtp.SMTPClient) Address(com.google.gerrit.entities.Address) EmailException(com.google.gerrit.exceptions.EmailException) IOException(java.io.IOException) BufferedWriter(java.io.BufferedWriter) Writer(java.io.Writer)

Example 9 with EmailException

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);
    }
}
Also used : AuthSMTPClient(org.apache.commons.net.smtp.AuthSMTPClient) EmailException(com.google.gerrit.exceptions.EmailException) IOException(java.io.IOException)

Example 10 with EmailException

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);
}
Also used : ResourceConflictException(com.google.gerrit.extensions.restapi.ResourceConflictException) ExternalId(com.google.gerrit.server.account.externalids.ExternalId) EmailException(com.google.gerrit.exceptions.EmailException) ResourceNotFoundException(com.google.gerrit.extensions.restapi.ResourceNotFoundException) UsedAt(com.google.gerrit.common.UsedAt)

Aggregations

EmailException (com.google.gerrit.exceptions.EmailException)12 StorageException (com.google.gerrit.exceptions.StorageException)3 BadRequestException (com.google.gerrit.extensions.restapi.BadRequestException)3 UsedAt (com.google.gerrit.common.UsedAt)2 Address (com.google.gerrit.entities.Address)2 EmailInput (com.google.gerrit.extensions.api.accounts.EmailInput)2 ResourceConflictException (com.google.gerrit.extensions.restapi.ResourceConflictException)2 ResourceNotFoundException (com.google.gerrit.extensions.restapi.ResourceNotFoundException)2 PublicKeyStore (com.google.gerrit.gpg.PublicKeyStore)2 PublicKeyStore.keyIdToString (com.google.gerrit.gpg.PublicKeyStore.keyIdToString)2 GerritPersonIdent (com.google.gerrit.server.GerritPersonIdent)2 IdentifiedUser (com.google.gerrit.server.IdentifiedUser)2 ExternalId (com.google.gerrit.server.account.externalids.ExternalId)2 IOException (java.io.IOException)2 AuthSMTPClient (org.apache.commons.net.smtp.AuthSMTPClient)2 PGPPublicKey (org.bouncycastle.openpgp.PGPPublicKey)2 CommitBuilder (org.eclipse.jgit.lib.CommitBuilder)2 PersonIdent (org.eclipse.jgit.lib.PersonIdent)2 ByteSource (com.google.common.io.ByteSource)1 Account (com.google.gerrit.entities.Account)1