2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
41
package org.netbeans.modules.subversion.config;
44
import java.io.FileNotFoundException;
45
import java.io.FileReader;
46
import java.io.IOException;
47
import java.util.ArrayList;
48
import java.util.Iterator;
49
import java.util.List;
50
import java.util.StringTokenizer;
51
import java.util.prefs.Preferences;
52
import java.util.regex.Matcher;
53
import java.util.regex.Pattern;
55
import org.netbeans.modules.subversion.util.FileUtils;
56
import org.netbeans.modules.subversion.util.ProxySettings;
57
import org.netbeans.modules.subversion.util.SvnUtils;
58
import org.openide.ErrorManager;
59
import org.openide.filesystems.FileUtil;
60
import org.openide.util.Utilities;
61
import org.tigris.subversion.svnclientadapter.SVNUrl;
65
* Handles the Subversions <b>servers</b> and <b>config</b> configuration files.</br>
66
* Everytime the singleton instance is created are the values from the commandline clients
67
* configuration directory merged into the Subversion modules configuration files.
68
* (registry on windows are ignored).
69
* Already present proxy setting values wan't be changed,
70
* the remaining values are always taken from the commandline clients configuration files.
71
* The only exception is the 'store-auth-creds' key, which is always set to 'no'.
73
* @author Tomas Stupka
75
public class SvnConfigFiles {
77
/** the only SvnConfigFiles instance */
78
private static SvnConfigFiles instance;
80
/** the Ini instance holding the configuration values stored in the <b>servers</b>
81
* file used by the Subversion module */
82
private Ini svnServers = null;
84
/** the Ini instance holding the configuration values stored in the <b>config</b>
85
* file used by the Subversion module */
86
private Ini config = null;
88
private ProxySettings proxySettings;
90
private static final String UNIX_CONFIG_DIR = ".subversion/"; // NOI18N
91
private static final String GROUPS_SECTION = "groups"; // NOI18N
92
private static final String GLOBAL_SECTION = "global"; // NOI18N
93
private static final String WINDOWS_USER_APPDATA = getAPPDATA();
94
private static final String WINDOWS_CONFIG_DIR = WINDOWS_USER_APPDATA + "\\Subversion"; // NOI18N
95
private static final String WINDOWS_GLOBAL_CONFIG_DIR = getGlobalAPPDATA() + "\\Subversion"; // NOI18N
96
private static final List<String> DEFAULT_GLOBAL_IGNORES =
97
parseGlobalIgnores("*.o *.lo *.la #*# .*.rej *.rej .*~ *~ .#* .DS_Store"); // NOI18N
99
private interface IniFilePatcher {
100
void patch(Ini file);
104
* The value for the 'store-auth-creds' key in the config cofiguration file is alway set to 'no'
105
* so the commandline client wan't create a file holding the authentication credentials when
106
* a svn command is called. The reason for this is that the Subverion module holds the credentials
107
* in files with the same format as the commandline client but with a different name.
109
private class ConfigIniFilePatcher implements IniFilePatcher {
110
public void patch(Ini file) {
111
// patch store-auth-creds to "no"
112
Ini.Section auth = (Ini.Section) file.get("auth"); // NOI18N
114
auth = file.add("auth"); // NOI18N
116
auth.put("store-auth-creds", "no"); // NOI18N
121
* Creates a new instance
123
private SvnConfigFiles() {
125
config = copyConfigFileToIDEConfigDir("config", new ConfigIniFilePatcher()); // NOI18N
126
// get the system servers file
127
svnServers = loadSystemIniFile("servers");
131
* Returns a singleton instance.
133
* @return the SvnConfigFiles instance
135
public static SvnConfigFiles getInstance() {
137
//T9Y - singleton is not required - always create new instance of this class
138
String t9yUserConfigPath = System.getProperty("netbeans.t9y.svn.user.config.path");
139
if (t9yUserConfigPath != null && t9yUserConfigPath.length() > 0) {
140
//make sure that new instance will be created
145
instance = new SvnConfigFiles();
151
* Stores the proxy host, port, username and password from the given
152
* {@link org.netbeans.modules.subversion.config.ProxyDescriptor} in the
153
* <b>servers</b> file used by the Subversion module.
155
* @param host the host
157
public void setProxy(SVNUrl url) {
159
assert url != null : "can\'t do anything for a null host"; // NOI18N
161
if(!(url.getProtocol().startsWith("http") ||
162
url.getProtocol().startsWith("https")) )
164
// a proxy will be needed only for remote http and https repositories
168
String host = SvnUtils.ripUserFromHost(url.getHost());
169
ProxySettings ps = new ProxySettings();
170
if(proxySettings != null && ps.equals(proxySettings)) {
176
Ini nbServers = new Ini();
177
Ini.Section nbGlobalSection = nbServers.add(GLOBAL_SECTION);
178
Ini.Section svnGlobalSection = svnServers.get(GLOBAL_SECTION);
179
if(proxySettings.isDirect()) {
180
mergeNonProxyKeys(host, svnGlobalSection, nbGlobalSection);
182
String proxyHost = "";
184
if(url.getProtocol().startsWith("https")) {
185
proxyHost = proxySettings.getHttpsHost();
186
proxyPort = proxySettings.getHttpsPort();
188
if(proxyHost.equals("")) {
189
proxyHost = proxySettings.getHttpHost();
190
proxyPort = proxySettings.getHttpPort();
192
String exceptions = proxySettings.getNotProxyHosts();
194
if(proxyHost != null && !proxyHost.equals("")) {
195
nbGlobalSection.put("http-proxy-host", proxyHost); // NOI18N
196
nbGlobalSection.put("http-proxy-port", Integer.toString(proxyPort)); // NOI18N
197
if(!exceptions.equals("")) {
198
nbGlobalSection.put("http-proxy-exceptions", exceptions); // NOI18N
201
// and the authentication
202
Preferences prefs = org.openide.util.NbPreferences.root ().node ("org/netbeans/core"); // NOI18N
203
boolean useAuth = prefs.getBoolean ("useProxyAuthentication", false); // NOI18N
205
String username = prefs.get ("proxyAuthenticationUsername", ""); // NOI18N
206
String password = prefs.get ("proxyAuthenticationPassword", ""); // NOI18N
208
nbGlobalSection.put("http-proxy-username", username); // NOI18N
209
nbGlobalSection.put("http-proxy-password", password); // NOI18N
212
// we have a proxy for the host, so check
213
// if in there are also some no proxy settings
214
// we should get from the original svn servers file
215
mergeNonProxyKeys(host, svnGlobalSection, nbGlobalSection);
217
// no proxy host means no proxy at all
218
if(svnGlobalSection != null) {
219
// if there is a global section than get the no proxy settings
220
mergeNonProxyKeys(svnGlobalSection, nbGlobalSection);
224
storeIni(nbServers, "servers"); // NOI18N
227
private void mergeNonProxyKeys(String host, Ini.Section svnGlobalSection, Ini.Section nbGlobalSection) {
228
if(svnGlobalSection != null) {
229
// if there is a global section, than get the no proxy settings
230
mergeNonProxyKeys(svnGlobalSection, nbGlobalSection);
232
Ini.Section svnHostGroup = getServerGroup(host);
233
if(svnHostGroup != null) {
234
// if there is a section for the given host, than get the no proxy settings
235
mergeNonProxyKeys(svnHostGroup, nbGlobalSection);
239
private void mergeNonProxyKeys(Ini.Section source, Ini.Section target) {
240
for (String key : source.keySet()) {
241
if(!isProxyConfigurationKey(key)) {
242
target.put(key, source.get(key));
247
public void setExternalCommand(String tunnelName, String command) {
248
Ini.Section tunnels = getSection(config, "tunnels", true);
249
tunnels.put(tunnelName, command);
250
storeIni(config, "config"); // NOI18N
253
public String getExternalCommand(String tunnelName) {
254
Ini.Section tunnels = getSection(config, "tunnels", true);
255
String cmd = tunnels.get(tunnelName);
256
return cmd != null ? cmd : "";
259
private Ini.Section getSection(Ini ini, String key, boolean create) {
260
Ini.Section section = ini.get(key);
261
if(section == null) {
267
private void storeIni(Ini ini, String iniFile) {
269
File file = FileUtil.normalizeFile(new File(getNBConfigPath() + "/" + iniFile)); // NOI18N
270
file.getParentFile().mkdirs();
271
ini.store(FileUtils.createOutputStream(file));
272
} catch (IOException ex) {
273
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
278
* Returns the miscellany/global-ignores setting from the config file.
280
* @return a list with the inore patterns
283
public List<String> getGlobalIgnores() {
284
Ini.Section miscellany = config.get("miscellany"); // NOI18N
285
if (miscellany != null) {
286
String ignores = miscellany.get("global-ignores"); // NOI18N
287
if (ignores != null && ignores.trim().length() > 0) {
288
return parseGlobalIgnores(ignores);
291
return DEFAULT_GLOBAL_IGNORES;
294
public String getClientCertFile(String host) {
295
return getMergeValue("ssl-client-cert-file", host); // NOI18N
298
public String getClientCertPassword(String host) {
299
return getMergeValue("ssl-client-cert-password", host); // NOI18N
302
private String getMergeValue(String key, String host) {
303
Ini.Section group = getServerGroup(host);
305
return group.get(key);
307
group = svnServers.get(GLOBAL_SECTION);
309
return group.get(key);
314
private static List<String> parseGlobalIgnores(String ignores) {
315
StringTokenizer st = new StringTokenizer(ignores, " "); // NOI18N
316
List<String> ret = new ArrayList<String>(10);
317
while (st.hasMoreTokens()) {
318
String entry = st.nextToken();
319
if (!entry.equals("")) // NOI18N
326
* Returns the path for the Sunbversion configuration dicectory used
327
* by the systems Subversion commandline client.
332
public static String getUserConfigPath() {
334
//T9Y - user svn config files should be changable
335
String t9yUserConfigPath = System.getProperty("netbeans.t9y.svn.user.config.path");
336
if (t9yUserConfigPath != null && t9yUserConfigPath.length() > 0) {
337
return t9yUserConfigPath;
340
if(Utilities.isUnix()) {
341
String path = System.getProperty("user.home") ; // NOI18N
342
return path + "/" + UNIX_CONFIG_DIR; // NOI18N
343
} else if (Utilities.isWindows()){
344
return WINDOWS_CONFIG_DIR;
350
* Returns the path for the Sunbversion configuration directory used
351
* by the Netbeans Subversion module.
356
public static String getNBConfigPath() {
358
//T9Y - nb svn confing should be changable
359
String t9yNbConfigPath = System.getProperty("netbeans.t9y.svn.nb.config.path");
360
if (t9yNbConfigPath != null && t9yNbConfigPath.length() > 0) {
361
return t9yNbConfigPath;
364
String nbHome = System.getProperty("netbeans.user"); // NOI18N
365
return nbHome + "/config/svn/config/"; // NOI18N
369
* Returns the section from the <b>servers</b> config file used by the Subversion module which
370
* is holding the proxy settings for the given host
372
* @param host the host
373
* @return the section holding the proxy settings for the given host
375
private Ini.Section getServerGroup(String host) {
376
if(host == null || host.equals("")) { // NOI18N
379
Ini.Section groups = svnServers.get(GROUPS_SECTION);
381
for (Iterator<String> it = groups.keySet().iterator(); it.hasNext();) {
382
String key = it.next();
383
String value = groups.get(key);
385
// XXX the same pattern everywhere when calling match()
386
value = value.trim();
387
if(value != null && match(value, host)) {
388
return svnServers.get(key);
397
* Evaluates if the given hostaname or IP address is in the given value String.
399
* @param value the value String. A list of host names or IP addresses delimited by ",".
400
* (e.g 192.168.0.1,*.168.0.1, some.domain.com, *.anything.com, ...)
401
* @param host the hostname or IP address
402
* @return true if the host name or IP address was found in the values String, otherwise false.
404
private boolean match(String value, String host) {
405
String[] values = value.split(","); // NOI18N
406
for (int i = 0; i < values.length; i++) {
407
value = values[i].trim();
409
if(value.equals("*") || value.equals(host) ) { // NOI18N
413
int idx = value.indexOf("*"); // NOI18N
414
if(idx > -1 && matchSegments(value, host) ) {
422
* Evaluates if the given hostaname or IP address matches with the given value String representing
423
* a hostaname or IP adress with one or more "*" wildcards in it.
425
* @param value the value String. A host name or IP addresse with a "*" wildcard. (e.g *.168.0.1 or *.anything.com)
426
* @param host the hostname or IP address
427
* @return true if the host name or IP address matches with the values String, otherwise false.
429
private boolean matchSegments(String value, String host) {
430
value = value.replace(".", "\\.");
431
value = value.replace("*", ".*");
432
Matcher m = Pattern.compile(value).matcher(host);
437
* Copies the given configuration file from the Subversion commandline client
438
* configuration directory into the configuration directory used by the Netbeans Subversion module. </br>
440
private Ini copyConfigFileToIDEConfigDir(String fileName, IniFilePatcher patcher) {
441
Ini systemIniFile = loadSystemIniFile(fileName);
443
patcher.patch(systemIniFile);
445
File file = FileUtil.normalizeFile(new File(getNBConfigPath() + "/" + fileName)); // NOI18N
447
file.getParentFile().mkdirs();
448
systemIniFile.store(FileUtils.createOutputStream(file));
449
} catch (IOException ex) {
450
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); // should not happen
452
return systemIniFile;
456
* Loads the ini configuration file from the directory used by
457
* the Subversion commandline client. The settings are loaded and merged together in
458
* in the folowing order:
460
* <li> The per-user INI files
461
* <li> The system-wide INI files
464
* @param fileName the file name
465
* @return an Ini instance holding the cofiguration file.
467
private Ini loadSystemIniFile(String fileName) {
468
// config files from userdir
469
String filePath = getUserConfigPath() + "/" + fileName; // NOI18N
470
File file = FileUtil.normalizeFile(new File(filePath));
473
system = new Ini(new FileReader(file));
474
} catch (FileNotFoundException ex) {
476
} catch (IOException ex) {
477
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
482
ErrorManager.getDefault().log(ErrorManager.WARNING, "Could not load the file " + filePath + ". Falling back on svn defaults."); // NOI18N
487
global = new Ini(new FileReader(getGlobalConfigPath() + "/" + fileName)); // NOI18N
488
} catch (FileNotFoundException ex) {
489
// just doesn't exist - ignore
490
} catch (IOException ex) {
491
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
495
merge(global, system);
501
* Merges only sections/keys/values into target which are not already present in source
503
* @param source the source ini file
504
* @param target the target ini file in which the values from the source file are going to be merged
506
private void merge(Ini source, Ini target) {
507
for (Iterator<String> itSections = source.keySet().iterator(); itSections.hasNext();) {
508
String sectionName = itSections.next();
509
Ini.Section sourceSection = source.get( sectionName );
510
Ini.Section targetSection = target.get( sectionName );
512
if(targetSection == null) {
513
targetSection = target.add(sectionName);
516
for (Iterator<String> itVariables = sourceSection.keySet().iterator(); itVariables.hasNext();) {
517
String key = itVariables.next();
519
if(!targetSection.containsKey(key)) {
520
targetSection.put(key, sourceSection.get(key));
527
* Evaluates if the value stored under the key is a proxy setting value.
530
* @return true if the value stored under the key is a proxy setting value. Otherwise false
532
private boolean isProxyConfigurationKey(String key) {
533
return key.equals("http-proxy-host") || // NOI18N
534
key.equals("http-proxy-port") || // NOI18N
535
key.equals("http-proxy-username") || // NOI18N
536
key.equals("http-proxy-password") || // NOI18N
537
key.equals("http-proxy-exceptions"); // NOI18N
541
* Return the path for the systemwide command lines configuration directory
543
private static String getGlobalConfigPath () {
544
if(Utilities.isUnix()) {
545
return "/etc/subversion"; // NOI18N
546
} else if (Utilities.isWindows()){
547
return WINDOWS_GLOBAL_CONFIG_DIR;
553
* Returns the value for the %APPDATA% env variable on windows
556
private static String getAPPDATA() {
558
if(Utilities.isWindows()) {
559
appdata = System.getenv("APPDATA");// NOI18N
561
return appdata!= null? appdata: "";
565
* Returns the value for the %ALLUSERSPROFILE% + the last foder segment from %APPDATA% env variables on windows
568
private static String getGlobalAPPDATA() {
569
if(Utilities.isWindows()) {
570
String globalProfile = System.getenv("ALLUSERSPROFILE"); // NOI18N
571
if(globalProfile == null || globalProfile.trim().equals("")) { // NOI18N
574
String appdataPath = WINDOWS_USER_APPDATA;
575
if(appdataPath == null || appdataPath.equals("")) { // NOI18N
578
String appdata = ""; // NOI18N
579
int idx = appdataPath.lastIndexOf("\\"); // NOI18N
581
appdata = appdataPath.substring(idx + 1);
582
if(appdata.trim().equals("")) { // NOI18N
583
int previdx = appdataPath.lastIndexOf("\\", idx); // NOI18N
585
appdata = appdataPath.substring(previdx + 1, idx);
591
return globalProfile + "/" + appdata; // NOI18N