1#**************************************************************
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing,
14#  software distributed under the License is distributed on an
15#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16#  KIND, either express or implied.  See the License for the
17#  specific language governing permissions and limitations
18#  under the License.
19#
20#**************************************************************
21
22
23
24package installer::windows::directory;
25
26use installer::exiter;
27use installer::files;
28use installer::globals;
29use installer::pathanalyzer;
30use installer::windows::idtglobal;
31use installer::windows::msiglobal;
32
33use strict;
34
35##############################################################
36# Collecting all directory trees in global hash
37##############################################################
38
39sub collectdirectorytrees
40{
41	my ( $directoryref ) = @_;
42
43	for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
44	{
45		my $onedir = ${$directoryref}[$i];
46		my $styles = "";
47		if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }
48
49		if ( $styles ne "" )
50		{
51			foreach my $treestyle ( keys %installer::globals::treestyles )
52			{
53				if ( $styles =~ /\b$treestyle\b/ )
54				{
55					my $hostname = $onedir->{'HostName'};
56					# -> hostname is the key, the style the value!
57					$installer::globals::hostnametreestyles{$hostname} = $treestyle;
58				}
59			}
60		}
61	}
62}
63
64##############################################################
65# Overwriting global programfilesfolder, if required
66##############################################################
67
68sub overwrite_programfilesfolder
69{
70	my ( $allvariables ) = @_;
71
72	if ( $allvariables->{'PROGRAMFILESFOLDERNAME'} )
73	{
74		$installer::globals::programfilesfolder = $allvariables->{'PROGRAMFILESFOLDERNAME'};
75	}
76}
77
78
79
80
81=head2 make_short_dir_version($longstring)
82
83    Transform the given string into one that is at most 70 characters long.
84    That is done in two steps:
85    - Cut all parts separated by '_' or '-' down to a length of 5.
86    - Cut down the result to a length of 60 and fill it up to length 70
87      with the MD5 checksum.
88
89    This transform always returns the same result for the same string.
90    There is no counter and reference to a global set of names to make the string unique.
91
92=cut
93sub make_short_dir_version ($)
94{
95	my ($longstring) = @_;
96
97	my $shortstring = "";
98	my $cutlength = 60;
99	my $length = 5; # So the directory can still be recognized
100	my $longstring_save = $longstring;
101
102	# Splitting the string at each "underline" and allowing only $length characters per directory name.
103	# Checking also uniqueness and length.
104
105	my @outer_parts = split(/_/, $longstring);
106	foreach my $onestring (@outer_parts)
107	{
108		my $partstring = "";
109
110		if ( $onestring =~ /\-/ )
111		{
112			my @inner_parts = split(/-/, $onestring);
113            @inner_parts = map {substr($_,0,$length)} @inner_parts;
114            $partstring = join("-", @inner_parts);
115			$partstring =~ s/^\s*\-//;
116		}
117		else
118		{
119			$partstring = substr($onestring, 0, $length);
120		}
121
122		$shortstring .= "_" . $partstring;
123	}
124
125	$shortstring =~ s/^\s*\_//;
126
127	# Setting unique ID to each directory
128	# No counter allowed, process must be absolute reproducable due to patch creation process.
129
130	my $subid = installer::windows::msiglobal::calculate_id($longstring_save, 9); # taking only the first 9 digits
131	$shortstring = substr($shortstring, 0, $cutlength) . "_" . $subid;
132
133	return $shortstring;
134}
135
136##############################################################
137# Adding unique directory names to the directory collection
138##############################################################
139
140sub create_unique_directorynames
141{
142	my ($directoryref, $allvariables) = @_;
143
144	$installer::globals::officeinstalldirectoryset = 0;
145
146	my %completedirhashstep1 = ();
147	my %shortdirhash = ();
148	my %shortdirhashreverse = ();
149	my $infoline = "";
150	my $errorcount = 0;
151
152	for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
153	{
154		my $onedir = ${$directoryref}[$i];
155		my $uniquename = $onedir->{'HostName'};
156
157		my $styles = "";
158		if ( $onedir->{'Styles'} ) { $styles = $onedir->{'Styles'}; }
159
160		$uniquename =~ s/^\s*//g;				# removing beginning white spaces
161		$uniquename =~ s/\s*$//g;				# removing ending white spaces
162		$uniquename =~ s/\s//g;					# removing white spaces
163		$uniquename =~ s/\_//g;					# removing existing underlines
164		$uniquename =~ s/\.//g;					# removing dots in directoryname
165		$uniquename =~ s/OpenOffice/OO/g;
166
167		$uniquename =~ s/\Q$installer::globals::separator\E/\_/g;	# replacing slash and backslash with underline
168
169		$uniquename =~ s/_registry/_rgy/g;
170		$uniquename =~ s/_registration/_rgn/g;
171		$uniquename =~ s/_extension/_ext/g;
172		$uniquename =~ s/_frame/_frm/g;
173		$uniquename =~ s/_table/_tbl/g;
174		$uniquename =~ s/_chart/_crt/g;
175
176		# The names after this small changes must still be unique!
177		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"); }
178		$completedirhashstep1{$uniquename} = 1;
179
180		# Starting to make unique name for the parent and its directory
181		my $originaluniquename = $uniquename;
182
183		$uniquename = make_short_dir_version($uniquename);
184
185		# Checking if the same directory already exists, but has another short version.
186		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"); }
187
188		# Also checking vice versa
189		# Checking if the same short directory already exists, but has another long version.
190		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"); }
191
192		# Creating assignment from long to short directory names
193		$shortdirhash{$originaluniquename} = $uniquename;
194		$shortdirhashreverse{$uniquename} = $originaluniquename;
195
196		# Important: The unique parent is generated from the string $originaluniquename (with the use of underlines).
197
198		my $uniqueparentname = $originaluniquename;
199		my $keepparent = 1;
200
201		if ( $uniqueparentname =~ /^\s*(.*)\_(.*?)\s*$/ )	# the underline is now the separator
202		{
203			$uniqueparentname = $1;
204			$keepparent = 0;
205		}
206		else
207		{
208			$uniqueparentname = $installer::globals::programfilesfolder;
209			$keepparent = 1;
210		}
211
212		if ( $styles =~ /\bPROGRAMFILESFOLDER\b/ )
213		{
214			$uniqueparentname = $installer::globals::programfilesfolder;
215			$keepparent = 1;
216		}
217		if ( $styles =~ /\bCOMMONFILESFOLDER\b/ )
218		{
219			$uniqueparentname = $installer::globals::commonfilesfolder;
220			$keepparent = 1;
221		}
222		if ( $styles =~ /\bCOMMONAPPDATAFOLDER\b/ )
223		{
224			$uniqueparentname = $installer::globals::commonappdatafolder;
225			$keepparent = 1;
226		}
227		if ( $styles =~ /\bLOCALAPPDATAFOLDER\b/ )
228		{
229			$uniqueparentname = $installer::globals::localappdatafolder;
230			$keepparent = 1;
231		}
232
233		if ( $styles =~ /\bSHAREPOINTPATH\b/ )
234		{
235			$uniqueparentname = "SHAREPOINTPATH";
236			$installer::globals::usesharepointpath = 1;
237			$keepparent = 1;
238		}
239
240		# also setting short directory name for the parent
241
242		my $originaluniqueparentname = $uniqueparentname;
243
244		if ( ! $keepparent )
245		{
246			$uniqueparentname = make_short_dir_version($uniqueparentname);
247		}
248
249		# Again checking if the same directory already exists, but has another short version.
250		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"); }
251
252		# Also checking vice versa
253		# Checking if the same short directory already exists, but has another long version.
254		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"); }
255
256		$shortdirhash{$originaluniqueparentname} = $uniqueparentname;
257		$shortdirhashreverse{$uniqueparentname} = $originaluniqueparentname;
258
259		# Hyphen not allowed in database
260		$uniquename =~ s/\-/\_/g;			# making "-" to "_"
261		$uniqueparentname =~ s/\-/\_/g;		# making "-" to "_"
262
263		# And finally setting the values for the directories
264		$onedir->{'uniquename'} = $uniquename;
265		$onedir->{'uniqueparentname'} = $uniqueparentname;
266
267		# setting the installlocation directory
268		if ( $styles =~ /\bISINSTALLLOCATION\b/ )
269		{
270			if ( $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION alread set: \"$installer::globals::installlocationdirectory\".", "create_unique_directorynames"); }
271			$installer::globals::installlocationdirectory = $uniquename;
272			$installer::globals::installlocationdirectoryset = 1;
273		}
274
275		# setting the sundirectory
276		if ( $styles =~ /\bSUNDIRECTORY\b/ )
277		{
278			if ( $installer::globals::vendordirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag SUNDIRECTORY alread set: \"$installer::globals::vendordirectory\".", "create_unique_directorynames"); }
279			$installer::globals::vendordirectory = $uniquename;
280			$installer::globals::vendordirectoryset = 1;
281		}
282	}
283}
284
285#####################################################
286# Adding ":." to selected default directory names
287#####################################################
288
289sub check_sourcedir_addon
290{
291	my ( $onedir, $allvariableshashref ) = @_;
292
293	if (($installer::globals::addchildprojects) ||
294		($installer::globals::patch) ||
295		($installer::globals::languagepack) ||
296		($allvariableshashref->{'CHANGETARGETDIR'}))
297	{
298		my $sourcediraddon = "\:\.";
299		$onedir->{'defaultdir'} = $onedir->{'defaultdir'} . $sourcediraddon;
300	}
301
302}
303
304#####################################################
305# The directory with the style ISINSTALLLOCATION
306# will be replaced by INSTALLLOCATION
307#####################################################
308
309sub set_installlocation_directory
310{
311	my ( $directoryref, $allvariableshashref ) = @_;
312
313	if ( ! $installer::globals::installlocationdirectoryset ) { installer::exiter::exit_program("ERROR: Directory with flag ISINSTALLLOCATION not set!", "set_installlocation_directory"); }
314
315	for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
316	{
317		my $onedir = ${$directoryref}[$i];
318
319		if ( $onedir->{'uniquename'} eq $installer::globals::installlocationdirectory )
320		{
321			$onedir->{'uniquename'} = "INSTALLLOCATION";
322			check_sourcedir_addon($onedir, $allvariableshashref);
323		}
324
325		if ( $onedir->{'uniquename'} eq $installer::globals::vendordirectory )
326		{
327			check_sourcedir_addon($onedir, $allvariableshashref);
328		}
329
330		if ( $onedir->{'uniqueparentname'} eq $installer::globals::installlocationdirectory )
331		{
332			$onedir->{'uniqueparentname'} = "INSTALLLOCATION";
333		}
334	}
335}
336
337#####################################################
338# Getting the name of the top level directory. This
339# can have only one letter
340#####################################################
341
342sub get_last_directory_name
343{
344	my ($completepathref) = @_;
345
346	if ( $$completepathref =~ /^.*[\/\\](.+?)\s*$/ )
347	{
348		$$completepathref = $1;
349	}
350}
351
352#####################################################
353# Creating the defaultdir for the file Director.idt
354#####################################################
355
356sub create_defaultdir_directorynames ($)
357{
358	my ($directoryref) = @_;
359
360	my @shortnames = ();
361	if ( $installer::globals::prepare_winpatch ) { @shortnames = values(%installer::globals::saved83dirmapping); }
362
363	for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
364	{
365		my $onedir = ${$directoryref}[$i];
366		my $hostname = $onedir->{'HostName'};
367
368		$hostname =~ s/\Q$installer::globals::separator\E\s*$//;
369		get_last_directory_name(\$hostname);
370		# installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$hostname);	# making program/classes to classes
371		my $uniquename = $onedir->{'uniquename'};
372		my $shortstring;
373		if (( $installer::globals::prepare_winpatch ) && ( exists($installer::globals::saved83dirmapping{$uniquename}) ))
374		{
375			$shortstring = $installer::globals::saved83dirmapping{$uniquename};
376		}
377		else
378		{
379			$shortstring = installer::windows::idtglobal::make_eight_three_conform($hostname, "dir", \@shortnames);
380		}
381
382		my $defaultdir;
383
384		if ( $shortstring eq $hostname )
385		{
386			$defaultdir = $hostname;
387		}
388		else
389		{
390			$defaultdir = $shortstring . "|" . $hostname;
391		}
392
393		$onedir->{'defaultdir'} = $defaultdir;
394
395		my $fontdir = "";
396		if ( $onedir->{'Dir'} ) { $fontdir = $onedir->{'Dir'}; }
397
398		my $fontdefaultdir = "";
399		if ( $onedir->{'defaultdir'} ) { $fontdefaultdir = $onedir->{'defaultdir'}; }
400
401		if (( $fontdir eq "PREDEFINED_OSSYSTEMFONTDIR" ) && ( $fontdefaultdir eq $installer::globals::fontsdirhostname ))
402		{
403			$installer::globals::fontsdirname = $onedir->{'defaultdir'};
404			$installer::globals::fontsdirparent = $onedir->{'uniqueparentname'};
405		}
406	}
407}
408
409###############################################
410# Fill content into the directory table
411###############################################
412
413sub create_directorytable_from_collection
414{
415	my ($directorytableref, $directoryref) = @_;
416
417	for ( my $i = 0; $i <= $#{$directoryref}; $i++ )
418	{
419		my $onedir = ${$directoryref}[$i];
420		my $hostname = $onedir->{'HostName'};
421		my $dir = "";
422
423		if ( $onedir->{'Dir'} ) { $dir = $onedir->{'Dir'}; }
424
425		if (( $dir eq "PREDEFINED_PROGDIR" ) && ( $hostname eq "" )) { next; }	# removing files from root directory
426
427		my $oneline = $onedir->{'uniquename'} . "\t" . $onedir->{'uniqueparentname'} . "\t" . $onedir->{'defaultdir'} . "\n";
428
429		push(@{$directorytableref}, $oneline);
430	}
431}
432
433###############################################
434# Defining the root installation structure
435###############################################
436
437sub add_root_directories
438{
439	my ($directorytableref, $allvariableshashref) = @_;
440
441#	my $sourcediraddon = "";
442#	if (($installer::globals::addchildprojects) ||
443#		($installer::globals::patch) ||
444#		($installer::globals::languagepack) ||
445#		($allvariableshashref->{'CHANGETARGETDIR'}))
446#	{
447#		$sourcediraddon = "\:\.";
448#	}
449
450	my $oneline = "";
451
452	if (( ! $installer::globals::patch ) && ( ! $installer::globals::languagepack ) && ( ! $allvariableshashref->{'DONTUSESTARTMENUFOLDER'} ))
453	{
454		my $productname = $allvariableshashref->{'PRODUCTNAME'};
455		my $productversion = $allvariableshashref->{'PRODUCTVERSION'};
456		my $baseproductversion = $productversion;
457
458		if (( $installer::globals::prepare_winpatch ) && ( $allvariableshashref->{'BASEPRODUCTVERSION'} ))
459		{
460			$baseproductversion = $allvariableshashref->{'BASEPRODUCTVERSION'};  # for example "2.0" for OOo
461		}
462
463		my $realproductkey = $productname . " " . $productversion;
464		my $productkey = $productname . " " . $baseproductversion;
465
466		if (( $allvariableshashref->{'POSTVERSIONEXTENSION'} ) && ( ! $allvariableshashref->{'DONTUSEEXTENSIONINDEFAULTDIR'} ))
467		{
468			$productkey = $productkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'};
469			$realproductkey = $realproductkey . " " . $allvariableshashref->{'POSTVERSIONEXTENSION'};
470		}
471		if ( $allvariableshashref->{'NOSPACEINDIRECTORYNAME'} )
472		{
473			$productkey =~ s/\ /\_/g;
474			$realproductkey =~ s/\ /\_/g;
475		}
476
477		my $shortproductkey = installer::windows::idtglobal::make_eight_three_conform($productkey, "dir");		# third parameter not used
478		$shortproductkey =~ s/\s/\_/g;									# changing empty space to underline
479
480		$oneline = "$installer::globals::officemenufolder\t$installer::globals::programmenufolder\t$shortproductkey|$realproductkey\n";
481		push(@{$directorytableref}, $oneline);
482	}
483
484	$oneline = "TARGETDIR\t\tSourceDir\n";
485	push(@{$directorytableref}, $oneline);
486
487	$oneline = "$installer::globals::programfilesfolder\tTARGETDIR\t.\n";
488	push(@{$directorytableref}, $oneline);
489
490	$oneline = "$installer::globals::programmenufolder\tTARGETDIR\t.\n";
491	push(@{$directorytableref}, $oneline);
492
493	$oneline = "$installer::globals::startupfolder\tTARGETDIR\t.\n";
494	push(@{$directorytableref}, $oneline);
495
496	$oneline = "$installer::globals::desktopfolder\tTARGETDIR\t.\n";
497	push(@{$directorytableref}, $oneline);
498
499	$oneline = "$installer::globals::startmenufolder\tTARGETDIR\t.\n";
500	push(@{$directorytableref}, $oneline);
501
502	$oneline = "$installer::globals::commonfilesfolder\tTARGETDIR\t.\n";
503	push(@{$directorytableref}, $oneline);
504
505	$oneline = "$installer::globals::commonappdatafolder\tTARGETDIR\t.\n";
506	push(@{$directorytableref}, $oneline);
507
508	$oneline = "$installer::globals::localappdatafolder\tTARGETDIR\t.\n";
509	push(@{$directorytableref}, $oneline);
510
511	if ( $installer::globals::usesharepointpath )
512	{
513		$oneline = "SHAREPOINTPATH\tTARGETDIR\t.\n";
514		push(@{$directorytableref}, $oneline);
515	}
516
517	$oneline = "$installer::globals::systemfolder\tTARGETDIR\t.\n";
518	push(@{$directorytableref}, $oneline);
519
520	my $localtemplatefoldername = $installer::globals::templatefoldername;
521	my $directorytableentry = $localtemplatefoldername;
522	my $shorttemplatefoldername = installer::windows::idtglobal::make_eight_three_conform($localtemplatefoldername, "dir");
523	if ( $shorttemplatefoldername ne $localtemplatefoldername ) { $directorytableentry = "$shorttemplatefoldername|$localtemplatefoldername"; }
524	$oneline = "$installer::globals::templatefolder\tTARGETDIR\t$directorytableentry\n";
525	push(@{$directorytableref}, $oneline);
526
527	if ( $installer::globals::fontsdirname )
528	{
529		$oneline = "$installer::globals::fontsfolder\t$installer::globals::fontsdirparent\t$installer::globals::fontsfoldername\:$installer::globals::fontsdirname\n";
530	}
531	else
532	{
533		$oneline = "$installer::globals::fontsfolder\tTARGETDIR\t$installer::globals::fontsfoldername\n";
534	}
535
536	push(@{$directorytableref}, $oneline);
537
538}
539
540###############################################
541# Creating the file Director.idt dynamically
542###############################################
543
544sub create_directory_table ($$$$)
545{
546	my ($directoryref, $basedir, $allvariableshashref, $loggingdir) = @_;
547
548	# Structure of the directory table:
549	# Directory Directory_Parent DefaultDir
550	# Directory is a unique identifier
551	# Directory_Parent is the unique identifier of the parent
552	# DefaultDir is .:APPLIC~1|Application Data with
553	# Before ":" : [sourcedir]:[destdir] (not programmed yet)
554	# After ":" : 8+3 and not 8+3 the destination directory name
555
556	$installer::logger::Lang->add_timestamp("Performance Info: Directory Table start");
557
558	my @directorytable = ();
559	my $infoline;
560
561	overwrite_programfilesfolder($allvariableshashref);
562	create_unique_directorynames($directoryref, $allvariableshashref);
563	create_defaultdir_directorynames($directoryref);	# only destdir!
564	set_installlocation_directory($directoryref, $allvariableshashref);
565	installer::windows::idtglobal::write_idt_header(\@directorytable, "directory");
566	add_root_directories(\@directorytable, $allvariableshashref);
567	create_directorytable_from_collection(\@directorytable, $directoryref);
568
569	# Saving the file
570
571	my $directorytablename = $basedir . $installer::globals::separator . "Director.idt";
572	installer::files::save_file($directorytablename ,\@directorytable);
573    $installer::logger::Lang->printf("Created idt file: %s\n", $directorytablename);
574
575	$installer::logger::Lang->add_timestamp("Performance Info: Directory Table end");
576}
577
5781;
579