Search in sources :

Example 36 with Address

use of com.google.gerrit.entities.Address 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 37 with Address

use of com.google.gerrit.entities.Address 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 38 with Address

use of com.google.gerrit.entities.Address in project gerrit by GerritCodeReview.

the class ChangeQueryBuilder method reviewerByState.

public Predicate<ChangeData> reviewerByState(String who, ReviewerStateInternal state, boolean forDefaultField) throws QueryParseException, IOException, ConfigInvalidException {
    Predicate<ChangeData> reviewerByEmailPredicate = null;
    Address address = Address.tryParse(who);
    if (address != null) {
        reviewerByEmailPredicate = ReviewerByEmailPredicate.forState(address, state);
    }
    Predicate<ChangeData> reviewerPredicate = null;
    try {
        Set<Account.Id> accounts = parseAccount(who);
        if (!forDefaultField || accounts.size() <= MAX_ACCOUNTS_PER_DEFAULT_FIELD) {
            reviewerPredicate = Predicate.or(accounts.stream().map(id -> ReviewerPredicate.forState(id, state)).collect(toList()));
        } else {
            logger.atFine().log("Skipping reviewer predicate for %s in default field query" + " because the number of matched accounts (%d) exceeds the limit of %d", who, accounts.size(), MAX_ACCOUNTS_PER_DEFAULT_FIELD);
        }
    } catch (QueryParseException e) {
        logger.atFine().log("Parsing %s as account failed: %s", who, e.getMessage());
        // Propagate this exception only if we can't use 'who' to query by email
        if (reviewerByEmailPredicate == null) {
            throw e;
        }
    }
    if (reviewerPredicate != null && reviewerByEmailPredicate != null) {
        return Predicate.or(reviewerPredicate, reviewerByEmailPredicate);
    } else if (reviewerPredicate != null) {
        return reviewerPredicate;
    } else if (reviewerByEmailPredicate != null) {
        return reviewerByEmailPredicate;
    } else {
        return Predicate.any();
    }
}
Also used : AllUsersName(com.google.gerrit.server.config.AllUsersName) GroupBackend(com.google.gerrit.server.account.GroupBackend) Arrays(java.util.Arrays) ProjectCache(com.google.gerrit.server.project.ProjectCache) RepositoryNotFoundException(org.eclipse.jgit.errors.RepositoryNotFoundException) Inject(com.google.inject.Inject) OperatorAliasConfig(com.google.gerrit.server.config.OperatorAliasConfig) PermissionBackend(com.google.gerrit.server.permissions.PermissionBackend) Config(org.eclipse.jgit.lib.Config) ChangeTriplet(com.google.gerrit.server.change.ChangeTriplet) VersionedAccountQueries(com.google.gerrit.server.account.VersionedAccountQueries) Map(java.util.Map) RefNames(com.google.gerrit.entities.RefNames) ProvisionException(com.google.inject.ProvisionException) Splitter(com.google.common.base.Splitter) Collectors.toSet(java.util.stream.Collectors.toSet) FieldDef(com.google.gerrit.index.FieldDef) Schema(com.google.gerrit.index.Schema) GerritServerConfig(com.google.gerrit.server.config.GerritServerConfig) Collection(java.util.Collection) LimitPredicate(com.google.gerrit.index.query.LimitPredicate) Account(com.google.gerrit.entities.Account) Set(java.util.Set) SubmitRecord(com.google.gerrit.entities.SubmitRecord) BranchNameKey(com.google.gerrit.entities.BranchNameKey) GroupReference(com.google.gerrit.entities.GroupReference) Objects(java.util.Objects) List(java.util.List) AllProjectsName(com.google.gerrit.server.config.AllProjectsName) CHANGE_ID_PATTERN(com.google.gerrit.entities.Change.CHANGE_ID_PATTERN) ChangeData.asChanges(com.google.gerrit.server.query.change.ChangeData.asChanges) PluginSetContext(com.google.gerrit.server.plugincontext.PluginSetContext) SubmitDryRun(com.google.gerrit.server.submit.SubmitDryRun) Optional(java.util.Optional) Providers(com.google.inject.util.Providers) Pattern(java.util.regex.Pattern) SchemaUtil(com.google.gerrit.index.SchemaUtil) ValOp(com.google.gerrit.server.query.change.PredicateArgs.ValOp) FluentLogger(com.google.common.flogger.FluentLogger) QueryRequiresAuthException(com.google.gerrit.index.query.QueryRequiresAuthException) ChangeIndexRewriter(com.google.gerrit.server.index.change.ChangeIndexRewriter) AccountCache(com.google.gerrit.server.account.AccountCache) Iterables(com.google.common.collect.Iterables) IndexConfig(com.google.gerrit.index.IndexConfig) ConfigInvalidException(org.eclipse.jgit.errors.ConfigInvalidException) MergeabilityComputationBehavior(com.google.gerrit.server.change.MergeabilityComputationBehavior) GroupBackends(com.google.gerrit.server.account.GroupBackends) HasOperandAliasConfig(com.google.gerrit.server.config.HasOperandAliasConfig) CommentsUtil(com.google.gerrit.server.CommentsUtil) Function(java.util.function.Function) Enums(com.google.common.base.Enums) GroupMembers(com.google.gerrit.server.account.GroupMembers) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Lists(com.google.common.collect.Lists) AccountResolver.isSelf(com.google.gerrit.server.account.AccountResolver.isSelf) GroupDescription(com.google.gerrit.entities.GroupDescription) QueryParseException(com.google.gerrit.index.query.QueryParseException) UnresolvableAccountException(com.google.gerrit.server.account.AccountResolver.UnresolvableAccountException) ImmutableSet.toImmutableSet(com.google.common.collect.ImmutableSet.toImmutableSet) Change(com.google.gerrit.entities.Change) PatchSet(com.google.gerrit.entities.PatchSet) Address(com.google.gerrit.entities.Address) Predicate(com.google.gerrit.index.query.Predicate) QueryBuilder(com.google.gerrit.index.query.QueryBuilder) AccountGroup(com.google.gerrit.entities.AccountGroup) CurrentUser(com.google.gerrit.server.CurrentUser) AccountResolver(com.google.gerrit.server.account.AccountResolver) ReviewerStateInternal(com.google.gerrit.server.notedb.ReviewerStateInternal) SubmitRule(com.google.gerrit.server.rules.SubmitRule) NotSignedInException(com.google.gerrit.exceptions.NotSignedInException) ExperimentFeatures(com.google.gerrit.server.experiments.ExperimentFeatures) PatchListCache(com.google.gerrit.server.patch.PatchListCache) StorageException(com.google.gerrit.exceptions.StorageException) IOException(java.io.IOException) VersionedAccountDestinations(com.google.gerrit.server.account.VersionedAccountDestinations) Ints(com.google.common.primitives.Ints) ChildProjects(com.google.gerrit.server.project.ChildProjects) ChangeField(com.google.gerrit.server.index.change.ChangeField) Collectors.toList(java.util.stream.Collectors.toList) Provider(com.google.inject.Provider) GitRepositoryManager(com.google.gerrit.server.git.GitRepositoryManager) DynamicMap(com.google.gerrit.extensions.registration.DynamicMap) IdentifiedUser(com.google.gerrit.server.IdentifiedUser) Project(com.google.gerrit.entities.Project) ChangeIndexCollection(com.google.gerrit.server.index.change.ChangeIndexCollection) VisibleForTesting(com.google.common.annotations.VisibleForTesting) AccountState(com.google.gerrit.server.account.AccountState) ChangeIndex(com.google.gerrit.server.index.change.ChangeIndex) Collections(java.util.Collections) GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY(com.google.gerrit.server.experiments.ExperimentFeaturesConstants.GERRIT_BACKEND_REQUEST_FEATURE_COMPUTE_FROM_ALL_USERS_REPOSITORY) Repository(org.eclipse.jgit.lib.Repository) StarredChangesUtil(com.google.gerrit.server.StarredChangesUtil) Address(com.google.gerrit.entities.Address) QueryParseException(com.google.gerrit.index.query.QueryParseException)

