blob: 60b4ecc09d9d123d3f4d5403c6bede0280b50e4a [file] [log] [blame]
/*
* Copyright (c) 2013 by syntevo GmbH. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of syntevo GmbH nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.syntevo.bugtraq;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class BugtraqConfig {
// Constants ==============================================================
private static final String DOT_GIT_BUGTRAQ = ".gitbugtraq";
private static final String DOT_TGITCONFIG = ".tgitconfig";
private static final String BUGTRAQ = "bugtraq";
private static final String URL = "url";
private static final String ENABLED = "enabled";
private static final String LOG_REGEX = "logregex";
private static final String LOG_FILTERREGEX = "logfilterregex";
private static final String LOG_LINKREGEX = "loglinkregex";
private static final String LOG_LINKTEXT = "loglinktext";
// Static =================================================================
@Nullable
public static BugtraqConfig read(@NotNull Repository repository) throws IOException, ConfigInvalidException {
Config baseConfig = getBaseConfig(repository, DOT_GIT_BUGTRAQ);
if (baseConfig == null) {
baseConfig = getBaseConfig(repository, DOT_TGITCONFIG);
}
final Set<String> allNames = new HashSet<String>();
final Config config;
try {
config = repository.getConfig();
}
catch (RuntimeException ex) {
final Throwable cause = ex.getCause();
if (cause instanceof IOException) {
throw (IOException)cause;
}
throw ex;
}
if (getString(null, URL, config, baseConfig) != null) {
allNames.add(null);
}
else {
allNames.addAll(config.getSubsections(BUGTRAQ));
if (baseConfig != null) {
allNames.addAll(baseConfig.getSubsections(BUGTRAQ));
}
}
final List<BugtraqEntry> entries = new ArrayList<BugtraqEntry>();
for (String name : allNames) {
final String url = getString(name, URL, config, baseConfig);
if (url == null) {
continue;
}
final String enabled = getString(name, ENABLED, config, baseConfig);
if (enabled != null && !"true".equals(enabled)) {
continue;
}
String idRegex = getString(name, LOG_REGEX, config, baseConfig);
if (idRegex == null) {
return null;
}
String filterRegex = getString(name, LOG_FILTERREGEX, config, baseConfig);
final String linkRegex = getString(name, LOG_LINKREGEX, config, baseConfig);
if (filterRegex == null && linkRegex == null) {
final String[] split = idRegex.split("\n", Integer.MAX_VALUE);
if (split.length == 2) {
// Compatibility with TortoiseGit
filterRegex = split[0];
idRegex = split[1];
}
else {
// Backwards compatibility with specification version < 0.3
final List<String> logIdRegexs = new ArrayList<String>();
for (int index = 1; index < Integer.MAX_VALUE; index++) {
final String logIdRegexN = getString(name, LOG_REGEX + index, config, baseConfig);
if (logIdRegexN == null) {
break;
}
logIdRegexs.add(logIdRegexN);
}
if (logIdRegexs.size() > 1) {
throw new ConfigInvalidException("More than three " + LOG_REGEX + " entries found. This is not supported anymore since bugtraq version 0.3, use " + LOG_FILTERREGEX + " and " + LOG_LINKREGEX + " instead.");
}
else if (logIdRegexs.size() == 1) {
filterRegex = idRegex;
idRegex = logIdRegexs.get(0);
}
}
}
final String linkText = getString(name, LOG_LINKTEXT, config, baseConfig);
entries.add(new BugtraqEntry(url, idRegex, linkRegex, filterRegex, linkText));
}
if (entries.isEmpty()) {
return null;
}
return new BugtraqConfig(entries);
}
// Fields =================================================================
@NotNull
private final List<BugtraqEntry> entries;
// Setup ==================================================================
BugtraqConfig(@NotNull List<BugtraqEntry> entries) {
this.entries = entries;
}
// Accessing ==============================================================
@NotNull
public List<BugtraqEntry> getEntries() {
return Collections.unmodifiableList(entries);
}
// Utils ==================================================================
@Nullable
private static Config getBaseConfig(@NotNull Repository repository, @NotNull String configFileName) throws IOException, ConfigInvalidException {
final Config baseConfig;
if (repository.isBare()) {
// read bugtraq config directly from the repository
String content = null;
RevWalk rw = new RevWalk(repository);
TreeWalk tw = new TreeWalk(repository);
tw.setFilter(PathFilterGroup.createFromStrings(configFileName));
try {
final Ref ref = repository.getRef(Constants.HEAD);
if (ref == null) {
return null;
}
ObjectId headId = ref.getTarget().getObjectId();
if (headId == null || ObjectId.zeroId().equals(headId)) {
return null;
}
RevCommit commit = rw.parseCommit(headId);
RevTree tree = commit.getTree();
tw.reset(tree);
while (tw.next()) {
ObjectId entid = tw.getObjectId(0);
FileMode entmode = tw.getFileMode(0);
if (FileMode.REGULAR_FILE == entmode) {
ObjectLoader ldr = repository.open(entid, Constants.OBJ_BLOB);
content = new String(ldr.getCachedBytes(), commit.getEncoding());
break;
}
}
}
finally {
rw.dispose();
tw.close();
}
if (content == null) {
// config not found
baseConfig = null;
}
else {
// parse the config
Config config = new Config();
config.fromText(content);
baseConfig = config;
}
}
else {
// read bugtraq config from work tree
final File baseFile = new File(repository.getWorkTree(), configFileName);
if (baseFile.isFile()) {
FileBasedConfig fileConfig = new FileBasedConfig(baseFile, repository.getFS());
fileConfig.load();
baseConfig = fileConfig;
}
else {
baseConfig = null;
}
}
return baseConfig;
}
@Nullable
private static String getString(@Nullable String subsection, @NotNull String key, @NotNull Config config, @Nullable Config baseConfig) {
final String value = config.getString(BUGTRAQ, subsection, key);
if (value != null) {
return trimMaybeNull(value);
}
if (baseConfig != null) {
return trimMaybeNull(baseConfig.getString(BUGTRAQ, subsection, key));
}
return value;
}
@Nullable
private static String trimMaybeNull(@Nullable String string) {
if (string == null) {
return null;
}
string = string.trim();
if (string.length() == 0) {
return null;
}
return string;
}
}