Merge branch 'stable-2.12'

* stable-2.12:
  EmailMerge: provide user when available
  Set version to 2.11.7
  Release notes for Gerrit 2.11.7
  Document new allowrcpt restriction on adding user email
  OutputStreamQuery: fix comments with current-patch-set option
  Add tests for ssh query command
  EqualsFilePredicate: Make it work in project watches
  commit-msg: respect commentChar from git-config
  EmailReviewComments: Provide the current user instead of exception
  Remove index defaultMaxClauseCount config setting while reusing maxTerms
  OutputStreamQuery: Take files into account when adding patch sets
  Update 2.12 release notes with fixes from stable-2.11
  Update 2.11.6 release notes
  OutgoingEmail: Fix breakage introduced in add() method
  ChangeData: Throw exception when failing to reload change
  SuggestReviewersIT: Test for case-preserved reviewer suggestion
  Return case-preserving email when suggesting reviewers
  ReceiveCommits: Improve error message when failing to insert patch set
  OutgoingEmail: Kill "Skipping delivery of email" logspam
  OutgoingEmail: Check for valid email address when adding recipients
  ldap.Helper: Kill "assuming empty group membership" logspam
  SmtpEmailSender: Refactor open method and improve logging
  CreateEmail: Don't allow to add email prohibited by sendemail.allowrcpt
  RegisterNewEmailSender: Don't unconditionally send verification mail
  Set version to 2.11.6
  Release notes for Gerrit 2.11.6
  Don't reload plugins every minute
  Update documentation of commentlink to reflect changed URL
  Fix display of active row marker in tag list
  Update replication plugin to latest revision
  Fix tcl syntax highlighting not working
  Fix index.maxTerms documented default from 500 to no limit
  Update replication plugin to latest revision
  Update replication plugin to latest revision
  XSRF token cookie should honor auth.cookieSecure setting
  Fix pushing a first patchset does not trigger replication
  Fix merge validator exception type is ignored
  Send email using email queue instead of the default queue
  Rename EmailReviewCommentsExecutor to SendEmailExecutor
  Expose IndexCollection in plugin guice injector
  Update replication plugin to latest revision
  Remove documentation of --type option from ls-groups command
  ReceiveCommits: Log error message when failing to insert patch set
  ReceiveCommits: Remove declaration of unthrown exception
  Schema_113: Fix push certificate column type
  Update replication plugin to latest version
  Return case-preserving email when suggesting reviewers
  ChangeEditUtil: Fix inconsistent change message
  Fix serving of documentation in application container
  Update replication plugin to latest revision
  Use merge strategy for mergeability testing on "Rebase if Necessary" strategy