Example 39 with Address

use of com.google.gerrit.entities.Address in project gerrit by GerritCodeReview.

the class AbstractSubmitOnPush method assertThatEmailsForChangeCreationAndSubmitWereSent.

/**
 * Makes sure that two emails are sent: one for the change creation, and one for the submit.
 *
 * @param expected The account expected to receive message.
 * @param expectedRecipientType The notification's type: To/Cc/Bcc. if {@code null} then it is not
 *     needed to check the recipientType. It is meant for -notify without other flags like
 *     notify-cc, notify-to, and notify-bcc. With the -notify flag, the message can sometimes be
 *     sent as "To" and sometimes can be sent as "Cc".
 */
private void assertThatEmailsForChangeCreationAndSubmitWereSent(TestAccount expected, @Nullable RecipientType expectedRecipientType) {
    String expectedEmail = expected.email();
    String expectedFullName = expected.fullName();
    Address expectedAddress = Address.create(expectedFullName, expectedEmail);
    assertThat(sender.getMessages()).hasSize(2);
    Message message = sender.getMessages().get(0);
    assertThat(message.body().contains("review")).isTrue();
    assertAddress(message, expectedAddress, expectedRecipientType);
    message = sender.getMessages().get(1);
    assertThat(message.rcpt()).containsExactly(expectedAddress);
    assertAddress(message, expectedAddress, expectedRecipientType);
    assertThat(message.body().contains("submitted")).isTrue();
}
Also used : Address(com.google.gerrit.entities.Address) Message(com.google.gerrit.testing.FakeEmailSender.Message)

