1# 2# This file is part of the LibreOffice project. 3# 4# This Source Code Form is subject to the terms of the Mozilla Public 5# License, v. 2.0. If a copy of the MPL was not distributed with this 6# file, You can obtain one at http://mozilla.org/MPL/2.0/. 7# 8# This file incorporates work covered by the following license notice: 9# 10# Licensed to the Apache Software Foundation (ASF) under one or more 11# contributor license agreements. See the NOTICE file distributed 12# with this work for additional information regarding copyright 13# ownership. The ASF licenses this file to you under the Apache 14# License, Version 2.0 (the "License"); you may not use this file 15# except in compliance with the License. You may obtain a copy of 16# the License at http://www.apache.org/licenses/LICENSE-2.0 . 17# 18 19package installer::windows::directory; 20 21use installer::exiter; 22use installer::files; 23use installer::globals; 24use installer::pathanalyzer; 25use installer::windows::idtglobal; 26use installer::windows::msiglobal; 27 28############################################################## 29# Collecting all directory trees in global hash 30############################################################## 31 32my @msistandarddirectorynames = qw( 33 AdminToolsFolder 34 AppDataFolder 35 CommonAppDataFolder 36 CommonFiles64Folder 37 CommonFilesFolder 38 DesktopFolder 39 FavoritesFolder 40 FontsFolder 41 LocalAppDataFolder 42 MyPicturesFolder 43 NetHoodFolder 44 PersonalFolder 45 PrintHoodFolder 46 ProgramFiles64Folder 47 ProgramFilesFolder 48 ProgramMenuFolder 49 RecentFolder 50 SendToFolder 51 StartMenuFolder 52 StartupFolder 53 System16Folder 54 System64Folder 55 SystemFolder 56 TempFolder 57 TemplateFolder 58 WindowsFolder 59 WindowsVolume 60); 61 62sub collectdirectorytrees 63{ 64 my ( $directoryref ) = @_; 65 66 for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) 67 { 68 my $onedir = ${$directoryref}[$i]; 69 my $styles = ""; 70 if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; } 71 72 if ( $styles ne "" ) 73 { 74 foreach my $treestyle ( keys %installer::globals::treestyles ) 75 { 76 if ( $styles =~ /\b$treestyle\b/ ) 77 { 78 my $hostname = $onedir->{'HostName'}; 79 # -> hostname is the key, the style the value! 80 $installer::globals::hostnametreestyles{$hostname} = $treestyle; 81 } 82 } 83 } 84 } 85} 86 87############################################################## 88# Overwriting global programfilesfolder, if required 89############################################################## 90 91sub overwrite_programfilesfolder 92{ 93 my ( $allvariables ) = @_; 94 95 if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 )) 96 { 97 $installer::globals::programfilesfolder = "ProgramFiles64Folder"; 98 } 99} 100 101############################################################## 102# Maximum length of directory name is 72. 103# Taking care of underlines, which are the separator. 104############################################################## 105 106sub make_short_dir_version 107{ 108 my ($longstring) = @_; 109 110 my $shortstring = ""; 111 my $cutlength = 60; 112 my $length = 5; # So the directory can still be recognized 113 my $longstring_save = $longstring; 114 115 # Splitting the string at each "underline" and allowing only 116 # $length characters per directory name. 117 # Checking also uniqueness and length. 118 119 for my $onestring ( split /_\s*/, $longstring ) 120 { 121 my $partstring = ""; 122 123 if ( $onestring =~ /\-/ ) 124 { 125 for my $onelocalstring ( split /-\s*/, $onestring ) 126 { 127 if ( length($onelocalstring) > $length ) { 128 $onelocalstring = substr($onelocalstring, 0, $length); 129 } 130 $partstring .= "-" . $onelocalstring; 131 } 132 $partstring =~ s/^\s*\-//; 133 } 134 else 135 { 136 if ( length($onestring) > $length ) { 137 $partstring = substr($onestring, 0, $length); 138 } 139 else { 140 $partstring = $onestring; 141 } 142 } 143 144 $shortstring .= "_" . $partstring; 145 } 146 147 $shortstring =~ s/^\s*\_//; 148 149 # Setting unique ID to each directory 150 # No counter allowed, process must be absolute reproducible due to patch creation process. 151 152 # chomp(my $id = `echo $longstring_save | md5sum | sed -e "s/ .*//g"`); # Very, very slow 153 # my $subid = substr($id, 0, 9); # taking only the first 9 digits 154 155 my $subid = installer::windows::msiglobal::calculate_id($longstring_save, 9); # taking only the first 9 digits 156 157 if ( length($shortstring) > $cutlength ) { $shortstring = substr($shortstring, 0, $cutlength); } 158 159 $shortstring = $shortstring . "_" . $subid; 160 161 return $shortstring; 162} 163 164############################################################## 165# Adding unique directory names to the directory collection 166############################################################## 167 168my $already_checked_the_frigging_directories_for_uniqueness = 0; 169 170sub create_unique_directorynames 171{ 172 my ($directoryref, $allvariables) = @_; 173 174 my %completedirhashstep1 = (); 175 my %shortdirhash = (); 176 my %shortdirhashreverse = (); 177 my $infoline = ""; 178 179 for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) 180 { 181 my $onedir = ${$directoryref}[$i]; 182 my $uniquename = $onedir->{'HostName'}; 183 184 my $styles = ""; 185 if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; } 186 187 $uniquename =~ s/^\s*//g; # removing beginning white spaces 188 $uniquename =~ s/\s*$//g; # removing ending white spaces 189 $uniquename =~ s/\s//g; # removing white spaces 190 $uniquename =~ s/\_//g; # removing existing underlines 191 $uniquename =~ s/\.//g; # removing dots in directoryname 192 $uniquename =~ s/\Q$installer::globals::separator\E/\_/g; # replacing slash and backslash with underline 193 $uniquename =~ s/OpenOffice/OO/g; 194 $uniquename =~ s/LibreOffice/LO/g; 195 $uniquename =~ s/_registry/_rgy/g; 196 $uniquename =~ s/_registration/_rgn/g; 197 $uniquename =~ s/_extension/_ext/g; 198 $uniquename =~ s/_frame/_frm/g; 199 $uniquename =~ s/_table/_tbl/g; 200 $uniquename =~ s/_chart/_crt/g; 201 $uniquename =~ s/_plat-linux/_plx/g; 202 203 # The names after this small changes must still be unique! 204 if ( exists($completedirhashstep1{$uniquename}) ) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 1): \"$uniquename\".", "create_unique_directorynames"); } 205 $completedirhashstep1{$uniquename} = 1; 206 207 # Starting to make unique name for the parent and its directory 208 my $originaluniquename = $uniquename; 209 210 $uniquename = make_short_dir_version($uniquename); 211 212 # Checking if the same directory already exists, but has another short version. 213 if (( exists($shortdirhash{$originaluniquename}) ) && ( $shortdirhash{$originaluniquename} ne $uniquename )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 2A): \"$uniquename\".", "create_unique_directorynames"); } 214 215 # Also checking vice versa 216 # Checking if the same short directory already exists, but has another long version. 217 if (( exists($shortdirhashreverse{$uniquename}) ) && ( $shortdirhashreverse{$uniquename} ne $originaluniquename )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 2B): \"$uniquename\".", "create_unique_directorynames"); } 218 219 # Creating assignment from long to short directory names 220 $shortdirhash{$originaluniquename} = $uniquename; 221 $shortdirhashreverse{$uniquename} = $originaluniquename; 222 223 # Important: The unique parent is generated from the string $originaluniquename (with the use of underlines). 224 225 my $uniqueparentname = $originaluniquename; 226 my $keepparent = 1; 227 228 if ( $uniqueparentname =~ /^\s*(.*)\_(.*?)\s*$/ ) # the underline is now the separator 229 { 230 $uniqueparentname = $1; 231 $keepparent = 0; 232 } 233 else 234 { 235 $uniqueparentname = $installer::globals::programfilesfolder; 236 $keepparent = 1; 237 } 238 239 if ( $styles =~ /\bPROGRAMFILESFOLDER\b/ ) 240 { 241 $uniqueparentname = $installer::globals::programfilesfolder; 242 $keepparent = 1; 243 } 244 if ( $styles =~ /\bCOMMONFILESFOLDER\b/ ) 245 { 246 $uniqueparentname = $installer::globals::commonfilesfolder; 247 $keepparent = 1; 248 } 249 if ( $styles =~ /\bCOMMONAPPDATAFOLDER\b/ ) 250 { 251 $uniqueparentname = $installer::globals::commonappdatafolder; 252 $keepparent = 1; 253 } 254 if ( $styles =~ /\bLOCALAPPDATAFOLDER\b/ ) 255 { 256 $uniqueparentname = $installer::globals::localappdatafolder; 257 $keepparent = 1; 258 } 259 260 if ( $styles =~ /\bSHAREPOINTPATH\b/ ) 261 { 262 $uniqueparentname = "SHAREPOINTPATH"; 263 $installer::globals::usesharepointpath = 1; 264 $keepparent = 1; 265 } 266 267 # also setting short directory name for the parent 268 269 my $originaluniqueparentname = $uniqueparentname; 270 271 if ( ! $keepparent ) 272 { 273 $uniqueparentname = make_short_dir_version($uniqueparentname); 274 } 275 276 # Again checking if the same directory already exists, but has another short version. 277 if (( exists($shortdirhash{$originaluniqueparentname}) ) && ( $shortdirhash{$originaluniqueparentname} ne $uniqueparentname )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 3A): \"$uniqueparentname\".", "create_unique_directorynames"); } 278 279 # Also checking vice versa 280 # Checking if the same short directory already exists, but has another long version. 281 if (( exists($shortdirhashreverse{$uniqueparentname}) ) && ( $shortdirhashreverse{$uniqueparentname} ne $originaluniqueparentname )) { installer::exiter::exit_program("ERROR: Error in packaging process. Unallowed modification of directory name, not unique (step 3B): \"$uniqueparentname\".", "create_unique_directorynames"); } 282 283 $shortdirhash{$originaluniqueparentname} = $uniqueparentname; 284 $shortdirhashreverse{$uniqueparentname} = $originaluniqueparentname; 285 286 # Hyphen not allowed in database 287 $uniquename =~ s/\-/\_/g; # making "-" to "_" 288 $uniqueparentname =~ s/\-/\_/g; # making "-" to "_" 289 290 # And finally setting the values for the directories 291 $onedir->{'uniquename'} = $uniquename; 292 $onedir->{'uniqueparentname'} = $uniqueparentname; 293 294 # setting the installlocation directory 295 if ( $styles =~ /\bISINSTALLLOCATION\b/ ) 296 { 297 if ( $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION already set: \"$installer::globals::installlocationdirectory\".", "create_unique_directorynames"); } 298 $installer::globals::installlocationdirectory = $uniquename; 299 $installer::globals::installlocationdirectoryset = 1; 300 } 301 } 302} 303 304##################################################### 305# Adding ":." to selected default directory names 306##################################################### 307 308sub check_sourcedir_addon 309{ 310 my ( $onedir, $allvariableshashref ) = @_; 311 312 if (($installer::globals::languagepack) || 313 ($installer::globals::helppack) || 314 ($allvariableshashref->{'CHANGETARGETDIR'})) 315 { 316 my $sourcediraddon = "\:\."; 317 $onedir->{'defaultdir'} = $onedir->{'defaultdir'} . $sourcediraddon; 318 } 319 320} 321 322##################################################### 323# The directory with the style ISINSTALLLOCATION 324# will be replaced by INSTALLLOCATION 325##################################################### 326 327sub set_installlocation_directory 328{ 329 my ( $directoryref, $allvariableshashref ) = @_; 330 331 if ( ! $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION not set!", "set_installlocation_directory"); } 332 333 for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) 334 { 335 my $onedir = ${$directoryref}[$i]; 336 337 if ( $onedir->{'uniquename'} eq $installer::globals::installlocationdirectory ) 338 { 339 $onedir->{'uniquename'} = "INSTALLLOCATION"; 340 check_sourcedir_addon($onedir, $allvariableshashref); 341 } 342 343 if ( $onedir->{'uniquename'} eq $installer::globals::vendordirectory ) 344 { 345 check_sourcedir_addon($onedir, $allvariableshashref); 346 } 347 348 if ( $onedir->{'uniqueparentname'} eq $installer::globals::installlocationdirectory ) 349 { 350 $onedir->{'uniqueparentname'} = "INSTALLLOCATION"; 351 } 352 } 353} 354 355##################################################### 356# Getting the name of the top level directory. This 357# can have only one letter 358##################################################### 359 360sub get_last_directory_name 361{ 362 my ($completepathref) = @_; 363 364 if ( $$completepathref =~ /^.*[\/\\](.+?)\s*$/ ) 365 { 366 $$completepathref = $1; 367 } 368} 369 370##################################################### 371# Creating the defaultdir for the file Director.idt 372##################################################### 373 374sub create_defaultdir_directorynames 375{ 376 my ($directoryref, $shortdirnamehashref) = @_; 377 378 my @shortnames = (); 379 if ( $installer::globals::updatedatabase ) { @shortnames = values(%{$shortdirnamehashref}); } 380 elsif ( $installer::globals::prepare_winpatch ) { @shortnames = values(%installer::globals::saved83dirmapping); } 381 382 for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) 383 { 384 my $onedir = ${$directoryref}[$i]; 385 my $hostname = $onedir->{'HostName'}; 386 387 $hostname =~ s/\Q$installer::globals::separator\E\s*$//; 388 get_last_directory_name(\$hostname); 389 my $uniquename = $onedir->{'uniquename'}; 390 my $shortstring; 391 if (( $installer::globals::updatedatabase ) && ( exists($shortdirnamehashref->{$uniquename}) )) 392 { 393 $shortstring = $shortdirnamehashref->{$uniquename}; 394 } 395 elsif (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::saved83dirmapping{$uniquename}) )) 396 { 397 $shortstring = $installer::globals::saved83dirmapping{$uniquename}; 398 } 399 else 400 { 401 $shortstring = installer::windows::idtglobal::make_eight_three_conform($hostname, "dir", \@shortnames); 402 } 403 404 my $defaultdir; 405 406 if ( $shortstring eq $hostname ) 407 { 408 $defaultdir = $hostname; 409 } 410 else 411 { 412 $defaultdir = $shortstring . "|" . $hostname; 413 } 414 415 $onedir->{'defaultdir'} = $defaultdir; 416 417 my $fontdir = ""; 418 if ( $onedir->{'Dir'} ) { $fontdir = $onedir->{'Dir'}; } 419 420 my $fontdefaultdir = ""; 421 if ( $onedir->{'defaultdir'} ) { $fontdefaultdir = $onedir->{'defaultdir'}; } 422 423 if (( $fontdir eq $installer::globals::fontsdirhostname ) && ( $fontdefaultdir eq $installer::globals::fontsdirhostname )) 424 { 425 $installer::globals::fontsdirname = $onedir->{'defaultdir'}; 426 $installer::globals::fontsdirparent = $onedir->{'uniqueparentname'}; 427 } 428 } 429} 430 431############################################### 432# Fill content into the directory table 433############################################### 434 435sub create_directorytable_from_collection 436{ 437 my ($directorytableref, $directoryref) = @_; 438 439 for ( my $i = 0; $i <= $#{$directoryref}; $i++ ) 440 { 441 my $onedir = ${$directoryref}[$i]; 442 my $hostname = $onedir->{'HostName'}; 443 my $dir = ""; 444 445 if ( $onedir->{'Dir'} ) { $dir = $onedir->{'Dir'}; } 446 447 if (( $dir eq "PREDEFINED_PROGDIR" ) && ( $hostname eq "" )) { next; } # removing files from root directory 448 449 my $oneline = $onedir->{'uniquename'} . "\t" . $onedir->{'uniqueparentname'} . "\t" . $onedir->{'defaultdir'} . "\n"; 450 451 push(@{$directorytableref}, $oneline); 452 } 453} 454 455############################################### 456# Defining the root installation structure 457############################################### 458 459sub add_root_directories 460{ 461 my ($directorytableref, $allvariableshashref, $onelanguage) = @_; 462 463 my $oneline = ""; 464 465 if (( ! $installer::globals::languagepack ) && ( ! $installer::globals::helppack ) && ( ! $allvariableshashref->{'DONTUSESTARTMENUFOLDER'} )) 466 { 467 my $productname; 468 469 $productname = $allvariableshashref->{'PRODUCTNAME'}; 470 my $productversion = $allvariableshashref->{'PRODUCTVERSION'}; 471 my $baseproductversion = $productversion; 472 473 if (( $installer::globals::prepare_winpatch ) && ( $allvariableshashref->{'BASEPRODUCTVERSION'} )) 474 { 475 $baseproductversion = $allvariableshashref->{'BASEPRODUCTVERSION'}; # for example "2.0" for OOo 476 } 477 478 my $realproductkey = $productname . " " . $productversion; 479 my $productkey = $productname . " " . $baseproductversion; 480 481 if (( $allvariableshashref->{'POSTVERSIONEXTENSION'} ) && ( ! $allvariableshashref->{'DONTUSEEXTENSIONINDEFAULTDIR'} )) 482 { 483 $productkey = $productkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'}; 484 $realproductkey = $realproductkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'}; 485 } 486 if ( $allvariableshashref->{'NOVERSIONINDIRNAME'} ) 487 { 488 $productkey = $productname; 489 $realproductkey = $realproductname; 490 } 491 if ( $allvariableshashref->{'NOSPACEINDIRECTORYNAME'} ) 492 { 493 $productkey =~ s/\ /\_/g; 494 $realproductkey =~ s/\ /\_/g; 495 } 496 497 my $shortproductkey = installer::windows::idtglobal::make_eight_three_conform($productkey, "dir"); # third parameter not used 498 $shortproductkey =~ s/\s/\_/g; # changing empty space to underline 499 500 $oneline = "$installer::globals::officemenufolder\t$installer::globals::programmenufolder\t$shortproductkey|$realproductkey\n"; 501 push(@{$directorytableref}, $oneline); 502 } 503 504 $oneline = "TARGETDIR\t\tSourceDir\n"; 505 push(@{$directorytableref}, $oneline); 506 507 $oneline = "WindowsFolder\tTARGETDIR\tWindows\n"; 508 push(@{$directorytableref}, $oneline); 509 510 $oneline = "$installer::globals::programfilesfolder\tTARGETDIR\t.\n"; 511 push(@{$directorytableref}, $oneline); 512 513 $oneline = "$installer::globals::programmenufolder\tTARGETDIR\t.\n"; 514 push(@{$directorytableref}, $oneline); 515 516 $oneline = "$installer::globals::startupfolder\tTARGETDIR\t.\n"; 517 push(@{$directorytableref}, $oneline); 518 519 $oneline = "$installer::globals::desktopfolder\tTARGETDIR\t.\n"; 520 push(@{$directorytableref}, $oneline); 521 522 $oneline = "$installer::globals::startmenufolder\tTARGETDIR\t.\n"; 523 push(@{$directorytableref}, $oneline); 524 525 $oneline = "$installer::globals::commonfilesfolder\tTARGETDIR\t.\n"; 526 push(@{$directorytableref}, $oneline); 527 528 $oneline = "$installer::globals::commonappdatafolder\tTARGETDIR\t.\n"; 529 push(@{$directorytableref}, $oneline); 530 531 $oneline = "$installer::globals::localappdatafolder\tTARGETDIR\t.\n"; 532 push(@{$directorytableref}, $oneline); 533 534 if ( $installer::globals::usesharepointpath ) 535 { 536 $oneline = "SHAREPOINTPATH\tTARGETDIR\t.\n"; 537 push(@{$directorytableref}, $oneline); 538 } 539 540 my $localtemplatefoldername = $installer::globals::templatefoldername; 541 my $directorytableentry = $localtemplatefoldername; 542 my $shorttemplatefoldername = installer::windows::idtglobal::make_eight_three_conform($localtemplatefoldername, "dir"); 543 if ( $shorttemplatefoldername ne $localtemplatefoldername ) { $directorytableentry = "$shorttemplatefoldername|$localtemplatefoldername"; } 544 $oneline = "$installer::globals::templatefolder\tTARGETDIR\t$directorytableentry\n"; 545 push(@{$directorytableref}, $oneline); 546 547 if ( $installer::globals::fontsdirname ) 548 { 549 $oneline = "$installer::globals::fontsfolder\t$installer::globals::fontsdirparent\t$installer::globals::fontsfoldername\:$installer::globals::fontsdirname\n"; 550 } 551 else 552 { 553 $oneline = "$installer::globals::fontsfolder\tTARGETDIR\t$installer::globals::fontsfoldername\n"; 554 } 555 556 push(@{$directorytableref}, $oneline); 557 558} 559 560############################################### 561# Creating the file Director.idt dynamically 562############################################### 563 564sub create_directory_table 565{ 566 my ($directoryref, $languagesarrayref, $basedir, $allvariableshashref, $shortdirnamehashref, $loggingdir) = @_; 567 568 # Structure of the directory table: 569 # Directory Directory_Parent DefaultDir 570 # Directory is a unique identifier 571 # Directory_Parent is the unique identifier of the parent 572 # DefaultDir is .:APPLIC~1|Application Data with 573 # Before ":" : [sourcedir]:[destdir] (not programmed yet) 574 # After ":" : 8+3 and not 8+3 the destination directory name 575 576 for ( my $m = 0; $m <= $#{$languagesarrayref}; $m++ ) 577 { 578 my $onelanguage = ${$languagesarrayref}[$m]; 579 $installer::globals::installlocationdirectoryset = 0; 580 581 my @directorytable = (); 582 my $infoline; 583 584 overwrite_programfilesfolder($allvariableshashref); 585 create_unique_directorynames($directoryref, $allvariableshashref); 586 $already_checked_the_frigging_directories_for_uniqueness++; 587 create_defaultdir_directorynames($directoryref, $shortdirnamehashref); # only destdir! 588 set_installlocation_directory($directoryref, $allvariableshashref); 589 installer::windows::idtglobal::write_idt_header(\@directorytable, "directory"); 590 add_root_directories(\@directorytable, $allvariableshashref, $onelanguage); 591 create_directorytable_from_collection(\@directorytable, $directoryref); 592 593 # Saving the file 594 595 my $directorytablename = $basedir . $installer::globals::separator . "Director.idt" . "." . $onelanguage; 596 installer::files::save_file($directorytablename ,\@directorytable); 597 $infoline = "Created idt file: $directorytablename\n"; 598 push(@installer::globals::logfileinfo, $infoline); 599 } 600} 601 602################################################ 603# Check if the string starts with another string 604################################################ 605 606sub starts_with 607{ 608 my ($first, $second) = @_; 609 610 return substr($first, 0, length($second)) eq $second; 611} 612 613############################################### 614# Check if the directory prefix is a standard 615# directory name. If it is the case, then the 616# standard directory name is returned in $var. 617############################################### 618 619sub has_standard_directory_prefix 620{ 621 my ($dir, $var) = @_; 622 623 for my $d (@msistandarddirectorynames) { 624 if (starts_with($dir, $d) && $dir ne $d) { 625 installer::logger::print_message("... match found: [$d]\n"); 626 ${$var} = $d; 627 return 1; 628 } 629 } 630 631 return 0; 632} 633 6341; 635