| // Copyright (C) 2013 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.google.gerrit.server.mail; | |
| import static com.google.gerrit.server.notedb.ReviewerStateInternal.CC; | |
| import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER; | |
| import com.google.common.flogger.FluentLogger; | |
| import com.google.gerrit.common.FooterConstants; | |
| import com.google.gerrit.entities.Account; | |
| import com.google.gerrit.extensions.restapi.UnprocessableEntityException; | |
| import com.google.gerrit.server.ReviewerSet; | |
| import com.google.gerrit.server.account.AccountResolver; | |
| import java.io.IOException; | |
| import java.util.Collections; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Set; | |
| import java.util.regex.Pattern; | |
| import org.eclipse.jgit.errors.ConfigInvalidException; | |
| import org.eclipse.jgit.revwalk.FooterKey; | |
| import org.eclipse.jgit.revwalk.FooterLine; | |
| public class MailUtil { | |
| private static final FluentLogger logger = FluentLogger.forEnclosingClass(); | |
| public static MailRecipients getRecipientsFromFooters( | |
| AccountResolver accountResolver, List<FooterLine> footerLines) | |
| throws IOException, ConfigInvalidException { | |
| MailRecipients recipients = new MailRecipients(); | |
| for (FooterLine footerLine : footerLines) { | |
| try { | |
| if (isReviewer(footerLine)) { | |
| Account.Id accountId = toAccountId(accountResolver, footerLine.getValue().trim()); | |
| recipients.reviewers.add(accountId); | |
| logger.atFine().log( | |
| "Added account %d from footer line \"%s\" as reviewer", accountId.get(), footerLine); | |
| } else if (footerLine.matches(FooterKey.CC)) { | |
| Account.Id accountId = toAccountId(accountResolver, footerLine.getValue().trim()); | |
| recipients.cc.add(accountId); | |
| logger.atFine().log( | |
| "Added account %d from footer line \"%s\" as cc", accountId.get(), footerLine); | |
| } | |
| } catch (UnprocessableEntityException e) { | |
| logger.atFine().log( | |
| "Skip adding reviewer/cc from footer line \"%s\": %s", footerLine, e.getMessage()); | |
| continue; | |
| } | |
| } | |
| return recipients; | |
| } | |
| public static MailRecipients getRecipientsFromReviewers(ReviewerSet reviewers) { | |
| MailRecipients recipients = new MailRecipients(); | |
| recipients.reviewers.addAll(reviewers.byState(REVIEWER)); | |
| recipients.cc.addAll(reviewers.byState(CC)); | |
| return recipients; | |
| } | |
| @SuppressWarnings("deprecation") | |
| private static Account.Id toAccountId(AccountResolver accountResolver, String nameOrEmail) | |
| throws UnprocessableEntityException, IOException, ConfigInvalidException { | |
| return accountResolver.resolveByExactNameOrEmail(nameOrEmail).asUnique().account().id(); | |
| } | |
| private static boolean isReviewer(FooterLine candidateFooterLine) { | |
| return candidateFooterLine.matches(FooterKey.SIGNED_OFF_BY) | |
| || candidateFooterLine.matches(FooterKey.ACKED_BY) | |
| || candidateFooterLine.matches(FooterConstants.REVIEWED_BY) | |
| || candidateFooterLine.matches(FooterConstants.TESTED_BY); | |
| } | |
| public static class MailRecipients { | |
| private final Set<Account.Id> reviewers; | |
| private final Set<Account.Id> cc; | |
| public MailRecipients() { | |
| this.reviewers = new HashSet<>(); | |
| this.cc = new HashSet<>(); | |
| } | |
| public MailRecipients(Set<Account.Id> reviewers, Set<Account.Id> cc) { | |
| this.reviewers = new HashSet<>(reviewers); | |
| this.cc = new HashSet<>(cc); | |
| } | |
| public void add(MailRecipients recipients) { | |
| reviewers.addAll(recipients.reviewers); | |
| cc.addAll(recipients.cc); | |
| } | |
| public void remove(Account.Id toRemove) { | |
| reviewers.remove(toRemove); | |
| cc.remove(toRemove); | |
| } | |
| public Set<Account.Id> getReviewers() { | |
| return Collections.unmodifiableSet(reviewers); | |
| } | |
| public Set<Account.Id> getCcOnly() { | |
| final Set<Account.Id> cc = new HashSet<>(this.cc); | |
| cc.removeAll(reviewers); | |
| return Collections.unmodifiableSet(cc); | |
| } | |
| public Set<Account.Id> getAll() { | |
| final Set<Account.Id> all = new HashSet<>(reviewers.size() + cc.size()); | |
| all.addAll(reviewers); | |
| all.addAll(cc); | |
| return Collections.unmodifiableSet(all); | |
| } | |
| } | |
| /** allow wildcard matching for {@code domains} */ | |
| public static Pattern glob(String[] domains) { | |
| // if domains is not set, match anything | |
| if (domains == null || domains.length == 0) { | |
| return Pattern.compile(".*"); | |
| } | |
| StringBuilder sb = new StringBuilder(); | |
| for (String domain : domains) { | |
| String quoted = "\\Q" + domain.replace("\\E", "\\E\\\\E\\Q") + "\\E|"; | |
| sb.append(quoted.replace("*", "\\E.*\\Q")); | |
| } | |
| return Pattern.compile(sb.substring(0, sb.length() - 1)); | |
| } | |
| } |