001/* 002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 003 * 004 * http://www.izforge.com/izpack/ 005 * http://developer.berlios.de/projects/izpack/ 006 * 007 * Copyright 2005 Klaus Bartz 008 * 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 */ 021 022package com.coi.tools.os.win; 023 024import java.util.ArrayList; 025import java.util.Iterator; 026import java.util.List; 027 028import com.izforge.izpack.util.Debug; 029 030/** 031 * System dependent helper for MS Windows registry handling. This class is only vaild on Windows. It 032 * declares naitve methods which are implemented in COIOSHelper.dll 033 * 034 * @author Klaus Bartz 035 */ 036public class RegistryImpl 037{ 038 039 /* 040 * Registry root values, extracted from winreg.h 041 */ 042 public static final int HKEY_CLASSES_ROOT = 0x80000000; 043 044 public static final int HKEY_CURRENT_USER = 0x80000001; 045 046 public static final int HKEY_LOCAL_MACHINE = 0x80000002; 047 048 public static final int HKEY_USERS = 0x80000003; 049 050 public static final int HKEY_PERFORMANCE_DATA = 0x80000004; 051 052 public static final int HKEY_CURRENT_CONFIG = 0x80000005; 053 054 public static final int HKEY_DYN_DATA = 0x80000006; 055 056 private static final String DEFAULT_PLACEHOLDER = "__#$&DEFAULT_PLACEHODER_VALUE#$?"; 057 058 private int currentRoot = HKEY_CURRENT_USER; 059 060 private List logging = new ArrayList(); 061 062 private boolean doLogging = false; 063 064 /** 065 * Creates a new empty RegistryImpl object. 066 */ 067 public RegistryImpl() 068 { 069 super(); 070 } 071 072 /** 073 * Returns current root. 074 * 075 * @return current root 076 */ 077 public int getRoot() 078 { 079 return currentRoot; 080 } 081 082 /** 083 * Sets the root id to the given value. 084 * 085 * @param i root id to be set 086 */ 087 public void setRoot(int i) 088 { 089 currentRoot = i; 090 } 091 092 /** 093 * Returns the value of the given value name as RegDataContainer. 094 * 095 * @param key key of the registry entry 096 * @param value value name of the registry entry 097 * @return the value of the given value name as RegDataContainer 098 * @throws NativeLibException 099 */ 100 public RegDataContainer getValue(String key, String value) throws NativeLibException 101 { 102 return (getValue(currentRoot, key, value)); 103 } 104 105 /** 106 * Returns the value of the given value name as Object. The real type depends to the type of the 107 * value. 108 * 109 * @param key key of the registry entry 110 * @param value value name of the registry entry 111 * @return the value of the given value name as RegDataContainer 112 * @throws NativeLibException 113 */ 114 public Object getValueAsObject(String key, String value) throws NativeLibException 115 { 116 return (getValue(currentRoot, key, value).getDataAsObject()); 117 } 118 119 /** 120 * Returns all sub keys under the given key which is under the current root. 121 * 122 * @param key key for which the sub keys should be detected 123 * @return all sub keys under the given key which is under the current root 124 * @throws NativeLibException 125 */ 126 public String[] getSubkeys(String key) throws NativeLibException 127 { 128 return (getSubkeyNames(currentRoot, key)); 129 } 130 131 /** 132 * Returns all value names under the given key which is under the current root. 133 * 134 * @param key key for which the values should be detected 135 * @return all value names under the given key which is under the current root 136 * @throws NativeLibException 137 */ 138 public String[] getValueNames(String key) throws NativeLibException 139 { 140 return (getValueNames(currentRoot, key)); 141 } 142 143 /** 144 * Creates the given key under the current root. 145 * 146 * @param key key to be created 147 * @throws NativeLibException 148 */ 149 public void createKey(String key) throws NativeLibException 150 { 151 createKey(currentRoot, key); 152 } 153 154 /** 155 * Creates the given key under the given root. 156 * 157 * @param key key to be created 158 * @throws NativeLibException 159 */ 160 public void createKey(int root, String key) throws NativeLibException 161 { 162 int pathEnd = key.lastIndexOf('\\'); 163 if( pathEnd < 0 ) 164 { 165 throw new NativeLibException("Keys directly under the root are not allowed!"); 166 } 167 String subkey = key.substring(0, pathEnd); 168 if (!exist(root, subkey)) 169 { // Create missing sub keys 170 createKey(root, subkey); 171 } 172 // Create key 173 createKeyN(root, key); 174 RegistryLogItem rli = new RegistryLogItem(RegistryLogItem.CREATED_KEY, root, key, null, 175 null, null); 176 log(rli); 177 178 } 179 180 /** 181 * Returns whether the given key under the current root exist or not. 182 * 183 * @param key key to be tested 184 * @return true if thekey exist, else false 185 * @throws NativeLibException 186 */ 187 public boolean keyExist(String key) throws NativeLibException 188 { 189 return (keyExist(currentRoot, key)); 190 } 191 192 /** 193 * Returns whether the given key under the given root exist or not. 194 * 195 * @param root to be used 196 * @param key key to be tested 197 * @return true if thekey exist, else false 198 * @throws NativeLibException 199 */ 200 public boolean keyExist(int root, String key) throws NativeLibException 201 { 202 try 203 { 204 return (exist(root, key)); 205 } 206 catch (NativeLibException ne) 207 { 208 String em = ne.getLibMessage(); 209 if (em.equals("functionFailed.RegOpenKeyEx")) { return (false); } 210 throw (ne); 211 } 212 } 213 214 /** 215 * Returns whether the given value exist under the current root or not. 216 * 217 * @param key key of the value for which should be tested 218 * @param value value to be tested 219 * @return true if the value exist, else false 220 * @throws NativeLibException 221 */ 222 public boolean valueExist(String key, String value) throws NativeLibException 223 { 224 try 225 { 226 this.getValue(currentRoot, key, value); 227 } 228 catch (NativeLibException ne) 229 { 230 String em = ne.getLibMessage(); 231 if (em.equals("functionFailed.RegOpenKeyEx")) { return (false); } 232 throw (ne); 233 } 234 return (true); 235 } 236 237 /** 238 * Sets the given contents to the given registry value. If a sub key or the registry value does 239 * not exist, it will be created. REG_SZ is used as registry value type. 240 * 241 * @param key the registry key which should be used or created 242 * @param value the registry value into which the contents should be set 243 * @param contents the contents for the value 244 * @throws NativeLibException 245 */ 246 public void setValue(String key, String value, String contents) throws NativeLibException 247 { 248 249 setValue(currentRoot, key, value, new RegDataContainer(contents)); 250 } 251 252 /** 253 * Sets the given contents to the given registry value. If a sub key or the registry value does 254 * not exist, it will be created. REG_MULTI_SZ is used as registry value type. 255 * 256 * @param key the registry key which should be used or created 257 * @param value the registry value into which the contents should be set 258 * @param contents the contents for the value 259 * @throws NativeLibException 260 */ 261 public void setValue(String key, String value, String[] contents) throws NativeLibException 262 { 263 264 setValue(currentRoot, key, value, new RegDataContainer(contents)); 265 } 266 267 /** 268 * Sets the given contents to the given registry value. If a sub key or the registry value does 269 * not exist, it will be created. REG_BINARY is used as registry value type. 270 * 271 * @param key the registry key which should be used or created 272 * @param value the registry value into which the contents should be set 273 * @param contents the contents for the value 274 * @throws NativeLibException 275 */ 276 public void setValue(String key, String value, byte[] contents) throws NativeLibException 277 { 278 279 setValue(currentRoot, key, value, new RegDataContainer(contents)); 280 } 281 282 /** 283 * Sets the given contents to the given registry value. If a sub key or the registry value does 284 * not exist, it will be created. REG_DWORD is used as registry value type. 285 * 286 * @param key the registry key which should be used or created 287 * @param value the registry value into which the contents should be set 288 * @param contents the contents for the value 289 * @throws NativeLibException 290 */ 291 public void setValue(String key, String value, long contents) throws NativeLibException 292 { 293 setValue(currentRoot, key, value, new RegDataContainer(contents)); 294 } 295 296 /** 297 * Sets the given contents to the given registry value under current root. If a sub key or the 298 * registry value does not exist, it will be created. The used registry value type will be 299 * determined by the type of the RegDataContainer. 300 * 301 * @param key the registry key which should be used or created 302 * @param value the registry value into which the contents should be set 303 * @param contents the contents for the value 304 * @throws NativeLibException 305 */ 306 public void setValue(String key, String value, RegDataContainer contents) 307 throws NativeLibException 308 { 309 setValue(currentRoot, key, value, contents); 310 } 311 312 /** 313 * Sets the given contents to the given registry value. If a sub key or the registry value does 314 * not exist, it will be created. The used registry value type will be determined by the type of 315 * the RegDataContainer. 316 * 317 * @param root id for the root of the key 318 * @param key the registry key which should be used or created 319 * @param value the registry value into which the contents should be set 320 * @param contents the contents for the value 321 * @throws NativeLibException 322 */ 323 public void setValue(int root, String key, String value, RegDataContainer contents) 324 throws NativeLibException 325 { 326 RegDataContainer oldContents = null; 327 String localValue = value; 328 // May be someone do not like backslashes ... 329 key = key.replace('/', '\\'); 330 331 synchronized (logging) 332 { 333 try 334 { 335 oldContents = getValue(currentRoot, key, value); 336 } 337 catch (NativeLibException ne) 338 { 339 String em = ne.getLibMessage(); 340 if (em.equals("functionFailed.RegOpenKeyEx") 341 || em.equals("functionFailed.RegQueryValueEx")) 342 { 343 setValueR(root, key, value, contents); 344 return; 345 } 346 throw (ne); 347 } 348 setValueN(root, key, value, contents); 349 // Add value changing to log list 350 if (value.length() == 0) // The default value ... 351 localValue = DEFAULT_PLACEHOLDER; // Rewind will fail if last 352 // token is 353 // empty. 354 355 RegistryLogItem rli = new RegistryLogItem(RegistryLogItem.CHANGED_VALUE, root, key, 356 localValue, contents, oldContents); 357 log(rli); 358 } 359 return; 360 } 361 362 /** 363 * Deletes a key under the current root if exist and it is empty, else throw an exception. 364 * 365 * @param key key to be deleted 366 * @throws NativeLibException 367 */ 368 public void deleteKey(String key) throws NativeLibException 369 { 370 deleteKeyL(currentRoot, key); 371 } 372 /** 373 * Deletes a key under the current root if it is empty, else do nothing. 374 * 375 * @param key key to be deleted 376 * @throws NativeLibException 377 */ 378 public void deleteKeyIfEmpty(String key) throws NativeLibException 379 { 380 deleteKeyIfEmpty(currentRoot, key); 381 } 382 383 /** 384 * Deletes a key if it is empty, else do nothing. 385 * 386 * @param root id for the root of the key 387 * @param key key to be deleted 388 * @throws NativeLibException 389 */ 390 public void deleteKeyIfEmpty(int root, String key) throws NativeLibException 391 { 392 if (keyExist(root, key) && isKeyEmpty(root, key) ) deleteKeyL(root, key); 393 394 } 395 396 /** 397 * Deletes a value. 398 * 399 * @param key key of the value which should be deleted 400 * @param value value name to be deleted 401 * @throws NativeLibException 402 */ 403 public void deleteValue(String key, String value) throws NativeLibException 404 { 405 deleteValueL(currentRoot, key, value); 406 } 407 408 /** 409 * Deletes a key with logging. 410 * 411 * @param root id for the root of the key 412 * @param key key to be deleted 413 * @throws NativeLibException 414 */ 415 private void deleteKeyL(int root, String key) throws NativeLibException 416 { 417 RegistryLogItem rli = new RegistryLogItem(RegistryLogItem.REMOVED_KEY, root, key, null, 418 null, null); 419 log(rli); 420 deleteKeyN(root, key); 421 } 422 423 /** 424 * Deletes a value with logging. 425 * 426 * @param root id for the root of the key 427 * @param key key of the value which should be deleted 428 * @param value value name to be deleted 429 * @throws NativeLibException 430 */ 431 private void deleteValueL(int root, String key, String value) throws NativeLibException 432 { 433 RegDataContainer oldContents = getValue(currentRoot, key, value); 434 RegistryLogItem rli = new RegistryLogItem(RegistryLogItem.REMOVED_VALUE, root, key, value, 435 null, oldContents); 436 log(rli); 437 deleteValueN(currentRoot, key, value); 438 } 439 440 /** 441 * Rewinds all logged actions. 442 * 443 * @throws IllegalArgumentException 444 * @throws NativeLibException 445 */ 446 public void rewind() throws IllegalArgumentException, NativeLibException 447 { 448 synchronized (logging) 449 { 450 Iterator iter = logging.iterator(); 451 suspendLogging(); 452 453 while (iter.hasNext()) 454 { 455 RegistryLogItem rli = (RegistryLogItem) iter.next(); 456 switch (rli.getType()) 457 { 458 case RegistryLogItem.CREATED_KEY: 459 deleteKeyIfEmpty(rli.getRoot(), rli.getKey()); 460 break; 461 case RegistryLogItem.REMOVED_KEY: 462 createKeyN(rli.getRoot(), rli.getKey()); 463 break; 464 case RegistryLogItem.CREATED_VALUE: 465 RegDataContainer currentContents = null; 466 // Delete value only if reg entry exists and equal to the 467 // stored 468 // value. 469 try 470 { 471 currentContents = getValue(rli.getRoot(), rli.getKey(), rli.getValueName()); 472 } 473 catch (NativeLibException nle) 474 { 475 break; 476 } 477 if (currentContents.equals(rli.getNewValue())) 478 { 479 Debug.error("delete it "); 480 deleteValueN(rli.getRoot(), rli.getKey(), rli.getValueName()); 481 } 482 // TODO: what todo if value has changed? 483 break; 484 case RegistryLogItem.REMOVED_VALUE: 485 // Set old value only if reg entry not exists. 486 try 487 { 488 getValue(rli.getRoot(), rli.getKey(), rli.getValueName()); 489 } 490 catch (NativeLibException nle) 491 { 492 setValueN(rli.getRoot(), rli.getKey(), rli.getValueName(), rli 493 .getOldValue()); 494 } 495 break; 496 case RegistryLogItem.CHANGED_VALUE: 497 // Change to old value only if reg entry exists and equal to 498 // the 499 // stored value. 500 try 501 { 502 currentContents = getValue(rli.getRoot(), rli.getKey(), rli.getValueName()); 503 } 504 catch (NativeLibException nle) 505 { 506 break; 507 } 508 if (currentContents.equals(rli.getNewValue())) 509 { 510 setValueN(rli.getRoot(), rli.getKey(), rli.getValueName(), rli 511 .getOldValue()); 512 } 513 break; 514 } 515 } 516 } 517 518 } 519 520 /** 521 * Sets the given contents to the given registry value. If a sub key or the registry value does 522 * not exist, it will be created. The used registry value type will be determined by the type of 523 * the RegDataContainer. 524 * 525 * @param root id for the root of the key 526 * @param key the registry key which should be used or created 527 * @param value the registry value into which the contents should be set 528 * @param contents the contents for the value 529 * @throws NativeLibException 530 */ 531 private void setValueR(int root, String key, String value, RegDataContainer contents) 532 throws NativeLibException 533 { 534 String localValue = value; 535 if (!exist(root, key)) 536 { // Create missing sub keys 537 createKey(root, key); 538 } 539 // Create value 540 setValueN(root, key, value, contents); 541 // Add value creation to log list 542 if (value.length() == 0) // The default value ... 543 localValue = DEFAULT_PLACEHOLDER; // Rewind will fail if last token 544 // is 545 // empty. 546 StringBuffer sb = new StringBuffer(); 547 sb.append("SetValue;").append(Integer.toString(root)).append(";").append(key).append(";") 548 .append(localValue); 549 RegistryLogItem rli = new RegistryLogItem(RegistryLogItem.CREATED_VALUE, root, key, 550 localValue, contents, null); 551 log(rli); 552 } 553 554 private native boolean exist(int root, String key) throws NativeLibException; 555 556 private native void createKeyN(int root, String key) throws NativeLibException; 557 558 private native void setValueN(int root, String key, String value, RegDataContainer contents) 559 throws NativeLibException; 560 561 private native int getValueType(int root, String key, String value) throws NativeLibException; 562 563 private native RegDataContainer getValue(int root, String key, String value) 564 throws NativeLibException; 565 566 private native void deleteValueN(int root, String key, String value) throws NativeLibException; 567 568 private native void deleteKeyN(int root, String key) throws NativeLibException; 569 570 private native boolean isKeyEmpty(int root, String key) throws NativeLibException; 571 572 private native int getSubkeyCount(int root, String key) throws NativeLibException; 573 574 private native int getValueCount(int root, String key) throws NativeLibException; 575 576 private native String getSubkeyName(int root, String key, int index) throws NativeLibException; 577 578 private native String getValueName(int root, String key, int index) throws NativeLibException; 579 580 private native String[] getSubkeyNames(int root, String key) throws NativeLibException; 581 582 private native String[] getValueNames(int root, String key) throws NativeLibException; 583 584 /** 585 * Creates a new (empty) logging list and activates logging. 586 */ 587 public void resetLogging() 588 { 589 logging = new ArrayList(); 590 activateLogging(); 591 } 592 593 /** 594 * Suspends logging. 595 */ 596 public void suspendLogging() 597 { 598 doLogging = false; 599 } 600 601 /** 602 * Activates logging. 603 */ 604 public void activateLogging() 605 { 606 doLogging = true; 607 } 608 609 /** 610 * Returns a copy of the colected logging informations. 611 */ 612 public List getLoggingInfo() 613 { 614 ArrayList retval = new ArrayList(logging.size()); 615 Iterator iter = logging.iterator(); 616 while (iter.hasNext()) 617 try 618 { 619 retval.add(((RegistryLogItem) iter.next()).clone()); 620 } 621 catch (CloneNotSupportedException e) 622 { // Should never be... 623 e.printStackTrace(); 624 } 625 return (retval); 626 } 627 628 /** 629 * Copies the contents of the given list of RegistryLogItems to a newly created internal logging 630 * list. 631 */ 632 public void setLoggingInfo(List info) 633 { 634 resetLogging(); 635 addLoggingInfo(info); 636 } 637 638 /** 639 * Adds copies of the contents of the given list of RegistryLogItems to the existent internal 640 * logging list. 641 */ 642 public void addLoggingInfo(List info) 643 { 644 Iterator iter = info.iterator(); 645 while (iter.hasNext()) 646 try 647 { 648 logging.add(((RegistryLogItem) iter.next()).clone()); 649 } 650 catch (CloneNotSupportedException e) 651 { // Should never be 652 e.printStackTrace(); 653 } 654 655 } 656 657 /** 658 * Adds the given item to the beginning of the logging list if logging is enabled, else do 659 * nothing. 660 * 661 * @param item 662 */ 663 private void log(RegistryLogItem item) 664 { 665 if (doLogging && logging != null) logging.add(0, item); 666 } 667 668 /** 669 * Adds the given item to the end of the logging list if logging is enabled, else do nothing. 670 * 671 * @param item 672 */ 673 private void logAtEnd(RegistryLogItem item) 674 { 675 if (doLogging && logging != null) logging.add(item); 676 } 677 678}