Example 40 with Address

use of com.google.gerrit.entities.Address in project gerrit by GerritCodeReview.

the class ProjectWatchIT method noNotificationForPrivateChangesForWatchersInNotifyConfig.

@Test
public void noNotificationForPrivateChangesForWatchersInNotifyConfig() throws Exception {
    Address addr = Address.create("Watcher", "watcher@example.com");
    NotifyConfig.Builder nc = NotifyConfig.builder();
    nc.addAddress(addr);
    nc.setName("team");
    nc.setHeader(NotifyConfig.Header.TO);
    nc.setNotify(EnumSet.of(NotifyType.NEW_CHANGES, NotifyType.ALL_COMMENTS));
    try (ProjectConfigUpdate u = updateProject(project)) {
        u.getConfig().putNotifyConfig("team", nc.build());
        u.save();
    }
    sender.clear();
    PushOneCommit.Result r = pushFactory.create(admin.newIdent(), testRepo, "private change", "a", "a1").to("refs/for/master%private");
    r.assertOkStatus();
    assertThat(sender.getMessages()).isEmpty();
    requestScopeOperations.setApiUser(admin.id());
    ReviewInput in = new ReviewInput();
    in.message = "comment";
    gApi.changes().id(r.getChangeId()).current().review(in);
    assertThat(sender.getMessages()).isEmpty();
}
Also used : Address(com.google.gerrit.entities.Address) NotifyConfig(com.google.gerrit.entities.NotifyConfig) ReviewInput(com.google.gerrit.extensions.api.changes.ReviewInput) PushOneCommit(com.google.gerrit.acceptance.PushOneCommit) Test(org.junit.Test) AbstractDaemonTest(com.google.gerrit.acceptance.AbstractDaemonTest)

Aggregations

Address (com.google.gerrit.entities.Address)61 Test (org.junit.Test)45 Account (com.google.gerrit.entities.Account)22 Change (com.google.gerrit.entities.Change)9 AbstractDaemonTest (com.google.gerrit.acceptance.AbstractDaemonTest)7 PushOneCommit (com.google.gerrit.acceptance.PushOneCommit)7 NotifyConfig (com.google.gerrit.entities.NotifyConfig)6 Message (com.google.gerrit.testing.FakeEmailSender.Message)5 ArrayList (java.util.ArrayList)5 ReviewerStateInternal (com.google.gerrit.server.notedb.ReviewerStateInternal)4 ImmutableList (com.google.common.collect.ImmutableList)3 VisibleForTesting (com.google.common.annotations.VisibleForTesting)2 ImmutableList.toImmutableList (com.google.common.collect.ImmutableList.toImmutableList)2 ImmutableTable (com.google.common.collect.ImmutableTable)2 Lists (com.google.common.collect.Lists)2 Table (com.google.common.collect.Table)2 FluentLogger (com.google.common.flogger.FluentLogger)2 Truth.assertWithMessage (com.google.common.truth.Truth.assertWithMessage)2 GroupReference (com.google.gerrit.entities.GroupReference)2 PatchSet (com.google.gerrit.entities.PatchSet)2