The changes done in Id1de81b31 ("Fix merge validator exception type is
ignored") are not compatible with the master branch and are reverted
in this merge.

This reverts commit dd41c53bd7.

Change-Id: I821a40bbfe77832c0bd2342ab22d37433a683799
This commit is contained in:
David Pursehouse
2016-02-10 17:37:19 +09:00
committed by David Ostrovsky
33 changed files with 726 additions and 113 deletions

View File

@@ -103,7 +103,8 @@ public class CreateEmail implements RestModifyView<AccountResource, EmailInput>
public Response<EmailInfo> apply(IdentifiedUser user, EmailInput input)
throws AuthException, BadRequestException, ResourceConflictException,
ResourceNotFoundException, OrmException, EmailException {
ResourceNotFoundException, OrmException, EmailException,
MethodNotAllowedException {
if (input.email != null && !email.equals(input.email)) {
throw new BadRequestException("email address must match URL");
}
@@ -126,7 +127,11 @@ public class CreateEmail implements RestModifyView<AccountResource, EmailInput>
}
} else {
try {
registerNewEmailFactory.create(email).send();
RegisterNewEmailSender sender = registerNewEmailFactory.create(email);
if (!sender.isAllowed()) {
throw new MethodNotAllowedException("Not allowed to add email address " + email);
}
sender.send();
info.pendingConfirmation = true;
} catch (EmailException | RuntimeException e) {
log.error("Cannot send email verification message to " + email, e);

View File

@@ -225,8 +225,6 @@ import javax.security.auth.login.LoginException;
try {
account = findAccount(schema, ctx, username, false);
} catch (AccountException e) {
LdapRealm.log.warn("Account " + username +
" not found, assuming empty group membership");
return Collections.emptySet();
}
}
@@ -248,8 +246,6 @@ import javax.security.auth.login.LoginException;
try {
account = findAccount(schema, ctx, username, true);
} catch (AccountException e) {
LdapRealm.log.warn("Account " + username +
" not found, assuming empty group membership");
return Collections.emptySet();
}
}

View File

@@ -17,12 +17,12 @@ package com.google.gerrit.server.change;
import static com.google.gerrit.server.PatchLineCommentsUtil.PLC_ORDER;
import com.google.gerrit.extensions.api.changes.ReviewInput.NotifyHandling;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.ChangeMessage;
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.SendEmailExecutor;
import com.google.gerrit.server.mail.CommentSender;
import com.google.gerrit.server.notedb.ChangeNotes;
@@ -32,7 +32,6 @@ import com.google.gerrit.server.util.ThreadLocalRequestContext;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.SchemaFactory;
import com.google.inject.Inject;
import com.google.inject.OutOfScopeException;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.assistedinject.Assisted;
@@ -51,7 +50,7 @@ public class EmailReviewComments implements Runnable, RequestContext {
NotifyHandling notify,
ChangeNotes notes,
PatchSet patchSet,
Account.Id authorId,
IdentifiedUser user,
ChangeMessage message,
List<PatchLineComment> comments);
}
@@ -65,13 +64,13 @@ public class EmailReviewComments implements Runnable, RequestContext {
private final NotifyHandling notify;
private final ChangeNotes notes;
private final PatchSet patchSet;
private final Account.Id authorId;
private final IdentifiedUser user;
private final ChangeMessage message;
private List<PatchLineComment> comments;
private ReviewDb db;
@Inject
EmailReviewComments (
EmailReviewComments(
@SendEmailExecutor ExecutorService executor,
PatchSetInfoFactory patchSetInfoFactory,
CommentSender.Factory commentSenderFactory,
@@ -80,7 +79,7 @@ public class EmailReviewComments implements Runnable, RequestContext {
@Assisted NotifyHandling notify,
@Assisted ChangeNotes notes,
@Assisted PatchSet patchSet,
@Assisted Account.Id authorId,
@Assisted IdentifiedUser user,
@Assisted ChangeMessage message,
@Assisted List<PatchLineComment> comments) {
this.sendEmailsExecutor = executor;
@@ -91,7 +90,7 @@ public class EmailReviewComments implements Runnable, RequestContext {
this.notify = notify;
this.notes = notes;
this.patchSet = patchSet;
this.authorId = authorId;
this.user = user;
this.message = message;
this.comments = PLC_ORDER.sortedCopy(comments);
}
@@ -107,7 +106,7 @@ public class EmailReviewComments implements Runnable, RequestContext {
CommentSender cm = commentSenderFactory.create(notes.getProjectName(),
notes.getChangeId());
cm.setFrom(authorId);
cm.setFrom(user.getAccountId());
cm.setPatchSet(patchSet,
patchSetInfoFactory.get(notes.getProjectName(), patchSet));
cm.setChangeMessage(message);
@@ -132,7 +131,7 @@ public class EmailReviewComments implements Runnable, RequestContext {
@Override
public CurrentUser getUser() {
throw new OutOfScopeException("No user on email thread");
return user.getRealUser();
}
@Override

View File

@@ -384,7 +384,7 @@ public class PostReview implements RestModifyView<RevisionResource, ReviewInput>
in.notify,
notes,
ps,
user.getAccountId(),
user,
message,
comments).sendAsync();
}

View File

@@ -172,9 +172,9 @@ public class ReviewerSuggestionCache {
doc.add(new TextField(NAME, a.getFullName(), Store.YES));
}
if (a.getPreferredEmail() != null) {
doc.add(new TextField(EMAIL, a.getPreferredEmail(), Store.YES));
doc.add(new StringField(EMAIL, a.getPreferredEmail().toLowerCase(),
Store.YES));
doc.add(new TextField(EMAIL, a.getPreferredEmail(), Store.YES));
}
AccountExternalIdAccess extIdAccess = db.get().accountExternalIds();
String username = AccountState.getUserName(

View File

@@ -247,7 +247,7 @@ public class ChangeEditUtil {
PatchSetInserter inserter =
patchSetInserterFactory.create(ctl, psId, squashed);
StringBuilder message = new StringBuilder("Patch set ")
StringBuilder message = new StringBuilder("Patch Set ")
.append(inserter.getPatchSetId().get())
.append(": ");

View File

@@ -20,6 +20,7 @@ import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.mail.MergedSender;
import com.google.gerrit.server.util.RequestContext;
import com.google.gerrit.server.util.ThreadLocalRequestContext;
@@ -48,6 +49,7 @@ public class EmailMerge implements Runnable, RequestContext {
private final MergedSender.Factory mergedSenderFactory;
private final SchemaFactory<ReviewDb> schemaFactory;
private final ThreadLocalRequestContext requestContext;
private final IdentifiedUser.GenericFactory identifiedUserFactory;
private final Project.NameKey project;
private final Change.Id changeId;
@@ -59,6 +61,7 @@ public class EmailMerge implements Runnable, RequestContext {
MergedSender.Factory mergedSenderFactory,
SchemaFactory<ReviewDb> schemaFactory,
ThreadLocalRequestContext requestContext,
IdentifiedUser.GenericFactory identifiedUserFactory,
@Assisted Project.NameKey project,
@Assisted Change.Id changeId,
@Assisted @Nullable Account.Id submitter) {
@@ -66,6 +69,7 @@ public class EmailMerge implements Runnable, RequestContext {
this.mergedSenderFactory = mergedSenderFactory;
this.schemaFactory = schemaFactory;
this.requestContext = requestContext;
this.identifiedUserFactory = identifiedUserFactory;
this.project = project;
this.changeId = changeId;
this.submitter = submitter;
@@ -102,6 +106,10 @@ public class EmailMerge implements Runnable, RequestContext {
@Override
public CurrentUser getUser() {
if (submitter != null) {
return identifiedUserFactory.create(
getReviewDbProvider(), submitter).getRealUser();
}
throw new OutOfScopeException("No user on email thread");
}

View File

@@ -798,7 +798,7 @@ public class ReceiveCommits {
} catch (RestApiException err) {
reject(replace.inputCommand, "internal server error");
log.error(String.format(
"Cannot add patch set to %d of %s",
"Cannot add patch set to change %d in project %s",
e.getKey().get(), project.getName()), err);
}
} else if (replace.inputCommand.getResult() == NOT_ATTEMPTED) {
@@ -2169,6 +2169,9 @@ public class ReceiveCommits {
try (RequestState state = requestState(caller)) {
return insertPatchSet(state);
}
} catch (OrmException | IOException e) {
log.error("Failed to insert patch set", e);
throw e;
} finally {
synchronizedIncrement(replaceProgress);
}

View File

@@ -216,8 +216,10 @@ public class RebaseIfNecessary extends SubmitStrategy {
static boolean dryRun(SubmitDryRun.Arguments args,
CodeReviewCommit mergeTip, CodeReviewCommit toMerge)
throws IntegrationException {
// Test for merge instead of cherry pick to avoid false negatives
// on commit chains.
return !args.mergeUtil.hasMissingDependencies(args.mergeSorter, toMerge)
&& args.mergeUtil.canCherryPick(args.mergeSorter, args.repo, mergeTip,
args.rw, toMerge);
&& args.mergeUtil.canMerge(args.mergeSorter, args.repo, mergeTip,
toMerge);
}
}

View File

@@ -29,7 +29,7 @@ import org.eclipse.jgit.lib.Config;
*/
@AutoValue
public abstract class IndexConfig {
private static final int DEFAULT_MAX_TERMS = 500;
private static final int DEFAULT_MAX_TERMS = 1024;
public static IndexConfig createDefault() {
return create(0, 0, DEFAULT_MAX_TERMS);
@@ -47,7 +47,7 @@ public abstract class IndexConfig {
return new AutoValue_IndexConfig(
checkLimit(maxLimit, "maxLimit", Integer.MAX_VALUE),
checkLimit(maxPages, "maxPages", Integer.MAX_VALUE),
checkLimit(maxTerms, "maxTerms", Integer.MAX_VALUE));
checkLimit(maxTerms, "maxTerms", DEFAULT_MAX_TERMS));
}
private static int checkLimit(int limit, String name, int defaultValue) {

View File

@@ -29,6 +29,7 @@ import com.google.gerrit.server.validators.ValidationException;
import com.google.gwtorm.server.OrmException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.validator.routines.EmailValidator;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.context.InternalContextAdapterImpl;
@@ -336,7 +337,6 @@ public abstract class OutgoingEmail {
protected boolean shouldSendMessage() {
if (body.length() == 0) {
// If we have no message body, don't send.
log.warn("Skipping delivery of email with no body");
return false;
}
@@ -344,7 +344,6 @@ public abstract class OutgoingEmail {
// If we have nobody to send this message to, then all of our
// selection filters previously for this type of message were
// unable to match a destination. Don't bother sending it.
log.warn("Skipping delivery of email with no recipients");
return false;
}
@@ -394,21 +393,21 @@ public abstract class OutgoingEmail {
/** Schedule delivery of this message to the given account. */
protected void add(final RecipientType rt, final Address addr) {
if (addr != null && addr.email != null && addr.email.length() > 0) {
if (args.emailSender.canEmail(addr.email)) {
if (smtpRcptTo.add(addr)) {
switch (rt) {
case TO:
((EmailHeader.AddressList) headers.get(HDR_TO)).add(addr);
break;
case CC:
((EmailHeader.AddressList) headers.get(HDR_CC)).add(addr);
break;
case BCC:
break;
}
}
} else {
if (!EmailValidator.getInstance().isValid(addr.email)) {
log.warn("Not emailing " + addr.email + " (invalid email address)");
} else if (!args.emailSender.canEmail(addr.email)) {
log.warn("Not emailing " + addr.email + " (prohibited by allowrcpt)");
} else if (smtpRcptTo.add(addr)) {
switch (rt) {
case TO:
((EmailHeader.AddressList) headers.get(HDR_TO)).add(addr);
break;
case CC:
((EmailHeader.AddressList) headers.get(HDR_CC)).add(addr);
break;
case BCC:
break;
}
}
}
}

View File

@@ -49,11 +49,6 @@ public class RegisterNewEmailSender extends OutgoingEmail {
add(RecipientType.TO, new Address(addr));
}
@Override
protected boolean shouldSendMessage() {
return true;
}
@Override
protected void format() throws EmailException {
appendText(velocifyFile("RegisterNewEmail.vm"));
@@ -70,4 +65,8 @@ public class RegisterNewEmailSender extends OutgoingEmail {
}
return emailToken;
}
public boolean isAllowed() {
return args.emailSender.canEmail(addr);
}
}

View File

@@ -249,16 +249,19 @@ public class SmtpEmailSender implements EmailSender {
client.enableSSL(sslVerify);
}
client.setConnectTimeout(connectTimeout);
try {
client.setConnectTimeout(connectTimeout);
client.connect(smtpHost, smtpPort);
if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
throw new EmailException("SMTP server rejected connection");
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()) {
String e = client.getReplyString();
throw new EmailException(
"SMTP server rejected HELO/EHLO greeting: " + e);
"SMTP server rejected HELO/EHLO greeting: " + replyString);
}
if (smtpEncryption == Encryption.TLS) {
@@ -266,16 +269,15 @@ public class SmtpEmailSender implements EmailSender {
throw new EmailException("SMTP server does not support TLS");
}
if (!client.login()) {
String e = client.getReplyString();
throw new EmailException("SMTP server rejected login: " + e);
throw new EmailException("SMTP server rejected login: " + replyString);
}
}
if (smtpUser != null && !client.auth(smtpUser, smtpPass)) {
String e = client.getReplyString();
throw new EmailException("SMTP server rejected auth: " + e);
throw new EmailException("SMTP server rejected auth: " + replyString);
}
} catch (IOException e) {
return client;
} catch (IOException | EmailException e) {
if (client.isConnected()) {
try {
client.disconnect();
@@ -283,17 +285,10 @@ public class SmtpEmailSender implements EmailSender {
//Ignored
}
}
throw new EmailException(e.getMessage(), e);
} catch (EmailException e) {
if (client.isConnected()) {
try {
client.disconnect();
} catch (IOException e2) {
// Ignored
}
if (e instanceof EmailException) {
throw (EmailException) e;
}
throw e;
throw new EmailException(e.getMessage(), e);
}
return client;
}
}

View File

@@ -14,8 +14,6 @@
package com.google.gerrit.server.plugins;
import static com.google.gerrit.common.FileUtil.lastModified;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.gerrit.common.Nullable;
@@ -171,6 +169,6 @@ public abstract class Plugin {
protected abstract boolean canReload();
boolean isModified(Path jar) {
return snapshot.lastModified() != lastModified(jar);
return snapshot.isModified(jar.toFile());
}
}

View File

@@ -564,7 +564,9 @@ public class PluginGuiceEnvironment {
return false;
}
Class<?> type = key.getTypeLiteral().getRawType();
if (LifecycleListener.class.isAssignableFrom(type)) {
if (LifecycleListener.class.isAssignableFrom(type)
// This is needed for secondary index to work from plugin listeners
&& !is("com.google.gerrit.server.index.IndexCollection", type)) {
return false;
}
if (StartPluginListener.class.isAssignableFrom(type)) {

View File

@@ -293,6 +293,10 @@ public class OutputStreamQuery {
eventFactory.addPatchSetFileNames(c.currentPatchSet,
d.change(), d.currentPatchSet());
}
if (includeComments) {
eventFactory.addPatchSetComments(c.currentPatchSet,
d.publishedComments());
}
}
}
@@ -301,7 +305,7 @@ public class OutputStreamQuery {
if (includePatchSets) {
eventFactory.addPatchSets(db.get(), rw, c, d.patchSets(),
includeApprovals ? d.approvals().asMap() : null,
labelTypes);
includeFiles, d.notes(), labelTypes);
for (PatchSetAttribute attribute : c.patchSets) {
eventFactory.addPatchSetComments(
attribute, d.publishedComments());