File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\engine\ConnectionInfo.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.engine;
8
9 import java.io.IOException;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.HashMap;
13 import java.util.HashSet;
14 import java.util.Properties;
15 import org.h2.command.dml.SetTypes;
16 import org.h2.constant.DbSettings;
17 import org.h2.constant.ErrorCode;
18 import org.h2.constant.SysProperties;
19 import org.h2.message.DbException;
20 import org.h2.security.SHA256;
21 import org.h2.store.fs.FilePathRec;
22 import org.h2.store.fs.FileUtils;
23 import org.h2.util.New;
24 import org.h2.util.SortedProperties;
25 import org.h2.util.StringUtils;
26 import org.h2.util.Utils;
27
28 /**
29 * Encapsulates the connection settings, including user name and password.
30 */
31 public class ConnectionInfo implements Cloneable {
32 private static final HashSet<String> KNOWN_SETTINGS = New.hashSet();
33
34 private Properties prop = new Properties();
35 private String originalURL;
36 private String url;
37 private String user;
38 private byte[] filePasswordHash;
39 private byte[] userPasswordHash;
40
41 /**
42 * The database name
43 */
44 private String name;
45 private String nameNormalized;
46 private boolean remote;
47 private boolean ssl;
48 private boolean persistent;
49 private boolean unnamed;
50
51 /**
52 * Create a connection info object.
53 *
54 * @param name the database name (including tags), but without the
55 * "jdbc:h2:" prefix
56 */
57 public ConnectionInfo(String name) {
58 this.name = name;
59 this.url = Constants.START_URL + name;
60 parseName();
61 }
62
63 /**
64 * Create a connection info object.
65 *
66 * @param u the database URL (must start with jdbc:h2:)
67 * @param info the connection properties
68 */
69 public ConnectionInfo(String u, Properties info) {
70 u = remapURL(u);
71 this.originalURL = u;
72 if (!u.startsWith(Constants.START_URL)) {
73 throw DbException.getInvalidValueException("url", u);
74 }
75 this.url = u;
76 readProperties(info);
77 readSettingsFromURL();
78 setUserName(removeProperty("USER", ""));
79 convertPasswords();
80 name = url.substring(Constants.START_URL.length());
81 parseName();
82 String recoverTest = removeProperty("RECOVER_TEST", null);
83 if (recoverTest != null) {
84 FilePathRec.register();
85 try {
86 Utils.callStaticMethod("org.h2.store.RecoverTester.init", recoverTest);
87 } catch (Exception e) {
88 throw DbException.convert(e);
89 }
90 name = "rec:" + name;
91 }
92 }
93
94 static {
95 ArrayList<String> list = SetTypes.getTypes();
96 HashSet<String> set = KNOWN_SETTINGS;
97 set.addAll(list);
98 String[] connectionTime = { "ACCESS_MODE_DATA", "AUTOCOMMIT", "CIPHER",
99 "CREATE", "CACHE_TYPE", "FILE_LOCK", "IGNORE_UNKNOWN_SETTINGS",
100 "IFEXISTS", "INIT", "PASSWORD", "RECOVER", "RECOVER_TEST",
101 "USER", "AUTO_SERVER", "AUTO_SERVER_PORT", "NO_UPGRADE",
102 "AUTO_RECONNECT", "OPEN_NEW", "PAGE_SIZE", "PASSWORD_HASH", "JMX" };
103 for (String key : connectionTime) {
104 if (SysProperties.CHECK && set.contains(key)) {
105 DbException.throwInternalError(key);
106 }
107 set.add(key);
108 }
109 }
110
111 private static boolean isKnownSetting(String s) {
112 return KNOWN_SETTINGS.contains(s);
113 }
114
115 public Object clone() throws CloneNotSupportedException {
116 ConnectionInfo clone = (ConnectionInfo) super.clone();
117 clone.prop = (Properties) prop.clone();
118 clone.filePasswordHash = Utils.cloneByteArray(filePasswordHash);
119 clone.userPasswordHash = Utils.cloneByteArray(userPasswordHash);
120 return clone;
121 }
122
123 private void parseName() {
124 if (".".equals(name)) {
125 name = "mem:";
126 }
127 if (name.startsWith("tcp:")) {
128 remote = true;
129 name = name.substring("tcp:".length());
130 } else if (name.startsWith("ssl:")) {
131 remote = true;
132 ssl = true;
133 name = name.substring("ssl:".length());
134 } else if (name.startsWith("mem:")) {
135 persistent = false;
136 if ("mem:".equals(name)) {
137 unnamed = true;
138 }
139 } else if (name.startsWith("file:")) {
140 name = name.substring("file:".length());
141 persistent = true;
142 } else {
143 persistent = true;
144 }
145 if (persistent && !remote) {
146 if ("/".equals(SysProperties.FILE_SEPARATOR)) {
147 name = name.replace('\\', '/');
148 } else {
149 name = name.replace('/', '\\');
150 }
151 }
152 }
153
154 /**
155 * Set the base directory of persistent databases, unless the database is in
156 * the user home folder (~).
157 *
158 * @param dir the new base directory
159 */
160 public void setBaseDir(String dir) {
161 if (persistent) {
162 String absDir = FileUtils.unwrap(FileUtils.toRealPath(dir));
163 boolean absolute = FileUtils.isAbsolute(name);
164 String n;
165 String prefix = null;
166 if (dir.endsWith(SysProperties.FILE_SEPARATOR)) {
167 dir = dir.substring(0, dir.length() - 1);
168 }
169 if (absolute) {
170 n = name;
171 } else {
172 n = FileUtils.unwrap(name);
173 prefix = name.substring(0, name.length() - n.length());
174 n = dir + SysProperties.FILE_SEPARATOR + n;
175 }
176 String normalizedName = FileUtils.unwrap(FileUtils.toRealPath(n));
177 if (normalizedName.equals(absDir) || !normalizedName.startsWith(absDir)) {
178 throw DbException.get(ErrorCode.IO_EXCEPTION_1, normalizedName + " outside " +
179 absDir);
180 }
181 if (!absolute) {
182 name = prefix + dir + SysProperties.FILE_SEPARATOR + FileUtils.unwrap(name);
183 }
184 }
185 }
186
187 /**
188 * Check if this is a remote connection.
189 *
190 * @return true if it is
191 */
192 public boolean isRemote() {
193 return remote;
194 }
195
196 /**
197 * Check if the referenced database is persistent.
198 *
199 * @return true if it is
200 */
201 public boolean isPersistent() {
202 return persistent;
203 }
204
205 /**
206 * Check if the referenced database is an unnamed in-memory database.
207 *
208 * @return true if it is
209 */
210 boolean isUnnamedInMemory() {
211 return unnamed;
212 }
213
214 private void readProperties(Properties info) {
215 Object[] list = new Object[info.size()];
216 info.keySet().toArray(list);
217 DbSettings s = null;
218 for (Object k : list) {
219 String key = StringUtils.toUpperEnglish(k.toString());
220 if (prop.containsKey(key)) {
221 throw DbException.get(ErrorCode.DUPLICATE_PROPERTY_1, key);
222 }
223 Object value = info.get(k);
224 if (isKnownSetting(key)) {
225 prop.put(key, value);
226 } else {
227 if (s == null) {
228 s = getDbSettings();
229 }
230 if (s.containsKey(key)) {
231 prop.put(key, value);
232 }
233 }
234 }
235 }
236
237 private void readSettingsFromURL() {
238 DbSettings dbSettings = DbSettings.getInstance(null);
239 int idx = url.indexOf(';');
240 if (idx >= 0) {
241 String settings = url.substring(idx + 1);
242 url = url.substring(0, idx);
243 String[] list = StringUtils.arraySplit(settings, ';', false);
244 for (String setting : list) {
245 if (setting.length() == 0) {
246 continue;
247 }
248 int equal = setting.indexOf('=');
249 if (equal < 0) {
250 throw getFormatException();
251 }
252 String value = setting.substring(equal + 1);
253 String key = setting.substring(0, equal);
254 key = StringUtils.toUpperEnglish(key);
255 if (!isKnownSetting(key) && !dbSettings.containsKey(key)) {
256 throw DbException.get(ErrorCode.UNSUPPORTED_SETTING_1, key);
257 }
258 String old = prop.getProperty(key);
259 if (old != null && !old.equals(value)) {
260 throw DbException.get(ErrorCode.DUPLICATE_PROPERTY_1, key);
261 }
262 prop.setProperty(key, value);
263 }
264 }
265 }
266
267 private char[] removePassword() {
268 Object p = prop.remove("PASSWORD");
269 if (p == null) {
270 return new char[0];
271 } else if (p instanceof char[]) {
272 return (char[]) p;
273 } else {
274 return p.toString().toCharArray();
275 }
276 }
277
278 /**
279 * Split the password property into file password and user password if
280 * necessary, and convert them to the internal hash format.
281 */
282 private void convertPasswords() {
283 char[] password = removePassword();
284 boolean passwordHash = removeProperty("PASSWORD_HASH", false);
285 if (getProperty("CIPHER", null) != null) {
286 // split password into (filePassword+' '+userPassword)
287 int space = -1;
288 for (int i = 0, len = password.length; i < len; i++) {
289 if (password[i] == ' ') {
290 space = i;
291 break;
292 }
293 }
294 if (space < 0) {
295 throw DbException.get(ErrorCode.WRONG_PASSWORD_FORMAT);
296 }
297 char[] np = new char[password.length - space - 1];
298 char[] filePassword = new char[space];
299 System.arraycopy(password, space + 1, np, 0, np.length);
300 System.arraycopy(password, 0, filePassword, 0, space);
301 Arrays.fill(password, (char) 0);
302 password = np;
303 filePasswordHash = hashPassword(passwordHash, "file", filePassword);
304 }
305 userPasswordHash = hashPassword(passwordHash, user, password);
306 }
307
308 private static byte[] hashPassword(boolean passwordHash, String userName, char[] password) {
309 if (passwordHash) {
310 return StringUtils.convertHexToBytes(new String(password));
311 }
312 if (userName.length() == 0 && password.length == 0) {
313 return new byte[0];
314 }
315 return SHA256.getKeyPasswordHash(userName, password);
316 }
317
318 /**
319 * Get a boolean property if it is set and return the value.
320 *
321 * @param key the property name
322 * @param defaultValue the default value
323 * @return the value
324 */
325 boolean getProperty(String key, boolean defaultValue) {
326 String x = getProperty(key, null);
327 if (x == null) {
328 return defaultValue;
329 }
330 // support 0 / 1 (like the parser)
331 if (x.length() == 1 && Character.isDigit(x.charAt(0))) {
332 return Integer.parseInt(x) != 0;
333 }
334 return Boolean.parseBoolean(x);
335 }
336
337 /**
338 * Remove a boolean property if it is set and return the value.
339 *
340 * @param key the property name
341 * @param defaultValue the default value
342 * @return the value
343 */
344 public boolean removeProperty(String key, boolean defaultValue) {
345 String x = removeProperty(key, null);
346 return x == null ? defaultValue : Boolean.parseBoolean(x);
347 }
348
349 /**
350 * Remove a String property if it is set and return the value.
351 *
352 * @param key the property name
353 * @param defaultValue the default value
354 * @return the value
355 */
356 String removeProperty(String key, String defaultValue) {
357 if (SysProperties.CHECK && !isKnownSetting(key)) {
358 DbException.throwInternalError(key);
359 }
360 Object x = prop.remove(key);
361 return x == null ? defaultValue : x.toString();
362 }
363
364 /**
365 * Get the unique and normalized database name (excluding settings).
366 *
367 * @return the database name
368 */
369 public String getName() {
370 if (persistent) {
371 if (nameNormalized == null) {
372 String suffix = Constants.SUFFIX_PAGE_FILE;
373 String n = FileUtils.toRealPath(name + suffix);
374 String fileName = FileUtils.getName(n);
375 if (fileName.length() < suffix.length() + 1) {
376 throw DbException.get(ErrorCode.INVALID_DATABASE_NAME_1, name);
377 }
378 nameNormalized = n.substring(0, n.length() - suffix.length());
379 }
380 return nameNormalized;
381 }
382 return name;
383 }
384
385 /**
386 * Get the file password hash if it is set.
387 *
388 * @return the password hash or null
389 */
390 byte[] getFilePasswordHash() {
391 return filePasswordHash;
392 }
393
394 /**
395 * Get the name of the user.
396 *
397 * @return the user name
398 */
399 public String getUserName() {
400 return user;
401 }
402
403 /**
404 * Get the user password hash.
405 *
406 * @return the password hash
407 */
408 byte[] getUserPasswordHash() {
409 return userPasswordHash;
410 }
411
412 /**
413 * Get the property keys.
414 *
415 * @return the property keys
416 */
417 String[] getKeys() {
418 String[] keys = new String[prop.size()];
419 prop.keySet().toArray(keys);
420 return keys;
421 }
422
423 /**
424 * Get the value of the given property.
425 *
426 * @param key the property key
427 * @return the value as a String
428 */
429 String getProperty(String key) {
430 Object value = prop.get(key);
431 if (value == null || !(value instanceof String)) {
432 return null;
433 }
434 return value.toString();
435 }
436
437 /**
438 * Get the value of the given property.
439 *
440 * @param key the property key
441 * @param defaultValue the default value
442 * @return the value as a String
443 */
444 int getProperty(String key, int defaultValue) {
445 if (SysProperties.CHECK && !isKnownSetting(key)) {
446 DbException.throwInternalError(key);
447 }
448 String s = getProperty(key);
449 return s == null ? defaultValue : Integer.parseInt(s);
450 }
451
452 /**
453 * Get the value of the given property.
454 *
455 * @param key the property key
456 * @param defaultValue the default value
457 * @return the value as a String
458 */
459 public String getProperty(String key, String defaultValue) {
460 if (SysProperties.CHECK && !isKnownSetting(key)) {
461 DbException.throwInternalError(key);
462 }
463 String s = getProperty(key);
464 return s == null ? defaultValue : s;
465 }
466
467 /**
468 * Get the value of the given property.
469 *
470 * @param setting the setting id
471 * @param defaultValue the default value
472 * @return the value as a String
473 */
474 String getProperty(int setting, String defaultValue) {
475 String key = SetTypes.getTypeName(setting);
476 String s = getProperty(key);
477 return s == null ? defaultValue : s;
478 }
479
480 /**
481 * Get the value of the given property.
482 *
483 * @param setting the setting id
484 * @param defaultValue the default value
485 * @return the value as an integer
486 */
487 int getIntProperty(int setting, int defaultValue) {
488 String key = SetTypes.getTypeName(setting);
489 String s = getProperty(key, null);
490 try {
491 return s == null ? defaultValue : Integer.decode(s);
492 } catch (NumberFormatException e) {
493 return defaultValue;
494 }
495 }
496
497 /**
498 * Check if this is a remote connection with SSL enabled.
499 *
500 * @return true if it is
501 */
502 boolean isSSL() {
503 return ssl;
504 }
505
506 /**
507 * Overwrite the user name. The user name is case-insensitive and stored in
508 * uppercase. English conversion is used.
509 *
510 * @param name the user name
511 */
512 public void setUserName(String name) {
513 this.user = StringUtils.toUpperEnglish(name);
514 }
515
516 /**
517 * Set the user password hash.
518 *
519 * @param hash the new hash value
520 */
521 public void setUserPasswordHash(byte[] hash) {
522 this.userPasswordHash = hash;
523 }
524
525 /**
526 * Set the file password hash.
527 *
528 * @param hash the new hash value
529 */
530 public void setFilePasswordHash(byte[] hash) {
531 this.filePasswordHash = hash;
532 }
533
534 /**
535 * Overwrite a property.
536 *
537 * @param key the property name
538 * @param value the value
539 */
540 public void setProperty(String key, String value) {
541 // value is null if the value is an object
542 if (value != null) {
543 prop.setProperty(key, value);
544 }
545 }
546
547 /**
548 * Get the database URL.
549 *
550 * @return the URL
551 */
552 public String getURL() {
553 return url;
554 }
555
556 /**
557 * Get the complete original database URL.
558 *
559 * @return the database URL
560 */
561 public String getOriginalURL() {
562 return originalURL;
563 }
564
565 /**
566 * Set the original database URL.
567 *
568 * @param url the database url
569 */
570 public void setOriginalURL(String url) {
571 originalURL = url;
572 }
573
574 /**
575 * Generate an URL format exception.
576 *
577 * @return the exception
578 */
579 DbException getFormatException() {
580 String format = Constants.URL_FORMAT;
581 return DbException.get(ErrorCode.URL_FORMAT_ERROR_2, format, url);
582 }
583
584 /**
585 * Switch to server mode, and set the server name and database key.
586 *
587 * @param serverKey the server name, '/', and the security key
588 */
589 public void setServerKey(String serverKey) {
590 remote = true;
591 persistent = false;
592 this.name = serverKey;
593 }
594
595 DbSettings getDbSettings() {
596 DbSettings defaultSettings = DbSettings.getInstance(null);
597 HashMap<String, String> s = null;
598 for (Object k : prop.keySet()) {
599 String key = k.toString();
600 if (!isKnownSetting(key) && defaultSettings.containsKey(key)) {
601 if (s == null) {
602 s = New.hashMap();
603 }
604 s.put(key, prop.getProperty(key));
605 }
606 }
607 return DbSettings.getInstance(s);
608 }
609
610 private static String remapURL(String url) {
611 String urlMap = SysProperties.URL_MAP;
612 if (urlMap != null && urlMap.length() > 0) {
613 try {
614 SortedProperties prop;
615 prop = SortedProperties.loadProperties(urlMap);
616 String url2 = prop.getProperty(url);
617 if (url2 == null) {
618 prop.put(url, "");
619 prop.store(urlMap);
620 } else {
621 url2 = url2.trim();
622 if (url2.length() > 0) {
623 return url2;
624 }
625 }
626 } catch (IOException e) {
627 throw DbException.convert(e);
628 }
629 }
630 return url;
631 }
632
633 }
634