blob: 40232bb954423291a2611199ef766ddc911ad5bb [file] [log] [blame]
// Copyright (C) 2008 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;
import com.google.gerrit.client.data.AccountCache;
import com.google.gerrit.client.data.ApprovalType;
import com.google.gerrit.client.data.GerritConfig;
import com.google.gerrit.client.data.GitwebLink;
import com.google.gerrit.client.data.GroupCache;
import com.google.gerrit.client.data.ProjectCache;
import com.google.gerrit.client.reviewdb.AccountGroup;
import com.google.gerrit.client.reviewdb.ApprovalCategory;
import com.google.gerrit.client.reviewdb.ApprovalCategoryValue;
import com.google.gerrit.client.reviewdb.Branch;
import com.google.gerrit.client.reviewdb.Change;
import com.google.gerrit.client.reviewdb.Project;
import com.google.gerrit.client.reviewdb.ProjectRight;
import com.google.gerrit.client.reviewdb.ReviewDb;
import com.google.gerrit.client.reviewdb.SchemaVersion;
import com.google.gerrit.client.reviewdb.SystemConfig;
import com.google.gerrit.client.reviewdb.TrustedExternalId;
import com.google.gerrit.client.reviewdb.SystemConfig.LoginType;
import com.google.gerrit.client.rpc.Common;
import com.google.gerrit.client.workflow.NoOpFunction;
import com.google.gerrit.client.workflow.SubmitFunction;
import com.google.gerrit.git.MergeQueue;
import com.google.gerrit.git.PushAllProjectsOp;
import com.google.gerrit.git.PushQueue;
import com.google.gerrit.git.RepositoryCache;
import com.google.gerrit.git.WorkQueue;
import com.google.gerrit.server.mail.EmailException;
import com.google.gerrit.server.patch.DiffCacheEntryFactory;
import com.google.gerrit.server.ssh.SshKeyCacheEntryFactory;
import com.google.gwtjsonrpc.server.SignedToken;
import com.google.gwtjsonrpc.server.XsrfException;
import com.google.gwtorm.client.OrmException;
import com.google.gwtorm.client.Transaction;
import com.google.gwtorm.jdbc.Database;
import com.google.gwtorm.jdbc.SimpleDataSource;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;
import net.sf.ehcache.config.DiskStoreConfiguration;
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.net.smtp.AuthSMTPClient;
import org.apache.commons.net.smtp.SMTPClient;
import org.apache.commons.net.smtp.SMTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spearce.jgit.lib.PersonIdent;
import org.spearce.jgit.lib.RepositoryConfig;
import org.spearce.jgit.lib.WindowCache;
import org.spearce.jgit.lib.WindowCacheConfig;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
/** Global server-side state for Gerrit. */
public class GerritServer {
private static final Logger log = LoggerFactory.getLogger(GerritServer.class);
private static DataSource datasource;
private static GerritServer impl;
private static CacheManager cacheMgr;
static void closeDataSource() {
if (datasource != null) {
try {
try {
Class.forName("com.mchange.v2.c3p0.DataSources").getMethod("destroy",
DataSource.class).invoke(null, datasource);
} catch (Throwable bad) {
// Oh well, its not a c3p0 pooled connection. Too bad its
// not standardized how "good applications cleanup".
}
} finally {
datasource = null;
}
}
if (cacheMgr != null) {
try {
cacheMgr.shutdown();
} catch (Throwable bad) {
} finally {
cacheMgr = null;
}
}
}
/**
* Obtain the singleton server instance for this web application.
*
* @return the server instance. Never null.
* @throws OrmException the database could not be configured. There is
* something wrong with the schema configuration in {@link ReviewDb}
* that must be addressed by a developer.
* @throws XsrfException the XSRF support could not be correctly configured to
* protect the application against cross-site request forgery. The JVM
* is most likely lacking critical security algorithms.
*/
public static synchronized GerritServer getInstance() throws OrmException,
XsrfException {
return getInstance(true);
}
public static synchronized GerritServer getInstance(final boolean startQueues)
throws OrmException, XsrfException {
if (impl == null) {
try {
impl = new GerritServer();
if (startQueues) {
impl.reloadSubmitQueue();
if (PushQueue.isReplicationEnabled()) {
WorkQueue.schedule(new PushAllProjectsOp(), 30, TimeUnit.SECONDS);
}
}
} catch (OrmException e) {
closeDataSource();
log.error("GerritServer ORM is unavailable", e);
throw e;
} catch (XsrfException e) {
closeDataSource();
log.error("GerritServer XSRF support failed to initailize", e);
throw e;
}
}
return impl;
}
public static String serverUrl(final HttpServletRequest req) {
// Assume this servlet is in the context with a simple name like "login"
// and we were accessed without any path info. Clipping the last part of
// the name from the URL should generate the web application's root path.
//
String uri = req.getRequestURL().toString();
final int s = uri.lastIndexOf('/');
if (s >= 0) {
uri = uri.substring(0, s + 1);
}
final String sfx = "/gerrit/rpc/";
if (uri.endsWith(sfx)) {
// Nope, it was one of our RPC servlets. Drop the rpc too.
//
uri = uri.substring(0, uri.length() - (sfx.length() - 1));
}
return uri;
}
private final Database<ReviewDb> db;
private final RepositoryConfig gerritConfigFile;
private final int sessionAge;
private SystemConfig sConfig;
private final SignedToken xsrf;
private final SignedToken account;
private final SignedToken emailReg;
private final RepositoryCache repositories;
private final SelfPopulatingCache diffCache;
private final SelfPopulatingCache sshKeysCache;
private GerritServer() throws OrmException, XsrfException {
db = createDatabase();
loadSystemConfig();
if (sConfig == null) {
throw new OrmException("No " + SystemConfig.class.getName() + " found");
}
final File cfgLoc = new File(getSitePath(), "gerrit.config");
gerritConfigFile = new RepositoryConfig(null, cfgLoc);
try {
gerritConfigFile.load();
} catch (FileNotFoundException e) {
log.info("No " + cfgLoc.getAbsolutePath() + "; assuming defaults");
} catch (IOException e) {
throw new OrmException("Cannot read " + cfgLoc.getAbsolutePath(), e);
}
reconfigureWindowCache();
sessionAge = gerritConfigFile.getInt("auth", "maxsessionage", 12 * 60) * 60;
xsrf = new SignedToken(getSessionAge(), sConfig.xsrfPrivateKey);
final int accountCookieAge;
switch (getLoginType()) {
case HTTP:
accountCookieAge = -1; // expire when the browser closes
break;
case OPENID:
default:
accountCookieAge = getSessionAge();
break;
}
account = new SignedToken(accountCookieAge, sConfig.accountPrivateKey);
emailReg = new SignedToken(5 * 24 * 60 * 60, sConfig.accountPrivateKey);
final String basePath =
getGerritConfig().getString("gerrit", null, "basepath");
if (basePath != null) {
File root = new File(basePath);
if (!root.isAbsolute()) {
root = new File(getSitePath(), basePath);
}
repositories = new RepositoryCache(root);
} else {
repositories = null;
}
final ReviewDb c = db.open();
try {
loadGerritConfig(c);
} finally {
c.close();
}
Common.setSchemaFactory(db);
Common.setProjectCache(new ProjectCache());
Common.setAccountCache(new AccountCache());
Common.setGroupCache(new GroupCache(sConfig));
cacheMgr = new CacheManager(createCacheConfiguration());
diffCache = startCacheDiff();
sshKeysCache = startCacheSshKeys();
}
private Configuration createCacheConfiguration() {
final Configuration mgrCfg = new Configuration();
configureDiskStore(mgrCfg);
configureDefaultCache(mgrCfg);
if (getLoginType() == LoginType.OPENID) {
final CacheConfiguration c;
c = configureNamedCache(mgrCfg, "openid", false, 5);
c.setTimeToLiveSeconds(c.getTimeToIdleSeconds());
mgrCfg.addCache(c);
}
mgrCfg.addCache(configureNamedCache(mgrCfg, "diff", true, 0));
mgrCfg.addCache(configureNamedCache(mgrCfg, "sshkeys", false, 0));
return mgrCfg;
}
private void configureDiskStore(final Configuration mgrCfg) {
String path = gerritConfigFile.getString("cache", null, "directory");
if (path == null || path.length() == 0) {
path = "disk_cache";
}
final File loc = new File(getSitePath(), path);
if (loc.exists() || loc.mkdirs()) {
if (loc.canWrite()) {
final DiskStoreConfiguration c = new DiskStoreConfiguration();
c.setPath(loc.getAbsolutePath());
mgrCfg.addDiskStore(c);
log.info("Enabling disk cache " + loc.getAbsolutePath());
} else {
log.warn("Can't write to disk cache: " + loc.getAbsolutePath());
}
} else {
log.warn("Can't create disk cache: " + loc.getAbsolutePath());
}
}
private void configureDefaultCache(final Configuration mgrCfg) {
final RepositoryConfig i = gerritConfigFile;
final CacheConfiguration c = new CacheConfiguration();
c.setMaxElementsInMemory(i.getInt("cache", "memorylimit", 1024));
c.setMemoryStoreEvictionPolicyFromObject(MemoryStoreEvictionPolicy.LFU);
c.setTimeToLiveSeconds(0);
final int oneday = 24 * 60;
c.setTimeToIdleSeconds(i.getInt("cache", "maxage", 3 * 30 * oneday) * 60);
c.setEternal(c.getTimeToIdleSeconds() == 0);
if (mgrCfg.getDiskStoreConfiguration() != null) {
c.setMaxElementsOnDisk(i.getInt("cache", "disklimit", 16384));
c.setOverflowToDisk(false);
c.setDiskPersistent(false);
int diskbuffer = i.getInt("cache", "diskbuffer", 5 * 1024 * 1024);
diskbuffer /= 1024 * 1024;
c.setDiskSpoolBufferSizeMB(Math.max(1, diskbuffer));
c.setDiskExpiryThreadIntervalSeconds(60 * 60);
}
mgrCfg.setDefaultCacheConfiguration(c);
}
private CacheConfiguration configureNamedCache(final Configuration mgrCfg,
final String name, final boolean disk, final int defaultAge) {
final RepositoryConfig i = gerritConfigFile;
final CacheConfiguration def = mgrCfg.getDefaultCacheConfiguration();
final CacheConfiguration cfg;
try {
cfg = def.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Cannot configure cache " + name, e);
}
cfg.setName(name);
cfg.setMaxElementsInMemory(i.getInt("cache", name, "memorylimit", def
.getMaxElementsInMemory()));
cfg.setTimeToIdleSeconds(i.getInt("cache", name, "maxage", defaultAge > 0
? defaultAge : (int) (def.getTimeToIdleSeconds() / 60)) * 60);
cfg.setEternal(cfg.getTimeToIdleSeconds() == 0);
if (disk && mgrCfg.getDiskStoreConfiguration() != null) {
cfg.setMaxElementsOnDisk(i.getInt("cache", name, "disklimit", def
.getMaxElementsOnDisk()));
final int m = 1024 * 1024;
final int diskbuffer =
i.getInt("cache", name, "diskbuffer", def.getDiskSpoolBufferSizeMB()
* m)
/ m;
cfg.setDiskSpoolBufferSizeMB(Math.max(1, diskbuffer));
cfg.setOverflowToDisk(true);
cfg.setDiskPersistent(true);
}
return cfg;
}
private SelfPopulatingCache startCacheDiff() {
final Cache dc = cacheMgr.getCache("diff");
final SelfPopulatingCache r;
r = new SelfPopulatingCache(dc, new DiffCacheEntryFactory(this));
cacheMgr.replaceCacheWithDecoratedCache(dc, r);
return r;
}
private SelfPopulatingCache startCacheSshKeys() {
final Cache dc = cacheMgr.getCache("sshkeys");
final SelfPopulatingCache r;
r = new SelfPopulatingCache(dc, new SshKeyCacheEntryFactory());
cacheMgr.replaceCacheWithDecoratedCache(dc, r);
return r;
}
public static Database<ReviewDb> createDatabase() throws OrmException {
final String dsName = "java:comp/env/jdbc/ReviewDb";
try {
datasource = (DataSource) new InitialContext().lookup(dsName);
} catch (NamingException namingErr) {
final Properties p = readGerritDataSource();
if (p == null) {
throw new OrmException("Initialization error:\n" + " * No DataSource "
+ dsName + "\n" + " * No -DGerritServer=GerritServer.properties"
+ " on Java command line", namingErr);
}
try {
datasource = new SimpleDataSource(p);
} catch (SQLException se) {
throw new OrmException("Database unavailable", se);
}
}
return new Database<ReviewDb>(datasource, ReviewDb.class);
}
private static Properties readGerritDataSource() throws OrmException {
final Properties srvprop = new Properties();
String name = System.getProperty("GerritServer");
if (name == null) {
name = "GerritServer.properties";
}
try {
final InputStream in = new FileInputStream(name);
try {
srvprop.load(in);
} finally {
in.close();
}
} catch (IOException e) {
throw new OrmException("Cannot read " + name, e);
}
final Properties dbprop = new Properties();
for (final Map.Entry<Object, Object> e : srvprop.entrySet()) {
final String key = (String) e.getKey();
if (key.startsWith("database.")) {
dbprop.put(key.substring("database.".length()), e.getValue());
}
}
return dbprop;
}
private void initSystemConfig(final ReviewDb c) throws OrmException {
final AccountGroup admin =
new AccountGroup(new AccountGroup.NameKey("Administrators"),
new AccountGroup.Id(c.nextAccountGroupId()));
admin.setDescription("Gerrit Site Administrators");
c.accountGroups().insert(Collections.singleton(admin));
final AccountGroup anonymous =
new AccountGroup(new AccountGroup.NameKey("Anonymous Users"),
new AccountGroup.Id(c.nextAccountGroupId()));
anonymous.setDescription("Any user, signed-in or not");
anonymous.setOwnerGroupId(admin.getId());
c.accountGroups().insert(Collections.singleton(anonymous));
final AccountGroup registered =
new AccountGroup(new AccountGroup.NameKey("Registered Users"),
new AccountGroup.Id(c.nextAccountGroupId()));
registered.setDescription("Any signed-in user");
registered.setOwnerGroupId(admin.getId());
c.accountGroups().insert(Collections.singleton(registered));
final SystemConfig s = SystemConfig.create();
s.xsrfPrivateKey = SignedToken.generateRandomKey();
s.accountPrivateKey = SignedToken.generateRandomKey();
s.adminGroupId = admin.getId();
s.anonymousGroupId = anonymous.getId();
s.registeredGroupId = registered.getId();
c.systemConfig().insert(Collections.singleton(s));
// By default with OpenID trust any http:// or https:// provider
//
initTrustedExternalId(c, "http://");
initTrustedExternalId(c, "https://");
initTrustedExternalId(c, "https://www.google.com/accounts/o8/id?id=");
}
private void initTrustedExternalId(final ReviewDb c, final String re)
throws OrmException {
c.trustedExternalIds().insert(
Collections.singleton(new TrustedExternalId(new TrustedExternalId.Key(
re))));
}
private void initWildCardProject(final ReviewDb c) throws OrmException {
final Project proj;
proj =
new Project(new Project.NameKey("-- All Projects --"),
ProjectRight.WILD_PROJECT);
proj.setDescription("Rights inherited by all other projects");
proj.setOwnerGroupId(sConfig.adminGroupId);
proj.setUseContributorAgreements(false);
c.projects().insert(Collections.singleton(proj));
}
private void initVerifiedCategory(final ReviewDb c) throws OrmException {
final Transaction txn = c.beginTransaction();
final ApprovalCategory cat;
final ArrayList<ApprovalCategoryValue> vals;
cat = new ApprovalCategory(new ApprovalCategory.Id("VRIF"), "Verified");
cat.setPosition((short) 0);
vals = new ArrayList<ApprovalCategoryValue>();
vals.add(value(cat, 1, "Verified"));
vals.add(value(cat, 0, "No score"));
vals.add(value(cat, -1, "Fails"));
c.approvalCategories().insert(Collections.singleton(cat), txn);
c.approvalCategoryValues().insert(vals, txn);
txn.commit();
}
private void initCodeReviewCategory(final ReviewDb c) throws OrmException {
final Transaction txn = c.beginTransaction();
final ApprovalCategory cat;
final ArrayList<ApprovalCategoryValue> vals;
cat = new ApprovalCategory(new ApprovalCategory.Id("CRVW"), "Code Review");
cat.setPosition((short) 1);
vals = new ArrayList<ApprovalCategoryValue>();
vals.add(value(cat, 2, "Looks good to me, approved"));
vals.add(value(cat, 1, "Looks good to me, but someone else must approve"));
vals.add(value(cat, 0, "No score"));
vals.add(value(cat, -1, "I would prefer that you didn't submit this"));
vals.add(value(cat, -2, "Do not submit"));
c.approvalCategories().insert(Collections.singleton(cat), txn);
c.approvalCategoryValues().insert(vals, txn);
txn.commit();
final ProjectRight approve =
new ProjectRight(new ProjectRight.Key(ProjectRight.WILD_PROJECT, cat
.getId(), sConfig.registeredGroupId));
approve.setMaxValue((short) 1);
approve.setMinValue((short) -1);
c.projectRights().insert(Collections.singleton(approve));
}
private void initReadCategory(final ReviewDb c) throws OrmException {
final Transaction txn = c.beginTransaction();
final ApprovalCategory cat;
final ArrayList<ApprovalCategoryValue> vals;
cat = new ApprovalCategory(ApprovalCategory.READ, "Read Access");
cat.setPosition((short) -1);
cat.setFunctionName(NoOpFunction.NAME);
vals = new ArrayList<ApprovalCategoryValue>();
vals.add(value(cat, 1, "Read access"));
vals.add(value(cat, -1, "No access"));
c.approvalCategories().insert(Collections.singleton(cat), txn);
c.approvalCategoryValues().insert(vals, txn);
txn.commit();
{
final ProjectRight read =
new ProjectRight(new ProjectRight.Key(ProjectRight.WILD_PROJECT, cat
.getId(), sConfig.anonymousGroupId));
read.setMaxValue((short) 1);
read.setMinValue((short) 1);
c.projectRights().insert(Collections.singleton(read));
}
{
final ProjectRight read =
new ProjectRight(new ProjectRight.Key(ProjectRight.WILD_PROJECT, cat
.getId(), sConfig.adminGroupId));
read.setMaxValue((short) 1);
read.setMinValue((short) 1);
c.projectRights().insert(Collections.singleton(read));
}
}
private void initSubmitCategory(final ReviewDb c) throws OrmException {
final Transaction txn = c.beginTransaction();
final ApprovalCategory cat;
final ArrayList<ApprovalCategoryValue> vals;
cat = new ApprovalCategory(ApprovalCategory.SUBMIT, "Submit");
cat.setPosition((short) -1);
cat.setFunctionName(SubmitFunction.NAME);
vals = new ArrayList<ApprovalCategoryValue>();
vals.add(value(cat, 1, "Submit"));
c.approvalCategories().insert(Collections.singleton(cat), txn);
c.approvalCategoryValues().insert(vals, txn);
txn.commit();
}
private void initPushTagCategory(final ReviewDb c) throws OrmException {
final Transaction txn = c.beginTransaction();
final ApprovalCategory cat;
final ArrayList<ApprovalCategoryValue> vals;
cat = new ApprovalCategory(ApprovalCategory.PUSH_TAG, "Push Annotated Tag");
cat.setPosition((short) -1);
cat.setFunctionName(NoOpFunction.NAME);
vals = new ArrayList<ApprovalCategoryValue>();
vals.add(value(cat, ApprovalCategory.PUSH_TAG_SIGNED, "Create Signed Tag"));
vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANNOTATED,
"Create Annotated Tag"));
vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANY, "Create Any Tag"));
c.approvalCategories().insert(Collections.singleton(cat), txn);
c.approvalCategoryValues().insert(vals, txn);
txn.commit();
}
private void initPushUpdateBranchCategory(final ReviewDb c)
throws OrmException {
final Transaction txn = c.beginTransaction();
final ApprovalCategory cat;
final ArrayList<ApprovalCategoryValue> vals;
cat = new ApprovalCategory(ApprovalCategory.PUSH_HEAD, "Push Branch");
cat.setPosition((short) -1);
cat.setFunctionName(NoOpFunction.NAME);
vals = new ArrayList<ApprovalCategoryValue>();
vals.add(value(cat, ApprovalCategory.PUSH_HEAD_UPDATE, "Update Branch"));
vals.add(value(cat, ApprovalCategory.PUSH_HEAD_CREATE, "Create Branch"));
vals.add(value(cat, ApprovalCategory.PUSH_HEAD_REPLACE,
"Force Push Branch; Delete Branch"));
c.approvalCategories().insert(Collections.singleton(cat), txn);
c.approvalCategoryValues().insert(vals, txn);
txn.commit();
}
private static ApprovalCategoryValue value(final ApprovalCategory cat,
final int value, final String name) {
return new ApprovalCategoryValue(new ApprovalCategoryValue.Id(cat.getId(),
(short) value), name);
}
private void loadSystemConfig() throws OrmException {
final ReviewDb c = db.open();
try {
SchemaVersion sVer;
try {
sVer = c.schemaVersion().get(new SchemaVersion.Key());
} catch (OrmException e) {
// Assume the schema doesn't exist.
//
sVer = null;
}
if (sVer == null) {
// Assume the schema is empty and populate it.
//
c.createSchema();
sVer = SchemaVersion.create();
sVer.versionNbr = ReviewDb.VERSION;
c.schemaVersion().insert(Collections.singleton(sVer));
initSystemConfig(c);
sConfig = c.systemConfig().get(new SystemConfig.Key());
initWildCardProject(c);
initReadCategory(c);
initVerifiedCategory(c);
initCodeReviewCategory(c);
initSubmitCategory(c);
initPushTagCategory(c);
initPushUpdateBranchCategory(c);
}
if (sVer.versionNbr == 2) {
initPushTagCategory(c);
initPushUpdateBranchCategory(c);
sVer.versionNbr = 3;
c.schemaVersion().update(Collections.singleton(sVer));
}
if (sVer.versionNbr == ReviewDb.VERSION) {
final List<SystemConfig> all = c.systemConfig().all().toList();
switch (all.size()) {
case 0:
throw new OrmException("system_config table is empty");
case 1:
sConfig = all.get(0);
break;
default:
throw new OrmException("system_config must have exactly 1 row;"
+ " found " + all.size() + " rows instead");
}
} else {
throw new OrmException("Unsupported schema version " + sVer.versionNbr
+ "; expected schema version " + ReviewDb.VERSION);
}
} finally {
c.close();
}
}
private void loadGerritConfig(final ReviewDb db) throws OrmException {
final GerritConfig r = new GerritConfig();
r.setCanonicalUrl(getCanonicalURL());
r.setUseContributorAgreements(getGerritConfig().getBoolean("auth",
"contributoragreements", false));
r.setGitDaemonUrl(getGerritConfig().getString("gerrit", null,
"canonicalgiturl"));
r.setUseRepoDownload(getGerritConfig().getBoolean("repo", null,
"showdownloadcommand", false));
r.setUseContactInfo(getContactStoreURL() != null);
r.setAllowRegisterNewEmail(isOutgoingMailEnabled());
r.setLoginType(getLoginType());
final String gitwebUrl = getGerritConfig().getString("gitweb", null, "url");
if (gitwebUrl != null) {
r.setGitwebLink(new GitwebLink(gitwebUrl));
}
for (final ApprovalCategory c : db.approvalCategories().all()) {
r.add(new ApprovalType(c, db.approvalCategoryValues().byCategory(
c.getId()).toList()));
}
Common.setGerritConfig(r);
}
public boolean isOutgoingMailEnabled() {
return getGerritConfig().getBoolean("sendemail", null, "enable", true);
}
public SMTPClient createOutgoingMail() throws EmailException {
if (!isOutgoingMailEnabled()) {
throw new EmailException("Sending email is disabled");
}
final RepositoryConfig cfg = getGerritConfig();
String smtpHost = cfg.getString("sendemail", null, "smtpserver");
if (smtpHost == null) {
smtpHost = "127.0.0.1";
}
int smtpPort = cfg.getInt("sendemail", null, "smtpserverport", 25);
String smtpUser = cfg.getString("sendemail", null, "smtpuser");
String smtpPass = cfg.getString("sendemail", null, "smtpuserpass");
final AuthSMTPClient client = new AuthSMTPClient("UTF-8");
client.setAllowRcpt(cfg.getStringList("sendemail", null, "allowrcpt"));
try {
client.connect(smtpHost, smtpPort);
if (!SMTPReply.isPositiveCompletion(client.getReplyCode())) {
throw new EmailException("SMTP server rejected connection");
}
if (!client.login()) {
String e = client.getReplyString();
throw new EmailException("SMTP server rejected login: " + e);
}
if (smtpUser != null && !client.auth(smtpUser, smtpPass)) {
String e = client.getReplyString();
throw new EmailException("SMTP server rejected auth: " + e);
}
} catch (IOException e) {
if (client.isConnected()) {
try {
client.disconnect();
} catch (IOException e2) {
}
}
throw new EmailException(e.getMessage(), e);
} catch (EmailException e) {
if (client.isConnected()) {
try {
client.disconnect();
} catch (IOException e2) {
}
}
throw e;
}
return client;
}
private void reconfigureWindowCache() {
final WindowCacheConfig c = new WindowCacheConfig();
c.fromConfig(gerritConfigFile);
WindowCache.reconfigure(c);
}
private void reloadSubmitQueue() {
WorkQueue.schedule(new Runnable() {
public void run() {
final HashSet<Branch.NameKey> pending = new HashSet<Branch.NameKey>();
try {
final ReviewDb c = db.open();
try {
for (final Change change : c.changes().allSubmitted()) {
pending.add(change.getDest());
}
} finally {
c.close();
}
} catch (OrmException e) {
log.error("Cannot reload MergeQueue", e);
}
for (final Branch.NameKey branch : pending) {
MergeQueue.schedule(branch);
}
}
@Override
public String toString() {
return "Reload Submit Queue";
}
}, 15, TimeUnit.SECONDS);
}
/** Time (in seconds) that user sessions stay "signed in". */
public int getSessionAge() {
return sessionAge;
}
/** Get the signature support used to protect against XSRF attacks. */
public SignedToken getXsrfToken() {
return xsrf;
}
/** Get the signature support used to protect user identity cookies. */
public SignedToken getAccountToken() {
return account;
}
/** Get the signature used for email registration/validation links. */
public SignedToken getEmailRegistrationToken() {
return emailReg;
}
private SystemConfig.LoginType getLoginType() {
String type = getGerritConfig().getString("auth", null, "type");
if (type == null) {
return SystemConfig.LoginType.OPENID;
}
for (SystemConfig.LoginType t : SystemConfig.LoginType.values()) {
if (type.equalsIgnoreCase(t.name())) {
return t;
}
}
throw new IllegalStateException("Unsupported auth.type: " + type);
}
public String getLoginHttpHeader() {
return getGerritConfig().getString("auth", null, "httpheader");
}
public String getEmailFormat() {
return getGerritConfig().getString("auth", null, "emailformat");
}
public String getContactStoreURL() {
return getGerritConfig().getString("contactstore", null, "url");
}
public String getContactStoreAPPSEC() {
return getGerritConfig().getString("contactstore", null, "appsec");
}
/** A binary string key to encrypt cookies related to account data. */
public String getAccountCookieKey() {
byte[] r = new byte[sConfig.accountPrivateKey.length()];
for (int k = r.length - 1; k >= 0; k--) {
r[k] = (byte) sConfig.accountPrivateKey.charAt(k);
}
r = Base64.decodeBase64(r);
final StringBuilder b = new StringBuilder();
for (int i = 0; i < r.length; i++) {
b.append((char) r[i]);
}
return b.toString();
}
/** Local filesystem location of header/footer/CSS configuration files. */
public File getSitePath() {
return sConfig.sitePath != null ? new File(sConfig.sitePath) : null;
}
/** Optional canonical URL for this application. */
public String getCanonicalURL() {
String u = getGerritConfig().getString("gerrit", null, "canonicalweburl");
if (u != null && !u.endsWith("/")) {
u += "/";
}
return u;
}
/** Get the parsed <code>$site_path/gerrit.config</code> file. */
public RepositoryConfig getGerritConfig() {
return gerritConfigFile;
}
/** Get the repositories maintained by this server. */
public RepositoryCache getRepositoryCache() {
return repositories;
}
/** Get all registered caches. */
public Ehcache[] getAllCaches() {
final String[] cacheNames = cacheMgr.getCacheNames();
Arrays.sort(cacheNames);
final Ehcache[] r = new Ehcache[cacheNames.length];
for (int i = 0; i < cacheNames.length; i++) {
r[i] = cacheMgr.getEhcache(cacheNames[i]);
}
return r;
}
/** Get any existing cache by name. */
public Cache getCache(final String name) {
return cacheMgr.getCache(name);
}
/** Get the self-populating cache of DiffCacheContent entities. */
public SelfPopulatingCache getDiffCache() {
return diffCache;
}
/** Get the self-populating cache of user SSH keys. */
public SelfPopulatingCache getSshKeysCache() {
return sshKeysCache;
}
/** Get a new identity representing this Gerrit server in Git. */
public PersonIdent newGerritPersonIdent() {
String name = getGerritConfig().getString("user", null, "name");
if (name == null) {
name = "Gerrit Code Review";
}
String email = getGerritConfig().getCommitterEmail();
if (email == null || email.length() == 0) {
email = "gerrit@localhost";
}
return new PersonIdent(name, email);
}
public boolean isAllowGoogleAccountUpgrade() {
return getGerritConfig().getBoolean("auth", "allowgoogleaccountupgrade",
false);
}
}