[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 var WPKG_VERSION = "1.3.0"; 2 /******************************************************************************* 3 * 4 * WPKG - Windows Packager pour SambaEdu : http://sambaedu.org 5 * 6 * Copyright 2003 Jerry Haltom<br> 7 * Copyright 2005 Aleksander Wysocki <papopypu (at) op . pl><br> 8 * Copyright 2005-2006 Tomasz Chmielewski <mangoo (at) wpkg . org><br> 9 * Copyright 2007-2011 Rainer Meier <r.meier (at) wpkg.org><br> 10 * Copyright 2007 Jean Le Bail <jean.lebail (at) etab . ac-caen . fr> 11 * 12 * Please report your issues to the list on http://wpkg.org/ 13 */ 14 15 /******************************************************************************* 16 * Version de wpkg.js modifiée pour SambaEdu3 17 18 * Le support de la remontée des logs d'execution des scripts batch est suspendu suite a des blocages msiexec du wpkg-se3.js adapté en version 1.2 avec la fonction exec() de la version wpkg-se3.js 1.1 de Jean Le Bail. 19 20 PATCH pour SE3 (Olivier Lacroix) 21 Les modifs par rapport à wpkg.js officiels sont délimitées par // PATCH SE3 et // FIN PATCH SE3 : 22 1. ajout de lignes dans installPackage() et removePackage() pour classement du menu démarrer 23 2. modif de la function QueryPackage pour compatibilité avec l'interface se3 actuelle et la lecture des logs d'install 24 */ 25 26 /** 27 * Displays command usage. 28 */ 29 function showUsage() { 30 var message = "" + 31 "If you cannot read this since it is displayed within a dialog-window please \n" + 32 "execute 'cscript wpkg.js /help' on the command line. This will print all \n" + 33 "messages to the console. \n\n" + 34 "Command Line Switches \n" + 35 "===================== \n" + 36 "Note: These command line switches overwrite parameters within config.xml. For \n" + 37 "example the /quiet flag overwrites an eventually present quiet parameter within \n" + 38 "config.xml. \n" + 39 "Usually you should specify as few parameters as possible since changing \n" + 40 "config.xml on the server might be much easier than changing all client-side \n" + 41 "stored parameters. Typically you would use the following command-line in \n" + 42 "production: \n" + 43 " wpkg.js /synchronize \n" + 44 "\n" + 45 "Frequently used parameters (package operations, you need to choose one): \n" + 46 "======================================================================== \n" + 47 "\n" + 48 "/install:<package>[,package2[,package3,[...]]] \n" + 49 " Install the specified package(s) on the system. \n" + 50 "\n" + 51 "/query:<option> \n" + 52 " Display a list of packages matching the specified criteria. Valid \n" + 53 " options are: \n" + 54 "\n" + 55 " a - Query all packages (includes installed packages and package database). \n" + 56 " x - List packages which are not installed but in package database. \n" + 57 " i - List all packages which are currently installed. \n" + 58 " I - List packages which are about to be installed during synchronization. \n" + 59 " u - List packages which are about to be upgraded during synchronization. \n" + 60 " d - List packages which are about to be downgraded during synchronization. \n" + 61 " r - List packages which are about to be removed during synchronization. \n" + 62 " m - List all modifications which would apply during synchronization \n" + 63 " (equal to Iudr) \n" + 64 " n - List packages which belong to the profile but are not modified during \n" + 65 " synchronization. \n" + 66 " s - List host attributes from settings (wpkg.xml). \n" + 67 " l - List host attributes read from local host. \n" + 68 "\n" + 69 "/remove:<package>[,package2[,package3,[...]]] \n" + 70 " Remove the specified package(s) from the system. \n" + 71 "\n" + 72 "/show:<package> \n" + 73 " Display a summary of the specified package, including it's state. \n" + 74 "\n" + 75 "/upgrade:<package>[,package2[,package3,[...]]] \n" + 76 " Upgrade the already installed package(s) on the system. \n" + 77 "\n" + 78 "/synchronize \n" + 79 " Synchronize the current program state with the suggested program state \n" + 80 " of the specified profile. This is the action that should be called at \n" + 81 " system boot time for this program to be useful. \n" + 82 "\n" + 83 "/help \n" + 84 " Show this message. \n" + 85 "\n" + 86 "\n" + 87 "Optional parameters (usually defined within config.xml): \n" + 88 "======================================================== \n" + 89 "\n" + 90 "/base:<path> \n" + 91 " Set the local or remote path to find the xml input files. \n" + 92 " Can also be set to a web URL for direct XML retrieval from wpkg_web. \n" + 93 "\n" + 94 "/dryrun[:<true>|<false>] \n" + 95 " Do not execute any actions. Implies debug mode. \n" + 96 "\n" + 97 "/quiet[:<true>|<false>] \n" + 98 " Use the event log to record all error/status messages. Use this when \n" + 99 " running unattended. \n" + 100 "\n" + 101 "/nonotify[:<true>|<false>] \n" + 102 " Logged in users are not notified about impending updates. \n" + 103 "\n" + 104 "/noreboot[:<true>|<false>] \n" + 105 " System does not reboot regardless of need. \n" + 106 "\n" + 107 "/quitonerror[:<true>|<false>] \n" + 108 " Quit execution if the installation of any package was unsuccessful \n" + 109 " (default: install next package and show the error summary). \n" + 110 "\n" + 111 "/sendStatus[:<true>|<false>] \n" + 112 " Send status messages on STDOUT which can be parsed by calling program to \n" + 113 " display status information to the user. \n" + 114 "\n" + 115 "/noUpgradeBeforeRemove[:<true>|<false>] \n" + 116 " Usually WPKG upgrades a package to the latest available version before it \n" + 117 " removes the package. This allows administrators to fix bugs in the package \n" + 118 " and assure proper removal.\n" + 119 "\n" + 120 "/applymultiple[:<true>|<false>] \n" + 121 " Apply profiles of all host nodes with matching attributes. \n" + 122 " Only first matching host node is returned if not switched on. \n" + 123 " This parameter must be used with caution, it can break existing setup. \n" + 124 "\n" + 125 "Rarely used parameters (mainly for testing): \n" + 126 "============================================ \n" + 127 "\n" + 128 "/config:<path> \n" + 129 " Path to the configuration file to be used. The path might be absolute \n" + 130 " or relative but including the XML file name. This parameter is entirely \n" + 131 " OPTIONAL and should normally not be specified at all. \n" + 132 " If not specified the configuration will be searched at: \n" + 133 " <script-path>\\config.xml \n" + 134 " where <script-path> is the directory from which the script is executed. \n" + 135 " e.g. '\\\\server\\share\\directory\\config.xml'. \n" + 136 " e.g. 'directory\\config.xml'. \n" + 137 " You can use environment variables as well as the following expressions: \n" + 138 " [HOSTNAME] Replaced by the executing hostname. \n" + 139 " [PROFILE] Replaced by the concatenated list of profiles applied. \n" + 140 "\n" + 141 "/settings:<path> \n" + 142 " Path to the settings (local WPKG database) file to be used. The path might \n" + 143 " be absolute or relative but including the XML file name. This parameter is \n" + 144 " entirely OPTIONAL and should normally not be specified at all. \n" + 145 " If not specified the settings file will be searched at: \n" + 146 " %SystemRoot%\\system32\\wpkg.xml \n" + 147 " e.g. '%SystemRoot%\\system32\\wpkg-custom.xml'. \n" + 148 " e.g. '\\\\server\share\directory\\[HOSTNAME].xml'. \n" + 149 " If you store the settings file on a share make sure the names is unique! \n" + 150 " You can use environment variables as well as the following expressions: \n" + 151 " [HOSTNAME] Replaced by the executing hostname. \n" + 152 " [PROFILE] Replaced by the concatenated list of profiles applied. \n" + 153 "\n" + 154 "/queryMode:<mode> \n" + 155 " Allows to switch to remote mode where package verification is skipped. \n" + 156 " remote: Disable package checks and assume that packages in settings \n" + 157 " database are still correctly installed. In remote mode also the \n" + 158 " host information is read from the local settings database. \n" + 159 " local: Default setting. Query verifies package status using all checks. \n" + 160 " Note: Query mode can only be used with the query switch. \n" + 161 "\n" + 162 "/profile:<profile> \n" + 163 " Force the name of the profile to use. If not specified, the profile to use \n" + 164 " is looked up in hosts.xml. \n" + 165 "\n" + 166 "/debug[:<true>|<false>] or /verbose[:<true>|<false>] \n" + 167 " Enable debug output. Please note that this parameter only influences \n" + 168 " notification and event log output. It does not affect the logging level. \n" + 169 " It is possible to get debug-level output even without using this \n" + 170 " switch. \n" + 171 "\n" + 172 "/force[:<true>|<false>] \n" + 173 " When used in conjunction with /synchronize WPKG will ignore the local \n" + 174 " settings file (wpkg.xml) and re-build the database with installed \n" + 175 " packages. \n" + 176 " When used in conjunction with /remove forces removal of the specified \n" + 177 " package even if not all packages which depend on the one to be removed \n" + 178 " could be removed. \n" + 179 "\n" + 180 "/forceinstall[:<true>|<false>] \n" + 181 " Force installation over existing packages. \n" + 182 "\n" + 183 "/host:<hostname> \n" + 184 " Use the specified hostname instead of reading it from the executing host. \n" + 185 "\n" + 186 "/os:<hostos> \n" + 187 " Use the specified operating system string instead of reading it from the \n" + 188 " executing host. \n" + 189 "\n" + 190 "/ip:<ip-address-1,ip-address-2,...,ip-address-n> \n" + 191 " Use the specified ipaddresses instead of reading it from the executing host. \n" + 192 "\n" + 193 "/domainname:<domain> \n" + 194 " Name of the windows domain the computer belongs to. \n" + 195 " This permit to use group membership even on a non-member workstation. \n" + 196 "\n" + 197 "/group:<group-name-1,group-name-2,...,group-name-n>\n" + 198 " Name of the group the computer must belongs to instead of reading it from \n" + 199 " the executing host. \n" + 200 "\n" + 201 "/ignoreCase[:<true>|<false>] \n" + 202 " Disable case sensitivity of packages and profiles. Therefore you can \n" + 203 " assign the package 'myapp' to a profile while only a package 'MyApp' is \n" + 204 " defined within the packages. \n" + 205 " Note that this change requires modification of the package/profile/host nodes \n" + 206 " read from the XML files. All IDs are converted to lowercase. \n" + 207 " Note: This requires converting all profile/package IDs to lowercase. \n" + 208 " Therefore you will only see lowercase entries within the log files \n" + 209 " and also within the local package database. \n" + 210 "\n" + 211 "/logAppend[:<true>|<false>] \n" + 212 " Append log files instead of overwriting existing files. \n" + 213 " NOTE: You can specify a log file pattern which will create a new file on \n" + 214 " each run. Appending logs might cause problems with some log rotation \n" + 215 " scripts as well. So use it with caution. \n" + 216 "\n" + 217 "/logfilePattern:<pattern> \n" + 218 " Pattern for log file naming: \n" + 219 " Recognized patterns: \n" + 220 " [HOSTNAME] Replaced by the executing hostname. \n" + 221 " [PROFILE] Replaced by the concatenated list of profiles applied. \n" + 222 " [YYYY] Replaced by year (4 digits). \n" + 223 " [MM] Replaced by month number (2 digits). \n" + 224 " [DD] Replaced by the day of the month (2 digits). \n" + 225 " [hh] Replaced by hour of the day (24h format, 2 digits). \n" + 226 " [mm] Replaced by minutes (2 digits). \n" + 227 " [ss] Replaced by seconds (2 digits). \n" + 228 "\n" + 229 " Examples: \n" + 230 " 'wpkg-[YYYY]-[MM]-[DD]-[HOSTNAME].log' \n" + 231 " results in a name like 'wpkg-2007-11-04-myhost.log' \n" + 232 " NOTE: Using [PROFILE] causes all log messages before reading profiles.xml \n" + 233 " to be temporarily written to local %TEMP% folder. So they might \n" + 234 " appear on the final log file with some delay. \n" + 235 "\n" + 236 "/logLevel:[0-31] \n" + 237 " Level of detail for log file: \n" + 238 " use the following values: \n" + 239 " Log level is defined as a bitmask. Just sum up the values of each log \n" + 240 " severity you would like to include within the log file and add this value \n" + 241 " to your config.xml or specify it at /logLevel:<#>. \n" + 242 " Specify 0 to disable logging. \n" + 243 " 1: log errors only \n" + 244 " 2: log warnings \n" + 245 " 4: log information \n" + 246 " 8: log audit success \n" + 247 " 16: log audit failure \n" + 248 " Example: \n" + 249 " 31 log everything (1+2+4+8+16=31) \n" + 250 " 13 log errors, information and audit success (1+4+8=13) \n" + 251 " 3 log errors and warnings only (1+2=3) \n" + 252 " Default is 0 which will suppress all messages printed before log level is \n" + 253 " properly initialized by config.xml or by /logLevel:<#> parameter. \n" + 254 "\n" + 255 "/log_file_path:<path> \n" + 256 " Path where the log files will be stored. Also allows specifying an UNC \n" + 257 " path (e.g. '\\server\share\directory'). Make sure the path exists and \n" + 258 " that the executing user has write access to it. \n" + 259 " NOTE: If you set this parameter within config.xml please note that you \n" + 260 " need to escape backslashes: \n" + 261 " e.g. '\\\\server\\share\\directory'. \n" + 262 "\n" + 263 "/noforcedremove[:<true>|<false>] \n" + 264 " Do not remove packages from local package database if remove fails even \n" + 265 " if the package does not exist in the package database on the server and \n" + 266 " is not referenced within the profile. \n" + 267 " By default packages which have been removed from the server package \n" + 268 " database and the profile will be uninstalled and then removed \n" + 269 " from the local package database even if uninstall failed. \n" + 270 " This has been introduced to prevent a package whose uninstall script \n" + 271 " fails to repeat its uninstall procedure on each execution without the \n" + 272 " possibility to fix the problem since the package (including its \n" + 273 " uninstall string) is available on the local machine only. \n" + 274 " HINT: If you like the package to stay in the local database (including \n" + 275 " uninstall-retry on next boot) just remove it from the profile but do not \n" + 276 " completely delete it from the package database. \n" + 277 "\n" + 278 "/noremove[:<true>|<false>] \n" + 279 " Disable the removal of all packages. If used in conjunction with the \n" + 280 " /synchronize parameter it will just add packages but never remove them. \n" + 281 " Instead of removing a list of packages which would have been removed \n" + 282 " during that session is printed on exit. Packages are not removed from \n" + 283 " the local settings database (wpkg.xml). Therefore it will contain a list \n" + 284 " of all packages ever installed. \n" + 285 " Note that each package which is requested to be removed (manually or by \n" + 286 " a synchronization request) will be checked for its state by executing its \n" + 287 " included package checks. If the package has been removed manually it will \n" + 288 " also be removed from the settings database. This does not apply to packages \n" + 289 " which do not specify any checks. Such packages will remain in the local \n" + 290 " settings database even if the software has been removed manually. \n" + 291 "\n" + 292 "/noDownload[:<true>|<false>] \n" + 293 " Ignore all download nodes in packages. \n" + 294 " Useful for testing and in case your download targets already exist. \n" + 295 "\n" + 296 "/norunningstate[:<true>|<false>] \n" + 297 " Do not export the running state to the registry. \n" + 298 "\n" + 299 "/rebootcmd:<option> \n" + 300 " Use the specified boot command, either with full path or \n" + 301 " relative to the location of wpkg.js \n" + 302 " Specifying 'special' as option uses tools\psshutdown.exe \n" + 303 " from www.sysinternals.com - if it exists - and a notification loop \n"; 304 305 alert(message); 306 } 307 308 /******************************************************************************* 309 * 310 * Global variables 311 * 312 ******************************************************************************/ 313 /** base where to find the XML input files */ 314 var wpkg_base = ""; 315 316 /** forces to check for package existence but ignores wpkg.xml */ 317 var force = false; 318 319 /** force installation */ 320 var forceInstall = false; 321 322 /** 323 * Forced remove of non-existing packages from wpkg.xml even if uninstall 324 * command fails. 325 */ 326 var noForcedRemove = false; 327 328 /** defined if script should quit on error */ 329 var quitonerror = false; 330 331 /** Debug output. */ 332 var debug = false; 333 334 /** Dry run */ 335 var dryrun = false; 336 337 /** notify user using net send? */ 338 var nonotify = false; 339 340 /** timeout for user notifications. Works only if msg.exe is available */ 341 var notificationDisplayTime = 10; 342 343 /** set to true to prevent reboot */ 344 var noreboot = false; 345 346 /** stores if package removal should be skipped - see /noremove flag */ 347 var noRemove = false; 348 349 /** allows disabling/enabling of running state export to registry */ 350 var noRunningState = false; 351 352 /** type of reboot command */ 353 var rebootCmd = "standard"; 354 355 /** set to true for quiet mode */ 356 var quietDefault = false; 357 358 /** registry path where WPKG stores its running state */ 359 var sRegWPKG_Running = "HKLM\\Software\\WPKG\\running"; 360 361 /** configuration file to hold the settings for the script */ 362 var config_file_name = "config.xml"; 363 364 /** name of package database file */ 365 var packages_file_name = "packages.xml"; 366 /** name of profiles database file */ 367 var profiles_file_name = "profiles.xml"; 368 /** name of hosts definition database file */ 369 var hosts_file_name = "hosts.xml"; 370 371 /** 372 * specify if manually installed packages should be kept during synchronization 373 * true: keep manually installed packages false: remove any manually installed 374 * package which does not belong to the profile 375 */ 376 var keepManual = true; 377 378 /** 379 * path where log-files are stored.<br> 380 * Defaults to "%TEMP%" if empty. 381 */ 382 var log_file_path = "%TEMP%"; 383 384 /** path where downloads are stored, defaults to %TEMP% if not defined */ 385 var downloadDir = "%TEMP%"; 386 387 /** timeout for downloads */ 388 var downloadTimeout = 7200; 389 390 /** if set to true logfiles will be appended, otherwise they are overwritten */ 391 var logAppend = false; 392 393 /** 394 * set to true to enable sending of status messages to STDOUT, regardless of the 395 * status of /debug 396 */ 397 var sendStatus = false; 398 399 /** 400 * Set to true to disable upgrade-before-remove feature by default 401 */ 402 var noUpgradeBeforeRemove = false; 403 404 /** 405 * use the following values: Log level is defined as a bitmask. Just add sum up 406 * the values of each log severity you would like to include within the log file 407 * and add this value to your config.xml or specify it at /logLevel:<num>. 408 * 409 * Specify 0 to disable logging. 410 * 411 * <pre> 412 * 1: log errors only 413 * 2 : log warnings 414 * 4 : log information 415 * 8 : log audit success 416 * </pre> 417 * 418 * Example: 419 * 420 * <pre> 421 * 31 log everything (1+2+4+8+16=32) 422 * 13 logs errors, information and audit success (1+4+8=13) 423 * 3 logs errors and warnings only (1+2=3) 424 * </pre> 425 * 426 * Default is 0 which will suppress all messages printed before log level is 427 * properly initialized by config.xml or by /logLevel:<#> parameter. 428 */ 429 var logLevelDefault = 0xFF; 430 431 /** 432 * var logfile pattern Recognized patterns: 433 * 434 * <pre> 435 * [HOSTNAME] replaced by the executing hostname 436 * [PROFILE] replaced by the name 437 * [YYYY] replaced by year (4 digits) 438 * [MM] replaced by month number (2 digits) 439 * [DD] replaced by the day of the month (2 digits) 440 * [HH] replaced by hour of the day (24h format, 2 digits) 441 * [mm] replaced by minute (2 digits) 442 * </pre> 443 * 444 * Examples: 445 * 446 * <pre> 447 * wpkg-[YYYY]-[MM]-[DD]-[HOSTNAME].log 448 * </pre> 449 * 450 * results in a name like "wpkg-2007-11-04-myhost.log" 451 */ 452 var logfilePattern = "wpkg-[HOSTNAME].log"; 453 454 /** web file name of package database if base is an http url */ 455 var web_packages_file_name = "packages_xml_out.php"; 456 /** web file name of profile database if base is an http url */ 457 var web_profiles_file_name = "profiles_xml_out.php"; 458 /** web file name of hosts database if base is an http url */ 459 var web_hosts_file_name = "hosts_xml_out.php"; 460 461 /** name of local settings file */ 462 var settings_file_name = "wpkg.xml"; 463 464 /** path to settings file, defaults to system folder if set to null */ 465 var settings_file_path = null; 466 467 /** defines if package/profile IDs are handled case sensitively */ 468 var caseSensitivity = true; 469 470 /** set to true to want to apply profiles of all matching host nodes */ 471 var applyMultiple = false; 472 473 /** globally disable any downloads */ 474 var noDownload = false; 475 476 /** 477 * Allows to disable insert of host attributes to local settings DB. This is 478 * handy for testing as the current test-suite compares the local wpkg.xml 479 * database and the file will look different on all test machines if these 480 * attribute are present. This setting might be removed if all test-cases 481 * are adapted. 482 */ 483 var settingsHostInfo = true; 484 485 /** 486 * Query mode. In order to "simulate" the result of a query on a system on 487 * anohter (remote-) system you can set queryMode to "remote". This will 488 * disable package checks because they will not return the same results on a 489 * remote system. 490 */ 491 var queryMode = "local"; 492 493 /** 494 * XML namespaces. 495 */ 496 var namespaceSettings = "http://www.wpkg.org/settings"; 497 var namespaceHosts = "http://www.wpkg.org/hosts"; 498 var namespaceProfiles = "http://www.wpkg.org/profiles"; 499 var namespacePackages = "http://www.wpkg.org/packages"; 500 var namespaceConfig = "http://www.wpkg.org/config"; 501 502 /******************************************************************************* 503 * 504 * Caching variables - used by internal functions. 505 * 506 ******************************************************************************/ 507 508 /** file to which the log is written to */ 509 var logfileHandler = null; 510 511 /** path to the log file (corresponds to logfileHandler) */ 512 var logfilePath = null; 513 514 /** store whether log file was opened in append mode */ 515 var logfileAppendMode = logAppend; 516 517 /** stores if the user was notified about start of actions */ 518 var was_notified = false; 519 520 /** 521 * holds a list of packages which have been installed during this execution this 522 * is used to prevent dependency packages without checks and always execution to 523 * be executed several times as well as preventing infinite- loops on recursive 524 * package installation. 525 */ 526 var packagesInstalled = new Array(); 527 528 /** 529 * holds a list of packages which have been removed during this execution This 530 * is used to prevent removing packages multiple times during a session because 531 * they are referenced as dependencies by multiple other packages. 532 */ 533 var packagesRemoved = new Array(); 534 535 /** host properties used within script */ 536 var hostName = null; 537 var hostOs = null; 538 var domainName = null; 539 var ipAddresses = null; 540 var hostGroups = null; 541 var hostArchitecture = null; 542 var hostAttributes = null; 543 544 /** String representing path where the settings are stored */ 545 var settings_file = null; 546 547 /** Flag whether settings path was processed to replace parameters */ 548 var settings_file_processed = false; 549 550 /** declare variables for data storage */ 551 var packages = null; 552 var profiles = null; 553 var hosts = null; 554 var settings = null; 555 var config = null; 556 557 /** List of profiles assigned directly to current host */ 558 var applyingProfilesDirect = null; 559 560 /** profiles applying to the current host (including dependencies) */ 561 var applyingProfilesAll = null; 562 563 /** caches the host node entries applying to the current host */ 564 var applyingHostNodes = null; 565 566 /** Packages belonging to current host (package nodes) */ 567 var profilePackageNodes = null; 568 569 /** stores the locale ID (LCID) which applies for the local executing user */ 570 var LCID = null; 571 572 /** stores the locale ID (LCID) which applies for the local machine */ 573 var LCIDOS = null; 574 575 /** caches the language node applying to the current system locale */ 576 var languageNode = null; 577 578 /** declare log level variable */ 579 var logLevel = null; 580 581 /** actual value for log level */ 582 var logLevelValue = 0x03; 583 584 /** buffer to store log entries while no real log file is available */ 585 var logBuffer = null; 586 587 /** flag which is true if log is ready to be initialize */ 588 var logInitReady = false; 589 590 /** flag which is set to true internally during log initialization */ 591 var logInitializing = false; 592 593 /** declare quiet mode variable */ 594 var quiet = null; 595 596 /** current value of quiet operation flag */ 597 var quietMode = quietDefault; 598 599 /** stores if a postponed reboot is scheduled */ 600 var postponedReboot = false; 601 602 /** set to true when a reboot is in progress */ 603 var rebooting = false; 604 605 /** set to true to skip write attempts to event log */ 606 var skipEventLog = false; 607 608 /** set to true to log event log entries to STDOUT (fallback mode) */ 609 var eventLogFallback = false; 610 611 /** holds an array of packages which were not removed due to the /noremove flag */ 612 var skippedRemoveNodes = null; 613 614 /** 615 * holds status of change: true: System has been changed (package 616 * installed/removed/updated/downgraded... false: System has not been touched 617 * (yet) 618 */ 619 var systemChanged = false; 620 621 /** 622 * Holds a list of packages which have been manually installed. 623 */ 624 var manuallyInstalled = null; 625 626 /** 627 * Marks volatile releases and "inverts" the algorithm that a longer version 628 * number is newer. For example 1.0RC2 would be newer than 1.0 because it 629 * appends characters to the version. Using "rc" as a volatile release marker 630 * the algorithm ignores the appendix and assumes that the string which omits 631 * the marker is newer. 632 * 633 * Resulting in 1.0 to be treated as newer than 1.0RC2. 634 * 635 * NOTE: The strings are compared as lower-case. So use lower-case definitions 636 * only! 637 */ 638 var volatileReleaseMarkers = ["rc", "i", "m", "alpha", "beta", "pre", "prerelease"]; 639 640 /** stores if system is running on a 64-bit OS */ 641 var x64 = null; 642 643 /** Stores variables assigned to host definitions applying to current host */ 644 var hostsVariables = null; 645 646 /** Stores variables from profiles assigned to current hsot */ 647 var profilesVariables = null; 648 649 /*********************************************************************************************************************** 650 * 651 * Program execution 652 * 653 **********************************************************************************************************************/ 654 655 /** 656 * Call the main function with arguments while catching all errors and 657 * forwarding them to the error output. 658 */ 659 try { 660 main(); 661 } catch (e) { 662 // Log error message. 663 error("Message: " + e.message + "\n" + 664 "Description: " + e.description + "\n" + 665 "Error number: " + hex(e.number) + "\n" + 666 "Stack: " + e.stack + "\n" + 667 "Line: " + e.lineNumber + "\n" 668 ); 669 notifyUserFail(); 670 // Make sure log is initialized to write the output. 671 if (logBuffer != null) { 672 initializeLog(); 673 } 674 exit(2); 675 } 676 677 /** 678 * Main execution method. Actually runs the script 679 */ 680 function main() { 681 // Do not open pop-up window while initializing. 682 setQuiet(true); 683 684 // PATCH SE3 : oubli dans le script officiel. 685 if (!isNoRunningState()) { 686 setRunningState("true"); 687 } 688 // FIN PATCH SE3. 689 690 // Initialize WPKG internals. 691 initialize(); 692 693 // Process command line arguments to determine course of action. 694 // Get special purpose argument lists. 695 var argv = getArgv(); 696 var argn = argv.Named; 697 // var argu = argv.Unnamed; 698 if (argn.Item("query") != null) { 699 // Do not log to event log during query. 700 var eventLogStatus = isSkipEventLog(); 701 setSkipEventLog(true); 702 703 if (getQueryMode() == "remote") { 704 getSettingHostAttributes(); 705 } 706 707 // Now all parameters are set to build the final log file name 708 // even if [PROFILE] is used. 709 // WScript.Echo("Initializing Log"); 710 // WScript.Echo("Buffer: " + logBuffer); 711 // Flag log file ready for initialization. 712 logInitReady = true; 713 714 // Do not log to log file during query. 715 var logValue = getLogLevel(); 716 // setLogLevel(0); 717 718 // Read query argument characters. 719 var queryRequest = argn.Item("query").split(""); 720 721 // Supported arguments: 722 // a Query all packages. 723 // x List packages which are not installed but in package database. 724 // i List all packages which are currently installed. 725 // I List packages which are about to be installed during synchronization. 726 // u List packages which are about to be upgraded during synchronization. 727 // d List packages which are about to be downgraded during synchronization. 728 // r List packages which are about to be removed during synchronization. 729 // m List all modifications which would apply during synchronization (equal to Iudr) 730 // n List packages which belong to the profile but are not modified during synchronization. 731 // s List host attributes from settings (wpkg.xml). 732 // l List host attributes read from local host. 733 var listSyncInstall = false; 734 var listSyncUpgrade = false; 735 var listSyncDowngrade = false; 736 var listSyncRemove = false; 737 var listSyncUnmodified = false; 738 for (var i=0; i<queryRequest.length; i++) { 739 var requestCharacter = queryRequest[i]; 740 switch (requestCharacter) { 741 case "a": 742 queryAllPackages(); 743 break; 744 745 case "x": 746 queryUninstalledPackages(); 747 break; 748 749 case "i": 750 queryInstalledPackages(); 751 break; 752 753 case "I": 754 listSyncInstall = true; 755 break; 756 757 case "u": 758 listSyncUpgrade = true; 759 break; 760 761 case "d": 762 listSyncDowngrade = true; 763 break; 764 765 case "r": 766 listSyncRemove = true; 767 break; 768 769 case "n": 770 listSyncUnmodified = true; 771 break; 772 773 case "m": 774 listSyncInstall = true; 775 listSyncUpgrade = true; 776 listSyncDowngrade = true; 777 listSyncRemove = true; 778 break; 779 780 case "s": 781 queryHostInformationFromSettings(); 782 break; 783 784 case "l": 785 queryHostInformation(); 786 break; 787 788 default: 789 break; 790 } 791 } 792 // Print requested output. 793 if (listSyncInstall || listSyncUpgrade || listSyncDowngrade || listSyncRemove || listSyncUnmodified) { 794 queryProfilePackages(listSyncInstall, listSyncUpgrade, listSyncDowngrade, listSyncRemove, listSyncUnmodified); 795 } 796 797 setSkipEventLog(eventLogStatus); 798 setLogLevel(logValue); 799 } else { 800 801 // set profile-based log level (if available) 802 var profileLogLevel = getProfilesLogLevel(); 803 if (profileLogLevel != null) { 804 setLogLevel(profileLogLevel); 805 } 806 807 // Now all parameters are set to build the final log file name 808 // even if [PROFILE] is used. 809 // WScript.Echo("Initializing Log"); 810 // WScript.Echo("Buffer: " + logBuffer); 811 // Flag log file ready for initialization. 812 logInitReady = true; 813 814 var message; 815 if(isDebug()) { 816 var hst = getHostNodes(); 817 message = "Hosts file contains " + hst.length + " hosts:"; 818 for (var iHost = 0; iHost < hst.length; iHost++ ) { 819 message += "\n'" + getHostNodeDescription(hst[iHost]) + "'"; 820 } 821 dinfo(message); 822 823 var settingsPkg = getSettingNodes(); 824 message = "Settings file contains " + settingsPkg.length + " packages:"; 825 for (var iSettings = 0; iSettings < settingsPkg.length; iSettings++) { 826 if (settingsPkg[iSettings] != null) { 827 message += "\n'" + getPackageID(settingsPkg[iSettings]) + "'"; 828 } 829 } 830 dinfo(message); 831 832 var packageNodes = getPackageNodes(); 833 message = "Packages file contains " + packageNodes.length + " packages:"; 834 for (var iPackage = 0; iPackage < packageNodes.length; iPackage++) { 835 if (packageNodes[iPackage] != null) { 836 message += "\n'" + getPackageID(packageNodes[iPackage]) + "'"; 837 } 838 } 839 dinfo(message); 840 841 var profileNodes = getProfileNodes(); 842 message = "Profile file contains " + profileNodes.length + " profiles:"; 843 for (var iProfile = 0; iProfile < profileNodes.length; iProfile++) { 844 if (profileNodes[iProfile] != null) { 845 message += "\n'" + getProfileID(profileNodes[iProfile]) + "'"; 846 } 847 } 848 dinfo(message); 849 850 // Get list of profiles directly assigned to host. 851 var profiles = getProfileList(); 852 message = "Using profile(s): "; 853 for (var i=0; i < profiles.length; i++) { 854 message += "\n'" + profiles[i] + "'"; 855 } 856 dinfo(message); 857 } 858 859 // Check if all referenced profiles are available. 860 var profileList = getProfileList(); 861 var error = false; 862 message = "Could not locate referenced profile(s):\n"; 863 for (var iProf = 0; iProf < profileList.length; iProf++) { 864 var currentProfile = getProfileNode(profileList[iProf]); 865 if (currentProfile == null) { 866 error = true; 867 message += profileList[iProf] + "\n"; 868 } 869 } 870 if (error) { 871 throw new Error(message); 872 } 873 874 if (argn.Item("show") != null) { 875 var requestedPackageName = argn.Item("show"); 876 // if case sensitive mode is disabled convert package name to lower case 877 if (!isCaseSensitive()) { 878 requestedPackageName = requestedPackageName.toLowerCase(); 879 } 880 var message = queryPackage(getPackageNodeFromAnywhere(requestedPackageName), null); 881 info(message); 882 } else if (argn.Item("install") != null) { 883 var packageList = argn.Item("install").split(","); 884 for (var iPackage=0; iPackage < packageList.length; iPackage++) { 885 installPackageName(packageList[iPackage], true); 886 } 887 } else if (argn.Item("remove") != null) { 888 var packageList = argn.Item("remove").split(","); 889 for (var iPackage=0; iPackage < packageList.length; iPackage++) { 890 removePackageName(packageList[iPackage]); 891 } 892 } else if (argn.Item("upgrade") != null) { 893 var packageList = argn.Item("upgrade").split(","); 894 for (var iPackage=0; iPackage < packageList.length; iPackage++) { 895 installPackageName(packageList[iPackage], false); 896 } 897 } else if (isArgSet(argv, "/synchronize")) { 898 synchronizeProfile(); 899 } else { 900 showUsage(); 901 throw new Error("No action specified."); 902 } 903 } 904 exit(0); 905 } 906 907 908 /** 909 * Adds a sub-node for the given XML node entry. 910 * 911 * @param XMLNode 912 * the XML node to add to (e.g. packages or settings) 913 * @param subNode 914 * the node to be added to the XMLNode (for example a package node) 915 * NOTE: The node will be cloned before add 916 * @return Returns true in case of success, returns false if no node could be 917 * added 918 */ 919 function addNode(XMLNode, subNode) { 920 var returnvalue = false; 921 var result = XMLNode.appendChild(subNode.cloneNode(true)); 922 if(result != null) { 923 returnvalue = true; 924 } 925 return returnvalue; 926 } 927 928 929 /** 930 * Adds a package node to the settings XML node. In case a package with the same 931 * ID already exists it will be replaced. 932 * 933 * @param packageNode 934 * the package XML node to add. 935 * @param saveImmediately 936 * Set to true in order to save settings immediately after adding. 937 * Settings will not be saved immediately if value is false. 938 * @return true in case of success, otherwise returns false 939 */ 940 function addSettingsNode(packageNode, saveImmediately) { 941 // first remove entry if one already exists 942 943 // get current settings node 944 var packageID = getPackageID(packageNode); 945 var settingsNode = getSettingNode(packageID); 946 947 if (settingsNode != null) { 948 dinfo("Removing currently existing settings node first: '" + 949 getPackageName(settingsNode) + "' (" + getPackageID(settingsNode) + 950 "), Revision " + getPackageRevision(settingsNode) + "."); 951 removeSettingsNode(settingsNode, false); 952 } 953 954 dinfo("Adding settings node: '" + 955 getPackageName(packageNode) + "' (" + getPackageID(packageNode) + 956 "), Revision " + getPackageRevision(packageNode) + "."); 957 958 var success = addNode(getSettings(), packageNode); 959 // save settings if remove was successful 960 if (success && saveImmediately) { 961 saveSettings(true); 962 } 963 return success; 964 } 965 966 /** 967 * Adds a package node to the list of skipped packages during removal process. 968 * 969 * @param packageNode 970 * the node which has been skipped during removal 971 */ 972 function addSkippedRemoveNodes(packageNode) { 973 var skippedNodes = getSkippedRemoveNodes(); 974 skippedNodes.push(packageNode); 975 } 976 977 /** 978 * Appends dependent profile nodes of the specified profile to the specified 979 * array. Recurses into self to get an entire dependency tree. 980 */ 981 function appendProfileDependencies(profileArray, profileNode) { 982 var profileNodes = getProfileDependencies(profileNode); 983 984 // add nodes if they are not yet part of the array 985 for (var i=0; i < profileNodes.length; i++) { 986 var currentNode = profileNodes[i]; 987 if(!searchArray(profileArray, currentNode)) { 988 dinfo("Adding profile dependencies of profile '" + 989 getProfileID(profileNode) + "': '" + 990 getProfileID(currentNode) + "'"); 991 profileArray.push(currentNode); 992 993 // add dependencies of these profiles as well 994 appendProfileDependencies(profileArray, currentNode); 995 } else { 996 dinfo("Profile '" + 997 getProfileID(currentNode) + "' " + 998 "already exists in profile dependency tree. Skipping."); 999 } 1000 } 1001 } 1002 1003 /** 1004 * Evaluates all checks in the check nodes array and returns its result. 1005 * @param checkNodes Array of XML <check /> nodes to be evaluated. 1006 * @returns {Boolean} true if all checks are true. False if at least one failed. 1007 */ 1008 function checkAll(checkNodes) { 1009 if (checkNodes == null) { 1010 return true; 1011 } 1012 1013 // Initialize return value. 1014 var result = true; 1015 1016 // Save environment. 1017 var previousEnv = getEnv(); 1018 1019 // Loop over every condition check. 1020 // If all are successful, we consider package as installed. 1021 for (var i = 0; i < checkNodes.length; i++) { 1022 try { 1023 if (!checkCondition(checkNodes[i])) { 1024 result = false; 1025 break; 1026 } 1027 } catch (err) { 1028 message = "Error evaluating check: " + err.description; 1029 if (isQuitOnError()) { 1030 throw new Error(message); 1031 } else { 1032 error(message); 1033 result = false; 1034 break; 1035 } 1036 } finally { 1037 // Restore environment. 1038 loadEnv(previousEnv); 1039 } 1040 } 1041 return result; 1042 } 1043 1044 /** 1045 * Checks for the success of a check condition for a package. 1046 * 1047 * @param checkNode 1048 * XML check node to be evaluated 1049 * @throws Error 1050 * Throws error in case of invalid XML node definition 1051 */ 1052 function checkCondition(checkNode) { 1053 var shell = new ActiveXObject("WScript.Shell"); 1054 1055 // get attributes of check 1056 var checkType = checkNode.getAttribute("type"); 1057 var checkCond = checkNode.getAttribute("condition"); 1058 var checkPath = checkNode.getAttribute("path"); 1059 var checkValue = checkNode.getAttribute("value"); 1060 1061 // In remote mode try to verify the check using cached check results in 1062 // settings database. 1063 if (getQueryMode() == "remote") { 1064 // Logical checks shall be evaluated as usual. 1065 // Only look for previous check results for other types of checks. 1066 if (checkType != "logical") { 1067 var result = getSettingsCheckResult(checkNode); 1068 if (result == null) { 1069 error("Result of check of type '" + checkType + "' with condition '" + 1070 checkCond + "', path '" + checkPath + "' and value '" + 1071 checkValue + "' is missing in settings database. " + 1072 "Trying to evaluate locally. Results might be inaccurate"); 1073 } else { 1074 return result; 1075 } 1076 } 1077 } 1078 1079 // Sanity check: must have Type set here. 1080 if (checkType == null) { 1081 throw new Error("Check Type is null - this is not permitted. Perhaps a typo? " + 1082 "To help find it, here are the other pieces of information: " + 1083 "condition='" + checkCond + "', path='" + checkPath + 1084 "', value='" + checkValue + "'."); 1085 } 1086 1087 // Initialize return value; 1088 var returnValue = false; 1089 1090 // get expanded values for path and value used by some checks 1091 var checkPathExpanded = null; 1092 if (checkPath != null) { 1093 checkPathExpanded = shell.ExpandEnvironmentStrings(checkPath); 1094 } 1095 var checkValueExpanded = null; 1096 if (checkValue != null) { 1097 checkValueExpanded = shell.ExpandEnvironmentStrings(checkValue); 1098 } 1099 1100 switch(checkType) { 1101 // check type: registry 1102 case "registry": 1103 // Sanity check: must have Cond and Path set for all registry checks. 1104 if ((checkCond == null) || (checkPath == null)) { 1105 throw new Error("Condition and / or path is null for a registry check. Perhaps " + 1106 "a typo? To help find it, here are the other pieces of information: " + 1107 "condition='" + checkCond + "', path='" + checkPath + 1108 "', value='" + checkValue + "'."); 1109 } 1110 1111 // branch on check condition 1112 switch (checkCond) { 1113 case "exists": 1114 if (getRegistryValue(checkPath) != null) { 1115 // Some debugging information. 1116 dinfo("The registry path '" + checkPath + "' exists: the check was successful."); 1117 returnValue = true; 1118 } else if (getRegistryValue(checkPathExpanded) != null) { 1119 dinfo("The expanded registry path '" + checkPathExpanded + "' exists: the check was successful."); 1120 returnValue = true; 1121 } else { 1122 // path does not exist 1123 dinfo("Neither the registry path '" + checkPath + "' nor its expanded value of '" + 1124 checkPathExpanded + "' exist: the check failed."); 1125 returnValue = false; 1126 } 1127 break; 1128 1129 case "equals": 1130 // read registry value and convert it to string in order to compare 1131 // to supplied 1132 // string within the 'value' attribute 1133 var readValue = getRegistryValue(checkPath); 1134 1135 // check if value is eventually null (non-existing) 1136 if (readValue == null) { 1137 // the path might have to be expanded 1138 readValue = getRegistryValue(checkPathExpanded); 1139 if (readValue == null) { 1140 dinfo("The registry path '" + checkPath + "' did not exist. Check failed."); 1141 returnValue = false; 1142 break; 1143 } 1144 dinfo("The expanded registry path '" + checkPathExpanded + "' could be read."); 1145 } else { 1146 dinfo("The registry path '" + checkPath+ "' could be read."); 1147 } 1148 1149 // try treating the value as array 1150 var registyValue = ""; 1151 try { 1152 var readArray = readValue.toArray(); 1153 dinfo("The registry value received is an array, concatenating values for comparison."); 1154 for (var iRegKey=0; iRegKey<readArray.length; iRegKey++) { 1155 registyValue = registyValue + readArray[iRegKey] + ""; 1156 if ( (iRegKey+1) < readArray.length) { 1157 registyValue += "\n"; 1158 } 1159 } 1160 } catch(notAnArray) { 1161 dinfo("The registry value received is a scalar value."); 1162 registyValue = readValue + ""; 1163 } 1164 1165 if (registyValue == checkValue) { 1166 // Some debugging information. 1167 dinfo("The registry path '" + checkPath + "' contained the correct value: '" + 1168 checkValue + "'. The check was successful."); 1169 returnValue = true; 1170 } else { 1171 // Try if expanded value matches (case-insensitive). 1172 if (registyValue.toLowerCase() == checkValueExpanded.toLowerCase()) { 1173 dinfo("The registry path '" + checkPath + "' contained the expanded value: '" + 1174 checkValueExpanded + "'. The check was successful."); 1175 returnValue = true; 1176 } else { 1177 dinfo("The registry path '" + checkPath + "' did not contain the value: '" + 1178 checkValue + "'. Instead it contained '" + registyValue + "'. the check failed."); 1179 returnValue = false; 1180 } 1181 } 1182 break; 1183 1184 default: 1185 throw new Error("Check condition " + checkCond + " unknown " + 1186 "for type registry."); 1187 break; 1188 } 1189 1190 // The result of Registry checks shall be stored in local settings node. 1191 addSettingsCheckResult(checkNode, returnValue); 1192 1193 break; 1194 1195 // check type: file 1196 case "file": 1197 // Sanity check: must have Cond and Path set for all file checks. 1198 if ((checkCond == null) || 1199 (checkPath == null)) { 1200 throw new Error("Condition and / or path is null for a file check. Perhaps " + 1201 "a typo? To help find it, here are the other pieces of information: " + 1202 "condition='" + checkCond + "', path='" + checkPath + 1203 "', value='" + checkValue + "'"); 1204 } 1205 1206 // expand environment variables 1207 // use only expanded value here 1208 checkPath = checkPathExpanded; 1209 1210 if (checkCond == "exists") { 1211 var fso = new ActiveXObject("Scripting.FileSystemObject"); 1212 if (fso.FileExists(checkPath)) { 1213 // Some debugging information. 1214 dinfo("The path '" + checkPath + "' exists and is a file: the test was successful."); 1215 returnValue = true; 1216 } else if (fso.FolderExists(checkPath)) { 1217 // Some debugging information. 1218 dinfo("The path '" + checkPath + "' exists and is a folder: the test was successful."); 1219 returnValue = true; 1220 } else { 1221 // Some debugging information. 1222 dinfo("The path '" + checkPath + "' does not exist: the test failed."); 1223 returnValue = false; 1224 } 1225 1226 } else if (checkCond == "sizeequals") { 1227 // sanity check: must have Value set for a size check. 1228 if (checkValue == null) { 1229 throw new Error("Value is null for a file sizeequals check. Perhaps " + 1230 "a typo? To help find it, here are the other pieces of information: " + 1231 "condition='" + checkCond + 1232 "', path='" + checkPath + 1233 "', value='" + checkValue + "'."); 1234 } 1235 1236 var filesize = getFileSize(checkPath); 1237 if (filesize == checkValueExpanded) { 1238 dinfo("The file '" + checkPath + "' has size " + filesize + ": the test was successful."); 1239 returnValue = true; 1240 } else { 1241 dinfo("The file '" + checkPath + "' has size " + filesize + " - wanted " + 1242 checkValueExpanded + ": the test fails."); 1243 returnValue = false; 1244 } 1245 } else if (checkCond.substring(0,7) == "version") { 1246 // Sanity check: Must have a value set for version check. 1247 if (checkValue == null) { 1248 throw new Error("Value is null for a file version check. Perhaps " + 1249 "a type? To help find it, here are the other pieces of information: " + 1250 "condition='" + checkCond + "', path='" + checkPath + 1251 "', value='" + checkValue + "'."); 1252 } // if checkValue == null 1253 1254 var fileVersion = getFileVersion(checkPath); 1255 1256 if (fileVersion == null || fileVersion == "") { 1257 // no file version could be obtained 1258 dinfo("Unable to find the file version for '" + checkPath + "'."); 1259 returnValue = false; 1260 } else { 1261 1262 var fileVersionCompare = versionCompare(fileVersion, checkValueExpanded); 1263 dinfo ("Checking file version " + fileVersion + " is " + checkCond + 1264 " (than) " + checkValueExpanded + " - got result " + fileVersionCompare + "."); 1265 1266 var fileVersionCompResult = false; 1267 switch (checkCond) { 1268 case "versionsmallerthan": 1269 if (fileVersionCompare < 0) { 1270 fileVersionCompResult = true; 1271 } 1272 break; 1273 case "versionlessorequal": 1274 if (fileVersionCompare <= 0) { 1275 fileVersionCompResult = true; 1276 } 1277 break; 1278 case "versionequalto": 1279 if (fileVersionCompare == 0) { 1280 fileVersionCompResult = true; 1281 } 1282 break; 1283 case "versiongreaterorequal": 1284 if (fileVersionCompare >= 0) { 1285 fileVersionCompResult = true; 1286 } 1287 break; 1288 case "versiongreaterthan": 1289 if (fileVersionCompare > 0) { 1290 fileVersionCompResult = true; 1291 } 1292 break; 1293 default: 1294 error("Unknown operation on file versions : " + checkCond); 1295 fileVersionCompResult = false; 1296 break; 1297 } 1298 1299 dinfo("File version check for file '" + checkPath + "' returned " + 1300 fileVersionCompResult + " for operation type " + checkCond + "."); 1301 returnValue = fileVersionCompResult; 1302 } 1303 1304 } else if (checkCond.substring(0,4) == "date") { 1305 var fileDate = null; 1306 var comparisonDesc = ""; 1307 var dateType = checkCond.substring(4,10); 1308 // Evaluate if modification date shall be checked. 1309 if (dateType == "modify") { 1310 dinfo("Checking file modification date."); 1311 // Evaluate file modification date. 1312 fileDate = getFileDateModification(checkPath); 1313 comparisonDesc = "Modification"; 1314 } else if (dateType == "create") { 1315 dinfo("Checking file creation date."); 1316 // Evaluate file creation date. 1317 fileDate = getFileDateCreation(checkPath); 1318 comparisonDesc = "Creation"; 1319 } else if (dateType == "access") { 1320 dinfo("Checking file access date."); 1321 // Evaluate file access date. 1322 fileDate = getFileDateLastAccess(checkPath); 1323 comparisonDesc = "Access"; 1324 } else { 1325 throw new Error ("Invalid file date comparison type: " + checkCond + "."); 1326 } 1327 1328 // If file date could not be read: Comparison failed. 1329 if (fileDate == null) { 1330 dinfo("File modification date could not be read, check failed."); 1331 returnValue = false; 1332 break; 1333 } 1334 // Make sure file date is in Date() format. 1335 fileDate = new Date(fileDate); 1336 1337 // Parse comparison date. 1338 var firstChar = checkValueExpanded.substring(0,1); 1339 var comparisonDate = null; 1340 var comparisonType = "string"; 1341 if (firstChar == "+" || firstChar == "-") { 1342 // Relative date. Create time offset in minutes. 1343 dinfo("Reading relative comparison date: " + checkValueExpanded + " minutes."); 1344 var timeOffset = parseInt(checkValueExpanded) * 1000 * 60; 1345 var now = new Date(); 1346 comparisonDate = new Date(now.getTime() + timeOffset); 1347 } else if (firstChar == "@" ) { 1348 // Remember type of comparison. 1349 comparisonType = "file"; 1350 // Evaluate date of reference file. 1351 var filePath = checkValueExpanded.substring(1); 1352 if (dateType == "modify") { 1353 dinfo("Reading file modification date of reference file '" + filePath + "'."); 1354 // Evaluate file modification date. 1355 comparisonDate = getFileDateModification(filePath); 1356 } else if (dateType == "create") { 1357 dinfo("Reading file creation date of reference file '" + filePath + "'."); 1358 // Evaluate file creation date. 1359 comparisonDate = getFileDateCreation(filePath); 1360 } else if (dateType == "access") { 1361 dinfo("Reading file access date of reference file '" + filePath + "'."); 1362 // Evaluate file access date. 1363 comparisonDate = getFileDateLastAccess(filePath); 1364 } 1365 // If comparison date could not be read then comparison failed. 1366 if (comparisonDate == null) { 1367 dinfo("File comparison date could not be read, check failed."); 1368 returnValue = false; 1369 break; 1370 } 1371 // Make sure comparison date is in Date() format. 1372 comparisonDate = new Date(comparisonDate); 1373 1374 } else { 1375 dinfo("Reading comparison date: " + checkValueExpanded + "."); 1376 switch (checkValueExpanded) { 1377 case "yesterday": 1378 // Relative date. Create time offset of one day. 1379 var timeOffset = -1000 * 60 * 60 * 24; 1380 var now = new Date(); 1381 comparisonDate = new Date(now.getTime() + timeOffset); 1382 break; 1383 1384 case "last-week": 1385 // Relative date. Create time offset of one week ago. 1386 var timeOffset = -1000 * 60 * 60 * 24 * 7; 1387 var now = new Date(); 1388 comparisonDate = new Date(now.getTime() + timeOffset); 1389 break; 1390 1391 case "last-month": 1392 // Relative date. Create time offset of one month ago. 1393 var timeOffset = -1000 * 60 * 60 * 24 * 30; 1394 var now = new Date(); 1395 comparisonDate = new Date(now.getTime() + timeOffset); 1396 break; 1397 1398 case "last-year": 1399 // Relative date. Create time offset of one year ago. 1400 var timeOffset = -1000 * 60 * 60 * 24 * 365; 1401 var now = new Date(); 1402 comparisonDate = new Date(now.getTime() + timeOffset); 1403 break; 1404 1405 default: 1406 // Date is supposed to be in ISO format. 1407 comparisonDate = parseISODate(checkValueExpanded, false); 1408 break; 1409 } 1410 } 1411 // Check whether comparison date has been evaluated properly. 1412 if (comparisonDate == null) { 1413 throw new Error ("Unable to evaluate date from value '" + checkValueExpanded + "'."); 1414 } 1415 1416 var success = false; 1417 1418 // Get file date of file specified in path. 1419 var comparison = checkCond.substring(10); 1420 1421 var comparisonCond = ""; 1422 switch (comparison) { 1423 case "olderthan": 1424 comparisonCond = "older than"; 1425 if (fileDate.getTime() < comparisonDate.getTime()) { 1426 success = true; 1427 } else { 1428 success = false; 1429 } 1430 break; 1431 1432 case "equalto": 1433 var fileDateCompare = new Date(fileDate); 1434 // Reduce accuracy to milliseconds for equal comparison when comparing to user string. 1435 if (comparisonType != "file") { 1436 fileDateCompare.setMilliseconds(0); 1437 comparisonDate.setMilliseconds(0); 1438 } 1439 comparisonCond = "equal to"; 1440 if (fileDateCompare.getTime() == comparisonDate.getTime()) { 1441 success = true; 1442 } else { 1443 success = false; 1444 } 1445 break; 1446 1447 case "newerthan": 1448 comparisonCond = "newer than"; 1449 if (fileDate.getTime() > comparisonDate.getTime()) { 1450 success = true; 1451 } else { 1452 success = false; 1453 } 1454 break; 1455 1456 default: 1457 throw new Error ("Invalid file date comparison parameter: '" + checkCond + "'."); 1458 break; 1459 } 1460 1461 if (success) { 1462 dinfo(comparisonDesc + " date of file '" + checkPath + "' is " + fileDate + 1463 " which is " + comparisonCond + " the comparison date " + 1464 comparisonDate + " check succeeded."); 1465 } else { 1466 dinfo(comparisonDesc + " date of file '" + checkPath + "' is " + fileDate + 1467 " which isn't " + comparisonCond + " the comparison date " + 1468 comparisonDate + " check failed."); 1469 } 1470 returnValue = success; 1471 } else { 1472 throw new Error("Check condition " + checkCond + " unknown for " + 1473 "type file."); 1474 } 1475 1476 // The result of Registry checks shall be stored in local settings node. 1477 addSettingsCheckResult(checkNode, returnValue); 1478 1479 break; 1480 1481 // check type: uninstall 1482 case "uninstall": 1483 // Sanity check: must have Cond and Path set for all uninstall checks. 1484 if ((checkCond == null) || 1485 (checkPath == null)) { 1486 throw new Error("Condition and / or path is null for an uninstall check. Perhaps " + 1487 "a typo? To help find it, here are the other pieces of information: " + 1488 "condition='" + checkCond + 1489 "', path='" + checkPath + "'."); 1490 } 1491 var uninstallLocations = scanUninstallKeys(checkPath); 1492 // If expanded path is different to path read these keys too. 1493 if (checkPath != checkPathExpanded) { 1494 var uninstallLocationsExpanded = scanUninstallKeys(checkPathExpanded); 1495 for (var i=0; i < uninstallLocationsExpanded.length; i++) { 1496 uninstallLocations.push(uninstallLocationsExpanded[i]); 1497 } 1498 } 1499 1500 if (checkCond == "exists") { 1501 if (uninstallLocations.length > 0) { 1502 dinfo("Uninstall entry for " + checkPath + " was found: test successful."); 1503 returnValue = true; 1504 } else { 1505 dinfo("Uninstall entry for " + checkPath + " missing: test failed."); 1506 returnValue = false; 1507 } 1508 } else if (checkCond.substring(0,7) == "version") { 1509 // check versions of all installed instances 1510 // for version checks we need a value 1511 if (checkValue == null) { 1512 throw new Error ("Uninstall entry version check has been specified but no" + 1513 "'value' is defined. Please add a 'value=<version>' attribute."); 1514 } 1515 1516 if (uninstallLocations.length <= 0) { 1517 dinfo("No uninstall entry for '" + checkPath + "' found. " + 1518 "Version comparison check failed."); 1519 returnValue = false; 1520 } else { 1521 1522 var uninstallCheckResult = true; 1523 for (var iUninstKey=0; iUninstKey < uninstallLocations.length; iUninstKey++) { 1524 var uninstallValue = getRegistryValue(uninstallLocations[iUninstKey] + "\\DisplayVersion"); 1525 1526 dinfo("Found version of '" + checkPath + "' at " + uninstallLocations[iUninstKey] + 1527 ": " + uninstallValue + "\n" + "Comparing to expected version: " + checkValue + "."); 1528 1529 // check if valid version value was returned 1530 if (uninstallValue == null || uninstallValue == "") { 1531 error("Check condition '" + checkCond + "' cannot be executed" + 1532 " since no version information is available for '" + checkPath + "'" + 1533 " at " + uninstallLocations[iUninstKey] + "."); 1534 uninstallCheckResult = false; 1535 break; 1536 } else { 1537 1538 var uninstallVersionCompare = versionCompare(uninstallValue, checkValueExpanded); 1539 dinfo ("Comparing uninstall version '" + uninstallValue + "' to expected version '" + 1540 checkValueExpanded + "' using condition '" + checkCond + "' returned " + uninstallVersionCompare + "."); 1541 1542 var uninstallVersionCompResult = false; 1543 switch (checkCond) { 1544 case "versionsmallerthan": 1545 if (uninstallVersionCompare < 0) { 1546 uninstallVersionCompResult = true; 1547 } 1548 break; 1549 case "versionlessorequal": 1550 if (uninstallVersionCompare <= 0) { 1551 uninstallVersionCompResult = true; 1552 } 1553 break; 1554 case "versionequalto": 1555 if (uninstallVersionCompare == 0) { 1556 uninstallVersionCompResult = true; 1557 } 1558 break; 1559 case "versiongreaterorequal": 1560 if (uninstallVersionCompare >= 0) { 1561 uninstallVersionCompResult = true; 1562 } 1563 break; 1564 case "versiongreaterthan": 1565 if (uninstallVersionCompare > 0) { 1566 uninstallVersionCompResult = true; 1567 } 1568 break; 1569 default: 1570 error("Unknown operation on uninstall version check: " + checkCond + "."); 1571 uninstallVersionCompResult = false; 1572 break; 1573 } 1574 1575 dinfo("Uninstall version check for package '" + checkPath + "' returned " + 1576 uninstallVersionCompResult + " for operation type " + checkCond + "."); 1577 1578 // in case the current entry does not match the condition, 1579 // immediately return 1580 // else the next uninstall entry might be checked 1581 if (uninstallVersionCompResult == false) { 1582 uninstallCheckResult = false; 1583 break; 1584 } 1585 } 1586 } 1587 // If all checks succeeded, set return value to true. 1588 if (uninstallCheckResult) { 1589 returnValue = true; 1590 } 1591 } 1592 } else { 1593 throw new Error("Check condition " + checkCond + " unknown for " + 1594 "type uninstall."); 1595 } 1596 1597 // The result of Registry checks shall be stored in local settings node. 1598 addSettingsCheckResult(checkNode, returnValue); 1599 1600 break; 1601 1602 // check type: execution 1603 case "execute": 1604 // check if path to script is given 1605 if (checkPath == null) { 1606 throw new Error("No path is specified for execute check!"); 1607 } 1608 if (checkCond == null) { 1609 dinfo("No execute condition specified, assuming 'exitcodeequalto'."); 1610 checkCond = "exitcodeequalto"; 1611 } 1612 if (checkValueExpanded == null || checkValueExpanded == "") { 1613 dinfo("No execute value specified, assuming '0'."); 1614 checkValueExpanded = 0; 1615 } else { 1616 checkValueExpanded = parseInt(checkValueExpanded); 1617 if(isNaN(checkValueExpanded) == true) { 1618 checkValueExpanded = 0; 1619 } 1620 } 1621 1622 // use expanded path only 1623 checkPath = checkPathExpanded; 1624 // execute and catch return code 1625 var exitCode = exec(checkPath, 3600, null); 1626 1627 var executeResult = false; 1628 switch (checkCond) { 1629 case "exitcodesmallerthan": 1630 if (exitCode < checkValueExpanded) { 1631 executeResult = true; 1632 } 1633 break; 1634 case "exitcodelessorequal": 1635 if (exitCode <= checkValueExpanded) { 1636 executeResult = true; 1637 } 1638 break; 1639 case "exitcodeequalto": 1640 if (exitCode == checkValueExpanded) { 1641 executeResult = true; 1642 } 1643 break; 1644 case "exitcodegreaterorequal": 1645 if (exitCode >= checkValueExpanded) { 1646 executeResult = true; 1647 } 1648 break; 1649 case "exitcodegreaterthan": 1650 if (exitCode > checkValueExpanded) { 1651 executeResult = true; 1652 } 1653 break; 1654 default: 1655 dinfo("Invalid execute condition specified '" + checkCond 1656 + "', check failed."); 1657 executeResult = false; 1658 break; 1659 } 1660 1661 dinfo("Execute check for program '" + checkPath + "' returned '" + 1662 exitCode + "'. Evaluating condition '" + checkCond + 1663 "' revealed " + executeResult + " when comparing to expected" + 1664 " value of '" + checkValueExpanded + "'."); 1665 returnValue = executeResult; 1666 break; 1667 1668 // check type: logical 1669 case "logical": 1670 1671 // check if logical condition is set 1672 if (checkCond == null) { 1673 throw new Error("Condition is null for a logical check."); 1674 } 1675 1676 var subcheckNodes = getChecks(checkNode); 1677 1678 switch (checkCond) { 1679 case "not": 1680 var checkResult = false; 1681 for (var iNotNodes=0; iNotNodes < subcheckNodes.length; iNotNodes++) { 1682 // check if one of the subchecks return false 1683 if (!checkCondition(subcheckNodes[iNotNodes])) { 1684 checkResult = true; 1685 break; 1686 } 1687 } 1688 if (checkResult) { 1689 dinfo("Result of logical 'NOT' check is true."); 1690 } else { 1691 dinfo("Result of logical 'NOT' check is false."); 1692 } 1693 returnValue = checkResult; 1694 break; 1695 1696 case "and": 1697 var checkResult = true; 1698 for (var iAndNodes = 0; iAndNodes < subcheckNodes.length; iAndNodes++) { 1699 // check if one of the subchecks return false 1700 if (!checkCondition(subcheckNodes[iAndNodes])) { 1701 checkResult = false; 1702 break; 1703 } 1704 } 1705 if (checkResult) { 1706 dinfo("Result of logical 'AND' check is true."); 1707 } else { 1708 dinfo("Result of logical 'AND' check is false."); 1709 } 1710 returnValue = checkResult; 1711 break; 1712 1713 case "or": 1714 // check if one of the sub-checks returns true 1715 var checkResult = false; 1716 for (var iOrNodes = 0; iOrNodes < subcheckNodes.length; iOrNodes++) { 1717 if (checkCondition(subcheckNodes[iOrNodes])) { 1718 checkResult = true; 1719 break; 1720 } 1721 } 1722 if (checkResult) { 1723 dinfo("Result of logical 'OR' check is true."); 1724 } else { 1725 dinfo("Result of logical 'OR' check is false"); 1726 } 1727 returnValue = checkResult; 1728 break; 1729 1730 case "atleast": 1731 if (checkValue == null) { 1732 throw new Error("Check condition logical 'atleast' requires a value."); 1733 } 1734 1735 // count number of checks which return true 1736 var numAtLeastNodes=0; 1737 var checkResult = false; 1738 for (var iAtLeastNodes = 0; iAtLeastNodes < subcheckNodes.length; iAtLeastNodes++) { 1739 if (checkCondition(subcheckNodes[iAtLeastNodes])) { 1740 numAtLeastNodes++; 1741 } 1742 // check if at least x checks revealed true 1743 if (numAtLeastNodes >= checkValue) { 1744 checkResult = true; 1745 break; 1746 } 1747 } 1748 if (checkResult) { 1749 dinfo("Result of logical 'AT LEAST' check is true."); 1750 } else { 1751 dinfo("Result of logical 'AT LEAST' check is false."); 1752 } 1753 returnValue = checkResult; 1754 break; 1755 1756 case "atmost": 1757 // check if maximum x checks return true 1758 var checkResult = true; 1759 var numAtMostNodes = 0; 1760 for (var iAtMostNodes = 0; iAtMostNodes < subcheckNodes.length; iAtMostNodes++) { 1761 if (checkCondition(subcheckNodes[iAtMostNodes])) { 1762 numAtMostNodes++; 1763 } 1764 if (numAtMostNodes > checkValue) { 1765 checkResult = false; 1766 break; 1767 } 1768 } 1769 if (checkResult) { 1770 dinfo("Result of logical 'AT MOST' check is true."); 1771 } else { 1772 dinfo("Result of logical 'AT MOST' check is false."); 1773 } 1774 returnValue = checkResult; 1775 break; 1776 1777 default: 1778 throw new Error("Check condition " + checkCond + " unknown for " + 1779 "type logical."); 1780 break; 1781 } 1782 1783 // Logical checks shall not be added to local settings node. 1784 break; 1785 1786 // Check type: host 1787 case "host": 1788 // check if logical condition is set 1789 if (checkCond == null) { 1790 throw new Error("Condition is null for a host check."); 1791 } 1792 if (checkValueExpanded == null) { 1793 throw new Error("Value is null for a host check."); 1794 } 1795 1796 // Verify if the host check matches current host. 1797 returnValue = checkHostAttribute(checkCond, checkValueExpanded); 1798 1799 // The result of Registry checks shall be stored in local settings node. 1800 addSettingsCheckResult(checkNode, returnValue); 1801 1802 break; 1803 1804 // no such check type 1805 default: 1806 throw new Error("Check condition type " + checkType + " unknown."); 1807 break; 1808 } 1809 1810 return returnValue; 1811 } 1812 1813 /** 1814 * Checks whether the specified host attribute matches the expression passed as 1815 * argument. 1816 * 1817 * @param attributeName 1818 * Name of host attribute to match. See getHostInformation() 1819 * function for valid host attributes. 1820 * @param expression 1821 * Regular expression (or list for certain attributes) to use for 1822 * matching. 1823 * @returns {Boolean} True if attribute matches the expression. 1824 */ 1825 function checkHostAttribute(attributeName, expression) { 1826 // Terminate if attribute name is not specified. 1827 if (attributeName == null) { 1828 error("Host attribute matching failed. No attribute name specified."); 1829 return false; 1830 } 1831 var hostAttribute = attributeName; 1832 1833 // Terminate if expression is not specified. 1834 if (expression == null) { 1835 error("Host attribute matching for attribute '" + hostAttribute + "' failed. No expression specified."); 1836 return false; 1837 } 1838 // Expand environment variables in expressions. 1839 var checkExpression = new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(expression); 1840 1841 // Initialize return value. 1842 var returnValue = false; 1843 1844 // Fetch current host attributes. 1845 var globalHostInformation = getHostInformation(); 1846 1847 // Add "environment" key since we want to support environment matching too. 1848 var hostInformation = new ActiveXObject("Scripting.Dictionary"); 1849 var keys = globalHostInformation.keys().toArray(); 1850 for (var i=0; i<keys.length; i++) { 1851 hostInformation.Add(keys[i], globalHostInformation.Item(keys[i])); 1852 } 1853 hostInformation.Add("environment", ""); 1854 1855 // First verify if the requested host information attribute exists. 1856 var hostInfoValue = hostInformation.Item(hostAttribute); 1857 if (hostInfoValue == null || (typeof(hostInfoValue) == "object" && hostInfoValue.length <= 0) ) { 1858 dinfo("Host match requires attribute '" + hostAttribute + "' " 1859 + "which is not defined for current host. No match found."); 1860 return false; 1861 } 1862 1863 var attrMatchExpression = new RegExp(checkExpression, "i"); 1864 // First try to match array objects. 1865 if (typeof(hostInfoValue) == "object" && hostInfoValue.length > 0) { 1866 for (var iHostInfo=0; iHostInfo < hostInfoValue.length; iHostInfo++) { 1867 // Get value from attribute array 1868 var hostInfoElement = hostInfoValue[iHostInfo]; 1869 dinfo("Comparing multi-valued attribute '" + hostAttribute + "' with value '" + 1870 hostInfoElement + "' using expression '" + checkExpression + "'."); 1871 1872 // Compare attribute array element with expected 1873 // value. 1874 if (attrMatchExpression.test(hostInfoElement) == true) { 1875 dinfo("Match for attribute '" + hostAttribute + "' with value '" + hostInfoElement + "' found."); 1876 returnValue = true; 1877 break; 1878 } 1879 } 1880 // } else if (typeof(host[hostNodeAttrName]) != "object") { 1881 } else { 1882 // Match simple attributes. 1883 switch (hostAttribute) { 1884 case "environment": 1885 // Match environment condition to actual environment variable. 1886 1887 // Get condition value from from parameter, could be multiple, separated by '|'. 1888 var environmentConditions = checkExpression.split('|'); 1889 returnValue = true; 1890 for (var iEnv=0; iEnv < environmentConditions.length; iEnv++) { 1891 var environmentCondition = environmentConditions[iEnv]; 1892 // Split environment conditions into key and value pairs. 1893 var envConditionSplit = environmentCondition.split("="); 1894 // Need at least the key and value. If there are less components, then skip it. 1895 if (envConditionSplit.length >= 2) { 1896 // The first value is the key. 1897 var envKey = envConditionSplit[0]; 1898 if (envKey == "") { 1899 dinfo("Invalid empty environment variable name."); 1900 returnValue = false; 1901 break; 1902 } 1903 1904 // Fetch environment value. 1905 var expandString = "%" + envKey + "%"; 1906 var envValueRead = new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(expandString); 1907 1908 if (envValueRead == expandString) { 1909 // Environment variable is not defined, match failed. 1910 dinfo("Required environment not matched. Environment variable '" + envKey + "' not defined."); 1911 returnValue = false; 1912 break; 1913 } 1914 1915 // All following values are belonging to the value. 1916 /* 1917 var valueParts = new Array(); 1918 for (var iValues=1; iValues < envConditionSplit.length; iValues++) { 1919 valueParts.push(envConditionSplit[iValues]); 1920 } 1921 // Join values to re-assemble the value specified. 1922 var envValue = valueParts.join(""); 1923 */ 1924 1925 // Re-assemble value. 1926 var valueStartOffset = envKey.length + 1; 1927 var envValue = environmentCondition.substr(valueStartOffset); 1928 1929 // Check environment using regular expression match. 1930 var envMatchExpression = new RegExp(envValue, "i"); 1931 if (envMatchExpression.test(envValueRead) == true) { 1932 dinfo("Required environment matched. Environment variable '" + envKey + 1933 "' with value '" + envValueRead + "' matches '" + envValue + "'."); 1934 // Check next value. All of them need to be true. 1935 continue; 1936 } else { 1937 dinfo("Required environment dit not match. Environment variable '" + envKey + 1938 "' with value '" + envValueRead + "' does not match '" + envValue + "'."); 1939 returnValue = false; 1940 break; 1941 } 1942 } else { 1943 error("Invalid environment match expression '" + environmentCondition + "'. Match skipped."); 1944 } 1945 } 1946 break; 1947 1948 case "lcid": 1949 // Check whether any LCID matches the current host executing user LCID. 1950 var attributeLCIDs = checkExpression.split(","); 1951 for (var iLCID=0; iLCID < attributeLCIDs.length; iLCID++) { 1952 // check if it corresponds to the system LCID 1953 var currentLcid = trimLeadingZeroes(trim(attributeLCIDs[iLCID])); 1954 if (currentLcid == hostInfoValue) { 1955 dinfo("Required LCID match found. LCID '" + currentLcid + "' matches current user LCID."); 1956 returnValue = true; 1957 break; 1958 } 1959 } 1960 if (!returnValue) { 1961 dinfo("None of the required LCID values (" + checkExpression + 1962 ") matched the current host LCID of '" + hostInfoValue + "'."); 1963 } 1964 break; 1965 1966 case "lcidOS": 1967 // Check whether any LCID matches the current host OS LCID. 1968 var attributeLCIDs = checkExpression.split(","); 1969 for (var iLCIDOS=0; iLCIDOS < attributeLCIDs.length; iLCIDOS++) { 1970 // check if it corresponds to the system LCID 1971 var currentLcid = trimLeadingZeroes(trim(attributeLCIDs[iLCIDOS])); 1972 if (currentLcid == hostInfoValue) { 1973 dinfo("Required OS LCID match found. LCID '" + currentLcid + "' matches current host LCID."); 1974 returnValue = true; 1975 break; 1976 } 1977 } 1978 if (!returnValue) { 1979 // Check if any LCID matched the current host. 1980 dinfo("None of the required LCID values (" + checkExpression + 1981 ") matched the current host LCID of '" + hostInfoValue + "'."); 1982 } 1983 break; 1984 1985 default: 1986 // perform simple regular expression match of 1987 // attribute 1988 if (attrMatchExpression.test(hostInfoValue) == true) { 1989 dinfo("Host attribute '" + hostAttribute + "' with value '" + 1990 hostInfoValue + "' matches expression '" + checkExpression + "'."); 1991 returnValue = true; 1992 } else { 1993 dinfo("Host attribute '" + hostAttribute + "' with value '" + 1994 hostInfoValue + "' does not match expression '" + checkExpression + "'."); 1995 returnValue = false; 1996 } 1997 break; 1998 } 1999 } 2000 return returnValue; 2001 } 2002 2003 2004 /** 2005 * Creates a new hosts XML root-node and returns it 2006 * 2007 * @return new hosts node 2008 */ 2009 function createHosts() { 2010 var newHosts = createXml("wpkg:wpkg", namespaceHosts); 2011 return newHosts; 2012 } 2013 2014 /** 2015 * Creates a new packages XML root-node and returns it 2016 * 2017 * @return new profiles node 2018 */ 2019 function createPackages() { 2020 var newPackages = createXml("packages:package", namespacePackages); 2021 return newPackages; 2022 } 2023 2024 /** 2025 * Creates a new profiles XML root-node and returns it 2026 * 2027 * @return new profiles node 2028 */ 2029 function createProfiles() { 2030 var newProfiles = createXml("profiles:profiles", namespaceProfiles); 2031 return newProfiles; 2032 } 2033 2034 /** 2035 * Creates a new settings XML root-node and returns it 2036 * 2037 * @return new settings node 2038 */ 2039 function createSettings() { 2040 var newSettings = createXml("wpkg:wpkg", namespaceSettings); 2041 if (settingsHostInfo) { 2042 // Add host attributes. 2043 // NOTE: These attributes are currently not used by WPKG but might be 2044 // useful if wpkg.xml is copied to an external system so wpkg.xml 2045 // will include some host information. 2046 var hostInformation = getHostInformation(); 2047 var attributes = hostInformation.keys().toArray(); 2048 for (var i=0; i<attributes.length; i++) { 2049 var value = hostInformation.Item(attributes[i]); 2050 newSettings.setAttribute(attributes[i], value); 2051 } 2052 } 2053 return newSettings; 2054 } 2055 2056 /** 2057 * Create a new settings XML root-node by reading a file and returns it 2058 * 2059 * @param fileName String pointing to the settings file to be created 2060 * (full path). 2061 * @return settings root node as stored within the file 2062 */ 2063 function createSettingsFromFile(fileName) { 2064 var newSettings = loadXml(fileName, null, "settings"); 2065 return newSettings; 2066 } 2067 2068 /** 2069 * Downloads a file as specified within a download node. 2070 * 2071 * @param downloadNode 2072 * XML 'download' node to be used 2073 * @return true in case of successful download, false in case of error 2074 */ 2075 function download(downloadNode) { 2076 // get attributes 2077 var url = getDownloadUrl(downloadNode); 2078 var target = getDownloadTarget(downloadNode); 2079 var timeout = getDownloadTimeout(downloadNode); 2080 var expandURL = getDownloadExandURL(downloadNode); 2081 2082 // initiate download 2083 return downloadFile(url, target, timeout, expandURL); 2084 } 2085 2086 /** 2087 * Downloads all files from the given array of download XML nodes 2088 * 2089 * @param downloadNodes 2090 * Array of download XML nodes to be downloaded 2091 * @return true in case of successful download, false in case of error 2092 */ 2093 function downloadAll(downloadNodes) { 2094 var returnValue = true; 2095 if (downloadNodes != null) { 2096 for (var i=0; i<downloadNodes.length; i++) { 2097 var result = download(downloadNodes[i]); 2098 // stop downloading if 2099 if (result != true) { 2100 returnValue = false; 2101 } 2102 } 2103 } 2104 return returnValue; 2105 } 2106 2107 /** 2108 * Removes eventually existing temporary downloads of the specified XML node 2109 * 2110 * @param downloadNode 2111 * XML node which contains the download definition to clean 2112 */ 2113 function downloadClean(downloadNode) { 2114 // get download attributes 2115 var target = getDownloadTarget(downloadNode); 2116 2117 // evaluate target directory 2118 if (target == null || target == "") { 2119 error("Invalid download target specified: " + target); 2120 target = downloadDir; 2121 } else { 2122 target = downloadDir + "\\" + target; 2123 } 2124 target = new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(target); 2125 var fso = new ActiveXObject("Scripting.FileSystemObject"); 2126 // delete temporary file if it already exists 2127 if (fso.FileExists(target)) { 2128 fso.DeleteFile(target); 2129 } 2130 } 2131 2132 2133 /** 2134 * Cleans all temporary files belonging to the download XML nodes within the 2135 * passed array of download XML nodes 2136 * 2137 * @param downloadNodes 2138 * Array of download XML nodes 2139 */ 2140 function downloadsClean(downloadNodes) { 2141 if (downloadNodes != null) { 2142 for (var i=0; i<downloadNodes.length; i++) { 2143 downloadClean(downloadNodes[i]); 2144 } 2145 } 2146 } 2147 2148 2149 /** 2150 * Builds settings document tree containing actually installed packages. Tests 2151 * all packages from given doc tree for "check" conditions. If given conditions 2152 * are positive, package is considered as installed. 2153 */ 2154 function fillSettingsWithInstalled() { 2155 2156 var packagesNodes = getPackageNodes(); 2157 2158 // check each available package 2159 var foundPackage = false; 2160 for (var i = 0; i < packagesNodes.length; i++) { 2161 var packNode = packagesNodes[i]; 2162 2163 // add package node to settings if it is installed 2164 if (isInstalled(packNode)) { 2165 addSettingsNode(packNode, true); 2166 foundPackage = true; 2167 } 2168 } 2169 if (foundPackage) { 2170 saveSettings(true); 2171 } 2172 } 2173 2174 /** 2175 * Returns the command line argument for this command node. A command node can 2176 * be an <install/>, <upgrade/> or <remove/> node. 2177 * 2178 * @param cmdNode 2179 * cmd XML node to read from 2180 * @return command defined within the given cmd XML node, returns null 2181 * if no command is defined. 2182 */ 2183 function getCommandCmd(cmdNode) { 2184 return cmdNode.getAttribute("cmd"); 2185 } 2186 2187 /** 2188 * Returns the value of an exit code node within the given command node. A 2189 * command node can be an <install/>, <upgrade/> or <remove/> node. In case no 2190 * such exit code was defined null will be returned. In case the code is defined 2191 * the string "success" is returned. In case the exit code specifies an 2192 * immediate reboot then the string "reboot" is returned. 2193 * 2194 * @return returns string "reboot" in case a reboot is required.<br> 2195 * returns string "delayedReboot" in case a reboot should be scheduled 2196 * as soon as possible<br> 2197 * returns string "postponedReboot" in case a reboot after installing 2198 * all packages is required<br> 2199 * returns string "success" in case exit code specifies successful 2200 * installation.<br> 2201 * returns null in case the exit code is not defined. 2202 */ 2203 function getCommandExitCodeAction(cmdNode, exitCode) { 2204 var returnValue = null; 2205 var exitNode = cmdNode.selectSingleNode("exit[@code='" + exitCode + "']"); 2206 if (exitNode == null) { 2207 exitNode = cmdNode.selectSingleNode("exit[@code='any']"); 2208 } 2209 if (exitNode == null) { 2210 exitNode = cmdNode.selectSingleNode("exit[@code='*']"); 2211 } 2212 if (exitNode != null) { 2213 if (exitNode.getAttribute("reboot") == "true") { 2214 // This exit code forces a reboot. 2215 info("Command '" + getCommandCmd(cmdNode) + "' returned " + 2216 " exit code [" + exitCode + "]. This exit code " + 2217 "requires an immediate reboot."); 2218 returnValue = "reboot"; 2219 } else if (exitNode.getAttribute("reboot") == "delayed") { 2220 info("Command '" + getCommandCmd(cmdNode) + "' returned " + 2221 " exit code [" + exitCode + "]. This exit code " + 2222 "schedules a reboot after execution of all commands."); 2223 returnValue = "delayedReboot"; 2224 } else if (exitNode.getAttribute("reboot") == "postponed") { 2225 info("Command '" + getCommandCmd(cmdNode) + "' returned " + 2226 " exit code [" + exitCode + "]. This exit code " + 2227 "schedules a reboot after execution of all packages."); 2228 returnValue = "postponedReboot"; 2229 } else { 2230 // This exit code is successful. 2231 info("Command '" + getCommandCmd(cmdNode) + "' returned " + 2232 " exit code [" + exitCode + "]. This exit code " + 2233 "indicates success."); 2234 returnValue = "success"; 2235 } 2236 } 2237 return returnValue; 2238 } 2239 2240 2241 /** 2242 * Return value of include attribute of the given cmd node. 2243 * Returns null if include attribute is not set. 2244 * 2245 * @param cmdNode 2246 * The command node to read the include attribute from. 2247 * 2248 * @returns Value of include attribute, returns null if attribute is undefined. 2249 */ 2250 function getCommandInclude(cmdNode) { 2251 return cmdNode.getAttribute("include"); 2252 } 2253 2254 2255 /** 2256 * Returns the timeout value for this command node. A command node can be an 2257 * <install/>, <upgrade/> or <remove/> node. 2258 * 2259 * @param cmdNode 2260 * cmd XML node to read from. 2261 * @return the timeout for the given cmd XML node - returns 0 if no timeout is 2262 * defined 2263 */ 2264 function getCommandTimeout(cmdNode) { 2265 var timeout = cmdNode.getAttribute("timeout"); 2266 if (timeout == null) { 2267 timeout = 0; 2268 } 2269 return parseInt(timeout); 2270 } 2271 2272 /** 2273 * Returns the value of the workdir attribute of the given cmd XML node. 2274 * 2275 * @param cmdNode 2276 * cmd XML node to read from 2277 * @return the workdir attribute value. Returns null in case value is not 2278 * defined. 2279 */ 2280 function getCommandWorkdir(cmdNode) { 2281 var workdir = cmdNode.getAttribute("workdir"); 2282 return workdir; 2283 } 2284 2285 /** 2286 * Returns condition node of a given XML node. Returns null if there is no 2287 * condition node specified. 2288 * 2289 * @param xmlNode XML node which is supposed to have a <condition /> sub-node. 2290 * @returns Array of condition XML-nodes, might be null if no condition is specified 2291 */ 2292 function getConditions(xmlNode) { 2293 // Read condition nodes (might be 0, 1 or any number) 2294 var conditionNodes = xmlNode.selectNodes("condition"); 2295 2296 /* 2297 var conditionNodes = xmlNode.selectNodes("wpkg:condition"); 2298 if (conditionNodes.length <= 0) { 2299 // Maybe namespace has not been specified correctly. 2300 // Try reading from default namespace. 2301 conditionNodes = xmlNode.selectNodes("condition"); 2302 } 2303 */ 2304 2305 // Per specification only one single condition node shall be specified 2306 /* 2307 if (conditionNodes != null && conditionNodes.length > 1) { 2308 error("More than one condition node specified. Ignoring all but the first condition."); 2309 } 2310 */ 2311 2312 // Return condition node. 2313 return conditionNodes; 2314 } 2315 2316 /** 2317 * Returns XML node which contains the configuration 2318 */ 2319 function getConfig() { 2320 if (config == null) { 2321 // load config 2322 2323 // get argument list 2324 var argv = getArgv(); 2325 // Get special purpose argument lists. 2326 var argn = argv.Named; 2327 2328 // if set to true it will throw an error to quit in case of 2329 // file-not-found 2330 var exitIfNotFound = false; 2331 2332 // stores config file path 2333 var config_file = null; 2334 2335 // will be used for file operations 2336 var fso = new ActiveXObject("Scripting.FileSystemObject"); 2337 2338 if (argn("config") != null) { 2339 var configPath = argn("config"); 2340 var wshObject = new ActiveXObject("WScript.Shell"); 2341 var expConfigPath = wshObject.ExpandEnvironmentStrings(configPath); 2342 config_file = fso.GetAbsolutePathName(expConfigPath); 2343 // config was explicitly specified - I think we should quit if it 2344 // is not available 2345 exitIfNotFound = true; 2346 } else { 2347 // if config_file_name (config.xml) exists, use it 2348 var fullScriptPATH = WScript.ScriptFullName; 2349 var base = fso.GetParentFolderName(fullScriptPATH); 2350 config_file = fso.BuildPath(base, config_file_name); 2351 // config is optional in this case 2352 exitIfNotFound = false; 2353 } 2354 2355 if (fso.FileExists(config_file)) { 2356 try { 2357 // Read in config.xml. 2358 config = loadXml(config_file, null, "config"); 2359 if (config == null) { 2360 throw new Error("Unable to parse config file!"); 2361 } 2362 } catch (e) { 2363 // There was an error processing the config.xml file. Alert the 2364 // user 2365 error("Error reading "+ config_file + ": " + e.description); 2366 exit(99); // Exit code 99 means config.xml read error. 2367 } 2368 } else { 2369 var message = config_file + " could not be found."; 2370 if (exitIfNotFound) { 2371 error(message); 2372 exit(99); // Exit code 99 means config.xml read error. 2373 } else { 2374 dinfo(message); 2375 } 2376 } 2377 // create empty config if no config could be read 2378 if (config == null) { 2379 config = createXml("config"); 2380 } 2381 } 2382 return config; 2383 } 2384 2385 /** 2386 * Returns array of <param> nodes from the configuration. Returns array of size 2387 * 0 in case no parameter is defined. 2388 * 2389 * @return <param> nodes 2390 */ 2391 function getConfigParamArray() { 2392 return getConfig().selectNodes("param"); 2393 } 2394 2395 /** 2396 * Returns download XML node array on a given XML node 2397 * 2398 * @param xmlNode 2399 * the xml node to read child-nodes of type download from 2400 * @param downloadsArray 2401 * array of downloads to be extended with the ones from the given XML 2402 * node, specify null to return a new array. 2403 * @return XML node array on a given package XML node containing all package 2404 * downloads. returns empty array if no downloads are defined 2405 */ 2406 function getDownloads(xmlNode, downloadsArray) { 2407 var downloadsArrayRef = downloadsArray; 2408 if (downloadsArrayRef == null) { 2409 downloadsArrayRef = new Array(); 2410 } 2411 // Only fetch download nodes if downloads are not disabled. 2412 // Just hide download nodes in case downloads are disabled. 2413 if (!isNoDownload()) { 2414 var downloads = xmlNode.selectNodes("download"); 2415 if (downloads != null) { 2416 var filteredDownloads = filterConditionalNodes(downloads, true); 2417 for(var i=0; i<filteredDownloads.length; i++) { 2418 downloadsArrayRef.push(filteredDownloads[i]); 2419 } 2420 } 2421 } 2422 return downloadsArrayRef; 2423 } 2424 2425 /** 2426 * Returns 'target' attribute from the given download XML node 2427 * 2428 * @param downloadNode 2429 * download XML node 2430 * @return value of 'target' attribute, null if attribute is not defined 2431 */ 2432 function getDownloadTarget(downloadNode){ 2433 return downloadNode.getAttribute("target"); 2434 } 2435 2436 /** 2437 * Returns 'timeout' attribute from the given download XML node 2438 * 2439 * @param downloadNode 2440 * download XML node 2441 * @return {Number} Value of 'timeout' attribute, returns value of downloadTimeout if no 2442 * timeout value exists or it cannot be parsed. Returns integer. 2443 */ 2444 function getDownloadTimeout(downloadNode) { 2445 var returnValue = downloadTimeout; 2446 var timeout = downloadNode.getAttribute("timeout"); 2447 if (timeout != null) { 2448 try { 2449 returnValue = parseInt(timeout); 2450 } catch(e) { 2451 error("Error parsing timeout attribute: " + e.description); 2452 } 2453 } 2454 2455 return returnValue; 2456 } 2457 2458 /** 2459 * Returns value of expandURL attribute from a download node. 2460 * @param downloadNode The download XML node. 2461 * @returns true if variables shall be expanded in URL attribute, 2462 * false if they should not be expanded. Defaults to true if attribute is undefined. 2463 */ 2464 function getDownloadExandURL(downloadNode) { 2465 var returnValue = true; 2466 var attributeValue = downloadNode.getAttribute("expandURL"); 2467 if (attributeValue != null && attributeValue == "false") { 2468 returnValue = false; 2469 } 2470 return returnValue; 2471 } 2472 2473 /** 2474 * Returns 'url' attribute from the given download XML node 2475 * 2476 * @param downloadNode 2477 * download XML node 2478 * @return value of 'url' attribute, null if attribute is not defined 2479 */ 2480 function getDownloadUrl(downloadNode) { 2481 return downloadNode.getAttribute("url"); 2482 } 2483 2484 /** 2485 * Gets the size of a file (in Bytes). The path is allowed to contain 2486 * environment variables like "%TEMP%\somefile.txt". 2487 * 2488 * @param file 2489 * path to the file whose size has to be returned 2490 * @return size of the file (in Bytes), returns -1 if file size could not be 2491 * determined 2492 */ 2493 function getFileSize (file) { 2494 var size = -1; 2495 try { 2496 dinfo ("Finding size of '" + file + "'\n"); 2497 var expandedPath = new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(file); 2498 var FSO = new ActiveXObject("Scripting.FileSystemObject"); 2499 var fsof = FSO.GetFile(expandedPath); 2500 size = fsof.Size; 2501 } catch (e) { 2502 size = -1; 2503 dinfo("Unable to get file size for '" + file + "': " + 2504 e.description); 2505 } 2506 dinfo ("Leaving getFileSize with size " + size); 2507 return size; 2508 } 2509 2510 /** 2511 * Gets the creation date of a file. 2512 * 2513 * @param file 2514 * Path to the file from which to read the creation date. 2515 * @returns Date when the file has been created. 2516 * Returns null if file date could not be read. 2517 */ 2518 function getFileDateCreation(file) { 2519 var fileDate = null; // new Date(); 2520 try { 2521 dinfo ("Reading creation date of '" + file + "'."); 2522 var expandedPath = new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(file); 2523 var FSO = new ActiveXObject("Scripting.FileSystemObject"); 2524 var fsof = FSO.GetFile(expandedPath); 2525 fileDate = fsof.DateCreated; 2526 } catch (e) { 2527 fileDate = null; 2528 dinfo("Unable to get file creation date for '" + file + "': " + 2529 e.description); 2530 } 2531 return fileDate; 2532 } 2533 2534 /** 2535 * Gets the last modified date of a file. 2536 * 2537 * @param file 2538 * Path to the file from which to read the last modification date. 2539 * @returns Date when the file has been last modified. 2540 * Returns null if file date could not be read. 2541 */ 2542 function getFileDateModification(file) { 2543 var fileDate = null; // new Date(); 2544 try { 2545 dinfo ("Reading last modification date of '" + file + "'."); 2546 var expandedPath = new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(file); 2547 var FSO = new ActiveXObject("Scripting.FileSystemObject"); 2548 var fsof = FSO.GetFile(expandedPath); 2549 fileDate = fsof.DateLastModified; 2550 } catch (e) { 2551 fileDate = null; 2552 dinfo("Unable to get file last modification date for '" + file + "': " + 2553 e.description); 2554 } 2555 return fileDate; 2556 } 2557 2558 /** 2559 * Gets the last access date of a file. 2560 * 2561 * @param file 2562 * Path to the file from which to read the last access date. 2563 * @returns Date when the file has been last accessed. 2564 * Returns null if file date could not be read. 2565 */ 2566 function getFileDateLastAccess(file) { 2567 var fileDate = null; // new Date(); 2568 try { 2569 dinfo ("Reading last access date of '" + file + "'."); 2570 var expandedPath = new ActiveXObject("WScript.Shell").ExpandEnvironmentStrings(file); 2571 var FSO = new ActiveXObject("Scripting.FileSystemObject"); 2572 var fsof = FSO.GetFile(expandedPath); 2573 fileDate = fsof.DateLastAccessed; 2574 } catch (e) { 2575 fileDate = null; 2576 dinfo("Unable to get file last accessed date for '" + file + "': " + 2577 e.description); 2578 } 2579 return fileDate; 2580 } 2581 2582 /** 2583 * Returns the version of a file. 2584 * 2585 * @return string representation of version, null in case no version could be 2586 * read. 2587 */ 2588 function getFileVersion (file) { 2589 var version = null; 2590 try { 2591 dinfo ("Trying to find version of " + file); 2592 var FSO = new ActiveXObject("Scripting.FileSystemObject"); 2593 version = FSO.GetFileVersion(file); 2594 dinfo ("Obtained version '" + version + "'."); 2595 } catch (e) { 2596 version = null; 2597 dinfo("Unable to find file version for " + file + " : " + 2598 e.description); 2599 } 2600 return version; 2601 } 2602 2603 /** 2604 * Returns the hostname of the machine running this script. The hostname might 2605 * be overwritten by the /host:<hostname> switch. 2606 */ 2607 function getHostname() { 2608 if (hostName == null) { 2609 var WshNetwork = WScript.CreateObject("WScript.Network"); 2610 setHostname(WshNetwork.ComputerName.toLowerCase()); 2611 } 2612 return hostName; 2613 } 2614 2615 /** 2616 * Returns a string representing the regular expression associated to the host 2617 * definition in hosts.xml. 2618 */ 2619 function getHostNameAttribute(hostNode) { 2620 return hostNode.getAttribute("name"); 2621 } 2622 2623 /** 2624 * Returns the operating system of the machine running this script. The return 2625 * format is: 2626 * 2627 * <pre> 2628 * <OS-caption>, <OS-description>, <CSD-version>, <OS-version> 2629 * example output: 2630 * microsoft windows 7 professional, , sp1, 6.1.7601 2631 * </pre> 2632 * 2633 * It might be overwritten by the /os:<hostos> switch. 2634 * 2635 * Note: Some values might be empty. 2636 * 2637 * @returns Host operating system specification as a plain string converted to 2638 * lower case letters to ease parsing 2639 */ 2640 function getHostOS() { 2641 if (hostOs == null) { 2642 var strComputer = "."; 2643 var strQuery = "Select * from Win32_OperatingSystem"; 2644 try { 2645 var objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\\\" + 2646 strComputer + "\\root\\cimv2"); 2647 var colOSes = objWMIService.ExecQuery(strQuery,"WQL",48); 2648 var osEnum = new Enumerator(colOSes); 2649 for (; !osEnum.atEnd(); osEnum.moveNext()) { 2650 var osItem = osEnum.item(); 2651 var OtherTypeDescription = ""; 2652 var CSDVersion = ""; 2653 if (osItem.OtherTypeDescription != null) { 2654 OtherTypeDescription = osItem.OtherTypeDescription; 2655 } 2656 if (osItem.CSDVersion != null) { 2657 CSDVersion = osItem.CSDVersion.replace(/Service Pack /i,"SP"); 2658 } 2659 var strSystem = trim(osItem.Caption) + ", " 2660 + OtherTypeDescription + ", " 2661 + CSDVersion + ", " 2662 + osItem.Version; 2663 hostOs = strSystem.toLowerCase(); 2664 dinfo("Host operating system: " + hostOs); 2665 } 2666 } catch (e) { 2667 dinfo("Warning: unable to get operating system information."); 2668 } 2669 } 2670 return hostOs; 2671 } 2672 2673 /** 2674 * Returns name of domain on which the executing host is member of. 2675 * 2676 * @returns Returns domain name string. 2677 */ 2678 function getDomainName() { 2679 if (domainName == null) { 2680 try { 2681 var strComputer = "." ; 2682 2683 // Get WMI object to read information from. 2684 var WMIServiceStr = "winmgmts:{impersonationLevel=impersonate}!\\\\" 2685 + strComputer + "\\root\\cimv2"; 2686 var objWMIService = GetObject(WMIServiceStr) ; 2687 2688 // Query domain name from WMI. 2689 var QueryRes = objWMIService.ExecQuery("Select * from Win32_ComputerSystem where PartOfDomain=True "); 2690 var items=new Enumerator(QueryRes); 2691 items.moveFirst(); 2692 if (items.atEnd() == true) { 2693 // Not a domain member 2694 dinfo("Not a domain member."); 2695 // set 2696 domainName = ""; 2697 } else { 2698 var First = items.item(); 2699 domainName = First.Domain.toLowerCase(); 2700 dinfo("Domain Name: " + domainName); 2701 } 2702 } catch (e) { 2703 dinfo("Message: Unable to get domain information."); 2704 } 2705 } 2706 return domainName; 2707 } 2708 2709 /** 2710 * Returns array of group names where the executing host is member of. 2711 * 2712 * @returns Returns list of membership groups. 2713 */ 2714 function getHostGroups() { 2715 if (hostGroups == null) { 2716 hostGroups = new Array(); 2717 try { 2718 var hostName = getHostname(); 2719 var domainName = getDomainName(); 2720 var obj = GetObject("WinNT://" + domainName + "/" + hostName + "$,user") ; 2721 var groups = obj.Groups(); 2722 for (var item =new Enumerator(groups); !item.atEnd(); item.moveNext() ) { 2723 var group = item.item(); 2724 dinfo("Found computer group: " + group.Name); 2725 hostGroups.push(group.Name); 2726 } 2727 } catch (e) { 2728 dinfo("Message: Unable to fetch computer membership groups. Probably not a domain member."); 2729 } 2730 } 2731 return hostGroups; 2732 } 2733 2734 /** 2735 * Returns a list of attribute/value pair associated to the host 2736 * definition in hosts.xml. 2737 * 2738 * @param hostNode XML node of the host definition 2739 * @return dictionary of attribute/value pair. 2740 */ 2741 function getHostAttributes(hostNode) { 2742 var hostAttributes = new ActiveXObject("Scripting.Dictionary"); 2743 2744 if(hostNode.attributes != null) { 2745 for (var i=0; i<hostNode.attributes.length; i++) { 2746 if (hostNode.attributes[i].value != null) { 2747 hostAttributes.Add(hostNode.attributes[i].name, hostNode.attributes[i].value); 2748 } 2749 } 2750 } 2751 return hostAttributes; 2752 } 2753 2754 /** 2755 * Returns a string identifying a host node including all attributes. 2756 * 2757 * @param hostNode 2758 * XML node of the host definition 2759 * @return a string of concatenate 'attribute=value' 2760 */ 2761 function getHostNodeDescription(hostNode) { 2762 // Get dictionary object of all attributes. 2763 var hostNodeAttrs = getHostAttributes(hostNode); 2764 2765 // Fill all attributes into array. 2766 var attrsKeys = hostNodeAttrs.keys().toArray(); 2767 var attrDesc = new Array(); 2768 for (var i=0; i<attrsKeys.length; i++) { 2769 var attrName = attrsKeys[i]; 2770 var attrValue = hostNodeAttrs.Item(attrName); 2771 attrDesc.push(attrName + "='" + attrValue + "'"); 2772 } 2773 // Convert array to comma-separated list 2774 // attr1='value1',attr2='value2' 2775 return attrDesc.join(","); 2776 } 2777 2778 2779 /** 2780 * Collects information from local host and stores it into a scripting 2781 * dictionary object. 2782 * 2783 * @returns host attributes stored within a dictionary object. This currently 2784 * includes the following attributes: name, architecture, os, 2785 * ipaddresses, domainname, groups, lcid 2786 */ 2787 function getHostInformation() { 2788 // Fetch host information if not already collected. 2789 // This information is supposed to be static during execution and 2790 // therefore it will be cached. 2791 if (hostAttributes == null) { 2792 hostAttributes = new ActiveXObject("Scripting.Dictionary"); 2793 hostAttributes.Add("hostname", getHostname()); 2794 hostAttributes.Add("architecture", getArchitecture()); 2795 hostAttributes.Add("os", getHostOS()); 2796 hostAttributes.Add("ipaddresses", getIPAddresses()); 2797 hostAttributes.Add("domainname", getDomainName()); 2798 hostAttributes.Add("groups", getHostGroups()); 2799 hostAttributes.Add("lcid", getLocale()); 2800 hostAttributes.Add("lcidOS", getLocaleOS()); 2801 2802 // Print information found for debug purposes. 2803 dinfo("Host properties: " 2804 + "hostname='" + hostAttributes.Item("hostname") + "'\n" 2805 + "architecture='" + hostAttributes.Item("architecture") + "'\n" 2806 + "os='" + hostAttributes.Item("os") + "'\n" 2807 + "ipaddresses='" + hostAttributes.Item("ipaddresses").join(",") + "'\n" 2808 + "domain name='" + hostAttributes.Item("domainname") + "'\n" 2809 + "groups='" + hostAttributes.Item("groups").join(",") + "'\n" 2810 + "lcid='" + hostAttributes.Item("lcid") + "'\n" 2811 + "lcidOS='" + hostAttributes.Item("lcidOS") + "'" 2812 ); 2813 } 2814 return hostAttributes; 2815 } 2816 2817 /** 2818 * Accepts a list of XML nodes (Array of XML nodes) which is then filtered for 2819 * XML nodes which either do not specify specific host matches or all specified 2820 * attributes match the current host. For example the following XML nodes would 2821 * match: 2822 * 2823 * E.g. 2824 * 2825 * <pre> 2826 * <host name="nodename"; os="windows"; attributeX="value" profile-id="default" /> 2827 * <host name="nodename" profile-id="default" /> 2828 * <package os="windows" package-id="value" ipaddresses="192\.168\.1\..*" /> 2829 * <package package-id="value" /> 2830 * </pre> 2831 * 2832 * The last example matches since there is no limitation to host attributes in the definition. 2833 * 2834 * The return value will be an Array object listing only the XML nodes which 2835 * match. 2836 * 2837 * @param xmlNodes 2838 * Array of XML nodes which shall be verified for current host match. 2839 * @param getAllMatches 2840 * If set to true returns all matches. If set to false just returns the first matching node from xmlNodes. In this case the return array will contain only one element (or 0 if no match was found). 2841 * @returns Array of XML nodes which match the current host. 2842 */ 2843 function filterConditionalNodes(xmlNodes, getAllMatches) { 2844 // Create array to store the XML nodes which match this host. 2845 var applyingNodes = new Array(); 2846 2847 if(getAllMatches == null) { 2848 getAllMatches = true; 2849 } 2850 2851 // Check if xmlNode array passed as argument is valid 2852 if (xmlNodes == null || xmlNodes.length <= 0) { 2853 return applyingNodes; 2854 } 2855 2856 // Fetch current host attributes. 2857 var globalHostInformation = getHostInformation(); 2858 2859 // Add "environment" key since we want to support environment matching too. 2860 var hostInformation = new ActiveXObject("Scripting.Dictionary"); 2861 var keys = globalHostInformation.keys().toArray(); 2862 for (var i=0; i<keys.length; i++) { 2863 hostInformation.Add(keys[i], globalHostInformation.Item(keys[i])); 2864 } 2865 hostInformation.Add("environment", ""); 2866 2867 // Check all nodes whether they match the current host. 2868 for (var i=0; i < xmlNodes.length; i++) { 2869 var xmlNode = xmlNodes[i]; 2870 if (xmlNode == null) { 2871 // Skip to next node 2872 continue; 2873 } 2874 // Set to true if all host attributes from XML specification match 2875 // this host. 2876 var hostMatchFound = true; 2877 2878 // Fetch all XML attributes which correspond to a defined host property. 2879 var xmlNodeAttrs = new ActiveXObject("Scripting.Dictionary"); 2880 for (var iAttribute=0; iAttribute < xmlNode.attributes.length; iAttribute++) { 2881 if( hostInformation.Item(xmlNode.attributes[iAttribute].name) != null ) { 2882 xmlNodeAttrs.Add(xmlNode.attributes[iAttribute].name, xmlNode.attributes[iAttribute].value); 2883 } 2884 } 2885 2886 // Check whether all of the attributes match the current host. 2887 var attrsKeys = xmlNodeAttrs.keys().toArray(); 2888 for (var iAttr=0; iAttr<attrsKeys.length; iAttr++) { 2889 var xmlNodeAttrName = attrsKeys[iAttr]; 2890 var xmlNodeAttrValue = xmlNodeAttrs.Item(xmlNodeAttrName); 2891 2892 // Check whether the attribute matches the current host. 2893 var attributeMatchFound = checkHostAttribute(xmlNodeAttrName, xmlNodeAttrValue); 2894 2895 // Verify if the attribute does match to current host. 2896 if (attributeMatchFound != true) { 2897 // No match found. Advance to next host. 2898 dinfo("No value of '" + xmlNodeAttrName + "' matched '" + xmlNodeAttrValue + "'. Skipping to next definition."); 2899 hostMatchFound = false; 2900 break; 2901 } 2902 /* 2903 * else { // This attribute matched, continue with next attribute hostMatchFound = true; continue; } 2904 */ 2905 } 2906 2907 // If not all attributes match the current host definition then the node is not included. 2908 // All nodes which do not specify advanced host match attributes are included too. 2909 if (hostMatchFound) { 2910 // All attributes matched 2911 2912 // Print some debug information about which extended host attributes matched. 2913 if (xmlNodeAttrs.count > 0) { 2914 var attrsKeys = xmlNodeAttrs.keys().toArray(); 2915 var attrDesc = new Array(); 2916 for (var iAttrKeys=0; iAttrKeys<attrsKeys.length; iAttrKeys++) { 2917 attrDesc.push(attrsKeys[iAttrKeys] + "=" + xmlNodeAttrs.Item(attrsKeys[iAttrKeys])); 2918 } 2919 dinfo("XML node with special host attribute match found: " + attrDesc.join(", ")); 2920 } 2921 2922 // Verify if the XML node has a <condition /> sub-node 2923 var conditionMatched = true; 2924 var conditionNode = getConditions(xmlNode); 2925 if (conditionNode != null) { 2926 for (var iCond=0; iCond < conditionNode.length; iCond++) { 2927 var condition = conditionNode[iCond]; 2928 // Run all checks 2929 conditionMatched = checkAll(getChecks(condition)); 2930 if (conditionMatched) { 2931 dinfo("Additional conditions matched successfully."); 2932 } else { 2933 conditionMatched = false; 2934 dinfo("Additional conditions did not match."); 2935 break; 2936 } 2937 } 2938 } 2939 2940 // Insert node to list of matched nodes. 2941 if (conditionMatched) { 2942 applyingNodes.push(xmlNode); 2943 if (!getAllMatches) { 2944 dinfo("Single-match mode. Host match finished."); 2945 break; 2946 } 2947 } 2948 } else { 2949 dinfo("Could not match all attributes of XML node to current host. Skipping to next definition."); 2950 } 2951 } 2952 2953 return applyingNodes; 2954 } 2955 2956 /** 2957 * Retrieves host nodes from given "hosts" XML documents. Searches for nodes 2958 * having matching attributes and returns their array. 2959 * 2960 * First matching host node is returned by default. If switch /applymultiple is 2961 * used all matching host nodes are returned. 2962 * 2963 * @return returns the first matching host XML node or the list of all matching 2964 * host XML nodes if applymultiple is true. Returns null if no host node 2965 * matches. 2966 */ 2967 function getHostsApplying() { 2968 if (applyingHostNodes == null) { 2969 // Create new array to store matching hosts. 2970 hostNodesApplying = new Array(); 2971 2972 // Get available host definitions. 2973 var hostNodes = getHostNodes(); 2974 2975 // Check each node independently. 2976 for (var iHost=0; iHost < hostNodes.length; iHost++) { 2977 var hostNode = hostNodes[iHost]; 2978 2979 // Check conditions to determine whether the host definition is 2980 // applied. 2981 var previousEnv = getEnv(); 2982 var variables = getVariables(hostNode, null); 2983 2984 // Apply variables to environment. 2985 for (var iVariable=0; iVariable < variables.length; iVariable++) { 2986 var varDefinition = variables[iVariable]; 2987 var variableKeys = varDefinition.keys().toArray(); 2988 for (var iVarKey = 0; iVarKey < variableKeys.length; iVarKey++) { 2989 var key = variableKeys[iVarKey]; 2990 var value = varDefinition.Item(key); 2991 setEnv(key, value); 2992 } 2993 } 2994 2995 // Checkthis host node for special conditions. 2996 var hostList = new Array(); 2997 hostList.push(hostNode); 2998 hostList = filterConditionalNodes(hostList, true); 2999 if (hostList.length < 1) { 3000 // Restore environment. 3001 loadEnv(previousEnv); 3002 // Skipt to next host node. 3003 continue; 3004 } 3005 3006 // Get host name attribute. 3007 var hostNameAttribute = getHostNameAttribute(hostNode); 3008 3009 if (hostNameAttribute != null && hostNameAttribute != "") { 3010 // Try direct match first (non-regular-expression matching). 3011 if (hostNameAttribute.toUpperCase() == getHostname().toUpperCase()) { 3012 // Append host to applying hosts. 3013 hostNodesApplying.push(hostNode); 3014 3015 } else { 3016 3017 // Flag to check if IP-address match succeeded. 3018 var ipMatchSuccess = false; 3019 try { 3020 // Try IPv4-address matching. 3021 // Get IPv4 addresses (might be multiple). 3022 var ipAddresses = getIPAddresses(); 3023 3024 // check for each address if a host node matches 3025 // try non-regular-expression matching 3026 for (var iIPAdresses=0; iIPAdresses < ipAddresses.length; iIPAdresses++) { 3027 var ipAddress = ipAddresses[iIPAdresses]; 3028 3029 // splitvalues 3030 // dinfo("Trying to match IP '" + ipAddress + "' to " + 3031 // "'" + matchPattern + "'"); 3032 var splitIP = ipAddress.split("."); 3033 var splitPattern = hostNameAttribute.split("."); 3034 // check if format was correct 3035 if (splitIP.length == 4 && 3036 splitPattern.length == 4) { 3037 var firstValue = 0; 3038 var secondValue = 0; 3039 var match = true; 3040 for (var k=0; k<splitIP.length; k++) { 3041 // get first range value 3042 var ipOctet = parseInt(splitIP[k]); 3043 var matchOctet = splitPattern[k]; 3044 3045 // check if ip octet defines a range 3046 var splitMatchOctet = matchOctet.split("-"); 3047 firstValue = parseInt(splitMatchOctet[0]); 3048 if (splitMatchOctet.length > 1) { 3049 secondValue = parseInt(splitMatchOctet[1]); 3050 } else { 3051 secondValue = firstValue; 3052 } 3053 if (firstValue > secondValue) { 3054 // swap values 3055 var temp = firstValue; 3056 firstValue = secondValue; 3057 secondValue = temp; 3058 } 3059 // let's finally see if the ip octet is outside the range 3060 if ((ipOctet < firstValue || ipOctet > secondValue)) { 3061 // if octet did not match the requirements 3062 // dinfo("no match!"); 3063 match = false; 3064 // no need to continue 3065 break; 3066 } 3067 } 3068 // If all matched, take this profile. 3069 if (match) { 3070 dinfo("Found host '" + hostNameAttribute + 3071 "' matching IP '" + ipAddress + "'"); 3072 // Append host to applying hosts. 3073 hostNodesApplying.push(hostNode); 3074 ipMatchSuccess = true; 3075 break; 3076 } 3077 } 3078 } 3079 } catch(e) { 3080 ipMatchSuccess = false; 3081 dinfo("IP-Address match failed: " + e.description); 3082 } 3083 3084 // If we still got no match with, then try regular expression matching. 3085 if (!ipMatchSuccess) { 3086 try { 3087 var hostNameAttributeMatcher = new RegExp("^" + hostNameAttribute + "$", "i"); 3088 3089 if (hostNameAttributeMatcher.test(getHostname()) == true) { 3090 hostNodesApplying.push(hostNode); 3091 } 3092 } catch (e) { 3093 warning("Invalid regular expression for host name matching: '" + 3094 hostNameAttribute + "'."); 3095 } 3096 } 3097 } 3098 3099 } else { 3100 3101 // Host "name" attribute is missing or empty. Include host as potential match. 3102 // This allows to filter this host later using extended host matching 3103 hostNodesApplying.push(hostNode); 3104 } 3105 3106 // Restore environment. 3107 loadEnv(previousEnv); 3108 } 3109 3110 // Filter host nodes by matching them to the local host. 3111 // hostNodesApplying = filterConditionalNodes(hostNodesApplying, isApplyMultiple()); 3112 3113 // Matches might have returned multiple matching results. In case of 3114 // single-matching mode (default) only the first result shall be 3115 // returned 3116 if (!isApplyMultiple() && hostNodesApplying.length > 1) { 3117 var applyingHostNode = hostNodesApplying[0]; 3118 hostNodesApplying = new Array(); 3119 hostNodesApplying.push(applyingHostNode); 3120 } 3121 3122 if (hostNodesApplying.length <= 0) { 3123 hostNodesApplying = null; 3124 throw new Error("Unable to find any matching host definition!"); 3125 } 3126 applyingHostNodes = hostNodesApplying; 3127 } 3128 3129 return applyingHostNodes; 3130 } 3131 3132 /** 3133 * Returns an array of host nodes which specify the host regular expression and 3134 * the corresponding profile 3135 */ 3136 function getHostNodes() { 3137 return getHosts().selectNodes("host"); 3138 } 3139 3140 /** 3141 * Returns the profile-id associated with the given host node. 3142 * The node structure is defined as follows: 3143 * 3144 * The profile-id or the enclosed <profile... /> nodes might be omitted but not 3145 * both! 3146 * 3147 * @param hostNode XML node of the host definition 3148 * @return array of strings with referenced profiles 3149 * (array might be of length 0 if no profiles are defined) 3150 */ 3151 function getHostProfiles(hostNode) { 3152 // create array to store profile IDs 3153 var profileList = new Array(); 3154 3155 // try to receive profile ID from host node 3156 var profileID = hostNode.getAttribute("profile-id"); 3157 3158 if (profileID != null) { 3159 // convert to lower case if case-sensitivity is off 3160 if (!isCaseSensitive()) { 3161 profileList.push(profileID.toLowerCase()); 3162 } else { 3163 profileList.push(profileID); 3164 } 3165 } 3166 3167 // Load host definition environment (environment might be used in condition 3168 // checks. 3169 var previousEnv = getEnv(); 3170 var variables = getVariables(hostNode, null); 3171 3172 // Apply variables to environment. 3173 for (var iVariable=0; iVariable < variables.length; iVariable++) { 3174 var varDefinition = variables[iVariable]; 3175 var variableKeys = varDefinition.keys().toArray(); 3176 for (var iVarKey = 0; iVarKey < variableKeys.length; iVarKey++) { 3177 var key = variableKeys[iVarKey]; 3178 var value = varDefinition.Item(key); 3179 setEnv(key, value); 3180 } 3181 } 3182 3183 var profileNodes = hostNode.selectNodes("profile"); 3184 if (profileNodes != null) { 3185 // Get only dependencies which match the current host. 3186 var matchingProfileNodes = filterConditionalNodes(profileNodes, true); 3187 for (var iProfile=0; iProfile<matchingProfileNodes.length; iProfile++) { 3188 var profileNode = matchingProfileNodes[iProfile]; 3189 // get id attribute 3190 var profileId = profileNode.getAttribute("id"); 3191 3192 // convert to lower case if case-sensitivity is off 3193 if (!isCaseSensitive()) { 3194 profileList.push(profileId.toLowerCase()); 3195 } else { 3196 profileList.push(profileId); 3197 } 3198 } 3199 } 3200 3201 // Restore environment. 3202 loadEnv(previousEnv); 3203 3204 if (profileList.length > 0) { 3205 var message = "Profiles applying to the current host:\n"; 3206 for (var iProfileIndex=0; iProfileIndex<profileList.length; iProfileIndex++) { 3207 message += profileList[iProfileIndex] + "\n"; 3208 } 3209 dinfo(message); 3210 } else { 3211 error("No profiles assigned to the current host!"); 3212 } 3213 3214 return profileList; 3215 } 3216 3217 /** 3218 * Returns XML node which contains all host configurations 3219 */ 3220 function getHosts() { 3221 if(hosts == null) { 3222 var newHosts = createHosts(); 3223 setHosts(newHosts); 3224 } 3225 return hosts; 3226 } 3227 3228 /** 3229 * Returns a list of variables from the applying hosts definition. 3230 * 3231 * @param array 3232 * Object of type Array to which the the variables appended. 3233 * In case null is supplied it returns a new Array object. 3234 * @return Object of type Scripting.Dictionary which contains all key/value 3235 * pairs from the applying hosts. 3236 */ 3237 function getHostsVariables(array) { 3238 dinfo("Reading variables from hosts[s]"); 3239 3240 // Fetch host definitions which apply to current host. 3241 if (hostsVariables == null) { 3242 hostsVariables = new Array(); 3243 var hostNodes = getHostsApplying() ; 3244 for (var iHostNode=0; iHostNode < hostNodes.length; iHostNode++) { 3245 var hostNode = hostNodes[iHostNode]; 3246 dinfo("Reading variables from host: " + getHostNodeDescription(hostNode)); 3247 3248 // Add variables from host XML node. 3249 hostsVariables = getVariables(hostNode, hostsVariables); 3250 } 3251 } 3252 3253 // Concatenate variable list if list was passed as parameter. 3254 var concatenatedVariables = hostsVariables; 3255 if (array != null) { 3256 // concatenatedVariables = concatenateDictionary(dictionary, hostsVariables); 3257 concatenatedVariables = hostsVariables.concat(array); 3258 } 3259 3260 return concatenatedVariables; 3261 } 3262 3263 /** 3264 * Returns the corresponding string defined within the configuration. 3265 * 3266 * @param stringID 3267 * the identification of the corresponding string as listed within 3268 * the configuration 3269 * 3270 * @return returns the string as it appears within the configuration. Returns 3271 * null if the string id is not defined. 3272 */ 3273 function getLocalizedString(stringID) { 3274 if (languageNode == null && getConfig() != null) { 3275 // read node which contains all the strings 3276 var languagesNodes = getConfig().selectNodes("languages"); 3277 3278 if (languagesNodes != null) { 3279 // there might be multiple languages nodes 3280 for (var i=0; i < languagesNodes.length; i++) { 3281 // get language nodes 3282 var languageNodes = languagesNodes[i].selectNodes("language"); 3283 3284 for (var j=0; j < languageNodes.length && languageNode == null; j++) { 3285 var currentLangNode = languageNodes[j]; 3286 3287 // get associated language LCIDs 3288 var lcidString = currentLangNode.getAttribute("lcid"); 3289 var lcids = lcidString.split(","); 3290 for (var k=0; k < lcids.length; k++) { 3291 // check if it corresponds to the system LCID 3292 var currentLcid = trimLeadingZeroes(trim(lcids[k])); 3293 if (currentLcid == getLocale()) { 3294 dinfo("Found language definition node for language ID " + currentLcid); 3295 languageNode = currentLangNode; 3296 break; 3297 } 3298 } 3299 } 3300 } 3301 } 3302 3303 } 3304 3305 // check if language has not been found 3306 if (languageNode == null) { 3307 // create empty node 3308 languageNode = createXml("language"); 3309 } 3310 3311 // try to find node matching the requested sting id 3312 var stringNode = languageNode.selectSingleNode("string[@id='" + stringID + "']"); 3313 if (stringNode != null) { 3314 return stringNode.text; 3315 } else { 3316 dinfo("No locale language definition found for message ID '" + stringID + 3317 "' (language LCID '" + getLocale() + "')."); 3318 return null; 3319 } 3320 } 3321 3322 /** 3323 * Returns array of package IDs which includes package IDs of chained packages. 3324 * Returns empty array in case the package does not have any chained packages. 3325 * 3326 * @param packageNode 3327 * the package node to read the list of chained packages from 3328 * @param packageList 3329 * optional reference to an array which is used to insert the chained 3330 * packages to. Specify null to create a new Array 3331 * @return Array specified in packageList parameter extended by package IDs 3332 * (string values) which represent the chained packages 3333 */ 3334 function getPackageChained(packageNode, packageList) { 3335 // output array 3336 if (packageList == null) { 3337 packageList = new Array(); 3338 } 3339 3340 if(packageNode != null) { 3341 var includeNodes = packageNode.selectNodes("chain"); 3342 if (includeNodes != null) { 3343 matchingChainNodes = filterConditionalNodes(includeNodes, true); 3344 for (var i=0; i < matchingChainNodes.length; i++) { 3345 var dependId = matchingChainNodes[i].getAttribute("package-id"); 3346 3347 // convert to lower case if case-insensitive mode is on 3348 if (dependId != null) { 3349 if (!isCaseSensitive()) { 3350 dependId = dependId.toLowerCase(); 3351 } 3352 packageList.push(dependId); 3353 } 3354 } 3355 } 3356 } 3357 3358 return packageList; 3359 } 3360 3361 /** 3362 * Defines how package checks are used during package installation. 3363 * 3364 * Currently supported values: 3365 * 3366 * "always" (default): 3367 * When a package is new to the host then first the checks are run in order to 3368 * verify whether the package is already installed. If the checks succeed then 3369 * it is assumed that no further installation is needed. The package is silently 3370 * added to the host without executing any commands. 3371 * 3372 * "never": 3373 * When a package is new to the host then the install commands are run in any 3374 * case (without doing checks first). Note: Checks will still be done after 3375 * package installation to verify whether installation was successful. 3376 * 3377 * @param packageNode Package XML node to read attribute from. 3378 * @returns "always" or "never" according to precheck-install attribute of 3379 * package. 3380 */ 3381 function getPackagePrecheckPolicyInstall(packageNode) { 3382 var checkPolicy = "always"; 3383 var installCheckPolicy = packageNode.getAttribute("precheck-install"); 3384 if (installCheckPolicy != null) { 3385 checkPolicy = installCheckPolicy; 3386 } 3387 return checkPolicy; 3388 } 3389 3390 /** 3391 * Defines how package checks are used during package removal. 3392 * 3393 * Currently supported values: 3394 * 3395 * "always": 3396 * When a package is removed from a host then the checks will be executed 3397 * before removal is processes. If the checks fail this potentially means that 3398 * the package has been removed already. In such case the package remove 3399 * commands will be skipped. 3400 * 3401 * "never" (default): 3402 * When a package is about to be removed from the host then WPKG will execute 3403 * the remove commands in any case without executing the checks first. 3404 * Note: Checks will still be done after package removal to verify whether the 3405 * removal was successful. 3406 * 3407 * @param packageNode Package XML node to read attribute from. 3408 * @returns "always" or "never" according to precheck-remove attribute of 3409 * package. 3410 */ 3411 function getPackagePrecheckPolicyRemove(packageNode) { 3412 var checkPolicy = "never"; 3413 var removeCheckPolicy = packageNode.getAttribute("precheck-remove"); 3414 if (removeCheckPolicy != null) { 3415 checkPolicy = removeCheckPolicy; 3416 } 3417 return checkPolicy; 3418 } 3419 3420 /** 3421 * Defines how package checks are used during package upgrade. 3422 * 3423 * Currently supported values: 3424 * 3425 * "always": 3426 * When a package is upgraded the checks specified will be be executed before 3427 * the upgrade takes place. If checks succeed, then the upgrade will not be 3428 * performed (WPKG just assumes that the new version is already applied 3429 * correctly. 3430 * Please note that your checks shall verify a specific software version and 3431 * not just a generic check which is true for all versions. If your checks 3432 * are true for the old version too then WPKG would never perform the upgrade 3433 * in this mode. 3434 * 3435 * "never" (default): 3436 * When a package is about to be upgraded then WPKG will execute the upgrade 3437 * commands in any case without executing the checks first. This is the 3438 * recommended behavior. 3439 * Note: Checks will still be done after package upgrade to verify whether the 3440 * upgrade was successful. 3441 * 3442 * @param packageNode Package XML node to read attribute from. 3443 * @returns "always" or "never" according to precheck-upgrade attribute of 3444 * package. 3445 */ 3446 function getPackagePrecheckPolicyUpgrade(packageNode) { 3447 var checkPolicy = "never"; 3448 var upgradeCheckPolicy = packageNode.getAttribute("precheck-upgrade"); 3449 if (upgradeCheckPolicy != null) { 3450 checkPolicy = upgradeCheckPolicy; 3451 } 3452 return checkPolicy; 3453 } 3454 3455 /** 3456 * Defines how package checks are used during package downgrade. 3457 * 3458 * Currently supported values: 3459 * 3460 * "always": 3461 * When a package is downgraded the checks specified will be be executed before 3462 * the downgrade takes place. If checks succeed, then the downgrade will not be 3463 * performed (WPKG just assumes that the old version is already applied 3464 * correctly. 3465 * Please note that your checks shall verify a specific software version and 3466 * not just a generic check which is true for all versions. If your checks 3467 * are true for the new/current version too then WPKG would never perform the 3468 * downgrade in this mode. 3469 * 3470 * "never" (default): 3471 * When a package is about to be downgraded then WPKG will execute the 3472 * downgrade commands in any case without executing the checks first. This is 3473 * the recommended behavior. 3474 * Note: Checks will still be done after package downgrade to verify whether 3475 * the downgrade was successful. 3476 * 3477 * @param packageNode Package XML node to read attribute from. 3478 * @returns "always" or "never" according to precheck-downgrade attribute of 3479 * package. 3480 */ 3481 function getPackagePrecheckPolicyDowngrade(packageNode) { 3482 var checkPolicy = "never"; 3483 var downgradeCheckPolicy = packageNode.getAttribute("precheck-downgrade"); 3484 if (downgradeCheckPolicy != null) { 3485 checkPolicy = downgradeCheckPolicy; 3486 } 3487 return checkPolicy; 3488 } 3489 3490 /** 3491 * Returns an array of <check /> XML sub-nodes on a given XML node. 3492 * In case extended host matching attributes are used only the checks which match the 3493 * current host are returned. 3494 * 3495 * @param xmlNode The XML node from which all 'check' sub-nodes are read 3496 * @return Array of XML nodes containing all 'check'-nodes which match to the current host. 3497 * Returns empty array if no checks are defined. 3498 * If extended host matching attributes like "hostname", "os" or similar are used 3499 * then checks which do not match the current host are not returned. 3500 */ 3501 function getChecks(xmlNode) { 3502 var checkNodes = xmlNode.selectNodes("check"); 3503 /* 3504 var checkNodes = xmlNode.selectNodes("wpkg:check"); 3505 if (checkNodes.length <= 0) { 3506 // Maybe amespace was wrongly specified. 3507 // Try default namespace. 3508 checkNodes = xmlNode.selectNodes("check"); 3509 } 3510 */ 3511 return filterConditionalNodes(checkNodes); 3512 } 3513 3514 /** 3515 * This is a convenience-method to get all downgrade commands. 3516 * 3517 * @param packageNode 3518 * package XML node which contains 'downgrade' nodes 3519 * @return Array of 'downgrade' XML nodes, returns empty array if no nodes are 3520 * defined 3521 */ 3522 function getPackageCmdDowngrade(packageNode, includeChain) { 3523 // Fetch commands from package node. 3524 var commandNodes = getPackageCmd(packageNode, "downgrade", null); 3525 3526 // Return list of applying install commands. 3527 return commandNodes; 3528 } 3529 3530 /** 3531 * This is a convenience-method to get all install commands. 3532 * 3533 * @param packageNode 3534 * package XML node which contains 'install' nodes 3535 * @return Array of 'install' XML nodes, returns empty array if no nodes are 3536 * defined 3537 */ 3538 function getPackageCmdInstall(packageNode, includeChain) { 3539 // Fetch commands from package node. 3540 var commandNodes = getPackageCmd(packageNode, "install", null); 3541 3542 // Return list of applying install commands. 3543 return commandNodes; 3544 } 3545 3546 3547 /** 3548 * This is a convenience-method to get all remove commands. 3549 * 3550 * @param packageNode 3551 * package XML node which contains 'remove' nodes 3552 * @return Array of 'remove' XML nodes, returns empty array if no nodes are 3553 * defined 3554 */ 3555 function getPackageCmdRemove(packageNode, includeChain) { 3556 // Fetch commands from package node. 3557 var commandNodes = getPackageCmd(packageNode, "remove", null); 3558 3559 // Return list of applying install commands. 3560 return commandNodes; 3561 } 3562 3563 /** 3564 * This is a convenience-method to get all upgrade commands. 3565 * 3566 * @param packageNode 3567 * package XML node which contains 'remove' nodes 3568 * @return Array of 'upgrade' XML nodes, returns empty array if no nodes are 3569 * defined 3570 */ 3571 function getPackageCmdUpgrade(packageNode, includeChain) { 3572 // Fetch commands from package node. 3573 var commandNodes = getPackageCmd(packageNode, "upgrade", null); 3574 3575 // Return list of applying install commands. 3576 return commandNodes; 3577 } 3578 3579 3580 /** 3581 * Returns a list of commands which apply to the given command type. 3582 * Common types are 'install', 'upgrade', 'downgrade' or 'remove' but WPKG 3583 * allows any custom type definition within the commands/command XML structure. 3584 * For example it is possible to specify <command type="test-type" /> and then 3585 * receive all "test-type" commands using this method. 3586 * 3587 * @param packageNode 3588 * package XML node which contains command nodes. 3589 * @param type 3590 * Type description. Defines which command group to receive. 3591 * @param includeChain 3592 * Array of command types (install/upgrade/downgrade/remove) already 3593 * included. 3594 * This is used to detect inclusion loops (recursive inclusion). 3595 * @return Array of command XML nodes, returns empty array if no nodes are 3596 * defined 3597 */ 3598 function getPackageCmd(packageNode, type, includeChain) { 3599 // Verify input parameters. 3600 if (packageNode == null) { 3601 return null; 3602 } 3603 3604 // Type must be specified in order to get command group. 3605 if (type == null || type == "") { 3606 return null; 3607 } 3608 3609 var alreadyIncluded; 3610 if (includeChain == null) { 3611 alreadyIncluded = new Array(); 3612 } else { 3613 alreadyIncluded = includeChain; 3614 } 3615 alreadyIncluded.push(type); 3616 3617 // This variable holds the result set returned. 3618 var commandNodeList = new Array(); 3619 3620 // Fetch commands directly attached to package node 3621 var directCommandNodes = null; 3622 switch (type) { 3623 case "install": 3624 directCommandNodes = packageNode.selectNodes("install"); 3625 break; 3626 case "upgrade": 3627 directCommandNodes = packageNode.selectNodes("upgrade"); 3628 break; 3629 case "downgrade": 3630 directCommandNodes = packageNode.selectNodes("downgrade"); 3631 break; 3632 case "remove": 3633 directCommandNodes = packageNode.selectNodes("remove"); 3634 break; 3635 default: 3636 // Command type is none of the "default" types This command type is 3637 // supported in command nodes only. 3638 break; 3639 } 3640 3641 // Fetch command-nodes from <commands><command type="type" /></commands> structure. 3642 var commandNodes = packageNode.selectNodes("commands/command[@type=\"" + type + "\"]"); 3643 3644 // Merge command lists. 3645 if (directCommandNodes != null) { 3646 for (var iCmd=0; iCmd < directCommandNodes.length; iCmd++) { 3647 commandNodeList.push(directCommandNodes[iCmd]); 3648 } 3649 } 3650 if (commandNodes != null) { 3651 for (var iCmd=0; iCmd < commandNodes.length; iCmd++) { 3652 commandNodeList.push(commandNodes[iCmd]); 3653 } 3654 } 3655 3656 // Filter out all packages which do not apply to current host. 3657 commandNodeList = filterConditionalNodes(commandNodeList, true); 3658 3659 // Expand command includes. 3660 // Create array which is returned as a complete command list. 3661 var fullCommandList = new Array(); 3662 3663 // Check all commands for inclusion. 3664 for (var iTypeCommands=0; iTypeCommands<commandNodeList.length; iTypeCommands++) { 3665 var command = commandNodeList[iTypeCommands]; 3666 var include = getCommandInclude(command); 3667 3668 // Inclusion found. 3669 if (include != null) { 3670 dinfo("Found inclusion for command type " + include + "."); 3671 3672 // Clone array of already included command types which helps to 3673 // detect duplicated includes. 3674 // The same loop will check whether the type to be included has 3675 // already been included (recursive inclusion detection). 3676 var prevIncluded = new Array(); 3677 for (var j=0; j<alreadyIncluded.length; j++) { 3678 var includeElement = alreadyIncluded[j]; 3679 if (includeElement == include) { 3680 throw new Error("Recursive inclusion detected!"); 3681 } else { 3682 prevIncluded.push(alreadyIncluded[j]); 3683 } 3684 } 3685 3686 // Fetch commands of specified type (if any) 3687 var includedCommands = getPackageCmd(packageNode, include, prevIncluded); 3688 3689 // Insert fetched commands to command list. 3690 if (includedCommands != null) { 3691 for (var iIncCmds=0; iIncCmds<includedCommands.length; iIncCmds++) { 3692 fullCommandList.push(includedCommands[iIncCmds]); 3693 } 3694 } 3695 } else { 3696 // Include command in command-list. 3697 fullCommandList.push(command); 3698 } 3699 } 3700 3701 // Return list of applying commands. 3702 return fullCommandList; 3703 } 3704 3705 3706 /** 3707 * Returns array of package IDs which represent the package dependencies. 3708 * Returns empty array in case the package does not have any dependency. 3709 * 3710 * @param packageNode 3711 * the package node to read the list of dependencies from 3712 * @param packageList 3713 * optional reference to an array which is used to insert the 3714 * dependencies to. Specify null to create a new Array 3715 * @return Array specified in packageList parameter extended by package IDs 3716 * (string values) which represent the dependencies 3717 */ 3718 function getPackageDependencies(packageNode, packageList) { 3719 // output array 3720 if (packageList == null) { 3721 packageList = new Array(); 3722 } 3723 3724 if(packageNode != null) { 3725 var dependNodes = packageNode.selectNodes("depends"); 3726 if (dependNodes != null) { 3727 // Get only dependencies which match the current host. 3728 var matchingDependNodes = filterConditionalNodes(dependNodes, true); 3729 for (var i=0; i < matchingDependNodes.length; i++) { 3730 var dependId = matchingDependNodes[i].getAttribute("package-id"); 3731 3732 // convert to lower case if case-insensitive mode is on 3733 if (dependId != null) { 3734 if (!isCaseSensitive()) { 3735 dependId = dependId.toLowerCase(); 3736 } 3737 packageList.push(dependId); 3738 } 3739 } 3740 } 3741 } 3742 3743 return packageList; 3744 } 3745 3746 /** 3747 * Returns the package execute attribute value (String) 3748 * 3749 * @param packageNode 3750 * the package node to get the attribute from 3751 * @return package execute attribute value, empty string if undefined 3752 */ 3753 function getPackageExecute(packageNode) { 3754 var execAttr = packageNode.getAttribute("execute"); 3755 if (execAttr == null) { 3756 execAttr = ""; 3757 } 3758 return execAttr; 3759 } 3760 3761 /** 3762 * Returns the package ID string from the given package XML node. 3763 * 3764 * @return package ID 3765 */ 3766 function getPackageID(packageNode) { 3767 return packageNode.getAttribute("id"); 3768 } 3769 3770 /** 3771 * Returns array of package IDs which represent the package includes. Returns 3772 * empty array in case the package does not have any dependency. 3773 * 3774 * @param packageNode 3775 * the package node to read the list of includes from 3776 * @param packageList 3777 * optional reference to an array which is used to insert the 3778 * includes to. Specify null to create a new Array 3779 * @return Array specified in packageList parameter extended by package IDs 3780 * (string values) which represent the includes 3781 */ 3782 function getPackageIncludes(packageNode, packageList) { 3783 // output array 3784 if (packageList == null) { 3785 packageList = new Array(); 3786 } 3787 3788 if(packageNode != null) { 3789 var includeNodes = packageNode.selectNodes("include"); 3790 if (includeNodes != null) { 3791 var matchingIncludeNodes = filterConditionalNodes(includeNodes, true); 3792 for (var i=0; i < matchingIncludeNodes.length; i++) { 3793 var dependId = matchingIncludeNodes[i].getAttribute("package-id"); 3794 3795 // convert to lower case if case-insensitive mode is on 3796 if (dependId != null) { 3797 if (!isCaseSensitive()) { 3798 dependId = dependId.toLowerCase(); 3799 } 3800 packageList.push(dependId); 3801 } 3802 } 3803 } 3804 } 3805 3806 return packageList; 3807 } 3808 3809 /** 3810 * Reads the "manualInstall" attribute from a package node. 3811 * This attribute is true only if the package as installed manually via 3812 * command line. It is false for packages which are initially installed by 3813 * package synchronization. 3814 * 3815 * @param packageNode the package from which the attribute is read. 3816 * @returns {Boolean} True if package was installed manually, false if it is 3817 * applied by profile. 3818 */ 3819 function getPackageManualInstallation(packageNode) { 3820 // Initialize return variable. 3821 var isManualInstall = false; 3822 3823 // Read yctual value. 3824 var manualInstall = packageNode.getAttribute("manualInstall"); 3825 3826 // Evaluate result. 3827 if (manualInstall != null && manualInstall == "true") { 3828 isManualInstall = true; 3829 } 3830 return isManualInstall; 3831 } 3832 3833 /** 3834 * Returns the package name from the given package XML node 3835 * 3836 * @return returns the package name attribute - empty string if no name is 3837 * defined 3838 */ 3839 function getPackageName(packageNode) { 3840 var packageName = ""; 3841 if(packageNode != null) { 3842 packageName = packageNode.getAttribute("name"); 3843 if (packageName == null) { 3844 packageName = ""; 3845 } 3846 } 3847 return packageName; 3848 } 3849 3850 /** 3851 * Returns the corresponding package XML node from the package database 3852 * (packages.xml). Returns null in case no such package exists. 3853 */ 3854 function getPackageNode(packageID) { 3855 // get first node which matched the specified ID 3856 return getPackages().selectSingleNode("package[@id='" + packageID +"']"); 3857 } 3858 3859 /** 3860 * Returns the corresponding package XML node to the requested package ID by 3861 * searching the packages database first. If the package cannot be located 3862 * within the package database it prints an error and looks for the node within 3863 * the local settings database. 3864 * If even the local database does not contain such a package entry then it 3865 * prints an error about missing package definition. In case '/quitonerror' is 3866 * set it exits. 3867 * 3868 * If the package could be located within the local package database it prints 3869 * a warning and returns the local package node. 3870 * 3871 * Algorithmic description: 3872 * 3873 * <pre> 3874 * search package node within local package database 3875 * if found 3876 * return it 3877 * else 3878 * print warning 3879 * look for package within local settings 3880 * if found 3881 * print warning 3882 * return it 3883 * else 3884 * print error (or exit by throwing error in case of /quitonerror) 3885 * return null 3886 * fi 3887 * fi 3888 * </pre> 3889 */ 3890 function getPackageNodeFromAnywhere(packageID) { 3891 var packageNode = null; 3892 3893 // try to get package node from package database 3894 var packageDBNode = getPackageNode(packageID); 3895 3896 // check if node exists; if not then try to get the node from the settings 3897 if(packageDBNode != null) { 3898 // package found in package database, mark for installation/upgrade 3899 dinfo("Found package node '" + getPackageName(packageDBNode) + "' (" + 3900 getPackageID(packageDBNode) + ") in package database."); 3901 packageNode = packageDBNode; 3902 } else { 3903 // error package not in package database 3904 // looking for package node within the local settings file 3905 /* 3906 * var packageNotFoundMessage = "Profile inconsistency: Package '" + packageID + "' does not exist within the 3907 * package database. " + "Please contact your system administrator!"; 3908 * 3909 * warning(packageNotFoundMessage); 3910 */ 3911 3912 // try to get package node from local settings 3913 var packageSettingsNode = getSettingNode(packageID); 3914 3915 // if no package definition has been found jet the package is not 3916 // installed 3917 if(packageSettingsNode != null) { 3918 // Check if the package has been manually installed. 3919 var messageLocalOnly = ""; 3920 var isManualInstall = getPackageManualInstallation(packageSettingsNode); 3921 if (isManualInstall == true) { 3922 messageLocalOnly = "Manually installed package not found in server database."; 3923 } else { 3924 messageLocalOnly = "Database inconsistency: Package with package ID '" + 3925 packageID + "' missing in package database. Package information " + 3926 "found on local installation:\n"; 3927 } 3928 messageLocalOnly += "Package ID: " + messageLocalOnly + "\n" + 3929 "Package Name: " + getPackageName(packageSettingsNode) + "\n" + 3930 "Package Revision: " + getPackageRevision(packageSettingsNode) + "\n"; 3931 warning(messageLocalOnly); 3932 packageNode = packageSettingsNode; 3933 } else { 3934 var messageNotFound = "Database inconsistency: Package with ID '" + packageID + 3935 "' does not exist within the package database or the local settings file. " + 3936 "Please contact your system administrator!"; 3937 if (isQuitOnError()) { 3938 throw new Error(messageNotFound); 3939 } else { 3940 error(messageNotFound); 3941 } 3942 } 3943 } 3944 3945 // return result 3946 return packageNode; 3947 } 3948 3949 /** 3950 * Returns an array of all package nodes that can be installed. This list 3951 * includes all packages found in the package database. It does not include 3952 * local packages from the settings file (currently installed ones). 3953 * 3954 * @return Array containing XML nodes (package nodes). Array might be of size 0 3955 */ 3956 function getPackageNodes() { 3957 // Retrieve packages. 3958 var packageNodes = getPackages().selectNodes("package"); 3959 3960 // make sure a package ID exists only once 3961 packageNodes = uniqueAttributeNodes(packageNodes, "id"); 3962 3963 // return this array 3964 return packageNodes; 3965 } 3966 3967 /** 3968 * Returns the package notify attribute value 3969 * 3970 * @param packageNode 3971 * the package node to get the notify attribute from 3972 * @return Notify attribute value (true in case of String "true" false 3973 * otherwise. 3974 */ 3975 function getPackageNotify(packageNode) { 3976 var returnvalue = true; 3977 var notify = packageNode.getAttribute("notify"); 3978 if (notify == "false") { 3979 returnvalue = false; 3980 } 3981 return returnvalue; 3982 } 3983 3984 /** 3985 * Returns the package priority from the given package XML node 3986 * 3987 * @return package priority - returns 0 if no priority is defined 3988 */ 3989 function getPackagePriority(packageNode) { 3990 var priority = packageNode.getAttribute("priority"); 3991 if (priority == null) { 3992 priority = 0; 3993 } 3994 return parseInt(priority); 3995 } 3996 3997 3998 /** 3999 * Returns the package reboot attribute value. This attribute can add 4000 * additional reboots but not limit or invalidate reboot flags set on the 4001 * command-level. 4002 * 4003 * This value can have three states: 4004 * 4005 * <pre> 4006 * "true" Immediate reboot after package installation. 4007 * This will take precedence of any command-level reboot="postponed" 4008 * attribute if present and reboot immediately after package 4009 * installation. 4010 * A reboot="true" attribute on command-level will still result in 4011 * an immediate reboot. 4012 * Resulting status depending on command-level reboot flag: 4013 * "true" immediate reboot after command execution 4014 * "delayed" reboot after package installation 4015 * "postponed" reboot after package installation 4016 * "false" reboot after package installation 4017 * "postponed" Schedule reboot after installing all packages within this 4018 * session, for example after synchronizing. 4019 * Resulting status depending on command-level reboot flag: 4020 * "true" immediate reboot after command execution 4021 * "delayed" reboot after package installation 4022 * "postponed" reboot after all actions are completed 4023 * "false" reboot after all actions are completed 4024 * "false" No reboot unless one is defined at command-level. 4025 * or not set Resulting status depending on command-level reboot flag: 4026 * "true" immediate reboot after command execution 4027 * "delayed" reboot after package installation 4028 * "postponed" reboot after all actions are completed 4029 * "false" no reboot 4030 * </pre> 4031 * 4032 * As a result there are four possibilities to schedule a reboot in order of 4033 * precedence: 4034 * 4035 * <pre> 4036 * immediate Command node specified reboot=true, immediate reboot takes place. 4037 * package Reboot is issued right after installing: 4038 * - package specifies reboot="true" 4039 * OR 4040 * - any command node specified reboot="delayed" 4041 * postponed Reboot will take place after all packages have been applied. 4042 * - package specifies reboot="postponed" 4043 * OR 4044 * - any command node specified reboot="postponed" 4045 * none No reboot is issued by this package: 4046 * - package does not specify reboot or specifies reboot="false" 4047 * AND 4048 * - no command node specified any form of reboot reboot 4049 * </pre> 4050 * 4051 * This means that an immediate reboot always has the highest priority. You 4052 * can just set "reboot markers" on a "timeline" on package and command level 4053 * where the closest reboot marker will be executed: 4054 * immediate => package => postponed => none 4055 * 4056 * @return one of the states (string values): 4057 * "true", always reboot after package installation 4058 * "postponed", reboot before script exits 4059 * "false", reboot only if command specified reboot=delayed/postponed 4060 * 4061 */ 4062 function getPackageReboot(packageNode) { 4063 var rebootAction = "false"; 4064 var packageReboot = packageNode.getAttribute("reboot"); 4065 if (packageReboot != null) { 4066 if (packageReboot == "true") { 4067 rebootAction = packageReboot; 4068 } else if (packageReboot == "postponed") { 4069 rebootAction = packageReboot; 4070 } 4071 } 4072 return rebootAction; 4073 } 4074 4075 /** 4076 * Adds all packages referenced by the specified package node to the given 4077 * array. In other words all dependencies, chained packages and includes of the 4078 * given node will be appended to the array. If you specify null or an empty 4079 * array the array returned will contain all packages from the dependency tree 4080 * of the given package node. 4081 * 4082 * @param packageNode 4083 * full dependency tree of the specified package will be added to the 4084 * given array. 4085 * @param packageArray 4086 * Array to which all referenced packages are added to. Specify null 4087 * to create a new array finally containing only the dependency tree 4088 * of the specified package. 4089 * @return array containing all referenced packages (full package nodes). NOTE: 4090 * The returned array is not sorted. 4091 */ 4092 function getPackageReferences(packageNode, packageArray) { 4093 if (packageArray == null) { 4094 packageArray = new Array(); 4095 } 4096 4097 // get dependencies, includes and chains 4098 var linkedPackageIDs = getPackageDependencies(packageNode, null); 4099 getPackageIncludes(packageNode, linkedPackageIDs); 4100 getPackageChained(packageNode, linkedPackageIDs); 4101 4102 // add nodes if they are not yet part of the array 4103 for (var i=0; i < linkedPackageIDs.length; i++) { 4104 var currentNode = getPackageNodeFromAnywhere(linkedPackageIDs[i]); 4105 if (currentNode != null) { 4106 if(!searchArray(packageArray, currentNode)) { 4107 dinfo("Adding referenced package '" + getPackageName(currentNode) + "' (" + 4108 getPackageID(currentNode) + ") for package '" + 4109 getPackageName(packageNode) + "' (" + getPackageID(packageNode) + 4110 ")"); 4111 // add the package first (so it's not added again, this prevents 4112 // loops) 4113 packageArray.push(currentNode); 4114 4115 // add dependencies of these package as well 4116 getPackageReferences(currentNode, packageArray); 4117 } else { 4118 dinfo("Referenced package '" + getPackageName(currentNode) + "' (" + 4119 getPackageID(currentNode) + ") for package '" + 4120 getPackageName(packageNode) + "' (" + getPackageID(packageNode) + 4121 ") already added."); 4122 } 4123 } 4124 } 4125 } 4126 4127 /** 4128 * Returns the package version string from the given package XML node. Returns 0 4129 * if package has no revision specified. 4130 * 4131 * @return String representing the package revision (might be a dot-separated 4132 * version) <#>[.<#>]* 4133 */ 4134 function getPackageRevision(packageNode) { 4135 var packageRevision = packageNode.getAttribute("revision"); 4136 if (packageRevision == null) { 4137 // set to string "0" if no revision is defined 4138 packageRevision = 0 + ""; 4139 } else { 4140 // check if the revision contains the "%" character (environment 4141 // variable) 4142 if( packageRevision.match(new RegExp("%.+%"), "ig") ) { 4143 // Generate the correct environment. 4144 var previousEnv = getEnv(); 4145 4146 // set package specific environment 4147 loadPackageEnv(packageNode); 4148 4149 // expand environment strings 4150 var wshObject = new ActiveXObject("WScript.Shell"); 4151 packageRevision = wshObject.ExpandEnvironmentStrings(packageRevision); 4152 4153 // reset environment 4154 loadEnv(previousEnv); 4155 } 4156 } 4157 return packageRevision; 4158 } 4159 4160 /** 4161 * Returns XML node which contains all packages (package database). 4162 */ 4163 function getPackages() { 4164 if(packages == null) { 4165 var newPackages = createPackages(); 4166 setPackages(newPackages); 4167 } 4168 return packages; 4169 } 4170 4171 /** 4172 * Returns the action to be performed on a given package if the package is 4173 * applied to the current host. 4174 * Valid actions are: 4175 * "none" No action; package installed already 4176 * "install" Installation, package is new on the host 4177 * "upgrade" Upgrade package which already exists on the system 4178 * New version higher than installed version 4179 * "downgrade" Downgrade package which already exists on the system 4180 * New version lower than installed version 4181 * 4182 * @param packageNode 4183 * The package to be checked. 4184 * @returns Action to be performed. Can be 0=nothing, 1=install, 2=upgrade, 3=downgrade. 4185 */ 4186 function getPackageInstallAction(packageNode) { 4187 // Action to be performed when 4188 var actionNone = "none"; 4189 var actionInstall = "install"; 4190 var actionUpgrade = "upgrade"; 4191 var actionDowngrade = "downgrade"; 4192 var action = actionNone; 4193 4194 var packageName = getPackageName(packageNode); 4195 var packageID = getPackageID(packageNode); 4196 var packageRev = getPackageRevision(packageNode); 4197 var executeAttr = getPackageExecute(packageNode); 4198 // var notifyAttr = getPackageNotify(packageNode); 4199 4200 // Search for the package in the local settings. 4201 var installedPackage = getSettingNode(packageID); 4202 4203 // String to print in events which identifies the package. 4204 var packageMessage = "Package '" + packageName + "' (" + packageID + "): "; 4205 4206 // Evaluate type of installation (install/upgrade/downgrade/none). 4207 // INSTALL: 4208 if (installedPackage == null) { 4209 // ONE-TIME INSTALL PACKAGE, NOT INSTALLED YET (according to settings) 4210 // Install the package after checking that it is not installed already. 4211 dinfo(packageMessage + "Not in local package database; Marking for installation."); 4212 action = actionInstall; 4213 4214 // UPGRADE/DOWNGRADE: 4215 } else { 4216 // Get revision of installed package. 4217 var packageRevInstalled = getPackageRevision(installedPackage); 4218 // Compare Versions. 4219 var comparisonResult = versionCompare(packageRev, packageRevInstalled); 4220 4221 if (comparisonResult > 0) { 4222 // ONE-TIME INSTALL PACKAGE, UPGRADE: 4223 info(packageMessage + 4224 "Already installed but version mismatch.\n" + 4225 "Installed revision: '" + packageRevInstalled + "'\n" + 4226 "Available revision: '" + packageRev + "'.\n" + 4227 "Preparing upgrade." 4228 ); 4229 action = actionUpgrade; 4230 4231 } else if (comparisonResult < 0) { 4232 // ONE-TIME INSTALL PACKAGE, DOWNGRADE: 4233 info(packageMessage + 4234 "Already installed but version mismatch.\n" + 4235 "Installed revision '" + packageRevInstalled + "'\n" + 4236 "Available revision: '" + packageRev + "'.\n" + 4237 "Preparing downgrade." 4238 ); 4239 action = actionDowngrade; 4240 4241 } else { 4242 // ONE-TIME INSTALL PACKAGE, ALREADY INSTALLED: 4243 4244 if (executeAttr == "always") { 4245 // ALWAYS EXECUTION PACKAGE 4246 // Packages with exec attribute "always" will be installed on each run; regardless of their version. 4247 dinfo(packageMessage + "Is requested to be executed 'always'. Preparing installation."); 4248 action = actionInstall; 4249 4250 } else if (isForceInstall()) { 4251 // if installation is forced, install anyway 4252 info(packageMessage + "Already installed. Re-installation enforced."); 4253 action = actionInstall; 4254 4255 } else { 4256 // If execute is 'once' then package checks are not executed. 4257 // We just trust that the package is installed. 4258 if (executeAttr == "once") { 4259 dinfo(packageMessage + "Installed already."); 4260 action = actionNone; 4261 } else { 4262 // In case no execution attribute is defined 4263 // check real package state. 4264 if (getQueryMode() == "remote") { 4265 // Assume package is properly installed. 4266 action = actionNone; 4267 } else { 4268 // Verify that package is still installed. 4269 if (isInstalled(installedPackage)) { 4270 action = actionNone; 4271 } else { 4272 // Package found in local database but checks failed. 4273 // Maybe the user uninstalled the package manually. 4274 dinfo(packageMessage + "Installed but checks failed. Re-Installing."); 4275 action = actionInstall; 4276 } 4277 } 4278 } 4279 } 4280 } 4281 } 4282 return action; 4283 } 4284 4285 /** 4286 * Returns list of packages which have been manually installed. 4287 * 4288 * @returns List of packages manually installed in local settings database. 4289 * Returns empty array if no package is found. 4290 */ 4291 function getPackagesManuallyInstalled() { 4292 if (manuallyInstalled == null) { 4293 // Get list of currently installed packages. 4294 var settings = getSettings(); 4295 4296 // Filter manually installed packages. 4297 // Fetch command-nodes from <commands><command type="type" /></commands> structure. 4298 manuallyInstalled = settings.selectNodes("package[@manualInstall=\"true\"]"); 4299 4300 // Return empty array if no package is found. 4301 if (manuallyInstalled == null) { 4302 manuallyInstalled = new Array(); 4303 } 4304 } 4305 return manuallyInstalled; 4306 } 4307 4308 /** 4309 * Returns an array of packages which are not assigned to the current host any more. 4310 * 4311 * Packages which are manually installed are not included in the list of packages 4312 * to be removed. Except if the package does not exist on server side any more. 4313 * Therefore in case a package is removed from the server it is removed from 4314 * clients as well even if the package was installed manually because it is to be 4315 * assumed tha the administrator no longer wants to support this type of software. 4316 * 4317 * @return Array of packages which will be removed during synchronization. 4318 */ 4319 function getPackagesRemoved() { 4320 dinfo("Evaluating packages to be removed."); 4321 /** 4322 * Get package nodes referenced within the profile (and profile 4323 * dependencies). This includes package dependencies as well. 4324 */ 4325 var profilePackageNodes = getProfilePackageNodes(); 4326 4327 // Get list of currently installed packages. 4328 var installedPackages = getSettingNodes(); 4329 4330 // Array to store packages to be removed. 4331 var removablesArray = new Array(); 4332 4333 // Loop over each installed package and check whether it still applies. 4334 for (var iInstalledPkg = 0; iInstalledPkg < installedPackages.length; iInstalledPkg++) { 4335 var installedPackageNode = installedPackages[iInstalledPkg]; 4336 dinfo("Found installed package '" + getPackageName(installedPackageNode) + "' (" + 4337 getPackageID(installedPackageNode) + ")."); 4338 4339 // Search for the installed package in available packages. 4340 var found = false; 4341 4342 for (var j=0; j < profilePackageNodes.length; j++) { 4343 var profilePackageNode = profilePackageNodes[j]; 4344 if (getPackageID(installedPackageNode) == getPackageID(profilePackageNode)) { 4345 dinfo("Package '" + getPackageName(installedPackageNode) + "' (" + 4346 getPackageID(installedPackageNode) + ") found in profile packages."); 4347 found = true; 4348 break; 4349 } 4350 } 4351 4352 // If package is no longer present, mark for remove if not installed manually. 4353 if (!found) { 4354 // Check if package was installed manually. 4355 // Manually installed packages remain on the system. 4356 var packageMessage = "Package '" + getPackageName(installedPackageNode) + "' (" + 4357 getPackageID(installedPackageNode) + "): "; 4358 var isManuallyInstalled = getPackageManualInstallation(installedPackageNode); 4359 if (isManuallyInstalled == true) { 4360 if (isZombie(installedPackageNode)) { 4361 // Package is not in server package database any more. 4362 dinfo("Package was manually installed but is " + 4363 "not in package database any more. Marking package for removal."); 4364 removablesArray.push(installedPackageNode); 4365 } else { 4366 dinfo("Package was manually installed and is " + 4367 "still available in package database. Keeping package."); 4368 } 4369 } else { 4370 dinfo(packageMessage + "Marked for removal."); 4371 removablesArray.push(installedPackageNode); 4372 } 4373 } 4374 } 4375 4376 return removablesArray; 4377 } 4378 4379 4380 /** 4381 * Returns a list of variables for the given package. 4382 * 4383 * @param packageNode 4384 * The package node to get the variables from. 4385 * @param array 4386 * Object of type Array to which the the variables appended. 4387 * In case null is supplied it returns a new Array object. 4388 * @return Object of type Scripting.Dictionary which contains all key/value 4389 * pairs from the given package including its dependencies 4390 */ 4391 function getPackageVariables(packageNode, array) { 4392 dinfo("Reading variables from package '" + getPackageName(packageNode) + "'."); 4393 array = getVariables(packageNode, array); 4394 return array; 4395 } 4396 4397 /** 4398 * Returns array of profile nodes which represent the profile dependencies. 4399 * Returns empty array in case the profile does not have any dependency. 4400 * 4401 * @return Array of strings representing the references to dependent profiles 4402 */ 4403 function getProfileDependencies(profileNode) { 4404 // output array 4405 var dependencyNodes = new Array(); 4406 4407 var dependNodes = profileNode.selectNodes("depends"); 4408 if (dependNodes != null) { 4409 // Get only dependencies which match the current host. 4410 var matchingDependNodes = filterConditionalNodes(dependNodes, true); 4411 for (var i=0; i < matchingDependNodes.length; i++) { 4412 var dependencyId = matchingDependNodes[i].getAttribute("profile-id"); 4413 4414 // convert dependency to lower case if case-sensitive mode is off 4415 if (dependencyId != null && !isCaseSensitive()) { 4416 dependencyId = dependencyId.toLowerCase(); 4417 } 4418 4419 // get the profile node 4420 var dependencyNode = getProfileNode(dependencyId); 4421 if (dependencyNode != null) { 4422 dependencyNodes.push(dependencyNode); 4423 } else { 4424 error("Profile '" + dependencyId + "' referenced but not " + 4425 "found. Ignoring profile."); 4426 } 4427 } 4428 } 4429 4430 return dependencyNodes; 4431 } 4432 4433 /** 4434 * Returns the corresponding profile ID stored within the given profile XML 4435 * node. 4436 * 4437 * @return String representing the ID of the supplied profile node. 4438 */ 4439 function getProfileID(profileNode) { 4440 return profileNode.getAttribute("id"); 4441 } 4442 4443 /** 4444 * Returns an array of strings which represents the profiles directly referenced 4445 * by the applying host node. The profiles are evaluated as follows: 4446 * <pre> 4447 * - /profile:<profile> parameter 4448 * - /host:<hostname> parameter matching within hosts.xml 4449 * - profiles defined within host.xml which are assigned to the matching hosts entry 4450 * </pre> 4451 * 4452 * @return array of strings representing the referenced profiles 4453 */ 4454 function getProfileList() { 4455 if (applyingProfilesDirect == null) { 4456 var profilesMatching = new Array(); 4457 4458 // get arguments 4459 var argn = getArgv().Named; 4460 4461 // Set the profile from either the command line or the hosts file. 4462 if (argn("profile") != null) { 4463 profilesMatching.push(argn("profile")); 4464 } else { 4465 var hostNodes = getHostsApplying(); 4466 for (var ihostNode=0; ihostNode < hostNodes.length; ihostNode++) { 4467 profilesMatching = profilesMatching.concat(getHostProfiles(hostNodes[ihostNode])); 4468 } 4469 if (profilesMatching.length <= 0) { 4470 throw new Error("Could not find any profile for host " + getHostname() + "."); 4471 } 4472 } 4473 applyingProfilesDirect = profilesMatching; 4474 } 4475 return applyingProfilesDirect; 4476 } 4477 4478 /** 4479 * Returns the corresponding profile XML node from the profile database 4480 * (profile.xml). Returns null in case no such profile exists. 4481 * 4482 * @param profileID 4483 * String representation of profile to get the node from. 4484 */ 4485 function getProfileNode(profileID) { 4486 // get first node which matched the specified ID 4487 return getProfiles().selectSingleNode("profile[@id='" + profileID +"']"); 4488 } 4489 4490 /** 4491 * Returns an array of all profile nodes available. 4492 * 4493 * @return array of profile XML nodes. 4494 */ 4495 function getProfileNodes() { 4496 // Retrieve packages. 4497 var profileNodes = getProfiles().selectNodes("profile"); 4498 4499 // make sure a package ID exists only once 4500 profileNodes = uniqueAttributeNodes(profileNodes, "id"); 4501 4502 // return this array 4503 return profileNodes; 4504 } 4505 4506 /** 4507 * Returns an array of strings which contains a list of package IDs referenced 4508 * by the currently applied profile(s). 4509 * 4510 * The list will contain all referenced IDs within profile.xml which apply to 4511 * the current profile(s) (including profile dependencies). Packages which are 4512 * referenced but do not exist within the package database (packages.xml) are 4513 * included as well. So be aware that in case of inconsistency between 4514 * profiles.xml and packages.xml it might be possible that the returned list 4515 * refers to packages not available within packages.xml. 4516 * 4517 * NOTE: The list does NOT contain IDs of package dependencies. Just the list of 4518 * packages as referred in profiles.xml. Dependency information is only available 4519 * within the concrete package nodes within packages.xml. Refer to 4520 * getProfilePackageNodes() to get packages including dependencies. 4521 * 4522 * If you like to get a list of full package nodes have a look at 4523 * getProfilePackageNodes() but note that it cannot return full nodes for 4524 * packages referenced within profiles.xml but missing in the package database. 4525 * 4526 * @return array of package IDs applying to this profile (empty array if no 4527 * packages are assigned). 4528 */ 4529 function getProfilePackageIDs() { 4530 // Get array of all profiles that apply to the base profile. 4531 // This includes depending profiles 4532 var profileArray = getProfilesApplying(); 4533 4534 // Create array to store all referenced package IDs 4535 var packageIDs = new Array(); 4536 4537 // New date object, used for install/uninstall date comparison. 4538 var now = new Date(); 4539 4540 // Add each profile's package IDs to the array. 4541 for (var i=0; i < profileArray.length; i++) { 4542 profileNode = profileArray[i]; 4543 4544 // Load profile environment. 4545 var previousEnv = getEnv(); 4546 4547 // Array to store all variables found. 4548 var variables = new Array(); 4549 4550 // Host variables first... 4551 variables = getHostsVariables(variables); 4552 4553 // Get variables of this profile. 4554 variables = getVariables(profileNode, variables); 4555 4556 // Apply variables to environment. 4557 for (var iVariable=0; iVariable < variables.length; iVariable++) { 4558 var varDefinition = variables[iVariable]; 4559 var variableKeys = varDefinition.keys().toArray(); 4560 for (var iVarKey = 0; iVarKey < variableKeys.length; iVarKey++) { 4561 var key = variableKeys[iVarKey]; 4562 var value = varDefinition.Item(key); 4563 setEnv(key, value); 4564 } 4565 } 4566 4567 // Fetch packages from profile. 4568 var profilePackageNodes = profileNode.selectNodes("package"); 4569 // Filter out packages which shall not apply to this host 4570 var packageNodes = filterConditionalNodes(profilePackageNodes, true); 4571 4572 // Add all package IDs to the array and avoid duplicates 4573 for (var j = 0; j < packageNodes.length; j++) { 4574 // get package ID 4575 var packageNode = packageNodes[j]; 4576 var packageId = packageNode.getAttribute("package-id"); 4577 // Skip package if package ID is not defined. 4578 if (packageId == null || packageId == "") { 4579 continue; 4580 } 4581 4582 // Use package methods for profile package node because the 4583 // attribute is the same. 4584 var installDate = getProfilePackageInstallDate(packageNode); 4585 var uninstallDate = getProfilePackageUninstallDate(packageNode); 4586 var includePackage = true; 4587 4588 // Check if package 4589 4590 // Check if the package should be included regarding installation 4591 // period. 4592 if (installDate != null || uninstallDate != null) { 4593 // either install or uninstall date was defined 4594 if (now >= installDate && 4595 now <= uninstallDate) { 4596 includePackage = true; 4597 dinfo("Package'" + packageId + "' specified an install date range: " + 4598 installDate + " to " + uninstallDate + 4599 "; current time (" + now + ") is within the time frame. Including package."); 4600 } else { 4601 includePackage = false; 4602 dinfo("Package '" + packageId + "' specified an install date range: " + 4603 installDate + " to " + uninstallDate + 4604 "; out of range, skipping package (local time: " + now + ")."); 4605 } 4606 } 4607 4608 // Search array for pre-existing ID, we don't want duplicates. 4609 if (includePackage) { 4610 // Check if package shall be included case-sensitive. If not; 4611 // convert to lower-case. 4612 if (!isCaseSensitive()) { 4613 packageId = packageId.toLowerCase(); 4614 } 4615 var alreadyAdded = false; 4616 for (var k=0; k < packageIDs.length; k++) { 4617 if (packageIDs[k] == packageId) { 4618 alreadyAdded = true; 4619 break; 4620 } 4621 } 4622 if (!alreadyAdded) { 4623 packageIDs.push(packageId); 4624 } 4625 } 4626 } 4627 // Restore environment. 4628 loadEnv(previousEnv); 4629 } 4630 4631 return packageIDs; 4632 } 4633 4634 /** 4635 * Returns date object reflecting installation date defined in given node 4636 * 4637 * @param packageNode 4638 * the package definition node as specified within the profile 4639 * definition 4640 * @return date object representing installation date. Null if date is undefined. 4641 */ 4642 function getProfilePackageInstallDate(packageNode) { 4643 var installDate = null; 4644 var packageInstallDate = packageNode.getAttribute("installdate"); 4645 if (packageInstallDate != null) { 4646 installDate = parseISODate(packageInstallDate, false); 4647 } 4648 return installDate; 4649 } 4650 4651 /** 4652 * Returns an array of package nodes that should be applied to the current 4653 * profile. This function returns full package nodes. 4654 * 4655 * NOTE: Since the profile 4656 * just contains the package IDs referenced within profiles.xml but not 4657 * existing within the packages database (packages.xml) will not be part of the 4658 * list. 4659 * 4660 * In case you like to get a list of package IDs referenced by the profile 4661 * (regardless if the package definition exists) have a look at 4662 * getProfilePackageIDs(). 4663 * 4664 * @return array of package nodes applying to the assigned profile(s) 4665 */ 4666 function getProfilePackageNodes() { 4667 if (profilePackageNodes == null) { 4668 // Create a new empty package array. 4669 packageNodes = new Array(); 4670 4671 /* 4672 * get package IDs which apply to the profile (without dependencies, includes and chained packages) regardless 4673 * if the package definition is available or not. 4674 */ 4675 var packageIDs = getProfilePackageIDs(); 4676 4677 // get package definitions and all dependencies 4678 for ( var i = 0; i < packageIDs.length; i++) { 4679 var packageID = packageIDs[i]; 4680 dinfo("Adding package with ID '" + packageID + "' to profile packages."); 4681 var packageNode = getPackageNodeFromAnywhere(packageID); 4682 4683 // add dependencies first 4684 if (packageNode != null) { 4685 getPackageReferences(packageNode, packageNodes); 4686 if (!searchArray(packageNodes, packageNode)) { 4687 // Add the new node to the array _after_ adding dependencies. 4688 packageNodes.push(packageNode); 4689 } 4690 } 4691 } 4692 profilePackageNodes = packageNodes; 4693 } 4694 return profilePackageNodes; 4695 } 4696 4697 /** 4698 * Returns Date representation of 'uninstalldate' attribute from the given 4699 * package definition as specified within the profile. 4700 * 4701 * @param packageNode 4702 * the package node to read the 'uninstalldate' attribute from 4703 * @return Date object representing uninstall date of the given package. Returns 4704 * null in case the 'uninstalldate' attribute is not set. 4705 */ 4706 function getProfilePackageUninstallDate(packageNode) { 4707 var uninstallDate = null; 4708 var packageUninstallDate = packageNode.getAttribute("uninstalldate"); 4709 if (packageUninstallDate != null) { 4710 uninstallDate = parseISODate(packageUninstallDate, true); 4711 } 4712 return uninstallDate; 4713 } 4714 4715 /** 4716 * Returns XML node which contains all profiles (profile database). 4717 */ 4718 function getProfiles() { 4719 if(profiles == null) { 4720 var newProfiles = createProfiles(); 4721 setProfiles(newProfiles); 4722 } 4723 return profiles; 4724 } 4725 4726 /** 4727 * Returns an array of profile nodes that should be applied to the current 4728 * profile. This includes also all profile dependencies. 4729 * 4730 * @return array of profiles (directly associated profiles and dependencies) 4731 */ 4732 function getProfilesApplying() { 4733 dinfo("Getting profiles which apply to this node."); 4734 if (applyingProfilesAll == null) { 4735 // create cache entry 4736 var profilesApplying = new Array(); 4737 4738 // get list of applying profiles 4739 var profileList = getProfileList(); 4740 4741 for (var i=0; i<profileList.length; i++) { 4742 // receive profile node 4743 var profileNode = getProfileNode(profileList[i]); 4744 4745 if (profileNode != null) { 4746 dinfo("Applying profile: " + getProfileID(profileNode)); 4747 4748 // Add the current profile's node as the first element in the 4749 // array. 4750 profilesApplying.push(profileNode); 4751 4752 appendProfileDependencies(profilesApplying, profileNode); 4753 } else { 4754 error("Profile '" + profileList[i] + "' applies to this host but was not found!"); 4755 } 4756 } 4757 applyingProfilesAll = profilesApplying; 4758 } 4759 return applyingProfilesAll; 4760 } 4761 4762 /** 4763 * Returns the log level associated with a given profile. 4764 * 4765 * @return merged log levels from all applying profiles. For example if one 4766 * profile specifies info logging and a second profile specifies error. 4767 * The resulting log level will be info+error. Returns null if no custom 4768 * log level is specified for this profile. 4769 */ 4770 function getProfilesLogLevel() { 4771 // set initial bitmask to 0x00; 4772 var logLevel = 0x00; 4773 4774 // merge log levels 4775 try { 4776 var profileList = getProfileList(); 4777 for (var i=0; i<profileList.length; i++) { 4778 var profileId = profileList[i]; 4779 var profileNode = getProfileNode(profileId); 4780 if (profileNode != null) { 4781 // add bitmask 4782 logLevel = logLevel | profileNode.getAttribute("logLevel"); 4783 } 4784 } 4785 } catch (e) { 4786 // Unable to read profile-specific log leve. 4787 // Maybe there is no profile found for this host. 4788 dinfo("No profile-specific log level found."); 4789 } 4790 if (logLevel > 0x00) { 4791 return logLevel; 4792 } else { 4793 return null; 4794 } 4795 } 4796 4797 /** 4798 * Returns a list of variables from the Profile. 4799 * 4800 * @param array 4801 * Object of type Array to which the the variables are appended. 4802 * In case null is supplied it returns a new Array object. 4803 * @return Object of type Scripting.Dictionary which contains all key/value 4804 * pairs from the given profile including its dependencies 4805 */ 4806 function getProfileVariables(array) { 4807 dinfo("Reading variables from profile[s]"); 4808 if (profilesVariables == null) { 4809 profilesVariables = new Array(); 4810 var profileArray = getProfilesApplying(); 4811 // dinfo(profileArray.length + " profiles apply to this host."); 4812 4813 /* 4814 * add each profile's variables to the array in reverse order reversing the order is done in order to allow 4815 * overwriting of variables from dependent profiles 4816 */ 4817 4818 for (var iProfiles=profileArray.length-1; iProfiles >= 0; iProfiles--) { 4819 var profileNode = profileArray[iProfiles]; 4820 dinfo("Reading variables from profile " + getProfileID(profileNode)); 4821 4822 // Add variables from profile XML node. 4823 profilesVariables = getVariables(profileNode, profilesVariables); 4824 } 4825 } 4826 4827 var concatenatedVariables = profilesVariables; 4828 if (array != null) { 4829 concatenatedVariables = profilesVariables.concat(array); 4830 } 4831 4832 return concatenatedVariables; 4833 } 4834 4835 /** 4836 * Returns current state of query mode. 4837 * @returns {String} Current query mode. 4838 */ 4839 function getQueryMode() { 4840 return queryMode; 4841 } 4842 4843 /** 4844 * Returns the corresponding package XML node from the settings database 4845 * (wpkg.xml). Returns null in case no such package is installed. 4846 * 4847 * @param packageID 4848 * ID of the package to be returned 4849 * @return returns package XML node as stored within the settings. Returns null 4850 * if no such package exists. 4851 */ 4852 function getSettingNode(packageID) { 4853 // get first node which matched the specified ID 4854 return getSettings().selectSingleNode("package[@id='" + packageID +"']"); 4855 } 4856 4857 4858 /** 4859 * Tries to read host attributes from the settings database. 4860 * All host attributes found in the settings database will be used to override 4861 * attributes of the local host. 4862 */ 4863 function getSettingHostAttributes() { 4864 // Fetch settings. 4865 var settings = getSettings(); 4866 var attributes = settings.attributes; 4867 4868 // Check whether attributes are defined. 4869 if (attributes.length > 0) { 4870 4871 // Reset cache for host information. 4872 resetHostInformationCache(); 4873 4874 for (var iAttribute=0; iAttribute < attributes.length; iAttribute++) { 4875 var node = attributes.item(iAttribute); 4876 var attribute = node.nodeName; 4877 4878 var value = node.nodeValue; 4879 switch (attribute) { 4880 case "hostname": 4881 setHostname(value); 4882 break; 4883 4884 case "architecture": 4885 setArchitecture(value); 4886 break; 4887 4888 case "os": 4889 setHostOS(value); 4890 break; 4891 4892 case "ipaddresses": 4893 var ipList = value.split(","); 4894 setIPAddresses(ipList); 4895 break; 4896 4897 case "domainname": 4898 setDomainName(value); 4899 break; 4900 4901 case "groups": 4902 var hostGroupList = value.split(","); 4903 setHostGroups(hostGroupList); 4904 break; 4905 4906 case "lcid": 4907 setLocale(value); 4908 break; 4909 4910 case "lcidOS": 4911 setLocaleOS(value); 4912 break; 4913 4914 default: 4915 break; 4916 } 4917 } 4918 } 4919 } 4920 4921 /** 4922 * Returns an array of all installed packages from the local wpkg.xml 4923 * 4924 * @return Array of installed packages (XML nodes) 4925 */ 4926 function getSettingNodes() { 4927 // retrieve packages 4928 var packageNodes = getSettings().selectNodes("package"); 4929 4930 // make sure a package ID exists only once 4931 // commented since the local database should not contain duplicated entries 4932 packageNodes = uniqueAttributeNodes(packageNodes, "id"); 4933 4934 // return this array 4935 return packageNodes; 4936 } 4937 4938 /** 4939 * Returns current path to settings file. 4940 * 4941 * @returns Settings file FS object. 4942 */ 4943 function getSettingsPath() { 4944 if (settings_file == null || settings_file == "") { 4945 // Will be used for file operations. 4946 var fso = new ActiveXObject("Scripting.FileSystemObject"); 4947 4948 // Evaluate path. 4949 // Our default settings file is located in %SystemRoot%\system32. 4950 // If settings path was not specified via command line, then evaluate it 4951 // from the configuration file or fall back to default. 4952 if (settings_file_path == null) { 4953 var SystemFolder = 1; 4954 settings_file_path = fso.GetSpecialFolder(SystemFolder); 4955 } 4956 settings_file = settings_file_path + "\\" + settings_file_name; 4957 settings_file_processed = false; 4958 } 4959 4960 if (!settings_file_processed) { 4961 // Check whether [PROFILE] epxression was used and repace it. 4962 var profileExp = new RegExp("\\[PROFILE\\]", "g"); 4963 if (profileExp.test(settings_file) == true) { 4964 // This will throw an error if profile is not available yet. 4965 var profileList = getProfileList(); 4966 4967 // concatenate profile names or throw error if no names 4968 // available 4969 if (profileList.length > 0) { 4970 var allProfiles = ""; 4971 for (var i=0; i<profileList.length; i++) { 4972 if (allProfiles == "") { 4973 allProfiles = profileList[i]; 4974 } else { 4975 allProfiles += "-" + profileList[i]; 4976 } 4977 } 4978 settings_file = settings_file.replace(profileExp, allProfiles); 4979 } else { 4980 throw new Error("Profile information not available."); 4981 } 4982 } 4983 4984 // Check whether [HOSTNAME] expression was used and replace it. 4985 var hostnameExp = new RegExp("\\[HOSTNAME\\]", "g"); 4986 if (hostnameExp.test(settings_file) == true) { 4987 settings_file = settings_file.replace(hostnameExp, getHostname()); 4988 } 4989 } 4990 4991 return settings_file; 4992 } 4993 4994 /** 4995 * Returns XML node which contains all settings (local package database). 4996 */ 4997 function getSettings() { 4998 if(settings == null) { 4999 var newSettings = createSettings(); 5000 setSettings(newSettings, true); 5001 } 5002 return settings; 5003 } 5004 5005 /** 5006 * Returns the checkResults node of the settings database. 5007 * 5008 * @returns checkResults node of currently loaded settings database. 5009 */ 5010 function getSettingsCheckResults() { 5011 var currentSettings = getSettings(); 5012 var checkResults = currentSettings.selectSingleNode("checkResults"); 5013 if (checkResults == null) { 5014 var xmlDoc = new ActiveXObject("Msxml2.DOMDocument.3.0"); 5015 checkResults = xmlDoc.createElement("checkResults"); 5016 currentSettings.appendChild(checkResults); 5017 } 5018 return checkResults; 5019 } 5020 5021 /** 5022 * Adds the given check node to the checkResults list in the settings database. 5023 * 5024 * @param checkNode Check XML node to be inserted. 5025 * @param result Result of the check on current node. 5026 */ 5027 function addSettingsCheckResult(checkNode, result) { 5028 try { 5029 // Clone XML node to be added to settings. 5030 var settingsCheckNode = checkNode.cloneNode(false); 5031 5032 // Check if there is already a check with the same attributes. 5033 var previousChecks = getSettingsCheck(settingsCheckNode); 5034 5035 // Fetching checkResults node from settings. 5036 var checkResults = getSettingsCheckResults(); 5037 5038 // If a check was found then remove it from the results in order to avoid 5039 // duplicate entries. Checks might also be executed multiple times (with 5040 // potentially different results) and only the last result should be kept. 5041 if (previousChecks != null) { 5042 for (var i=0; i < previousChecks.length; i++) { 5043 dinfo("Replacing check results of previous evaluation"); 5044 var previousCheck = previousChecks[i]; 5045 checkResults.removeChild(previousCheck); 5046 } 5047 } 5048 5049 // Add result attribute. 5050 var resultValue = "false"; 5051 if(result != null && result == true) { 5052 resultValue = "true"; 5053 } 5054 settingsCheckNode.setAttribute("result", resultValue); 5055 5056 // Add check results node. 5057 checkResults.appendChild(settingsCheckNode); 5058 5059 // Save modified settings. 5060 saveSettings(false); 5061 } catch (e) { 5062 error("Unable to add result of check to settings: " + e.message); 5063 } 5064 } 5065 5066 /** 5067 * Returns result of pre-evaluated check from settings node. 5068 * 5069 * @param checkNode the check node for which to look in the settings 5070 * "checkResults" nodes to verify if the check has been executed already. 5071 * 5072 * @returns result of already evaluated check. Returns null if the check has 5073 * not been evaluated and saved to settings node before. 5074 */ 5075 function getSettingsCheckResult(checkNode) { 5076 var result = null; 5077 var previousChecks = getSettingsCheck(checkNode); 5078 if (previousChecks != null) { 5079 // Get latest check result. 5080 var previousCheck = previousChecks[previousChecks.length-1]; 5081 var checkResult = previousCheck.getAttribute("result"); 5082 if (checkResult != null && checkResult == "true") { 5083 result = true; 5084 } else { 5085 result = false; 5086 } 5087 dinfo("Found previously executed check with result '" + result + "'."); 5088 } 5089 return result; 5090 } 5091 5092 /** 5093 * Takes a check as a parameter and looks for the same check in the local 5094 * settings database. If an identical check with results is found, then this 5095 * check is returned in an array. Returns null if no identical check could be 5096 * found in the local settings database. 5097 * 5098 * @param checkNode check to seek for in local settings databse. 5099 * 5100 * @returns Array of matching checks; returns null if no check match. 5101 */ 5102 function getSettingsCheck(checkNode) { 5103 if (checkNode == null) { 5104 return null; 5105 } 5106 var result = null; 5107 var currentSettings = getSettings(); 5108 5109 var checkResults = currentSettings.selectSingleNode("checkResults"); 5110 5111 if (checkResults != null) { 5112 var attributes = checkNode.attributes; 5113 5114 // Check whether attributes are defined. 5115 if (attributes.length > 0) { 5116 var attributesClause = ""; 5117 var checkMessage = ""; 5118 for (var iAttribute=0; iAttribute < attributes.length; iAttribute++) { 5119 if (attributesClause != "") { 5120 attributesClause += " and "; 5121 checkMessage += ", "; 5122 } 5123 var node = attributes.item(iAttribute); 5124 var attribute = node.nodeName; 5125 var value = node.nodeValue.replace(new RegExp("\\\\", "g"), "\\\\"); 5126 attributesClause += "@" + attribute + "=\"" + value + "\""; 5127 checkMessage += attribute + "='" + value +"'"; 5128 } 5129 // Get all nodes which match the attributes. 5130 dinfo("Searching for previously executed checks with attributes " + checkMessage); 5131 var xPathQuery = "check[" + attributesClause + "]"; 5132 // dinfo("Query to find previously executed check: " + xPathQuery); 5133 var checkNodes = checkResults.selectNodes(xPathQuery); 5134 5135 if (checkNodes != null) { 5136 // Make sure the check nodes found do not contain any attributes 5137 // not present in comparison node. 5138 for (var iCheck=0; iCheck < checkNodes.length; iCheck++) { 5139 var checkNode = checkNodes[iCheck]; 5140 // dinfo("Found previously executed check: " + checkNode.xml); 5141 var checkAttributes = checkNode.attributes; 5142 var allAttrFound = true; 5143 5144 // Iterate over all attributes of the check node found and 5145 // verify that the attribute is found in comparison node. 5146 // (Except the result attribute) 5147 for (var iAttr=0; iAttr < checkAttributes.length; iAttr++) { 5148 var attrFound = false; 5149 var checkAttrSettings = checkAttributes.item(iAttr).nodeName; 5150 5151 if (checkAttrSettings == "result") { 5152 attrFound = true; 5153 } else { 5154 for (var iRefAttr=0; iRefAttr < attributes.length; iRefAttr++) { 5155 var checkAttrRef = attributes.item(iRefAttr).nodeName; 5156 if (checkAttrRef == checkAttrSettings) { 5157 attrFound = true; 5158 break; 5159 } 5160 } 5161 } 5162 5163 // If attribute has not been found in comparison node 5164 // Then the node contains different checks. 5165 if (attrFound == false) { 5166 allAttrFound = false; 5167 break; 5168 } 5169 } 5170 5171 // If all attributes were found in original query node then 5172 // the check is identical to the one in the settings DB. 5173 if (allAttrFound) { 5174 if (result == null) { 5175 result = new Array(); 5176 } 5177 result.push(checkNode); 5178 } 5179 } 5180 } 5181 if (result != null) { 5182 dinfo("Found " + result.length + " previously executed checks."); 5183 } else { 5184 dinfo("Unable to find any previously executed checks with these attributes."); 5185 } 5186 } 5187 } 5188 return result; 5189 } 5190 5191 5192 /** 5193 * Returns a list of package nodes (Array object) which have been scheduled for 5194 * removal but are not removed due to the /noremove flag. 5195 * 5196 * @return Array of package nodes which would have been removed during this 5197 * session 5198 */ 5199 function getSkippedRemoveNodes() { 5200 if (skippedRemoveNodes == null) { 5201 skippedRemoveNodes = new Array(); 5202 } 5203 return skippedRemoveNodes; 5204 } 5205 5206 /** 5207 * Returns a list of key/value pairs representing all variable definitions from 5208 * the given XML node. 5209 * 5210 * @param XMLNode 5211 * The XML node to get the variables from 5212 * @param array 5213 * Object of type Array to which the the variables are appended. 5214 * In case null is supplied it returns a new Array object. 5215 * Each array element is a dictionary object containing a key and 5216 * a value. The key is the variable name and the value is the 5217 * variable value to be assigned. 5218 * @return Object of type Scripting.Dictionary which contains all key/value 5219 * pairs from the given XML node. 5220 */ 5221 function getVariables(XMLNode, array) { 5222 // a new empty array of variables 5223 var variables = null; 5224 5225 // make sure variables is either created or assigned 5226 if(array == null) { 5227 // variables = new ActiveXObject("Scripting.Dictionary"); 5228 variables = new Array(); 5229 } else { 5230 variables = array; 5231 } 5232 5233 var variableNodes = XMLNode.selectNodes("variable"); 5234 5235 // Perform host matching on variables. 5236 variableNodes = filterConditionalNodes(variableNodes, true); 5237 5238 for (var i=0; i < variableNodes.length; i++) { 5239 var variableName = variableNodes[i].getAttribute("name"); 5240 var variableValue = variableNodes[i].getAttribute("value"); 5241 5242 if (variableName == null || variableValue == null) { 5243 error("Incomplete variable specification found. " + 5244 "Variable name is '" + variableName + "' and variable value is '" + 5245 variableValue + "'. Ignoring variable."); 5246 continue; 5247 } 5248 5249 // Expand environment variables in value. 5250 // variableValue = shell.ExpandEnvironmentStrings(variableValue); 5251 dinfo("Got variable '" + variableName + "' of value '" + variableValue + "'"); 5252 5253 var variable = new ActiveXObject("Scripting.Dictionary"); 5254 variable.Add(variableName, variableValue); 5255 5256 // Add to variables list. 5257 variables.push(variable); 5258 } 5259 5260 return variables; 5261 } 5262 5263 /** 5264 * Installs the specified package node to the system. If an old package node is 5265 * supplied performs an upgrade. In case the old package node is null an 5266 * installation is performed. 5267 * 5268 */ 5269 function installPackage(packageNode) { 5270 // Initialize return value. 5271 var success = false; 5272 5273 var packageName = getPackageName(packageNode); 5274 var packageID = getPackageID(packageNode); 5275 var packageRev = getPackageRevision(packageNode); 5276 var executeAttr = getPackageExecute(packageNode); 5277 var notifyAttr = getPackageNotify(packageNode); 5278 var rebootAttr = getPackageReboot(packageNode); 5279 5280 // Get check policies. 5281 var installCheckPolicy = getPackagePrecheckPolicyInstall(packageNode); 5282 var upgradeCheckPolicy = getPackagePrecheckPolicyUpgrade(packageNode); 5283 var downgradeCheckPolicy = getPackagePrecheckPolicyDowngrade(packageNode); 5284 5285 dinfo("Going to install package '" + packageName + "' (" + packageID + 5286 "), Revision " + packageRev + ", (execute flag is '" + executeAttr + 5287 "', notify flag is '" + notifyAttr + "')."); 5288 5289 // search for the package in the local settings 5290 var installedPackage = getSettingNode(packageID); 5291 5292 // Check if package is manually installed. 5293 if (installedPackage != null) { 5294 var isManual = getPackageManualInstallation(installedPackage); 5295 if (isManual == true) { 5296 // Transfer manual install flag to new package. 5297 setPackageManualInstallation(packageNode, true); 5298 } 5299 } 5300 5301 5302 // if set then the package installation will be bypassed 5303 var bypass = false; 5304 // type of installation "install" or "upgrade" 5305 var typeInstall = "install"; 5306 var typeUpgrade = "upgrade"; 5307 var typeDowngrade = "downgrade"; 5308 var installType = typeInstall; 5309 5310 // string to print in events which identifies the package 5311 var packageMessage = "Package '" + packageName + "' (" + packageID + ")" + 5312 ": "; 5313 5314 // check if the package has been executed already 5315 if(searchArray(packagesInstalled, packageNode)) { 5316 // has been installed already during this session 5317 dinfo(packageMessage + 5318 "Already installed once during this session.\n" + 5319 "Checking if package is properly installed."); 5320 bypass=true; 5321 5322 // check if installation of package node was successful 5323 if ((installedPackage != null) && 5324 (versionCompare(getPackageRevision(installedPackage), packageRev) >= 0)) { 5325 // package successfully installed 5326 dinfo(packageMessage + "Verified; " + 5327 "package successfully installed during this session."); 5328 5329 success = true; 5330 5331 } else { 5332 dinfo(packageMessage + 5333 "Installation failed during this session."); 5334 // package installation must have been failed 5335 5336 success = false; 5337 5338 } 5339 } else { 5340 // mark package as processed 5341 packagesInstalled.push(packageNode); 5342 5343 dinfo(packageMessage + "Not yet processed during this session."); 5344 bypass = false; 5345 // evaluate what do do with the package 5346 5347 // Get action of package to be installed. 5348 var packageAction = getPackageInstallAction(packageNode); 5349 5350 // Evaluate installation actions. 5351 switch (packageAction) { 5352 case "none": 5353 // No package actions shall be performed. 5354 dinfo(packageMessage + "Already installed."); 5355 installType = typeUpgrade; 5356 bypass = true; 5357 success = true; 5358 break; 5359 5360 case "install": 5361 // Package needs to be installed. 5362 dinfo(packageMessage + "Prepared for installation."); 5363 installType = typeInstall; 5364 bypass = false; 5365 success = false; 5366 5367 // If execute attribute is set to "always" just continue with installation. 5368 if (executeAttr == "always") { 5369 break; 5370 } 5371 if (installCheckPolicy == "never") { 5372 // Checks shall be bypassed and package is installed in any case. 5373 dinfo(packageMessage + "Skipping checks whether package is already installed."); 5374 } else { 5375 // Default is to execute checks first in order to evaluate if 5376 // package is already installed. 5377 if (isInstalled(packageNode)) { 5378 info(packageMessage + 5379 "Already installed (checks succeeded). Checking dependencies and chained packages."); 5380 5381 // append new node to local xml 5382 addSettingsNode(packageNode, true); 5383 5384 // install all dependencies 5385 var depSuccess = installPackageReferences(packageNode, "dependencies"); 5386 if (depSuccess) { 5387 info(packageMessage + 5388 "Package and all dependencies are already installed. Skipping."); 5389 5390 } else { 5391 info(packageMessage + 5392 "Installed but at least one dependency is missing."); 5393 } 5394 5395 // install all chained packages 5396 var chainedSuccess = installPackageReferences(packageNode, "chained"); 5397 if (chainedSuccess) { 5398 info(packageMessage + 5399 "Package and all chained packages are already installed. Skipping."); 5400 5401 } else { 5402 info(packageMessage + 5403 "Installed but at least one chained package is missing."); 5404 } 5405 5406 // Bypass installation as installations seems to be done already. 5407 bypass = true; 5408 installType = typeInstall; 5409 5410 // Still set success to true since the package seems to be 5411 // installed properly (check succeed). 5412 success = true; 5413 5414 } else { 5415 // Package not installed yet. Perform normal installation. 5416 info(packageMessage + 5417 "Not installed (checks failed). Preparing installation."); 5418 } 5419 } 5420 break; 5421 5422 case "upgrade": 5423 // Package needs to be upgraded. 5424 dinfo(packageMessage + "Prepared for upgrade."); 5425 installType = typeUpgrade; 5426 bypass = false; 5427 success = false; 5428 5429 // If check policy is set to "always" then verify if the upgrade 5430 // might have been performed already. 5431 if (upgradeCheckPolicy == "always" && isInstalled(packageNode)) { 5432 // Package marked for upgrade but upgrade is not necessary. 5433 dinfo(packageMessage + 5434 "Forced checks on upgrades succeeded. Package already up to date."); 5435 5436 // Update local package database. 5437 addSettingsNode(packageNode, true); 5438 5439 // Package does not need to be upgraded. 5440 bypass = true; 5441 success = true; 5442 } 5443 break; 5444 5445 case "downgrade": 5446 // Package needs to be downgraded. 5447 dinfo(packageMessage + "Prepared for downgrade."); 5448 installType = typeDowngrade; 5449 bypass = false; 5450 success = false; 5451 5452 // If check policy is set to "always" then verify if the downgrade 5453 // might have been performed already. 5454 if (downgradeCheckPolicy == "always" && isInstalled(packageNode)) { 5455 dinfo(packageMessage + 5456 "Forced checks on downgrade succeeded. Package already downgraded."); 5457 5458 // Update local package database. 5459 addSettingsNode(packageNode, true); 5460 5461 // Package does not need to be downgraded. 5462 bypass = true; 5463 success = true; 5464 } 5465 break; 5466 5467 default: 5468 bypass = true; 5469 error("Unknown package action: " + packageAction); 5470 break; 5471 } 5472 } 5473 5474 if (!bypass) { 5475 // Store current environment. 5476 var previousEnv = getEnv(); 5477 5478 try { 5479 // install dependencies 5480 var depInstallSuccess = installPackageReferences(packageNode, "dependencies"); 5481 5482 // abort installation in case dependencies could not be installed 5483 if (!depInstallSuccess) { 5484 throw new Error("Installing dependencies failed"); 5485 } 5486 5487 // print event log entry 5488 info("Installing '" + packageName + "' (" + packageID + ")..."); 5489 logStatus("Performing operation (" + installType + ") on '" + packageName + "' (" + packageID + ")"); 5490 5491 // stores if the package needs a reboot after installation 5492 var rebootRequired = false; 5493 5494 // stores if the package needs a reboot after installing all 5495 // packages 5496 var rebootPostponed = false; 5497 5498 // Generate the correct environment. 5499 5500 // Set package specific environment. 5501 loadPackageEnv(packageNode); 5502 5503 // Select command lines to install. 5504 var cmds; 5505 dinfo("Install type: " + installType); 5506 if (installType == typeUpgrade) { 5507 // installation is an upgrade 5508 cmds = getPackageCmdUpgrade(packageNode, null); 5509 dinfo("Fetched " + cmds.length + " upgrade command(s)."); 5510 } else if (installType == typeDowngrade) { 5511 // prepare downgrade 5512 cmds = getPackageCmdDowngrade(packageNode, null); 5513 dinfo("Fetched " + cmds.length + " downgrade command(s)."); 5514 }else { 5515 // installation is default 5516 cmds = getPackageCmdInstall(packageNode, null); 5517 dinfo("Fetched " + cmds.length + " install command(s)."); 5518 } 5519 5520 // Get downloads from package node (if any). 5521 var downloadNodes = getDownloads(packageNode, null); 5522 // Append downloads from command node. 5523 for (var iCommands = 0; iCommands < cmds.length; iCommands++) { 5524 var commandNode = cmds[iCommands ]; 5525 getDownloads(commandNode, downloadNodes); 5526 } 5527 5528 // Download all specified downloads. 5529 var downloadResult = downloadAll(downloadNodes); 5530 if (downloadResult != true) { 5531 var failureMessage = "Failed to download all files."; 5532 if (isQuitOnError()) { 5533 throw new Error(failureMessage); 5534 } else { 5535 error(failureMessage); 5536 } 5537 } 5538 5539 // execute each command line 5540 for (var iCmd = 0; iCmd < cmds.length; iCmd++) { 5541 // execute commands 5542 var cmdNode = cmds[iCmd]; 5543 var cmd = getCommandCmd(cmdNode); 5544 if(cmd == null) { 5545 error("Error: Command missing. Please fix the package. Ignoring command."); 5546 continue; 5547 } 5548 var timeout = getCommandTimeout(cmdNode); 5549 var workdir = getCommandWorkdir(cmdNode); 5550 5551 // mark system as changed (command execution in progress) 5552 setSystemChanged(); 5553 if (notifyAttr) { 5554 // notify user about start of installation 5555 notifyUserStart(); 5556 } 5557 5558 var result = 0; 5559 result = exec(cmd, timeout, workdir); 5560 5561 // search for exit code 5562 var exitAction = getCommandExitCodeAction(cmdNode, result); 5563 5564 // check for special exit codes 5565 if (exitAction != null) { 5566 if (exitAction == "reboot") { 5567 // This exit code forces a reboot. 5568 info("Command in installation of " + packageName + 5569 " returned exit code [" + result + "]. This " + 5570 "exit code requires an immediate reboot."); 5571 reboot(); 5572 } else if (exitAction == "delayedReboot") { 5573 // This exit code schedules a reboot 5574 info("Command in installation of " + packageName + 5575 " returned exit code [" + result + "]. This " + 5576 "exit code schedules a reboot."); 5577 // schedule reboot 5578 rebootRequired = true; 5579 // proceed with next command 5580 continue; 5581 } else if (exitAction == "postponedReboot") { 5582 info("Command in installation of " + packageName + 5583 " returned exit code [" + result + "]. This " + 5584 "exit code schedules a postponed reboot."); 5585 rebootPostponed = true; 5586 setPostponedReboot(rebootPostponed); 5587 // execute next command 5588 continue; 5589 } else { 5590 // this exit code is successful 5591 info("Command in installation of " + packageName + 5592 " returned exit code [" + result + "]. This " + 5593 "exit code indicates success."); 5594 // execute next command 5595 continue; 5596 } 5597 } else if(result == 0) { 5598 // if exit code is 0, return success 5599 // execute next command 5600 dinfo("Command in installation of " + packageName + 5601 " returned exit code [" + result + "]. Success."); 5602 continue; 5603 } else { 5604 // command did not succeed, throw an error 5605 throw new Error("Exit code returned non-successful value (" + 5606 result + ") on command '" + cmd + "'"); 5607 } 5608 } 5609 5610 // packages with checks have to pass the isInstalled() test 5611 if (getChecks(packageNode).length > 0 && !isInstalled(packageNode)) { 5612 // package failed for now 5613 success = false; 5614 5615 // check if a delayed reboot has been scheduled 5616 // if reboot is scheduled it might be OK if the package check 5617 // fails 5618 if (rebootRequired || rebootAttr == "true") { 5619 warning("Package processing (" + installType + ") failed for package " + 5620 packageName + ".\nHowever the package requires a reboot to complete. Rebooting."); 5621 // reboot system without adding to local settings yet 5622 reboot(); 5623 } else if (rebootPostponed || rebootAttr == "postponed") { 5624 warning("Package processing (" + installType + ") failed for package " + 5625 packageName + ".\nHowever the package schedules a postponed reboot."); 5626 } else { 5627 // package installation failed 5628 var failMessage = "Could not process (" + installType + ") " + packageName + ".\n" + 5629 "Failed checking after installation."; 5630 if (isQuitOnError()) { 5631 throw new Error(failMessage); 5632 } else { 5633 error(failMessage); 5634 } 5635 } 5636 } else { 5637 success = true; 5638 // append new node to local xml 5639 addSettingsNode(packageNode, true); 5640 5641 // install chained packages 5642 var chainedStatus = installPackageReferences(packageNode, "chained"); 5643 if (chainedStatus) { 5644 info(packageMessage + 5645 "Package and all chained packages installed successfully."); 5646 5647 } else { 5648 info(packageMessage + 5649 "Package installed but at least one chained package failed to install."); 5650 } 5651 5652 // Reboot the system if needed. 5653 if (rebootRequired || rebootAttr == "true") { 5654 info("Installation of " + packageName + " successful, system " + 5655 "rebooting."); 5656 reboot(); 5657 } else if (rebootPostponed || rebootAttr == "postponed") { 5658 info("Installation of " + packageName + " successful, postponed reboot scheduled."); 5659 setPostponedReboot(true); 5660 } else { 5661 info("Processing (" + installType + ") of " + packageName + " successful."); 5662 } 5663 } 5664 } catch (err) { 5665 success = false; 5666 var errorMessage = "Could not process (" + installType + ") package '" + 5667 packageName + "' (" + packageID + "):\n" + err.description + "."; 5668 if (isQuitOnError()) { 5669 throw new Error(errorMessage); 5670 } else { 5671 error(errorMessage); 5672 } 5673 } finally { 5674 // cleaning up temporary downloaded files 5675 dinfo("Cleaning up temporary downloaded files"); 5676 // clean downloads 5677 downloadsClean(downloadNodes); 5678 5679 // PATCH SE3 5680 var packageID = getPackageID(packageNode); 5681 //var packageID = packageNode.getAttribute("id"); 5682 var lnkDir = packageNode.getAttribute("lnk"); 5683 var category = packageNode.getAttribute("category"); 5684 if (lnkDir != null) { 5685 if (category != null) { 5686 info("Deplacement automatique des raccourcis de id=" + packageID + " depuis %AllUsersProfile%\\Menu Démarrer\\Programmes\\" + lnkDir + " dans la Category " + category + "."); 5687 exec("%ComSpec% /C call %Z%\\wpkg\\AnalyseCategory.bat \"" + packageID + "\" \"" + lnkDir + "\"", timeout, workdir); 5688 } else { 5689 info("Pas de deplacement automatique des raccourcis car 'category' est absent dans " + packageID + "."); 5690 } 5691 } else { 5692 info("Pas de deplacement automatique des raccourcis car 'lnk' est absent dans " + packageID + "."); 5693 } 5694 log(4,"==============================================================="); // saut ligne entre les paquets à installer 5695 // FIN PATCH SE3 5696 5697 // restore old environment 5698 dinfo("Restoring previous environment."); 5699 // restore previous environment 5700 loadEnv(previousEnv); 5701 } 5702 } 5703 return success; 5704 } 5705 5706 /** 5707 * Installs all packages references of the selected type. Returns true in 5708 * case all references could be installed. Returns false if at least one 5709 * reference failed. 5710 * 5711 * @param packageNode 5712 * package to install the references of (XML node) NOTE: The 5713 * package itself is not installed. 5714 * @param referenceType 5715 * select "dependencies" or "chained". Defaults to 5716 * "dependencies". 5717 * @return true=all dependencies installed successful; false=at least one 5718 * dependency failed 5719 */ 5720 function installPackageReferences(packageNode, referenceType) { 5721 var problemDesc = ""; 5722 var refSuccess = true; 5723 5724 // get references 5725 var type; 5726 var references = new Array(); 5727 switch (referenceType) { 5728 case "chained": 5729 type = "chained"; 5730 references = getPackageChained(packageNode, null); 5731 break; 5732 5733 default: 5734 type = "dependencies"; 5735 references = getPackageDependencies(packageNode, null); 5736 break; 5737 } 5738 if (references.length > 0) { 5739 info("Installing references (" + type + ") of '" + 5740 getPackageName(packageNode) + 5741 "' (" + getPackageID(packageNode) + ")."); 5742 } 5743 for (var i=0; i < references.length; i++) { 5744 var refPackage = getPackageNodeFromAnywhere(references[i]); 5745 if (refPackage == null) { 5746 problemDesc += "Package references '" + references[i] + 5747 "' but no such package exists"; 5748 refSuccess = false; 5749 break; 5750 } else { 5751 // install this package 5752 var success = installPackage(refPackage); 5753 if (!success) { 5754 problemDesc += "Installation of reference (" + type + ") package '" 5755 + getPackageName(refPackage) + "' (" 5756 + getPackageID(refPackage) + ") failed"; 5757 refSuccess = false; 5758 // skip remaining references 5759 break; 5760 } 5761 } 5762 } 5763 if (refSuccess) { 5764 var successMessage = "Installation of references (" + type + ") for '" + 5765 getPackageName(packageNode) + "' (" + 5766 getPackageID(packageNode) + ") successfully finished."; 5767 dinfo(successMessage); 5768 } else { 5769 var failMessage = "Installation of references (" + type + ") for '" + 5770 getPackageName(packageNode) + "' (" + 5771 getPackageID(packageNode) + ") failed. " + problemDesc; 5772 if (isQuitOnError()) { 5773 throw new Error(failMessage); 5774 } else { 5775 error(failMessage); 5776 } 5777 } 5778 5779 return refSuccess; 5780 } 5781 5782 5783 /** 5784 * Installs a package by name. 5785 * 5786 * @param name Package ID of package to be installed. 5787 * @param manualInstall Boolean value specifying whether the package is 5788 * manually added. These packages are handled differently and not 5789 * removed during synchronization. 5790 */ 5791 function installPackageName(name, manualInstall) { 5792 // Check package name. 5793 if (name == null || name == "") { 5794 info("Package ID missing!"); 5795 return; 5796 } 5797 5798 // Query manual installation flag. 5799 var isManual = false; 5800 if (manualInstall != null && manualInstall == true) { 5801 isManual = true; 5802 } 5803 5804 // Query the package node. 5805 var node = getPackageNode(name); 5806 5807 if (node == null) { 5808 info("Package " + name + " not found!"); 5809 return; 5810 } 5811 5812 // Set manual installation flag. 5813 if (isManual) { 5814 setPackageManualInstallation(node, true); 5815 } 5816 5817 installPackage(node); 5818 } 5819 5820 /** 5821 * Returns true if running on a 64-bit system. False if running on a 32-bit 5822 * system. 5823 * 5824 * Please note that WPKG needs to be run from the local 64-bit cscript 5825 * instance in order to be able to access 64-bit directories and registry keys. 5826 * The 64-bit instance of cscript is located at %SystemRoot%\system32\. If 5827 * cscript from %SystemRoot%\SysWOW64\ is used (32-bit binary) then all reads to 5828 * %ProgramFiles% will be redirected to %ProgramFiles(x86). Hence it is not 5829 * possible for WPKG to access the "real" %ProgramFiles% folder with the 64-bit 5830 * binaries. The same applies for the registry. If 32-bit cscript is used all 5831 * reads to HKLM\Software\* are redirected to HKLM\Software\Wow6432Node\*. 5832 * 5833 * WARNING: If cscript is invoked from a 32-bit application it is not possible 5834 * to run the 64-bit version of cscript since the real %SystemRoot%\System32 5835 * directory is not visible to 32-bit applications. So Windows will invoke the 5836 * 32-bit version even if the full path is specified! 5837 * 5838 * A work-around is to copy the 64-bit cmd.exe from %SystemRoot%\System32 5839 * manually to a temporary folder and invoke it by using 5840 * c:\path\to\64-bit\cmd.exe /c \\path\to\wpkg.js 5841 * 5842 * @return true in case the system is running on a 64-bit Windows version. 5843 * otherwise false is returned. 5844 */ 5845 function is64bit() { 5846 if (x64 == null) { 5847 x64 = false; 5848 var architecture = getArchitecture(); 5849 if (architecture != "x86") { 5850 x64 = true; 5851 } 5852 } 5853 return x64; 5854 } 5855 5856 /** 5857 * Returns the current setting of apply multiple configuration. 5858 * 5859 * @returns Current state of apply multiple setting. 5860 */ 5861 function isApplyMultiple() { 5862 return applyMultiple; 5863 } 5864 5865 5866 /** 5867 * returns current state of case sensitivity flag 5868 * 5869 * @return true if case case sensitivity is enabled, false if it is disabled 5870 * (boolean) 5871 */ 5872 function isCaseSensitive() { 5873 return caseSensitivity; 5874 } 5875 5876 /** 5877 * Returns current debug status. 5878 * 5879 * @return true if debug state is on, false if debug is off 5880 */ 5881 function isDebug() { 5882 return debug; 5883 } 5884 5885 /** 5886 * Returns current dry run status. 5887 * 5888 * @return true if dry run state is on, false if dry run is off 5889 */ 5890 function isDryRun() { 5891 return dryrun; 5892 } 5893 5894 /** 5895 * Returns current value of the force flag. 5896 * 5897 * @return true if force is enabled, false if it is disabled (boolean). 5898 */ 5899 function isForce() { 5900 return force; 5901 } 5902 5903 /** 5904 * Returns current value of the forceinstall flag. 5905 * 5906 * @return true if forced installation is enabled, false if it is disabled 5907 * (boolean). 5908 */ 5909 function isForceInstall() { 5910 return forceInstall; 5911 } 5912 5913 /** 5914 * Returns if log should be appended or overwritten 5915 * 5916 * @return true in case log should be appended. false if it should be 5917 * overwritten (boolean). 5918 */ 5919 function isLogAppend() { 5920 return logAppend; 5921 } 5922 5923 /** 5924 * Check if package is installed. 5925 * 5926 * @return returns true in case the package is installed, false otherwise 5927 * @throws Error 5928 * in case checks could not be evaluated 5929 */ 5930 function isInstalled(packageNode) { 5931 var packageName = getPackageName(packageNode); 5932 var result = true; 5933 5934 dinfo ("Checking existence of package: " + packageName); 5935 5936 // Get a list of checks to perform before installation. 5937 var checkNodes = getChecks(packageNode); 5938 5939 // When there are no check conditions, say "not installed". 5940 if (checkNodes.length == 0) { 5941 return false; 5942 } 5943 5944 // Save current environment. 5945 var previousEnv = getEnv(); 5946 5947 // load package specific environment 5948 loadPackageEnv(packageNode); 5949 5950 // Verify checks 5951 result = checkAll(checkNodes); 5952 5953 // restore environment 5954 loadEnv(previousEnv); 5955 5956 return result; 5957 } 5958 5959 /** 5960 * Returns current status of /noDownload parameter 5961 * 5962 * @return true in case downloads shall be disabled, false if downloads are enabled 5963 */ 5964 function isNoDownload() { 5965 return noDownload; 5966 } 5967 5968 /** 5969 * Returns current status of /noforcedremove parameter 5970 * 5971 * @return true in case forced remove is enabled, false if it is disabled 5972 */ 5973 function isNoForcedRemove() { 5974 return noForcedRemove; 5975 } 5976 5977 /** 5978 * Returns if the nonotify flag is set or not. 5979 * 5980 * @return true if nonotify flag is set, false if nonotify is not set (boolean) 5981 */ 5982 function isNoNotify() { 5983 return nonotify; 5984 } 5985 5986 /** 5987 * Returns if the noreboot flag is set or not. 5988 * 5989 * @return true if noreboot flag is set, false if noreboot is not set (boolean) 5990 */ 5991 function isNoReboot() { 5992 return noreboot; 5993 } 5994 5995 /** 5996 * Returns the current state (boolean) of the noremove flag. 5997 * 5998 * @return true if noremove flag is set, false if noremove is not set (boolean) 5999 */ 6000 function isNoRemove() { 6001 return noRemove; 6002 } 6003 6004 /** 6005 * Returns if the noRunningState flag is set or not. 6006 * 6007 * @return true if noRunningState flag is set, false if noRunningState is not 6008 * set (boolean) 6009 */ 6010 function isNoRunningState() { 6011 return noRunningState; 6012 } 6013 6014 /** 6015 * Returns the current state of postponed reboots. If it returns true a reboot 6016 * is scheduled when the script exits (after completing all actions). 6017 * 6018 * @return current status of postponed reboot (boolean) 6019 */ 6020 function isPostponedReboot() { 6021 return postponedReboot; 6022 } 6023 6024 /** 6025 * Returns current value of the sendStatus flag 6026 * 6027 * @return true in case status should be sent, otherwise returns false 6028 */ 6029 function isSendStatus() { 6030 return sendStatus; 6031 } 6032 6033 /** 6034 * Returns true in case a package has been processed yet. Returns false if no 6035 * package has been processed yet at all. 6036 * 6037 * @return true in case a package has been processed, false otherwise. 6038 */ 6039 function isSystemChanged() { 6040 return systemChanged; 6041 } 6042 6043 /** 6044 * Returns the current value of the upgrade-before-remove feature flag. 6045 * 6046 * @return true in case upgrade-before-remove should be enabled, otherwise 6047 * returns false. 6048 */ 6049 function isUpgradeBeforeRemove() { 6050 return !noUpgradeBeforeRemove; 6051 } 6052 6053 /** 6054 * Returns current value of skip event log setting. 6055 * 6056 * @return true in case event log logging is enabled, false if it is disabled 6057 * (boolean). 6058 */ 6059 function isSkipEventLog() { 6060 return skipEventLog; 6061 } 6062 6063 /** 6064 * Returns current state of event log fallback mode (logging to STDOUT instead 6065 * of event log. 6066 * 6067 * @returns {Boolean} Current status of event log fallback mode. 6068 */ 6069 function isEventLogFallback() { 6070 return eventLogFallback; 6071 } 6072 6073 /** 6074 * Returns true if quiet mode is on. False otherwise. 6075 * 6076 * @return true if quiet flag is set, false if it is unset (boolean) 6077 */ 6078 function isQuiet() { 6079 return quietMode; 6080 } 6081 6082 /** 6083 * Returns current value of quit on error setting (see '/quitonerror' parameter) 6084 * 6085 * @return true in case quit on error is enabled, false if it is disabled 6086 * (boolean). 6087 */ 6088 function isQuitOnError() { 6089 return quitonerror; 6090 } 6091 6092 /** 6093 * Checks if a package is a zombie package which means that it exists within the 6094 * locale package database (wpkg.xml) but not on server database (packages.xml). 6095 * 6096 * @return true in case the package is a zombie, false otherwise 6097 */ 6098 function isZombie(packageNode) { 6099 var packageName = getPackageID(packageNode); 6100 var allPackagesArray = getPackageNodes(); 6101 var zombie = true; 6102 dinfo("Checking " + packageName + " zombie state."); 6103 for (var i=0; i < allPackagesArray.length; i++) { 6104 if (getPackageID(allPackagesArray[i]) == packageName) { 6105 zombie = false; 6106 break; 6107 } 6108 } 6109 6110 // print message for zombie packages 6111 if (zombie) { 6112 var errorMessage = "Error while synchronizing package " + packageName + 6113 "\nZombie found: package installed but not in packages database."; 6114 if (isQuitOnError()) { 6115 errorMessage += " Aborting synchronization."; 6116 error(errorMessage); 6117 throw new Error(errorMessage); 6118 } else { 6119 errorMessage += " Removing package."; 6120 error(errorMessage); 6121 } 6122 } 6123 6124 return zombie; 6125 } 6126 6127 6128 /** 6129 * Query and print local host information (read from the host where wpkg.js is 6130 * executed). 6131 */ 6132 function queryHostInformation() { 6133 // Reset cache for host information. 6134 resetHostInformationCache(); 6135 6136 var hostInfoAttributes = getHostInformation(); 6137 var hostInfo = hostInfoAttributes.keys().toArray(); 6138 // Initialize output message. 6139 var message = "Host information attributes from local host:\n"; 6140 // Fetch all host information attributes. 6141 for (var i=0; i < hostInfo.length; i++) { 6142 var hostInfoKey = hostInfo[i]; 6143 message += " " + hostInfoKey + ":"; 6144 6145 // Pad label to 20 characters (minus one for the colon ":"). 6146 var padding = 19 - hostInfoKey.length; 6147 for (var iPadding=0; iPadding < padding; iPadding++) { 6148 message += " "; 6149 } 6150 message += hostInfoAttributes.Item(hostInfoKey) + "\n"; 6151 } 6152 message += "\n\n"; 6153 6154 // If remote query mode is active reset host information. 6155 if (getQueryMode() == "remote") { 6156 dinfo("Query mode: remote"); 6157 getSettingHostAttributes(); 6158 } 6159 6160 // Print message. 6161 alert(message); 6162 } 6163 6164 6165 /** 6166 * Query and print host information fread from settings file. This requires 6167 * that host information is available in settings file. You must have 6168 * settingsHostInfo enabled in your configuration. 6169 */ 6170 function queryHostInformationFromSettings() { 6171 // Fetch settings. 6172 var settings = getSettings(); 6173 var attributes = settings.attributes; 6174 6175 // Initialize output message. 6176 var message = "Host information attributes from settings database:\n"; 6177 6178 // Check whether attributes are defined. 6179 if (attributes.length > 0) { 6180 6181 for (var iAttribute=0; iAttribute < attributes.length; iAttribute++) { 6182 var node = attributes.item(iAttribute); 6183 var attribute = node.nodeName; 6184 var value = node.nodeValue; 6185 6186 message += " " + attribute + ":"; 6187 6188 // Pad label to 20 characters (minus one for the colon ":"). 6189 var padding = 19 - attribute.length; 6190 for (var iPadding=0; iPadding < padding; iPadding++) { 6191 message += " "; 6192 } 6193 message += value + "\n"; 6194 } 6195 } else { 6196 message += " No host attributes found in settings database.\n" + 6197 "Make sure \"settingsHostInfo\" is enabled in your configuration.\n"; 6198 } 6199 message += "\n\n"; 6200 6201 // Print message. 6202 alert(message); 6203 } 6204 6205 /** 6206 * Queries all available packages (from package database and local settings) and 6207 * prints a quick summary. 6208 */ 6209 function queryAllPackages() { 6210 // Retrieve packages. 6211 var settingsNodes = getSettingNodes(); 6212 var packagesNodes = getPackageNodes(); 6213 6214 // Concatenate both lists. 6215 var packageNodes = concatenateList(settingsNodes, packagesNodes); 6216 packageNodes = uniqueAttributeNodes(packageNodes, "id"); 6217 6218 // Create a string to append package descriptions to. 6219 var message = "All available packages (" + packageNodes.length + "):\n"; 6220 6221 // query all packages 6222 for (var i = 0; i < packageNodes.length; i++) { 6223 message += queryPackage(packageNodes[i], null) + "\n\n"; 6224 } 6225 6226 alert(message); 6227 } 6228 6229 /** 6230 * Show the user a list of packages that are currently installed. 6231 */ 6232 function queryInstalledPackages() { 6233 // Retrieve currently installed nodes. 6234 var packageNodes = getSettingNodes(); 6235 6236 // Create a string to append package descriptions to. 6237 var message = "Packages currently installed:\n"; 6238 6239 for (var i = 0; i < packageNodes.length; i++) { 6240 message += queryPackage(packageNodes[i], null) + "\n\n"; 6241 } 6242 6243 alert(message); 6244 } 6245 6246 /** 6247 * Show the user information about a specific package. 6248 * 6249 * @param packageNode 6250 * The package node to print information of 6251 * @param packageAction 6252 * Optional argument to include the action applied to the package 6253 * in the package information. If set to null the package action 6254 * information is omitted from the output. 6255 * @return string representing the package information 6256 */ 6257 function queryPackage(packageNode, packageAction) { 6258 var message = ""; 6259 if (packageNode != null) { 6260 var settingNode = getSettingNode(getPackageID(packageNode)); 6261 var executeAttribute = getPackageExecute(packageNode); 6262 if (executeAttribute == null || executeAttribute == "") { 6263 executeAttribute = "-"; 6264 } 6265 6266 message = getPackageName(packageNode) + "\n"; 6267 message += " ID: " + getPackageID(packageNode) + "\n"; 6268 6269 if (settingNode != null && packageAction != null) { 6270 var newPackageRevision = getPackageRevision(packageNode); 6271 var oldPackageRevision = getPackageRevision(settingNode); 6272 if (newPackageRevision != oldPackageRevision) { 6273 message += " Revision (new): " + getPackageRevision(packageNode) + "\n"; 6274 message += " Revision (old): " + getPackageRevision(settingNode) + "\n"; 6275 } else { 6276 message += " Revision: " + getPackageRevision(packageNode) + "\n"; 6277 } 6278 } else { 6279 message += " Revision: " + getPackageRevision(packageNode) + "\n"; 6280 } 6281 6282 // PATCH SE3 6283 //if (packageAction != null) { 6284 // message += " Action: " + packageAction + "\n"; 6285 //} 6286 // FIN PATCH SE3 6287 6288 message += " Reboot: " + getPackageReboot(packageNode) + "\n"; 6289 // PATCH SE3 6290 //message += " Execute: " + executeAttribute + "\n"; 6291 //message += " Priority: " + getPackagePriority(packageNode) + "\n"; 6292 // FIN PATCH SE3 6293 6294 if (settingNode != null) { 6295 message += " Status: Installed\n"; 6296 } else { 6297 message += " Status: Not Installed\n"; 6298 } 6299 6300 } else { 6301 message += "No such package\n"; 6302 } 6303 6304 return message; 6305 } 6306 6307 /** 6308 * Shows the user a list of packages that are currently not installed. 6309 */ 6310 function queryUninstalledPackages() { 6311 // Create a string to append package descriptions to. 6312 var message = "Packages not installed:\n"; 6313 6314 // Get list of all available packages from package database. 6315 var packageNodes = getPackageNodes(); 6316 6317 // Check for each package if it is installed. 6318 for (var i = 0; i < packageNodes.length; i++) { 6319 if (getSettingNode(getPackageID(packageNodes[i])) == null) { 6320 message += queryPackage(packageNodes[i], null) + "\n\n"; 6321 } 6322 } 6323 6324 alert(message); 6325 } 6326 6327 /** 6328 * Query packages listed in the current profile. 6329 * @param listInstall List packages which are pending to be installed. 6330 * @param listUpgrade List packages which are pending to be upgraded. 6331 * @param listDowngrade List packages which are pending to be downgraded. 6332 * @param listRemove List packages which are pending to be removed. 6333 * @param listUnmodified List packages which are not modified during synchronization. 6334 */ 6335 function queryProfilePackages(listInstall, listUpgrade, listDowngrade, listRemove, listUnmodified) { 6336 // Message to be shown as a result of query. 6337 var message = "Current profile packages:\n"; 6338 6339 // Message which is appended when system is modified (includes execute="change" packages). 6340 var messageOnChangeOnly = ""; 6341 6342 // Flag whether the system would be modified when WPKG is run. 6343 var systemModified = false; 6344 6345 var profilePackageNodes = getProfilePackageNodes(); 6346 // Read all packages applying to current profile. 6347 for (var i=0; i<profilePackageNodes.length; i++) { 6348 var packageNode = profilePackageNodes[i]; 6349 6350 // Check package action which would be applied during synchronization. 6351 var packageAction = getPackageInstallAction(packageNode); 6352 6353 // "none" No action; package installed already 6354 // "install" Installation, package is new on the host 6355 // "upgrade" Upgrade package which already exists on the system 6356 // New version higher than installed version 6357 // "downgrade" Downgrade package which already exists on the system 6358 // New version lower than installed version 6359 var packageMessage = ""; 6360 var changesSystem = false; 6361 switch (packageAction) { 6362 case "none": 6363 if (listUnmodified) { 6364 packageMessage += queryPackage(packageNode, "None") + "\n\n"; 6365 } 6366 break; 6367 6368 case "install": 6369 if (listInstall) { 6370 packageMessage += queryPackage(packageNode, "Installation pending") + "\n\n"; 6371 changesSystem = true; 6372 } 6373 break; 6374 6375 case "upgrade": 6376 if (listUpgrade) { 6377 packageMessage += queryPackage(packageNode, "Upgrade pending") + "\n\n"; 6378 changesSystem = true; 6379 } 6380 break; 6381 6382 case "downgrade": 6383 if (listDowngrade) { 6384 packageMessage += queryPackage(packageNode, "Downgrade pending") + "\n\n"; 6385 changesSystem = true; 6386 } 6387 break; 6388 6389 default: 6390 break; 6391 } 6392 var executeAttribute = getPackageExecute(packageNode); 6393 if (executeAttribute == "changed") { 6394 messageOnChangeOnly += packageMessage; 6395 } else { 6396 message += packageMessage; 6397 // If the package modifies the system also packages which are 6398 // executed on change only shall be executed. 6399 if (changesSystem) { 6400 systemModified = changesSystem; 6401 } 6402 } 6403 } 6404 if (systemModified) { 6405 message += messageOnChangeOnly; 6406 } 6407 6408 // Print packages which are pending for removal. 6409 if (listRemove) { 6410 var removeList = getPackagesRemoved(); 6411 for (var i=0; i<removeList.length; i++) { 6412 var packageNode = removeList[i]; 6413 message += queryPackage(packageNode, "Remove pending") + "\n\n"; 6414 } 6415 } 6416 alert(message); 6417 } 6418 6419 /** 6420 * Removes the specified package node from the system. This function will remove 6421 * all packages which depend on the one to be removed prior to the package 6422 * itself. In case the /force parameter is set the function will even remove the 6423 * requested package if not all packages depending on it could be removed. Note 6424 * that these packages might probably not work any more in such case. 6425 * 6426 * @param packageNode 6427 * Package to be removed 6428 * @return True in case of successful remove of package and all packages 6429 * depending on it. False in case of failed package uninstall of failed 6430 * uninstall of package depending on it. 6431 */ 6432 function removePackage(packageNode) { 6433 var packageName = getPackageName(packageNode); 6434 var packageID = getPackageID(packageNode); 6435 var notifyAttr = getPackageNotify(packageNode); 6436 6437 // stores if the package needs a reboot after removing 6438 var rebootRequired = false; 6439 // stores if a postponed reboot should be scheduled 6440 var rebootPostponed = false; 6441 6442 // Get package removal check policy. 6443 var checkPolicy = getPackagePrecheckPolicyRemove(packageNode); 6444 6445 var success = true; 6446 var bypass = false; 6447 6448 // string to print in events which identifies the package 6449 var packageMessage = "Package '" + packageName + "' (" + packageID + ")" + 6450 ": "; 6451 6452 // check if package has been processed already 6453 if(searchArray(packagesRemoved, packageNode)) { 6454 // package has been removed during this session already 6455 dinfo(packageMessage + 6456 "Already removed once during this session.\n" + 6457 "Checking if package has been removed properly."); 6458 bypass=true; 6459 6460 // check if installation of package node was successful 6461 var installedPackage = getSettingNode(packageID); 6462 if (installedPackage == null) { 6463 // package successfully removed 6464 dinfo(packageMessage + "Verified; " + 6465 "package successfully removed during this session."); 6466 6467 success = true; 6468 6469 } else { 6470 dinfo(packageMessage + 6471 "Package removal failed during this session."); 6472 // package removal must have failed 6473 6474 success = false; 6475 } 6476 } else { 6477 dinfo(packageMessage + "Not yet processed during this session."); 6478 } 6479 6480 // Verify whether checks shall be used to verify if the package 6481 // has been removed already. 6482 if (checkPolicy == "always" && !isInstalled(packageNode)) { 6483 dinfo(packageMesseage + "Package already removed from system. Skipping removal."); 6484 // Package already removed. Skip removal. 6485 success = true; 6486 6487 // Remove package node from local xml. 6488 removeSettingsNode(packageNode, true); 6489 6490 // set package as processed in order to prevent processing multiple 6491 // times 6492 packagesRemoved.push(packageNode); 6493 6494 // Cancel further removal processing. 6495 bypass = true; 6496 } 6497 6498 6499 if (!bypass) { 6500 // set package as processed in order to prevent processing multiple 6501 // times 6502 packagesRemoved.push(packageNode); 6503 6504 if (isNoRemove()) { 6505 var message = "Package removal disabled: "; 6506 // check if the package is still installed 6507 if (isInstalled(packageNode)) { 6508 // the package is installed - keep it and add to skipped nodes 6509 dinfo(message + "Package " + packageName + " (" + packageID + 6510 ") will not be removed."); 6511 addSkippedRemoveNodes(packageNode); 6512 6513 // package is not effectively removed 6514 success = false; 6515 } else { 6516 // Get a list of checks to perform before installation. 6517 var checkNodes = getChecks(packageNode); 6518 6519 if (checkNodes.length != 0) { 6520 // package not installed - remove from local settings file 6521 dinfo(message + "Package " + packageName + " (" + packageID + 6522 ") will be removed from local settings because it is not installed."); 6523 removeSettingsNode(packageNode, true); 6524 success = true; 6525 } else { 6526 // unable to detect if the package is installed properly 6527 // assume it's still installed 6528 dinfo(message + "Package " + packageName + " (" + packageID + 6529 ") remains within local settings (no checks defined so WPKG " + 6530 "cannot verify if the package is still installed properly)."); 6531 success = false; 6532 } 6533 } 6534 } else { 6535 // remove dependent packages first 6536 var allSuccess = removePackagesDependent(packageNode); 6537 if (!allSuccess && !isForce()) { 6538 // removing of at least one dependent package failed 6539 var failedRemove = "Failed to remove package which depends on '" 6540 + packageName + " (" + packageID + "), skipping removal.\n" 6541 + "You might use the /force flag to force removal but " 6542 + "remember that the package depending on this one might " 6543 + "stop working."; 6544 success = false; 6545 6546 if (isQuitOnError()) { 6547 throw new Error(0, failedRemove); 6548 } else { 6549 error(failedRemove); 6550 } 6551 } else { 6552 // Save environment. 6553 var previousEnv = getEnv(); 6554 6555 try { 6556 info("Removing " + packageName + " (" + packageID + ")..."); 6557 6558 // select command lines to remove 6559 var cmds = getPackageCmdRemove(packageNode, null); 6560 6561 // set package specific environment 6562 loadPackageEnv(packageNode); 6563 6564 // Get downloads from package node (if any). 6565 var downloadNodes = getDownloads(packageNode, null); 6566 // Append downloads from command node. 6567 for (var iCommand = 0; iCommand < cmds.length; iCommand++) { 6568 var commandNode = cmds[iCommand ]; 6569 getDownloads(commandNode, downloadNodes); 6570 } 6571 6572 // Download all specified downloads. 6573 var downloadResult = downloadAll(downloadNodes); 6574 if (downloadResult != true) { 6575 var failureMessage = "Failed to download all files."; 6576 if (isQuitOnError()) { 6577 throw new Error(failureMessage); 6578 } else { 6579 error(failureMessage); 6580 } 6581 } 6582 6583 // execute all remove commands 6584 for (var iCommand = 0; iCommand < cmds.length; iCommand++) { 6585 // execute commands 6586 var cmdNode = cmds[iCommand ]; 6587 var cmd = getCommandCmd(cmdNode); 6588 if(cmd == null) { 6589 error("Error: Command missing. Please fix the package. Ignoring command."); 6590 continue; 6591 } 6592 var timeout = getCommandTimeout(cmdNode); 6593 var workdir = getCommandWorkdir(cmdNode); 6594 6595 // mark system as changed (command execution in 6596 // progress) 6597 setSystemChanged(); 6598 if(notifyAttr) { 6599 notifyUserStart(); 6600 } 6601 6602 var result = exec(cmd, timeout, workdir); 6603 6604 dinfo("Command returned result: " + result); 6605 6606 // check if there is an exit code defined 6607 var exitAction = getCommandExitCodeAction(cmdNode, result); 6608 6609 // Check for special exit codes. 6610 if (exitAction != null) { 6611 if (exitAction == "reboot") { 6612 // This exit code forces a reboot. 6613 info("Command in removal of " + packageName + " returned " + 6614 "exit code [" + result + "]. This exit code " + 6615 "requires an immediate reboot."); 6616 6617 // Verify if the package is a zombie (not in package 6618 // database any more). If it is a zombie, and not referenced 6619 // in the profile then prevent endless reboots by removing 6620 // the package from local database. 6621 if(isZombie(packageNode)) { 6622 // check if still referenced within the profile 6623 var profilePackageArray = getProfilePackageNodes(); 6624 var referenceFound = false; 6625 for (var iPackage = 0; iPackage < profilePackageArray.length; iPackage++) { 6626 if (packageID == getPackageID(profilePackageArray[iPackage])) { 6627 referenceFound = true; 6628 break; 6629 } 6630 } 6631 // if package is a zombie and not referenced 6632 // within the profile remove the settings entry 6633 if(!referenceFound && !isNoForcedRemove()) { 6634 removeSettingsNode(packageNode, true); 6635 info("Removed '" + packageName + "' (" 6636 + packageID + ") from local settings.\n" + 6637 "Package initiated immediate reboot and is a zombie."); 6638 } 6639 } 6640 6641 reboot(); 6642 } else if(exitAction == "delayedReboot") { 6643 // This exit code schedules a reboot 6644 info("Command in removal of " + packageName + 6645 " returned exit code [" + result + "]. This " + 6646 "exit code schedules a reboot."); 6647 // schedule reboot 6648 rebootRequired = true; 6649 // execute next command 6650 continue; 6651 } else if(exitAction == "postponedReboot") { 6652 info("Command in removal of " + packageName + 6653 " returned exit code [" + result + "]. This " + 6654 "exit code schedules a postponed reboot."); 6655 rebootPostponed = true; 6656 setPostponedReboot(rebootPostponed); 6657 // execute next command 6658 continue; 6659 } else { 6660 // This exit code is successful. 6661 info("Command in removal of " + packageName + " returned " + 6662 " exit code [" + result + "]. This exit code " + 6663 "indicates success."); 6664 continue; 6665 } 6666 } else if(result == 0) { 6667 // if exit code is 0, return success 6668 // execute next command 6669 dinfo("Command in removal of " + packageName + 6670 " returned exit code [" + result + "]. Success."); 6671 continue; 6672 } else { 6673 // command did not succeed, log error 6674 var failedCmd = "Exit code returned non-successful value: " + 6675 result + "\nPackage: " + packageName + ".\nCommand:\n" + cmd; 6676 // error occurred during remove 6677 success = false; 6678 6679 if (isQuitOnError()) { 6680 throw new Error(0, failedCmd); 6681 } else { 6682 error(failedCmd); 6683 } 6684 } 6685 } 6686 } catch (err) { 6687 success = false; 6688 var errorMessage = "Could not process (remove) package '" + 6689 packageName + "' (" + packageID + "):\n" + err.description + "."; 6690 if (isQuitOnError()) { 6691 throw new Error(errorMessage); 6692 } else { 6693 error(errorMessage); 6694 } 6695 } finally { 6696 6697 // PATCH SE3 6698 var packageID = getPackageID(packageNode); 6699 //var packageID = packageNode.getAttribute("id"); 6700 var lnkDir = packageNode.getAttribute("lnk"); 6701 var category = packageNode.getAttribute("category"); 6702 if (lnkDir != null) { 6703 if (category != null) { 6704 info("Suppression des raccourcis de id=" + packageID + " depuis %AllUsersProfile%\\Menu Démarrer\\Programmes\\" + category + "\\" + lnkDir); 6705 exec("%ComSpec% /C call %Z%\\wpkg\\AnalyseCategory.bat \"" + packageID + "\" \"" + lnkDir + "\" remove", timeout, workdir); 6706 } else { 6707 info("Pas de suppression automatique des raccourcis car 'category' est absent dans " + packageID + "."); 6708 } 6709 } else { 6710 info("Pas de suppression automatique des raccourcis car 'lnk' est absent dans " + packageID + "."); 6711 } 6712 // FIN PATCH SE3 6713 6714 // restore old environment 6715 dinfo("Restoring previous environment."); 6716 6717 // restore previous environment 6718 loadEnv(previousEnv); 6719 } 6720 } 6721 6722 // read reboot attribute 6723 var rebootAttr = getPackageReboot(packageNode); 6724 6725 // Use package checks to prove if package has been removed. 6726 // Zombies are removed in any case (even if uninstall failed) except 6727 // if the 6728 // "/noforcedremove" parameter was set 6729 if (!isInstalled(packageNode)) { 6730 // Remove package node from local xml. 6731 removeSettingsNode(packageNode, true); 6732 6733 if (rebootRequired || rebootAttr == "true") { 6734 info("Removal of " + packageName + " successful, system " + 6735 "rebooting."); 6736 reboot(); 6737 } else if (rebootPostponed || rebootAttr == "postponed") { 6738 info("Removal of " + packageName + " successful, postponed reboot scheduled."); 6739 } else { 6740 info("Removal of " + packageName + " successful."); 6741 } 6742 } else { 6743 // Check if package is a zombie. 6744 if(isZombie(packageNode)) { 6745 // Check if still referenced within the profile. 6746 var packageArray = getProfilePackageNodes(); 6747 var referenced = false; 6748 for (var i=0; i < packageArray.length; i++) { 6749 if (packageID == getPackageID(packageArray[i])) { 6750 referenced = true; 6751 break; 6752 } 6753 } 6754 // If package is a zombie and not referenced within the profile 6755 // remove the settings entry. 6756 if(!referenced && !isNoForcedRemove()) { 6757 removeSettingsNode(packageNode, true); 6758 warning("Errors occurred while removing '" + packageName + "' (" 6759 + packageID + ").\nPackage has been removed anyway because it was a zombie " + 6760 "and not referenced within the profile."); 6761 } 6762 } else if (rebootRequired || rebootAttr == "true") { 6763 warning("Package processing (remove) failed for package " + 6764 packageName + ".\nHowever the package requires a reboot to complete. Rebooting."); 6765 // reboot system without adding to local settings yet 6766 reboot(); 6767 } else if (rebootPostponed || rebootAttr == "postponed") { 6768 warning("Package processing (remove) failed for package " + 6769 packageName + ".\nHowever the package schedules a postponed reboot."); 6770 } else { 6771 // package installation failed 6772 success = false; 6773 message = "Could not process (remove) " + packageName + ".\n" + 6774 "Package still installed."; 6775 if (isQuitOnError()) { 6776 throw new Error(message); 6777 } else { 6778 error(message); 6779 } 6780 } 6781 } 6782 log(4,"==============================================================="); // saut ligne entre les paquets à installer 6783 } 6784 } 6785 6786 // return status 6787 return success; 6788 } 6789 6790 /** 6791 * Removes a package by name. 6792 * 6793 * @param name 6794 * name of the package to be removed (package ID). 6795 * @return True in case of successful remove of package and all packages 6796 * depending on it. False in case of failed package uninstall of failed 6797 * uninstall of package depending on it. 6798 */ 6799 function removePackageName(name) { 6800 // Query the package node. 6801 var node = getSettingNode(name); 6802 6803 // return code 6804 var success = false; 6805 6806 dinfo("Removing package '" + name + "'."); 6807 6808 if (node == null) { 6809 6810 // check if the package has been removed during this session 6811 var alreadyRemoved = false; 6812 for (var iRemovedPkg = 0; iRemovedPkg < packagesRemoved.length; iRemovedPkg++) { 6813 var removedPackage = packagesRemoved[iRemovedPkg]; 6814 if (name == getPackageID(removedPackage)) { 6815 alreadyRemoved = true; 6816 break; 6817 } 6818 } 6819 if (alreadyRemoved) { 6820 dinfo("Package '" + name + "' already removed during this session."); 6821 success = true; 6822 } else { 6823 info("Package '" + name + "' currently not installed."); 6824 success = false; 6825 } 6826 } else { 6827 success = removePackage(node); 6828 } 6829 return success; 6830 } 6831 6832 /** 6833 * Removes all packages which depends on the given package. Returns true in case 6834 * all packages could be removed. Returns false if at least one dependent 6835 * package failed to remove. 6836 * 6837 * @param packageNode 6838 * package to install the dependencies of (XML node) NOTE: The 6839 * package itself is not installed. 6840 * @return true=all dependencies installed successful; false=at least one 6841 * dependency failed 6842 */ 6843 function removePackagesDependent(packageNode) { 6844 var packageID = getPackageID(packageNode); 6845 var packageName = getPackageName(packageNode); 6846 6847 var problemDesc = ""; 6848 // search for all packages which depend on the one to be removed 6849 var dependencies = new Array(); 6850 var installedPackages = getSettingNodes(); 6851 for (var iInstPkg = 0; iInstPkg<installedPackages.length; iInstPkg++) { 6852 // get dependencies of this package 6853 var pkgDeps = getPackageDependencies(installedPackages[iInstPkg]); 6854 for (var j=0; j<pkgDeps.length; j++) { 6855 if (pkgDeps[j] == packageID) { 6856 dependencies.push(installedPackages[iInstPkg]); 6857 break; 6858 } 6859 } 6860 } 6861 if (dependencies.length > 0) { 6862 info("Removing packages depending on '" + packageName + 6863 "' (" + packageID + ")."); 6864 } 6865 var depSuccess = true; 6866 for (var iDependencies = 0; iDependencies < dependencies.length; iDependencies++) { 6867 var dependingPackage = dependencies[iDependencies]; 6868 // install this package 6869 var success = removePackage(dependingPackage); 6870 if (!success) { 6871 problemDesc += "Removal of depending package '" 6872 + getPackageName(dependingPackage) + "' (" 6873 + getPackageID(dependingPackage) + ") failed"; 6874 depSuccess = false; 6875 // skip remaining dependencies 6876 break; 6877 } 6878 } 6879 6880 if (depSuccess) { 6881 dinfo("Removal of depending packages for '" + 6882 packageName + "' (" + 6883 packageID + ") successfully finished."); 6884 } else { 6885 var failMessage = "Removal of depending packages for '" + 6886 packageName + "' (" + 6887 packageID + ") failed. " + problemDesc; 6888 if (isQuitOnError()) { 6889 throw new Error(failMessage); 6890 } else { 6891 error(failMessage); 6892 } 6893 } 6894 6895 return depSuccess; 6896 } 6897 6898 /** 6899 * Removes a package node from the settings XML node 6900 * 6901 * @param packageNode 6902 * The package node to be removed from settings. 6903 * @param saveImmediately 6904 * Set to true in order to save settings immediately after removing. 6905 * Settings will not be saved immediately if value is false. 6906 * @return Returns true in case of success, returns false if no node could be 6907 * removed 6908 */ 6909 function removeSettingsNode(packageNode, saveImmediately) { 6910 // make sure the settings node is selected 6911 var packageID = getPackageID(packageNode); 6912 dinfo("Removing package id '" + packageID + "' from settings."); 6913 var settingsNode = getSettingNode(packageID); 6914 var success = false; 6915 if(settingsNode != null) { 6916 success = removeNode(getSettings(), settingsNode); 6917 } 6918 // save settings if remove was successful 6919 if (success && saveImmediately) { 6920 saveSettings(true); 6921 } 6922 return success; 6923 } 6924 6925 /** 6926 * Erases host information cache to enforce re-reading of host information when 6927 * getter methods like getHostInformation(), getHostOS(), getLocale() etc are 6928 * executed. 6929 */ 6930 function resetHostInformationCache() { 6931 // Empty caches. 6932 hostName = null; 6933 hostOs = null; 6934 domainName = null; 6935 ipAddresses = null; 6936 hostGroups = null; 6937 hostArchitecture = null; 6938 hostAttributes = null; 6939 } 6940 6941 6942 /** 6943 * Sets state of multiple profile assignment. 6944 * 6945 * @param newState 6946 * new debug state 6947 */ 6948 function setApplyMultiple(newState) { 6949 applyMultiple = newState; 6950 } 6951 6952 /** 6953 * Set new architecture for this host. 6954 * @param newArchitecture Architecture to used for this host. 6955 */ 6956 function setArchitecture(newArchitecture) { 6957 hostArchitecture = newArchitecture; 6958 } 6959 6960 /** 6961 * Sets new status of the case-sensitive flag 6962 * 6963 * @param newSensitivity 6964 * true to enable case sensitivity, false to disable it (boolean) 6965 */ 6966 function setCaseSensitivity(newSensitivity) { 6967 caseSensitivity = newSensitivity; 6968 } 6969 6970 /** 6971 * Sets debug value to the given state. 6972 * 6973 * @param newState 6974 * new debug state 6975 */ 6976 function setDebug(newState) { 6977 debug = newState; 6978 } 6979 6980 /** 6981 * Sets domain name used by the script. 6982 * 6983 * @param newDomainName 6984 * new domain name 6985 */ 6986 function setDomainName(newDomainName) { 6987 domainName = newDomainName; 6988 } 6989 6990 /** 6991 * Sets dry run value to the given state. 6992 * 6993 * @param newState 6994 * new dry run state 6995 */ 6996 function setDryRun(newState) { 6997 dryrun = newState; 6998 } 6999 7000 /** 7001 * Sets a new value for the forceinstall flag. 7002 * 7003 * @param newState 7004 * new value for the forceinstall flag (boolean) 7005 */ 7006 function setForce(newState) { 7007 force = newState; 7008 } 7009 7010 /** 7011 * Sets a new value for the forceinstall flag. 7012 * 7013 * @param newState 7014 * new value for the forceinstall flag (boolean) 7015 */ 7016 function setForceInstall(newState) { 7017 forceInstall = newState; 7018 } 7019 7020 /** 7021 * Set new group names the host belongs to. 7022 * 7023 * @param newGroupNames 7024 * Array of group names the host belongs to. 7025 */ 7026 function setHostGroups(newGroupNames) { 7027 hostGroups = newGroupNames; 7028 } 7029 7030 /** 7031 * Set a new host name which will be used by the script. This is useful for 7032 * debugging purposes. 7033 * 7034 * @param newHostname 7035 * host name to be used 7036 */ 7037 function setHostname(newHostname) { 7038 hostName = newHostname; 7039 } 7040 7041 /** 7042 * Set new host OS variable overwriting automatically-detected value. 7043 * 7044 * @param newHostOS 7045 * host OS name 7046 */ 7047 function setHostOS(newHostOS) { 7048 hostOs = newHostOS; 7049 } 7050 7051 7052 /** 7053 * Sets a new profile-id attribute to the given host XML node 7054 * 7055 * @param hostNode 7056 * the host XML node to modify 7057 * @param profileID 7058 * the new profile ID to be written to this node 7059 */ 7060 function setHostProfile(hostNode, profileID) { 7061 hostNode.setAttribute("profile-id", profileID); 7062 } 7063 7064 /** 7065 * Set a new hosts node 7066 * 7067 * @param newHosts 7068 * the new hosts XML node to be used fro now on 7069 */ 7070 function setHosts(newHosts) { 7071 hosts = newHosts; 7072 } 7073 7074 /** 7075 * Set a new IP address list array. 7076 * 7077 * @param newIPAdresses 7078 * Array of IP addresses to be used by script. 7079 */ 7080 function setIPAddresses(newIPAdresses) { 7081 ipAddresses = newIPAdresses; 7082 } 7083 7084 /** 7085 * Set new value for log file pattern 7086 * 7087 * @param pattern 7088 * new pattern to be used 7089 * @return returns the pattern with expanded environment variables 7090 */ 7091 function setLogfilePattern(pattern) { 7092 var wshShell = new ActiveXObject("WScript.Shell"); 7093 logfilePattern = wshShell.ExpandEnvironmentStrings(pattern); 7094 return logfilePattern; 7095 } 7096 7097 /** 7098 * Sets new value for the no-download flag. 7099 * 7100 * @param newState 7101 * new value for the no-download flag (boolean). 7102 * If set to true then all downloads are disabled (just skipped). 7103 */ 7104 function setNoDownload(newState) { 7105 noDownload = newState; 7106 } 7107 7108 /** 7109 * Sets new value for the noforcedremove flag. 7110 * 7111 * @param newState 7112 * new value for the noforcedremove flag (boolean). 7113 */ 7114 function setNoForcedRemove(newState) { 7115 noForcedRemove = newState; 7116 } 7117 7118 /** 7119 * Sets new state for the noreboot flag. 7120 * 7121 * @param newState 7122 * new state of the noreboot flag (boolean) 7123 */ 7124 function setNoReboot(newState) { 7125 noreboot = newState; 7126 } 7127 7128 /** 7129 * Sets new state for the noremove flag. 7130 * 7131 * @param newState 7132 * new state of the noremove flag (boolean) 7133 */ 7134 function setNoRemove(newState) { 7135 noRemove = newState; 7136 } 7137 7138 /** 7139 * Sets new state for the noRunningState flag. 7140 * 7141 * @param newState 7142 * new state of the noreboot flag (boolean) 7143 */ 7144 function setNoRunningState(newState) { 7145 noRunningState = newState; 7146 } 7147 7148 /** 7149 * Sets a new package id-attribute to the given host XML node 7150 * 7151 * @param packageNode 7152 * the package XML node to modify 7153 * @param packageID 7154 * the new package ID to be written to this node 7155 */ 7156 function setPackageID(packageNode, packageID) { 7157 packageNode.setAttribute("id", packageID); 7158 } 7159 7160 /** 7161 * Set a new value for the manual installation flag of the given package. 7162 * Manual installations are flagged only for packages which are installed via 7163 * command line directly and not via synchronization. 7164 * 7165 * @param packageNode package to be modified. 7166 * @param manualInstall {Boolean} new value of package installation flag. 7167 */ 7168 function setPackageManualInstallation(packageNode, manualInstall) { 7169 if (packageNode == null) { 7170 error("No package node specified. Cannot set manual installation flag."); 7171 return; 7172 } 7173 if (manualInstall == null) { 7174 error("No manual installation flag value specified."); 7175 return; 7176 } 7177 if (manualInstall == true) { 7178 packageNode.setAttribute("manualInstall", "true"); 7179 } 7180 } 7181 7182 /** 7183 * Set a new packages node. 7184 * 7185 * @param newPackages 7186 * the new packages XML node to be used fro now on 7187 */ 7188 function setPackages(newPackages) { 7189 packages = newPackages; 7190 // iterate through all packages and set the package id to lower case 7191 // this allows XPath search for lowercase value later on (case-insensitive) 7192 if (packages != null && !isCaseSensitive()) { 7193 var packageNodes = getPackageNodes(); 7194 for (var i=0; i<packageNodes.length; i++) { 7195 var packageNode = packageNodes[i]; 7196 setPackageID(packageNode, getPackageID(packageNode).toLowerCase()); 7197 } 7198 } 7199 } 7200 7201 /** 7202 * Sets the status of postponed reboot. A postponed reboot schedules a system 7203 * reboot after finishing all actions (right before the script exits). 7204 * 7205 * @param newState 7206 * new state of postponed reboot 7207 */ 7208 function setPostponedReboot(newState) { 7209 postponedReboot = newState; 7210 } 7211 7212 /** 7213 * Sets a new profile id-attribute to the given profile XML node 7214 * 7215 * @param profileNode 7216 * the profile XML node to modify 7217 * @param profileID 7218 * the new profile ID to be written to this node 7219 */ 7220 function setProfileID(profileNode, profileID) { 7221 profileNode.setAttribute("id", profileID); 7222 } 7223 7224 /** 7225 * Set a new profiles node 7226 * 7227 * @param newProfiles 7228 * the new profiles XML node to be used fro now on 7229 */ 7230 function setProfiles(newProfiles) { 7231 profiles = newProfiles; 7232 // iterate through all profiles and set the profile id to lower case 7233 // this allows XPath search for lowercase value later on (case-insensitive) 7234 if (profiles != null && !isCaseSensitive()) { 7235 var profileNodes = getProfileNodes(); 7236 for (var i=0; i<profileNodes.length; i++) { 7237 var profileNode = profileNodes[i]; 7238 setProfileID(profileNode, getProfileID(profileNode).toLowerCase()); 7239 } 7240 } 7241 } 7242 7243 /** 7244 * Sets query mode to new state. Allowed states are "remote" and "local". 7245 * 7246 * @param newState query mode value to be set. 7247 */ 7248 function setQueryMode(newState) { 7249 if (newState != null && (newState == "remote" || newState == "local")) { 7250 queryMode = newState; 7251 } 7252 } 7253 7254 /** 7255 * Sets new state of the quiet flag 7256 * 7257 * @param newState 7258 * new status of quiet flag (boolean) 7259 */ 7260 function setQuiet(newState) { 7261 quietMode = newState; 7262 } 7263 7264 /** 7265 * Sets a new value for the quit on error flag. 7266 * 7267 * @param newState 7268 * new value for the quit on error flag (boolean). 7269 */ 7270 function setQuitOnError(newState) { 7271 quitonerror = newState; 7272 } 7273 7274 /** 7275 * Sets new value for the reboot command (rebootCmd). 7276 * 7277 * @param newCommand 7278 */ 7279 function setRebootCmd(newCommand) { 7280 var wshShell = new ActiveXObject("WScript.Shell"); 7281 rebootCmd = wshShell.ExpandEnvironmentStrings(newCommand); 7282 } 7283 7284 /** 7285 * Set state of application so other applications can see that it is running by 7286 * reading from the registry. 7287 * 7288 * @param statename 7289 * String which is written to the registry as a value of the 7290 * "running" key 7291 */ 7292 function setRunningState(statename) { 7293 var WshShell = new ActiveXObject("WScript.Shell"); 7294 var val; 7295 7296 try { 7297 val = WshShell.RegWrite(sRegWPKG_Running, statename); 7298 } catch (e) { 7299 val = null; 7300 } 7301 7302 return val; 7303 } 7304 7305 /** 7306 * Sets new value for the sendStatus flag which defines if status messages are 7307 * sent to the calling program using STDOUT 7308 * 7309 * @param newStatus 7310 * new value for the sendStatus flag (boolean) 7311 */ 7312 function setSendStatus(newStatus) { 7313 sendStatus = newStatus; 7314 } 7315 7316 /** 7317 * Set a new settings node 7318 * 7319 * @param newSettings 7320 * the new settings XML node to be used fro now on 7321 */ 7322 function setSettings(newSettings, saveImmediately) { 7323 settings = newSettings; 7324 // iterate through all packages and set the package id to lower case 7325 // this allows XPath search for lowercase value later on (case-insensitive) 7326 if (settings != null && !isCaseSensitive()) { 7327 var packageNodes = getSettingNodes(); 7328 for (var i=0; i<packageNodes.length; i++) { 7329 var packageNode = packageNodes[i]; 7330 setPackageID(packageNode, getPackageID(packageNode).toLowerCase()); 7331 } 7332 } 7333 // save new settings 7334 if(saveImmediately) { 7335 saveSettings(true); 7336 } 7337 } 7338 7339 /** 7340 * Set path to local settings file (locak package database). 7341 * The path might contain environment variables as well as the following 7342 * expressions: 7343 * [HOSTNAME] Replaced by the executing hostname. 7344 * [PROFILE] Replaced by the concatenated list of profiles applied. 7345 * @param path path to settings XML file. 7346 */ 7347 function setSettingsPath(path) { 7348 if (path == null || path == "") { 7349 error("Path to settings is required"); 7350 return; 7351 } 7352 7353 var wshObject = new ActiveXObject("WScript.Shell"); 7354 var expandedSettingsPath = wshObject.ExpandEnvironmentStrings(path); 7355 7356 // Set global variable holding settings file path. 7357 settings_file = expandedSettingsPath; 7358 } 7359 7360 7361 /** 7362 * Sets the system changed attribute to true. Call this method to make WPKG 7363 * aware that a system change has been done. 7364 * 7365 * @return returns current system change status (always true after this method 7366 * has been called 7367 */ 7368 function setSystemChanged() { 7369 systemChanged = true; 7370 return systemChanged; 7371 } 7372 7373 /** 7374 * Set new value for the boolean flag to disable/enable event log logging. 7375 * 7376 * @param newValue 7377 * value to be used for the skip event log flag from now on. 7378 */ 7379 function setSkipEventLog(newValue) { 7380 skipEventLog = newValue; 7381 } 7382 7383 /** 7384 * Set event log fallback to new value (enabled/disabled). 7385 * 7386 * @param newValue 7387 * value to be used for the event log fallback flag. 7388 */ 7389 function setEventLogFallback(newValue) { 7390 eventLogFallback = newValue; 7391 } 7392 7393 /** 7394 * Sorts package nodes by priority flag. 7395 * 7396 * @param packageNodes 7397 * JScript Array containing package node entries 7398 * @param sortBy 7399 * select the field to sort on. Supported Values are "PRIORITY" and 7400 * "NAME" 7401 * @param sortOrder 7402 * order in which the elements are sorted (integer) valid values:<br>1 7403 * sort ascending (default)<br>2 sort descending 7404 * 7405 * @return new Array containing the same package nodes in sorted order (sorted 7406 * by priority) 7407 */ 7408 function sortPackageNodes(packageNodes, sortBy, sortOrder) { 7409 // create array to do the sorting on 7410 var sortedPackages = new Array(); 7411 for (var iPkgNodes = 0; iPkgNodes < packageNodes.length; iPkgNodes++) { 7412 sortedPackages.push(packageNodes[iPkgNodes]); 7413 } 7414 // Classic bubble-sort algorithm on selected attribute 7415 for (var iSortedPkg = 0; iSortedPkg < sortedPackages.length - 1; iSortedPkg++) { 7416 for (var j=0; j < sortedPackages.length - 1 - iSortedPkg; j++) { 7417 var prio1; 7418 var prio2; 7419 var priVal1 = null; 7420 var priVal2 = null; 7421 7422 switch(sortBy) { 7423 case "NAME": 7424 priVal1 = getPackageName(sortedPackages[j]); 7425 priVal2 = getPackageName(sortedPackages[j + 1]); 7426 break; 7427 default: 7428 priVal1 = parseInt(getPackagePriority(sortedPackages[j])); 7429 priVal2 = parseInt(getPackagePriority(sortedPackages[j + 1])); 7430 break; 7431 } 7432 // If a priority is not set, we assume 0. 7433 7434 if (priVal1 == null) { 7435 prio1 = 0; 7436 } else { 7437 prio1 = priVal1; 7438 } 7439 7440 if (priVal2 == null) { 7441 prio2 = 0; 7442 } else { 7443 prio2 = priVal2; 7444 } 7445 7446 var swapElements = false; 7447 switch (sortOrder) { 7448 case 2: 7449 if (prio1 < prio2) { 7450 swapElements = true; 7451 } 7452 break; 7453 default: 7454 if (prio1 > prio2) { 7455 swapElements = true; 7456 } 7457 break; 7458 } 7459 // If the priority of the first one in the list exceeds the second, 7460 // swap the packages. 7461 if (swapElements) { 7462 var tmp = sortedPackages[j]; 7463 sortedPackages[j] = sortedPackages[j + 1]; 7464 sortedPackages[j + 1] = tmp; 7465 } 7466 } 7467 } 7468 return sortedPackages; 7469 } 7470 7471 /** 7472 * Sorts the settings file by package name. Returns sorted package XML node. 7473 */ 7474 function sortSettings() { 7475 // sort current setting nodes 7476 var sortedPackages = sortPackageNodes(getSettingNodes(), "NAME", 1); 7477 7478 // Get setting checks. 7479 var settingsChecks = getSettingsCheckResults(); 7480 7481 // create new (empty) settings node 7482 var sortedSettings = createSettings(); 7483 sortedSettings.appendChild(settingsChecks); 7484 7485 // use this settings node 7486 setSettings(sortedSettings, false); 7487 7488 // fill new settings node with sorted packages (same order) 7489 for (var i=0; i<sortedPackages.length; i++) { 7490 addSettingsNode(sortedPackages[i], false); 7491 } 7492 } 7493 7494 /** 7495 * Synchronizes the current package state to that of the specified profile, 7496 * adding, removing or upgrading packages. 7497 */ 7498 function synchronizeProfile() { 7499 // send message to client 7500 logStatus("Starting software synchronization"); 7501 7502 /** 7503 * Get package nodes referenced within the profile (and profile 7504 * dependencies). This includes package dependencies as well. 7505 */ 7506 var profilePackageNodes = getProfilePackageNodes(); 7507 dinfo("Synchronizing. Number of packages referenced by profile: " + profilePackageNodes.length + "."); 7508 7509 var localPackages = getPackagesManuallyInstalled(); 7510 if (localPackages.length > 0) { 7511 dinfo("Synchronizing. Locally installed packages: " + localPackages.length + "."); 7512 for(var i=0; i<localPackages.length; i++) { 7513 // Fetch latest package node to schedule installation/upgrade. 7514 var localPackage = localPackages[i]; 7515 var latestVersion = getPackageNode(getPackageID(localPackage)); 7516 if (latestVersion != null) { 7517 profilePackageNodes.push(latestVersion); 7518 } 7519 } 7520 } 7521 7522 // Get list of packages scheduled for removal. 7523 // This excludes manually installed packages except if they do not exist. 7524 var removablesArray = getPackagesRemoved(); 7525 7526 dinfo("Number of packages to remove: " + removablesArray.length); 7527 logStatus("Number of packages to be removed: " + removablesArray.length); 7528 /* 7529 * upgrade packages to be removed to latest version first. This allows system administrators to provide a fixed 7530 * version of the package which allows clean uninstall. 7531 * 7532 * This was done to allow fixing a broken uninstall-procedure on server side. Without upgrading to the latest 7533 * version here it might happen that the package cannot be removed without the possibility to fix it. If you remove 7534 * the package completely from the package database it will be forced to be removed from the local settings file 7535 * even if uninstall fails. 7536 * 7537 * NOTE: This is not done within the same loop as the removal (see below) in order to prevent re-installing already 7538 * removed dependencies. 7539 */ 7540 // sort packages to upgrade the ones with highest priority first 7541 if (isUpgradeBeforeRemove()) { 7542 var sortedUpgradeList = sortPackageNodes(removablesArray, "PRIORITY", 2); 7543 for (var iSortedPkg = 0; iSortedPkg < sortedUpgradeList.length; iSortedPkg++) { 7544 var upgradePkgNode = sortedUpgradeList[iSortedPkg]; 7545 // upgrade package if package is available on server database 7546 var serverPackage = getPackageNode(getPackageID(upgradePkgNode)); 7547 if (serverPackage != null) { 7548 logStatus("Remove: Checking status of '" + getPackageName(serverPackage) + 7549 "' (" + (iSortedPkg+1) + "/" + sortedUpgradeList.length + ")"); 7550 // start upgrade first 7551 installPackage(serverPackage); 7552 } 7553 } 7554 } 7555 7556 // Remove packages which do not exist in package database or do not apply 7557 // to the profile 7558 // reverse-sort packages to remove the one with lowest priority first 7559 var sortedRemovablesArray = sortPackageNodes(removablesArray, "PRIORITY", 1); 7560 for (var iRemovables = 0; iRemovables < sortedRemovablesArray.length; iRemovables++) { 7561 var removePkgNode = sortedRemovablesArray[iRemovables]; 7562 // remove package from system 7563 // the settings node might have been changed during update before 7564 // reload it. 7565 logStatus("Remove: Removing package '" + getPackageName(removePkgNode) + 7566 "' (" + (iRemovables+1) + "/" + sortedRemovablesArray.length + ")"); 7567 // removePackage(getSettingNode(getPackageID(removePkgNode))); 7568 removePackageName(getPackageID(removePkgNode)); 7569 } 7570 7571 // create array to do the sorting on 7572 var sortedPackages = sortPackageNodes(profilePackageNodes, "PRIORITY", 2); 7573 7574 /* 7575 * Move packages with execute=changed attribute to independent array in order to allow them to be executed after the 7576 * other packages. 7577 */ 7578 var packagesToInstall = new Array(); 7579 var packagesAwaitingChange = new Array(); 7580 // NOTE: This should not change the sort order of the packages. 7581 for (var iPkg = 0; iPkg < sortedPackages.length; iPkg++) { 7582 var packageNode = sortedPackages[iPkg]; 7583 var executeAttribute = getPackageExecute(packageNode); 7584 if (executeAttribute == "changed") { 7585 packagesAwaitingChange.push(packageNode); 7586 } else { 7587 packagesToInstall.push(packageNode); 7588 } 7589 } 7590 7591 /* 7592 * Loop over each available package and install it. No check required if package is already installed or not. The 7593 * install method will check by itself if the package needs to be installed/upgraded or no action is needed. 7594 */ 7595 for (var iInstallPkg=0; iInstallPkg < packagesToInstall.length; iInstallPkg++) { 7596 // install/upgrade package 7597 logStatus("Install: Verifying package '" + getPackageName(packagesToInstall[iInstallPkg]) + 7598 "' (" + (iInstallPkg + 1) + "/" + packagesToInstall.length + ")"); 7599 installPackage(packagesToInstall[iInstallPkg]); 7600 } 7601 7602 /* 7603 * Install packages which might have been postponed because no other change has been done to the system. 7604 */ 7605 for(var iChangeAwait = 0; iChangeAwait < packagesAwaitingChange.length; iChangeAwait++) { 7606 // try applying this packages again now. 7607 if (isSystemChanged()) { 7608 logStatus("Install: Verifying package (system changed) '" + getPackageName(packagesAwaitingChange[iChangeAwait]) + 7609 "' (" + (packagesToInstall.length + iChangeAwait + 1) + "/" + sortedPackages.length + ")"); 7610 7611 installPackage(packagesAwaitingChange[iChangeAwait]); 7612 } else { 7613 logStatus("Install: No system change, skipping '" + getPackageName(packagesAwaitingChange[iChangeAwait]) + 7614 "' (" + (packagesToInstall.length + iChangeAwait + 1) + "/" + sortedPackages.length + ")"); 7615 } 7616 } 7617 7618 logStatus("Finished software synchronization"); 7619 7620 // If we had previously warned the