| /* |
| * Copyright 2000-2013 JetBrains s.r.o. |
| * |
| * 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 org.jetbrains.idea.svn; |
| |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.components.*; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.vcs.changes.VcsAnnotationRefresher; |
| import com.intellij.util.containers.ContainerUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.idea.svn.api.Depth; |
| import org.jetbrains.idea.svn.auth.SvnAuthenticationManager; |
| import org.jetbrains.idea.svn.auth.SvnAuthenticationProvider; |
| import org.jetbrains.idea.svn.auth.SvnInteractiveAuthenticationProvider; |
| import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationManager; |
| import org.jetbrains.idea.svn.config.SvnServerFileKeys; |
| import org.jetbrains.idea.svn.diff.DiffOptions; |
| import org.jetbrains.idea.svn.update.MergeRootInfo; |
| import org.jetbrains.idea.svn.update.UpdateRootInfo; |
| import org.tmatesoft.svn.core.SVNErrorMessage; |
| import org.tmatesoft.svn.core.SVNURL; |
| import org.tmatesoft.svn.core.auth.ISVNAuthenticationProvider; |
| import org.tmatesoft.svn.core.auth.SVNAuthentication; |
| import org.tmatesoft.svn.core.internal.wc.ISVNAuthenticationStorage; |
| import org.tmatesoft.svn.core.internal.wc.SVNConfigFile; |
| import org.tmatesoft.svn.core.internal.wc.SVNFileUtil; |
| import org.tmatesoft.svn.core.wc.ISVNOptions; |
| import org.tmatesoft.svn.core.wc.SVNWCUtil; |
| |
| import java.io.File; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.TreeSet; |
| |
| @State( |
| name = "SvnConfiguration", |
| storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)} |
| ) |
| public class SvnConfiguration implements PersistentStateComponent<SvnConfigurationState> { |
| |
| public final static int ourMaxAnnotateRevisionsDefault = 500; |
| |
| private final static long UPGRADE_TO_15_VERSION_ASKED = 123; |
| private final static long CHANGELIST_SUPPORT = 124; |
| private final static long UPGRADE_TO_16_VERSION_ASKED = 125; |
| |
| private final Project myProject; |
| private SvnConfigurationState myState = new SvnConfigurationState(); |
| |
| private ISVNOptions myOptions; |
| private SvnAuthenticationManager myAuthManager; |
| private SvnAuthenticationManager myPassiveAuthManager; |
| private SvnAuthenticationManager myInteractiveManager; |
| |
| public static final AuthStorage RUNTIME_AUTH_CACHE = new AuthStorage(); |
| |
| private final Map<File, MergeRootInfo> myMergeRootInfos = new HashMap<File, MergeRootInfo>(); |
| private final Map<File, UpdateRootInfo> myUpdateRootInfos = new HashMap<File, UpdateRootInfo>(); |
| private SvnInteractiveAuthenticationProvider myInteractiveProvider; |
| private IdeaSVNConfigFile myConfigFile; |
| |
| public boolean isCommandLine() { |
| return UseAcceleration.commandLine.equals(getUseAcceleration()); |
| } |
| |
| @Override |
| public SvnConfigurationState getState() { |
| return myState; |
| } |
| |
| @Override |
| public void loadState(SvnConfigurationState state) { |
| myState = state; |
| } |
| |
| public long getHttpTimeout() { |
| initServers(); |
| final String timeout = myConfigFile.getDefaultGroup().getTimeout(); |
| try { |
| return Long.parseLong(timeout) * 1000; |
| } catch (NumberFormatException e) { |
| return 0; |
| } |
| } |
| |
| @NotNull |
| public DiffOptions getMergeOptions() { |
| return new DiffOptions(isIgnoreSpacesInMerge(), isIgnoreSpacesInMerge(), isIgnoreSpacesInMerge()); |
| } |
| |
| private void initServers() { |
| if (myConfigFile == null) { |
| myConfigFile = new IdeaSVNConfigFile(new File(getConfigurationDirectory(), IdeaSVNConfigFile.SERVERS_FILE_NAME)); |
| } |
| myConfigFile.updateGroups(); |
| } |
| |
| // uses configuration directory property - it should be saved first |
| public void setHttpTimeout(final long value) { |
| initServers(); |
| long cut = value / 1000; |
| myConfigFile.setValue("global", SvnServerFileKeys.TIMEOUT, String.valueOf(cut)); |
| myConfigFile.save(); |
| } |
| |
| public static SvnConfiguration getInstance(final Project project) { |
| return ServiceManager.getService(project, SvnConfiguration.class); |
| } |
| |
| public SvnConfiguration(final Project project) { |
| myProject = project; |
| } |
| |
| public void setIgnoreSpacesInAnnotate(final boolean value) { |
| final boolean changed = myState.IGNORE_SPACES_IN_ANNOTATE != value; |
| myState.IGNORE_SPACES_IN_ANNOTATE = value; |
| if (changed) { |
| getProject().getMessageBus().syncPublisher(VcsAnnotationRefresher.LOCAL_CHANGES_CHANGED).configurationChanged(SvnVcs.getKey()); |
| } |
| } |
| |
| public long getSshConnectionTimeout() { |
| return myState.sshConnectionTimeout; |
| } |
| |
| public void setSshConnectionTimeout(long sshConnectionTimeout) { |
| myState.sshConnectionTimeout = sshConnectionTimeout; |
| } |
| |
| public long getSshReadTimeout() { |
| return myState.sshReadTimeout; |
| } |
| |
| public void setSshReadTimeout(long sshReadTimeout) { |
| myState.sshReadTimeout = sshReadTimeout; |
| } |
| |
| public Project getProject() { |
| return myProject; |
| } |
| |
| public Boolean isKeepNewFilesAsIsForTreeConflictMerge() { |
| return myState.keepNewFilesAsIsForTreeConflictMerge; |
| } |
| |
| public void setKeepNewFilesAsIsForTreeConflictMerge(Boolean keepNewFilesAsIsForTreeConflictMerge) { |
| myState.keepNewFilesAsIsForTreeConflictMerge = keepNewFilesAsIsForTreeConflictMerge; |
| } |
| |
| public SSLProtocols getSslProtocols() { |
| return myState.sslProtocols; |
| } |
| |
| public void setSslProtocols(SSLProtocols sslProtocols) { |
| myState.sslProtocols = sslProtocols; |
| } |
| |
| public Depth getUpdateDepth() { |
| return myState.UPDATE_DEPTH; |
| } |
| |
| public void setUpdateDepth(Depth updateDepth) { |
| myState.UPDATE_DEPTH = updateDepth; |
| } |
| |
| public UseAcceleration getUseAcceleration() { |
| return myState.accelerationType; |
| } |
| |
| public void setUseAcceleration(UseAcceleration useAcceleration) { |
| myState.accelerationType = useAcceleration; |
| } |
| |
| public boolean isIgnoreExternals() { |
| return myState.IGNORE_EXTERNALS; |
| } |
| |
| public void setIgnoreExternals(boolean ignoreExternals) { |
| myState.IGNORE_EXTERNALS = ignoreExternals; |
| } |
| |
| public boolean isMergeDryRun() { |
| return myState.MERGE_DRY_RUN; |
| } |
| |
| public void setMergeDryRun(boolean mergeDryRun) { |
| myState.MERGE_DRY_RUN = mergeDryRun; |
| } |
| |
| public boolean isMergeDiffUseAncestry() { |
| return myState.MERGE_DIFF_USE_ANCESTRY; |
| } |
| |
| public void setMergeDiffUseAncestry(boolean mergeDiffUseAncestry) { |
| myState.MERGE_DIFF_USE_ANCESTRY = mergeDiffUseAncestry; |
| } |
| |
| public boolean isUpdateLockOnDemand() { |
| return myState.UPDATE_LOCK_ON_DEMAND; |
| } |
| |
| public void setUpdateLockOnDemand(boolean updateLockOnDemand) { |
| myState.UPDATE_LOCK_ON_DEMAND = updateLockOnDemand; |
| } |
| |
| public boolean isIgnoreSpacesInMerge() { |
| return myState.IGNORE_SPACES_IN_MERGE; |
| } |
| |
| public void setIgnoreSpacesInMerge(boolean ignoreSpacesInMerge) { |
| myState.IGNORE_SPACES_IN_MERGE = ignoreSpacesInMerge; |
| } |
| |
| public boolean isCheckNestedForQuickMerge() { |
| return myState.CHECK_NESTED_FOR_QUICK_MERGE; |
| } |
| |
| public void setCheckNestedForQuickMerge(boolean checkNestedForQuickMerge) { |
| myState.CHECK_NESTED_FOR_QUICK_MERGE = checkNestedForQuickMerge; |
| } |
| |
| public boolean isIgnoreSpacesInAnnotate() { |
| return myState.IGNORE_SPACES_IN_ANNOTATE; |
| } |
| |
| public boolean isShowMergeSourcesInAnnotate() { |
| return myState.SHOW_MERGE_SOURCES_IN_ANNOTATE; |
| } |
| |
| public void setShowMergeSourcesInAnnotate(boolean showMergeSourcesInAnnotate) { |
| myState.SHOW_MERGE_SOURCES_IN_ANNOTATE = showMergeSourcesInAnnotate; |
| } |
| |
| public boolean isForceUpdate() { |
| return myState.FORCE_UPDATE; |
| } |
| |
| public void setForceUpdate(boolean forceUpdate) { |
| myState.FORCE_UPDATE = forceUpdate; |
| } |
| |
| private static Long fixSupportedVersion(final Long version) { |
| return version == null || version.longValue() < CHANGELIST_SUPPORT |
| ? UPGRADE_TO_15_VERSION_ASKED |
| : version; |
| } |
| |
| public boolean changeListsSynchronized() { |
| ensureSupportedVersion(); |
| return myState.supportedVersion != null && myState.supportedVersion >= CHANGELIST_SUPPORT; |
| } |
| |
| public void upgrade() { |
| myState.supportedVersion = UPGRADE_TO_16_VERSION_ASKED; |
| } |
| |
| private void ensureSupportedVersion() { |
| if (myState.supportedVersion == null) { |
| myState.supportedVersion = fixSupportedVersion(SvnBranchConfigurationManager.getInstance(myProject).getSupportValue()); |
| } |
| } |
| |
| public String getConfigurationDirectory() { |
| if (myState.directory.path == null || isUseDefaultConfiguation()) { |
| myState.directory.path = IdeaSubversionConfigurationDirectory.getPath(); |
| } |
| return myState.directory.path; |
| } |
| |
| public boolean isUseDefaultConfiguation() { |
| return myState.directory.useDefault; |
| } |
| |
| public void setConfigurationDirParameters(final boolean newUseDefault, final String newConfigurationDirectory) { |
| final String defaultPath = IdeaSubversionConfigurationDirectory.getPath(); |
| final String oldEffectivePath = isUseDefaultConfiguation() ? defaultPath : getConfigurationDirectory(); |
| final String newEffectivePath = newUseDefault ? defaultPath : newConfigurationDirectory; |
| |
| boolean directoryChanged = !Comparing.equal(getConfigurationDirectory(), newConfigurationDirectory); |
| if (directoryChanged) { |
| setConfigurationDirectory(newConfigurationDirectory); |
| } |
| boolean usageChanged = isUseDefaultConfiguation() != newUseDefault; |
| if (usageChanged) { |
| setUseDefaultConfiguation(newUseDefault); |
| } |
| |
| if (directoryChanged || usageChanged) { |
| if (! Comparing.equal(oldEffectivePath, newEffectivePath)) { |
| clear(); |
| } |
| } |
| } |
| |
| private void setConfigurationDirectory(String path) { |
| myState.directory.path = path; |
| File dir = path == null ? new File(IdeaSubversionConfigurationDirectory.getPath()) : new File(path); |
| SVNConfigFile.createDefaultConfiguration(dir); |
| } |
| |
| public void clear() { |
| myOptions = null; |
| myAuthManager = null; |
| myPassiveAuthManager = null; |
| myInteractiveManager = null; |
| myInteractiveProvider = null; |
| RUNTIME_AUTH_CACHE.clear(); |
| } |
| |
| private void setUseDefaultConfiguation(boolean useDefault) { |
| myState.directory.useDefault = useDefault; |
| } |
| |
| public ISVNOptions getOptions(Project project) { |
| if (myOptions == null) { |
| File path = new File(getConfigurationDirectory()); |
| myOptions = SVNWCUtil.createDefaultOptions(path.getAbsoluteFile(), true); |
| } |
| return myOptions; |
| } |
| |
| public SvnAuthenticationManager getAuthenticationManager(final SvnVcs svnVcs) { |
| if (myAuthManager == null) { |
| // reloaded when configuration directory changes |
| myAuthManager = new SvnAuthenticationManager(svnVcs.getProject(), new File(getConfigurationDirectory())); |
| Disposer.register(svnVcs.getProject(), new Disposable() { |
| @Override |
| public void dispose() { |
| myAuthManager = null; |
| } |
| }); |
| getInteractiveManager(svnVcs); |
| // to init |
| myAuthManager.setAuthenticationProvider(new SvnAuthenticationProvider(svnVcs, myInteractiveProvider, myAuthManager)); |
| myAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE); |
| } |
| return myAuthManager; |
| } |
| |
| public SvnAuthenticationManager getPassiveAuthenticationManager(Project project) { |
| if (myPassiveAuthManager == null) { |
| myPassiveAuthManager = new SvnAuthenticationManager(project, new File(getConfigurationDirectory())); |
| myPassiveAuthManager.setAuthenticationProvider(new ISVNAuthenticationProvider() { |
| @Override |
| public SVNAuthentication requestClientAuthentication(String kind, |
| SVNURL url, |
| String realm, |
| SVNErrorMessage errorMessage, |
| SVNAuthentication previousAuth, |
| boolean authMayBeStored) { |
| return null; |
| } |
| |
| @Override |
| public int acceptServerAuthentication(SVNURL url, String realm, Object certificate, boolean resultMayBeStored) { |
| return REJECTED; |
| } |
| }); |
| myPassiveAuthManager.setRuntimeStorage(RUNTIME_AUTH_CACHE); |
| } |
| return myPassiveAuthManager; |
| } |
| |
| public SvnAuthenticationManager getInteractiveManager(final SvnVcs svnVcs) { |
| if (myInteractiveManager == null) { |
| myInteractiveManager = new SvnAuthenticationManager(svnVcs.getProject(), new File(getConfigurationDirectory())); |
| myInteractiveManager.setRuntimeStorage(RUNTIME_AUTH_CACHE); |
| myInteractiveProvider = new SvnInteractiveAuthenticationProvider(svnVcs, myInteractiveManager); |
| myInteractiveManager.setAuthenticationProvider(myInteractiveProvider); |
| } |
| return myInteractiveManager; |
| } |
| |
| public void getServerFilesManagers(final Ref<SvnServerFileManager> systemManager, final Ref<SvnServerFileManager> userManager) { |
| // created only if does not exist |
| final File dir = new File(getConfigurationDirectory()); |
| if (! dir.exists()) { |
| SVNConfigFile.createDefaultConfiguration(dir); |
| } |
| |
| systemManager.set(new SvnServerFileManagerImpl(new IdeaSVNConfigFile(new File(SVNFileUtil.getSystemConfigurationDirectory(), IdeaSVNConfigFile.SERVERS_FILE_NAME)))); |
| initServers(); |
| userManager.set(new SvnServerFileManagerImpl(myConfigFile)); |
| } |
| |
| public boolean isAutoUpdateAfterCommit() { |
| return myState.autoUpdateAfterCommit; |
| } |
| |
| public void setAutoUpdateAfterCommit(boolean autoUpdateAfterCommit) { |
| myState.autoUpdateAfterCommit = autoUpdateAfterCommit; |
| } |
| |
| public boolean isKeepLocks() { |
| return myState.keepLocks; |
| } |
| |
| public void setKeepLocks(boolean keepLocks) { |
| myState.keepLocks = keepLocks; |
| } |
| |
| public boolean isIsUseDefaultProxy() { |
| return myState.useDefaultProxy; |
| } |
| |
| public void setIsUseDefaultProxy(final boolean isUseDefaultProxy) { |
| myState.useDefaultProxy = isUseDefaultProxy; |
| } |
| |
| // TODO: Rewrite AutoStorage to use MemoryPasswordSafe at least |
| public static class AuthStorage implements ISVNAuthenticationStorage { |
| |
| private final TreeSet<String> myKeys = ContainerUtil.newTreeSet(); |
| private final Map<String, Object> myStorage = ContainerUtil.newHashMap(); |
| |
| @NotNull |
| public static String getKey(@NotNull String type, @NotNull String realm) { |
| return type + "$" + realm; |
| } |
| |
| public synchronized void clear() { |
| myStorage.clear(); |
| myKeys.clear(); |
| } |
| |
| public synchronized void putData(String kind, String realm, Object data) { |
| String key = getKey(kind, realm); |
| |
| if (data == null) { |
| myStorage.remove(key); |
| myKeys.remove(key); |
| } else { |
| myStorage.put(key, data); |
| myKeys.add(key); |
| } |
| } |
| |
| public synchronized Object getData(String kind, String realm) { |
| return myStorage.get(getKey(kind, realm)); |
| } |
| |
| public synchronized Object getDataWithLowerCheck(String kind, String realm) { |
| String key = getKey(kind, realm); |
| Object result = myStorage.get(key); |
| |
| if (result == null) { |
| String lowerKey = myKeys.lower(key); |
| |
| if (lowerKey != null && key.startsWith(lowerKey)) { |
| result = myStorage.get(lowerKey); |
| } |
| } |
| |
| return result; |
| } |
| } |
| |
| public MergeRootInfo getMergeRootInfo(final File file, final SvnVcs svnVcs) { |
| if (!myMergeRootInfos.containsKey(file)) { |
| myMergeRootInfos.put(file, new MergeRootInfo(file, svnVcs)); |
| } |
| return myMergeRootInfos.get(file); |
| } |
| |
| public UpdateRootInfo getUpdateRootInfo(File file, final SvnVcs svnVcs) { |
| if (!myUpdateRootInfos.containsKey(file)) { |
| myUpdateRootInfos.put(file, new UpdateRootInfo(file, svnVcs)); |
| } |
| return myUpdateRootInfos.get(file); |
| } |
| |
| // TODO: Check why SvnUpdateEnvironment.validationOptions is fully commented and then remove this method if necessary |
| public Map<File, UpdateRootInfo> getUpdateInfosMap() { |
| return Collections.unmodifiableMap(myUpdateRootInfos); |
| } |
| |
| public void acknowledge(final String kind, final String realm, final Object object) { |
| RUNTIME_AUTH_CACHE.putData(kind, realm, object); |
| } |
| |
| public void clearCredentials(final String kind, final String realm) { |
| RUNTIME_AUTH_CACHE.putData(kind, realm, null); |
| } |
| |
| public void clearRuntimeStorage() { |
| RUNTIME_AUTH_CACHE.clear(); |
| } |
| |
| |
| public int getMaxAnnotateRevisions() { |
| return myState.maxAnnotateRevisions; |
| } |
| |
| public void setMaxAnnotateRevisions(int maxAnnotateRevisions) { |
| myState.maxAnnotateRevisions = maxAnnotateRevisions; |
| } |
| |
| public enum UseAcceleration { |
| commandLine, |
| nothing |
| } |
| |
| public boolean isCleanupRun() { |
| return myState.cleanupOnStartRun; |
| } |
| |
| public void setCleanupRun(boolean cleanupRun) { |
| myState.cleanupOnStartRun = cleanupRun; |
| } |
| |
| public enum SSLProtocols { |
| sslv3, tlsv1, all |
| } |
| } |