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::msiglobal;
25
26use Cwd;
27use Digest::MD5;
28use installer::converter;
29use installer::exiter;
30use installer::files;
31use installer::globals;
32use installer::logger;
33use installer::pathanalyzer;
34use installer::remover;
35use installer::scriptitems;
36use installer::systemactions;
37use installer::worker;
38use installer::windows::idtglobal;
39use installer::windows::language;
40
41###########################################################################
42# Generating the header of the ddf file.
43# The usage of ddf files is needed, because makecab.exe can only include
44# one sourcefile into a cab file
45###########################################################################
46
47sub write_ddf_file_header
48{
49	my ($ddffileref, $cabinetfile, $installdir) = @_;
50
51	my $oneline;
52
53	$oneline = ".Set CabinetName1=" . $cabinetfile . "\n";
54	push(@{$ddffileref} ,$oneline);
55	$oneline = ".Set ReservePerCabinetSize=128\n";	# This reserves space for a digital signature.
56	push(@{$ddffileref} ,$oneline);
57	$oneline = ".Set MaxDiskSize=2147483648\n";		# This allows the .cab file to get a size of 2 GB.
58	push(@{$ddffileref} ,$oneline);
59	$oneline = ".Set CompressionType=LZX\n";
60	push(@{$ddffileref} ,$oneline);
61	$oneline = ".Set Compress=ON\n";
62	push(@{$ddffileref} ,$oneline);
63	$oneline = ".Set CompressionLevel=$installer::globals::cabfilecompressionlevel\n";
64	push(@{$ddffileref} ,$oneline);
65	$oneline = ".Set Cabinet=ON\n";
66	push(@{$ddffileref} ,$oneline);
67	$oneline = ".Set DiskDirectoryTemplate=" . $installdir . "\n";
68	push(@{$ddffileref} ,$oneline);
69}
70
71##########################################################################
72# Lines in ddf files must not contain more than 256 characters
73##########################################################################
74
75sub check_ddf_file
76{
77	my ( $ddffile, $ddffilename ) = @_;
78
79	my $maxlength = 0;
80	my $maxline = 0;
81	my $linelength = 0;
82	my $linenumber = 0;
83
84	for ( my $i = 0; $i <= $#{$ddffile}; $i++ )
85	{
86		my $oneline = ${$ddffile}[$i];
87
88		$linelength = length($oneline);
89		$linenumber = $i + 1;
90
91		if ( $linelength > 256 )
92		{
93			installer::exiter::exit_program("ERROR \"$ddffilename\" line $linenumber: Lines in ddf files must not contain more than 256 characters!", "check_ddf_file");
94		}
95
96		if ( $linelength > $maxlength )
97		{
98			$maxlength = $linelength;
99			$maxline = $linenumber;
100		}
101	}
102
103	my $infoline = "Check of ddf file \"$ddffilename\": Maximum length \"$maxlength\" in line \"$maxline\" (allowed line length: 256 characters)\n";
104	$installer::logger::Lang->print($infoline);
105}
106
107##########################################################################
108# Lines in ddf files must not be longer than 256 characters.
109# Therefore it can be useful to use relative pathes. Then it is
110# necessary to change into temp directory before calling
111# makecab.exe.
112##########################################################################
113
114sub make_relative_ddf_path
115{
116	my ( $sourcepath ) = @_;
117
118	my $windowstemppath = $installer::globals::temppath;
119
120	if ( $^O =~ /cygwin/i )
121	{
122		$windowstemppath = $installer::globals::cyg_temppath;
123	}
124
125	$sourcepath =~ s/\Q$windowstemppath\E//;
126	$sourcepath =~ s/^\\//;
127
128	return $sourcepath;
129}
130
131##########################################################################
132# Returning the order of the sequences in the files array.
133##########################################################################
134
135sub get_sequenceorder
136{
137	my ($filesref) = @_;
138
139	my %order = ();
140
141	for ( my $i = 0; $i <= $#{$filesref}; $i++ )
142	{
143		my $onefile = ${$filesref}[$i];
144		if ( ! $onefile->{'assignedsequencenumber'} ) { installer::exiter::exit_program("ERROR: No sequence number assigned to $onefile->{'gid'} ($onefile->{'uniquename'})!", "get_sequenceorder"); }
145		$order{$onefile->{'assignedsequencenumber'}} = $i;
146	}
147
148	return \%order;
149}
150
151##########################################################################
152# Generation the list, in which the source of the files is connected
153# with the cabinet destination file. Because more than one file needs
154# to be included into a cab file, this has to be done via ddf files.
155##########################################################################
156
157sub generate_cab_file_list
158{
159	my ($filesref, $installdir, $ddfdir, $allvariables) = @_;
160
161	my @cabfilelist = ();
162
163	installer::logger::include_header_into_logfile("Generating ddf files");
164
165	$installer::logger::Lang->add_timestamp("Performance Info: ddf file generation start");
166
167	if ( $^O =~ /cygwin/i ) { installer::worker::generate_cygwin_pathes($filesref); }
168
169	if (( $installer::globals::fix_number_of_cab_files ) && ( $installer::globals::updatedatabase ))
170	{
171		my $sequenceorder = get_sequenceorder($filesref);
172
173		my $counter = 1;
174		my $currentcabfile = "";
175
176		while ( ( exists($sequenceorder->{$counter}) ) || ( exists($installer::globals::allmergemodulefilesequences{$counter}) ) ) # Taking care of files from merge modules
177		{
178#			if ( exists($installer::globals::allmergemodulefilesequences{$counter}) )
179#			{
180#				# Skipping this sequence, it is not included in $filesref, because it is assigned to a file from a merge module.\n";
181#				$counter++;
182#				next;
183#			}
184
185			my $onefile = ${$filesref}[$sequenceorder->{$counter}];
186			$counter++;
187
188			my $cabinetfile = $onefile->{'cabinet'};
189			my $sourcepath =  $onefile->{'sourcepath'};
190			if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
191			my $uniquename =  $onefile->{'uniquename'};
192
193			my $styles = "";
194			my $doinclude = 1;
195			if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
196			if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
197
198			# to avoid lines with more than 256 characters, it can be useful to use relative pathes
199			if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
200
201			my @ddffile = ();
202
203			write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
204
205			my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
206			if ( $doinclude ) { push(@ddffile, $ddfline); }
207
208			my $nextfile = "";
209			if ( ${$filesref}[$sequenceorder->{$counter}] ) { $nextfile = ${$filesref}[$sequenceorder->{$counter}]; }
210
211			my $nextcabinetfile = "";
212
213			if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
214
215			while ( $nextcabinetfile eq $cabinetfile )
216			{
217				$sourcepath =  $nextfile->{'sourcepath'};
218				if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; }
219				# to avoid lines with more than 256 characters, it can be useful to use relative pathes
220				if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
221				$uniquename =  $nextfile->{'uniquename'};
222				my $localdoinclude = 1;
223				my $nextfilestyles = "";
224				if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; }
225				if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; }
226				$ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
227				if ( $localdoinclude ) { push(@ddffile, $ddfline); }
228				$counter++;											# increasing the counter!
229				$nextfile = "";
230				$nextcabinetfile = "_lastfile_";
231				if (( exists($sequenceorder->{$counter}) ) && ( ${$filesref}[$sequenceorder->{$counter}] ))
232				{
233					$nextfile = ${$filesref}[$sequenceorder->{$counter}];
234					$nextcabinetfile = $nextfile->{'cabinet'};
235				}
236			}
237
238			# creating the DDF file
239
240			my $ddffilename = $cabinetfile;
241			$ddffilename =~ s/.cab/.ddf/;
242			$ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
243			$ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
244
245			installer::files::save_file($ddffilename ,\@ddffile);
246			my $infoline = "Created ddf file: $ddffilename\n";
247			$installer::logger::Lang->print($infoline);
248
249			# lines in ddf files must not be longer than 256 characters
250			check_ddf_file(\@ddffile, $ddffilename);
251
252			# Writing the makecab system call
253
254			my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
255
256			push(@cabfilelist, $oneline);
257
258			# collecting all ddf files
259			push(@installer::globals::allddffiles, $ddffilename);
260		}
261	}
262	elsif ( $installer::globals::fix_number_of_cab_files )
263	{
264		for ( my $i = 0; $i <= $#{$filesref}; $i++ )
265		{
266			my $onefile = ${$filesref}[$i];
267			my $cabinetfile = $onefile->{'cabinet'};
268			my $sourcepath =  $onefile->{'sourcepath'};
269			if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
270			my $uniquename =  $onefile->{'uniquename'};
271
272			my $styles = "";
273			my $doinclude = 1;
274			if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
275			if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
276
277
278			# to avoid lines with more than 256 characters, it can be useful to use relative pathes
279			if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
280
281			# all files with the same cabinetfile are directly behind each other in the files collector
282
283			my @ddffile = ();
284
285			write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
286
287			my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
288			if ( $doinclude ) { push(@ddffile, $ddfline); }
289
290			my $nextfile = ${$filesref}[$i+1];
291			my $nextcabinetfile = "";
292
293			if ( $nextfile->{'cabinet'} ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
294
295			while ( $nextcabinetfile eq $cabinetfile )
296			{
297				$sourcepath =  $nextfile->{'sourcepath'};
298				if ( $^O =~ /cygwin/i ) { $sourcepath = $nextfile->{'cyg_sourcepath'}; }
299				# to avoid lines with more than 256 characters, it can be useful to use relative pathes
300				if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
301				$uniquename =  $nextfile->{'uniquename'};
302				my $localdoinclude = 1;
303				my $nextfilestyles = "";
304				if ( $nextfile->{'Styles'} ) { $nextfilestyles = $nextfile->{'Styles'}; }
305				if ( $nextfilestyles =~ /\bDONT_PACK\b/ ) { $localdoinclude = 0; }
306				$ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
307				if ( $localdoinclude ) { push(@ddffile, $ddfline); }
308				$i++;											# increasing the counter!
309				$nextfile = ${$filesref}[$i+1];
310				if ( $nextfile ) { $nextcabinetfile = $nextfile->{'cabinet'}; }
311				else { $nextcabinetfile = "_lastfile_"; }
312			}
313
314			# creating the DDF file
315
316			my $ddffilename = $cabinetfile;
317			$ddffilename =~ s/.cab/.ddf/;
318			$ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
319			$ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
320
321			installer::files::save_file($ddffilename ,\@ddffile);
322			my $infoline = "Created ddf file: $ddffilename\n";
323			$installer::logger::Lang->print($infoline);
324
325			# lines in ddf files must not be longer than 256 characters
326			check_ddf_file(\@ddffile, $ddffilename);
327
328			# Writing the makecab system call
329
330			my $oneline = "makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n";
331
332			push(@cabfilelist, $oneline);
333
334			# collecting all ddf files
335			push(@installer::globals::allddffiles, $ddffilename);
336		}
337	}
338	elsif ( $installer::globals::one_cab_file )
339	{
340		my @ddffile = ();
341
342		my $cabinetfile = "";
343
344		for ( my $i = 0; $i <= $#{$filesref}; $i++ )
345		{
346			my $onefile = ${$filesref}[$i];
347			$cabinetfile = $onefile->{'cabinet'};
348			my $sourcepath =  $onefile->{'sourcepath'};
349			if ( $^O =~ /cygwin/i ) { $sourcepath = $onefile->{'cyg_sourcepath'}; }
350			my $uniquename =  $onefile->{'uniquename'};
351
352			# to avoid lines with more than 256 characters, it can be useful to use relative pathes
353			if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} ) { $sourcepath = make_relative_ddf_path($sourcepath); }
354
355			if ( $i == 0 ) { write_ddf_file_header(\@ddffile, $cabinetfile, $installdir); }
356
357			my $styles = "";
358			my $doinclude = 1;
359			if ( $onefile->{'Styles'} ) { $styles = $onefile->{'Styles'}; };
360			if ( $styles =~ /\bDONT_PACK\b/ ) { $doinclude = 0; }
361
362			my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
363			if ( $doinclude ) { push(@ddffile, $ddfline); }
364		}
365
366		# creating the DDF file
367
368		my $ddffilename = $cabinetfile;
369		$ddffilename =~ s/.cab/.ddf/;
370		$ddfdir =~ s/[\/\\]\s*$//;
371		$ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
372
373		installer::files::save_file($ddffilename ,\@ddffile);
374		my $infoline = "Created ddf file: $ddffilename\n";
375		$installer::logger::Lang->print($infoline);
376
377		# lines in ddf files must not be longer than 256 characters
378		check_ddf_file(\@ddffile, $ddffilename);
379
380		# Writing the makecab system call
381
382		my $oneline = "makecab.exe /F " . $ddffilename . "\n";
383
384		push(@cabfilelist, $oneline);
385
386		# collecting all ddf files
387		push(@installer::globals::allddffiles, $ddffilename);
388	}
389	else
390	{
391		installer::exiter::exit_program("ERROR: No cab file specification in globals.pm !", "create_media_table");
392	}
393
394	$installer::logger::Lang->add_timestamp("Performance Info: ddf file generation end");
395
396	return \@cabfilelist;	# contains all system calls for packaging process
397}
398
399########################################################################
400# Returning the file sequence of a specified file.
401########################################################################
402
403sub get_file_sequence
404{
405	my ($filesref, $uniquefilename) = @_;
406
407	my $sequence = "";
408	my $found_sequence = 0;
409
410	for ( my $i = 0; $i <= $#{$filesref}; $i++ )
411	{
412		my $onefile = ${$filesref}[$i];
413		my $uniquename = $onefile->{'uniquename'};
414
415		if ( $uniquename eq $uniquefilename )
416		{
417			$sequence = $onefile->{'sequencenumber'};
418			$found_sequence = 1;
419			last;
420		}
421	}
422
423	if ( ! $found_sequence ) { installer::exiter::exit_program("ERROR: No sequence found for $uniquefilename !", "get_file_sequence"); }
424
425	return $sequence;
426}
427
428########################################################################
429# For update and patch reasons the pack order needs to be saved.
430# The pack order is saved in the ddf files; the names and locations
431# of the ddf files are saved in @installer::globals::allddffiles.
432# The outputfile "packorder.txt" can be saved in
433# $installer::globals::infodirectory .
434########################################################################
435
436sub save_packorder
437{
438	installer::logger::include_header_into_logfile("Saving pack order");
439
440	$installer::logger::Lang->add_timestamp("Performance Info: saving pack order start");
441
442	my $packorderfilename = "packorder.txt";
443	$packorderfilename = $installer::globals::infodirectory . $installer::globals::separator . $packorderfilename;
444
445	my @packorder = ();
446
447	my $headerline = "\# Syntax\: Filetable_Sequence Cabinetfilename Physical_FileName Unique_FileName\n\n";
448	push(@packorder, $headerline);
449
450	for ( my $i = 0; $i <= $#installer::globals::allddffiles; $i++ )
451	{
452		my $ddffilename = $installer::globals::allddffiles[$i];
453		my $ddffile = installer::files::read_file($ddffilename);
454		my $cabinetfile = "";
455
456		for ( my $j = 0; $j <= $#{$ddffile}; $j++ )
457		{
458			my $oneline = ${$ddffile}[$j];
459
460			# Getting the Cabinet file name
461
462			if ( $oneline =~ /^\s*\.Set\s+CabinetName.*\=(.*?)\s*$/ ) { $cabinetfile = $1; }
463			if ( $oneline =~ /^\s*\.Set\s+/ ) { next; }
464
465			if ( $oneline =~ /^\s*\"(.*?)\"\s+(.*?)\s*$/ )
466			{
467				my $sourcefile = $1;
468				my $uniquefilename = $2;
469
470				installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$sourcefile);
471
472				# Using the hash created in create_files_table for performance reasons to get the sequence number
473				my $filesequence = "";
474				if ( exists($installer::globals::uniquefilenamesequence{$uniquefilename}) ) { $filesequence = $installer::globals::uniquefilenamesequence{$uniquefilename}; }
475				else { installer::exiter::exit_program("ERROR: No sequence number value for $uniquefilename !", "save_packorder"); }
476
477				my $line = $filesequence . "\t" . $cabinetfile . "\t" . $sourcefile . "\t" . $uniquefilename . "\n";
478				push(@packorder, $line);
479			}
480		}
481	}
482
483	installer::files::save_file($packorderfilename ,\@packorder);
484
485	$installer::logger::Lang->add_timestamp("Performance Info: saving pack order end");
486}
487
488#################################################################
489# Returning the name of the msi database
490#################################################################
491
492sub get_msidatabasename
493{
494	my ($allvariableshashref, $language) = @_;
495
496	my $databasename = $allvariableshashref->{'PRODUCTNAME'} . $allvariableshashref->{'PRODUCTVERSION'};
497	$databasename = lc($databasename);
498	$databasename =~ s/\.//g;
499	$databasename =~ s/\-//g;
500	$databasename =~ s/\s//g;
501
502	# possibility to overwrite the name with variable DATABASENAME
503	if ( $allvariableshashref->{'DATABASENAME'} )
504	{
505		$databasename = $allvariableshashref->{'DATABASENAME'};
506	}
507
508	if ( $language )
509	{
510		if (!($language eq ""))
511		{
512			$databasename .= "_$language";
513		}
514	}
515
516	$databasename .= ".msi";
517
518	return $databasename;
519}
520
521#################################################################
522# Creating the msi database
523# This works only on Windows
524#################################################################
525
526sub create_msi_database
527{
528	my ($idtdirbase ,$msifilename) = @_;
529
530	# -f : path containing the idt files
531	# -d : msi database, including path
532	# -c : create database
533	# -i : include the following tables ("*" includes all available tables)
534
535	my $msidb = "msidb.exe";	# Has to be in the path
536	my $extraslash = "";		# Has to be set for non-ActiveState perl
537
538	installer::logger::include_header_into_logfile("Creating msi database");
539
540	$idtdirbase = installer::converter::make_path_conform($idtdirbase);
541
542	$msifilename = installer::converter::make_path_conform($msifilename);
543
544	if ( $^O =~ /cygwin/i ) {
545		# msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
546		$idtdirbase =~ s/\//\\\\/g;
547		$msifilename =~ s/\//\\\\/g;
548		$extraslash = "\\";
549	}
550	my $systemcall = $msidb . " -f " . $idtdirbase . " -d " . $msifilename . " -c " . "-i " . $extraslash . "*";
551
552	my $returnvalue = system($systemcall);
553
554	my $infoline = "Systemcall: $systemcall\n";
555	$installer::logger::Lang->print($infoline);
556
557	if ($returnvalue)
558	{
559		$infoline = "ERROR: Could not execute $msidb!\n";
560		$installer::logger::Lang->print($infoline);
561	}
562	else
563	{
564		$infoline = "Success: Executed $msidb successfully!\n";
565		$installer::logger::Lang->print($infoline);
566	}
567}
568
569#####################################################################
570# Returning the value from sis.mlf for Summary Information Stream
571#####################################################################
572
573sub get_value_from_sis_lng
574{
575	my ($language, $languagefile, $searchstring) = @_;
576
577	my $language_block = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $languagefile);
578	my $newstring = installer::windows::idtglobal::get_language_string_from_language_block($language_block, $language, $searchstring);
579	$newstring = "\"" . $newstring . "\"";
580
581	return $newstring;
582}
583
584#################################################################
585# Returning the msi version for the Summary Information Stream
586#################################################################
587
588sub get_msiversion_for_sis
589{
590	my $msiversion = "200";
591	return $msiversion;
592}
593
594#################################################################
595# Returning the word count for the Summary Information Stream
596#################################################################
597
598sub get_wordcount_for_sis
599{
600	my $wordcount = "0";
601	return $wordcount;
602}
603
604#################################################################
605# Returning the codepage for the Summary Information Stream
606#################################################################
607
608sub get_codepage_for_sis
609{
610	my ( $language ) = @_;
611
612	my $codepage = installer::windows::language::get_windows_encoding($language);
613
614	# Codepage 65001 does not work in Summary Information Stream
615	if ( $codepage == 65001 ) { $codepage = 0; }
616
617	# my $codepage = "1252";	# determine dynamically in a function
618	# my $codepage = "65001";		# UTF-8
619	return $codepage;
620}
621
622#################################################################
623# Returning the template for the Summary Information Stream
624#################################################################
625
626sub get_template_for_sis
627{
628	my ( $language, $allvariables ) = @_;
629
630	my $windowslanguage = installer::windows::language::get_windows_language($language);
631
632	my $architecture = "Intel";
633
634	# Adding 256, if this is a 64 bit installation set.
635	if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 )) { $architecture = "x64"; }
636
637	my $value = "\"" . $architecture . ";" . $windowslanguage;	# adding the Windows language
638
639	$value = $value . "\"";						# adding ending '"'
640
641	return $value ;
642}
643
644#################################################################
645# Returning the PackageCode for the Summary Information Stream
646#################################################################
647
648sub get_packagecode_for_sis
649{
650	# always generating a new package code for each package
651
652	my $guidref = get_guid_list(1, 1);	# only one GUID shall be generated
653
654	${$guidref}[0] =~ s/\s*$//;		# removing ending spaces
655
656	my $guid = "\{" . ${$guidref}[0] . "\}";
657
658	my $infoline = "PackageCode: $guid\n";
659	$installer::logger::Lang->print($infoline);
660
661	return $guid;
662}
663
664#################################################################
665# Returning the title for the Summary Information Stream
666#################################################################
667
668sub get_title_for_sis
669{
670	my ( $language, $languagefile, $searchstring ) = @_;
671
672	my $title = get_value_from_sis_lng($language, $languagefile, $searchstring );
673
674	return $title;
675}
676
677#################################################################
678# Returning the author for the Summary Information Stream
679#################################################################
680
681sub get_author_for_sis
682{
683	my $author = $installer::globals::longmanufacturer;
684
685	$author = "\"" . $author . "\"";
686
687	return $author;
688}
689
690#################################################################
691# Returning the subject for the Summary Information Stream
692#################################################################
693
694sub get_subject_for_sis
695{
696	my ( $allvariableshashref ) = @_;
697
698	my $subject = $allvariableshashref->{'PRODUCTNAME'} . " " . $allvariableshashref->{'PRODUCTVERSION'};
699
700	$subject = "\"" . $subject . "\"";
701
702	return $subject;
703}
704
705#################################################################
706# Returning the comment for the Summary Information Stream
707#################################################################
708
709sub get_comment_for_sis
710{
711	my ( $language, $languagefile, $searchstring ) = @_;
712
713	my $comment = get_value_from_sis_lng($language, $languagefile, $searchstring );
714
715	return $comment;
716}
717
718#################################################################
719# Returning the keywords for the Summary Information Stream
720#################################################################
721
722sub get_keywords_for_sis
723{
724	my ( $language, $languagefile, $searchstring ) = @_;
725
726	my $keywords = get_value_from_sis_lng($language, $languagefile, $searchstring );
727
728	return $keywords;
729}
730
731######################################################################
732# Returning the application name for the Summary Information Stream
733######################################################################
734
735sub get_appname_for_sis
736{
737	my ( $language, $languagefile, $searchstring ) = @_;
738
739	my $appname = get_value_from_sis_lng($language, $languagefile, $searchstring );
740
741	return $appname;
742}
743
744######################################################################
745# Returning the security for the Summary Information Stream
746######################################################################
747
748sub get_security_for_sis
749{
750	my $security = "0";
751	return $security;
752}
753
754#################################################################
755# Writing the Summary information stream into the msi database
756# This works only on Windows
757#################################################################
758
759sub write_summary_into_msi_database
760{
761	my ($msifilename, $language, $languagefile, $allvariableshashref) = @_;
762
763	# -g : requrired msi version
764	# -c : codepage
765	# -p : template
766
767	installer::logger::include_header_into_logfile("Writing summary information stream");
768
769	my $msiinfo = "msiinfo.exe";	# Has to be in the path
770
771	my $sislanguage = "en-US";	# title, comment, keyword and appname alway in english
772
773	my $msiversion = get_msiversion_for_sis();
774	my $codepage = get_codepage_for_sis($language);
775	my $template = get_template_for_sis($language, $allvariableshashref);
776	my $guid = get_packagecode_for_sis();
777	my $title = get_title_for_sis($sislanguage,$languagefile, "OOO_SIS_TITLE");
778	my $author = get_author_for_sis();
779	my $subject = get_subject_for_sis($allvariableshashref);
780	my $comment = get_comment_for_sis($sislanguage,$languagefile, "OOO_SIS_COMMENT");
781	my $keywords = get_keywords_for_sis($sislanguage,$languagefile, "OOO_SIS_KEYWORDS");
782	my $appname = get_appname_for_sis($sislanguage,$languagefile, "OOO_SIS_APPNAME");
783	my $security = get_security_for_sis();
784	my $wordcount = get_wordcount_for_sis();
785
786	$msifilename = installer::converter::make_path_conform($msifilename);
787
788	my $systemcall = $msiinfo . " " . $msifilename . " -g " . $msiversion . " -c " . $codepage
789					. " -p " . $template . " -v " . $guid . " -t " . $title . " -a " . $author
790					. " -j " . $subject . " -o " . $comment . " -k " . $keywords . " -n " . $appname
791					. " -u " . $security . " -w " . $wordcount;
792
793	my $returnvalue = system($systemcall);
794
795	my $infoline = "Systemcall: $systemcall\n";
796	$installer::logger::Lang->print($infoline);
797
798	if ($returnvalue)
799	{
800		$infoline = "ERROR: Could not execute $msiinfo!\n";
801		$installer::logger::Lang->print($infoline);
802	}
803	else
804	{
805		$infoline = "Success: Executed $msiinfo successfully!\n";
806		$installer::logger::Lang->print($infoline);
807	}
808}
809
810#########################################################################
811# For more than one language in the installation set:
812# Use one database and create Transformations for all other languages
813#########################################################################
814
815sub create_transforms
816{
817	my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_;
818
819	installer::logger::include_header_into_logfile("Creating Transforms");
820
821	my $msitran = "msitran.exe";	# Has to be in the path
822
823	$installdir = installer::converter::make_path_conform($installdir);
824
825	# Syntax for creating a transformation
826	# msitran.exe -g <baseDB> <referenceDB> <transformfile> [<errorhandling>}
827
828	my $basedbname = get_msidatabasename($allvariableshashref, $defaultlanguage);
829	$basedbname = $installdir . $installer::globals::separator . $basedbname;
830
831	my $errorhandling = "f";	# Suppress "change codepage" error
832
833	# Iterating over all files
834
835	foreach ( @{$languagesarray} )
836	{
837		my $onelanguage = $_;
838
839		if ( $onelanguage eq $defaultlanguage ) { next; }
840
841		my $referencedbname = get_msidatabasename($allvariableshashref, $onelanguage);
842		$referencedbname = $installdir . $installer::globals::separator . $referencedbname;
843
844		my $transformfile = $installdir . $installer::globals::separator . "trans_" . $onelanguage . ".mst";
845
846		my $systemcall = $msitran . " " . " -g " . $basedbname . " " . $referencedbname . " " . $transformfile . " " . $errorhandling;
847
848		my $returnvalue = system($systemcall);
849
850		my $infoline = "Systemcall: $systemcall\n";
851		$installer::logger::Lang->print($infoline);
852
853		# Problem: msitran.exe in version 4.0 always returns "1", even if no failure occured.
854		# Therefore it has to be checked, if this is version 4.0. If yes, if the mst file
855		# exists and if it is larger than 0 bytes. If this is true, then no error occured.
856		# File Version of msitran.exe: 4.0.6000.16384 has checksum: "b66190a70145a57773ec769e16777b29".
857		# Same for msitran.exe from wntmsci12: "aa25d3445b94ffde8ef0c1efb77a56b8"
858
859		if ($returnvalue)
860		{
861			$infoline = "WARNING: Returnvalue of $msitran is not 0. Checking version of $msitran!\n";
862			$installer::logger::Lang->print($infoline);
863
864			open(FILE, "<$installer::globals::msitranpath") or die "ERROR: Can't open $installer::globals::msitranpath for creating file hash";
865			binmode(FILE);
866			my $digest = Digest::MD5->new->addfile(*FILE)->hexdigest;
867			close(FILE);
868
869			my @problemchecksums = ("b66190a70145a57773ec769e16777b29", "aa25d3445b94ffde8ef0c1efb77a56b8");
870			my $isproblemchecksum = 0;
871
872			foreach my $problemchecksum ( @problemchecksums )
873			{
874				$infoline = "Checksum of problematic MsiTran.exe: $problemchecksum\n";
875				$installer::logger::Lang->print($infoline);
876				$infoline = "Checksum of used MsiTran.exe: $digest\n";
877				$installer::logger::Lang->print($infoline);
878				if ( $digest eq $problemchecksum ) { $isproblemchecksum = 1; }
879			}
880
881			if ( $isproblemchecksum )
882			{
883				# Check existence of mst
884				if ( -f $transformfile )
885				{
886					$infoline = "File $transformfile exists.\n";
887					$installer::logger::Lang->print($infoline);
888					my $filesize = ( -s $transformfile );
889					$infoline = "Size of $transformfile: $filesize\n";
890					$installer::logger::Lang->print($infoline);
891
892					if ( $filesize > 0 )
893					{
894						$infoline = "Info: Returnvalue $returnvalue of $msitran is no problem :-) .\n";
895						$installer::logger::Lang->print($infoline);
896						$returnvalue = 0; # reset the error
897					}
898					else
899					{
900						$infoline = "Filesize indicates that an error occured.\n";
901						$installer::logger::Lang->print($infoline);
902					}
903				}
904				else
905				{
906					$infoline = "File $transformfile does not exist -> An error occured.\n";
907					$installer::logger::Lang->print($infoline);
908				}
909			}
910			else
911			{
912				$infoline = "This is not a problematic version of msitran.exe. Therefore the error is not caused by problematic msitran.exe.\n";
913				$installer::logger::Lang->print($infoline);
914			}
915		}
916
917		if ($returnvalue)
918		{
919			$infoline = "ERROR: Could not execute $msitran!\n";
920			$installer::logger::Lang->print($infoline);
921		}
922		else
923		{
924			$infoline = "Success: Executed $msitran successfully!\n";
925			$installer::logger::Lang->print($infoline);
926		}
927
928		# The reference database can be deleted
929
930		my $result = unlink($referencedbname);
931		# $result contains the number of deleted files
932
933		if ( $result == 0 )
934		{
935			$infoline = "ERROR: Could not remove file $$referencedbname !\n";
936			$installer::logger::Lang->print($infoline);
937			installer::exiter::exit_program($infoline, "create_transforms");
938		}
939	}
940}
941
942#########################################################################
943# The default language msi database does not need to contain
944# the language in the database name. Therefore the file
945# is renamed. Example: "openofficeorg20_01.msi" to "openofficeorg20.msi"
946#########################################################################
947
948sub rename_msi_database_in_installset
949{
950	my ($defaultlanguage, $installdir, $allvariableshashref) = @_;
951
952	installer::logger::include_header_into_logfile("Renaming msi database");
953
954	my $olddatabasename = get_msidatabasename($allvariableshashref, $defaultlanguage);
955	$olddatabasename = $installdir . $installer::globals::separator . $olddatabasename;
956
957	my $newdatabasename = get_msidatabasename($allvariableshashref);
958
959	$installer::globals::shortmsidatabasename = $newdatabasename;
960
961	$newdatabasename = $installdir . $installer::globals::separator . $newdatabasename;
962
963	installer::systemactions::rename_one_file($olddatabasename, $newdatabasename);
964
965	$installer::globals::msidatabasename = $newdatabasename;
966}
967
968#########################################################################
969# Adding the language to the name of the msi databasename,
970# if this is required (ADDLANGUAGEINDATABASENAME)
971#########################################################################
972
973sub add_language_to_msi_database
974{
975	my ($defaultlanguage, $installdir, $allvariables) = @_;
976
977	my $languagestring = $defaultlanguage;
978	if ( $allvariables->{'USELANGUAGECODE'} ) { $languagestring = installer::windows::language::get_windows_language($defaultlanguage); }
979	my $newdatabasename = $installer::globals::shortmsidatabasename;
980	$newdatabasename =~ s/\.msi\s*$/_$languagestring\.msi/;
981	$installer::globals::shortmsidatabasename = $newdatabasename;
982	$newdatabasename = $installdir . $installer::globals::separator . $newdatabasename;
983
984	my $olddatabasename = $installer::globals::msidatabasename;
985
986	installer::systemactions::rename_one_file($olddatabasename, $newdatabasename);
987
988	$installer::globals::msidatabasename = $newdatabasename;
989}
990
991##########################################################################
992# Writing the databasename into the setup.ini.
993##########################################################################
994
995sub put_databasename_into_setupini
996{
997	my ($setupinifile, $allvariableshashref) = @_;
998
999	my $databasename = get_msidatabasename($allvariableshashref);
1000	my $line = "database=" . $databasename . "\n";
1001
1002	push(@{$setupinifile}, $line);
1003}
1004
1005##########################################################################
1006# Writing the required msi version into setup.ini
1007##########################################################################
1008
1009sub put_msiversion_into_setupini
1010{
1011	my ($setupinifile) = @_;
1012
1013	my $msiversion = "2.0";
1014	my $line = "msiversion=" . $msiversion . "\n";
1015
1016	push(@{$setupinifile}, $line);
1017}
1018
1019##########################################################################
1020# Writing the productname into setup.ini
1021##########################################################################
1022
1023sub put_productname_into_setupini
1024{
1025	my ($setupinifile, $allvariableshashref) = @_;
1026
1027	my $productname = $allvariableshashref->{'PRODUCTNAME'};
1028	my $line = "productname=" . $productname . "\n";
1029
1030	push(@{$setupinifile}, $line);
1031}
1032
1033##########################################################################
1034# Writing the productcode into setup.ini
1035##########################################################################
1036
1037sub put_productcode_into_setupini
1038{
1039	my ($setupinifile) = @_;
1040
1041	my $productcode = $installer::globals::productcode;
1042	my $line = "productcode=" . $productcode . "\n";
1043
1044	push(@{$setupinifile}, $line);
1045}
1046
1047##########################################################################
1048# Writing the ProductVersion from Property table into setup.ini
1049##########################################################################
1050
1051sub put_productversion_into_setupini
1052{
1053	my ($setupinifile) = @_;
1054
1055	my $line = "productversion=" . $installer::globals::msiproductversion . "\n";
1056	push(@{$setupinifile}, $line);
1057}
1058
1059##########################################################################
1060# Writing the key for Minor Upgrades into setup.ini
1061##########################################################################
1062
1063sub put_upgradekey_into_setupini
1064{
1065	my ($setupinifile) = @_;
1066
1067	if ( $installer::globals::minorupgradekey ne "" )
1068	{
1069		my $line = "upgradekey=" . $installer::globals::minorupgradekey . "\n";
1070		push(@{$setupinifile}, $line);
1071	}
1072}
1073
1074##########################################################################
1075# Writing the number of languages into setup.ini
1076##########################################################################
1077
1078sub put_languagecount_into_setupini
1079{
1080	my ($setupinifile, $languagesarray) = @_;
1081
1082	my $languagecount = $#{$languagesarray} + 1;
1083	my $line = "count=" . $languagecount . "\n";
1084
1085	push(@{$setupinifile}, $line);
1086}
1087
1088##########################################################################
1089# Writing the defaultlanguage into setup.ini
1090##########################################################################
1091
1092sub put_defaultlanguage_into_setupini
1093{
1094	my ($setupinifile, $defaultlanguage) = @_;
1095
1096	my $windowslanguage = installer::windows::language::get_windows_language($defaultlanguage);
1097	my $line = "default=" . $windowslanguage . "\n";
1098	push(@{$setupinifile}, $line);
1099}
1100
1101##########################################################################
1102# Writing the information about transformations into setup.ini
1103##########################################################################
1104
1105sub put_transforms_into_setupini
1106{
1107	my ($setupinifile, $onelanguage, $counter) = @_;
1108
1109	my $windowslanguage = installer::windows::language::get_windows_language($onelanguage);
1110	my $transformfilename = "trans_" . $onelanguage . ".mst";
1111
1112	my $line = "lang" . $counter . "=" . $windowslanguage . "," . $transformfilename . "\n";
1113
1114	push(@{$setupinifile}, $line);
1115}
1116
1117###################################################
1118# Including Windows line ends in ini files
1119# Profiles on Windows shall have \r\n line ends
1120###################################################
1121
1122sub include_windows_lineends
1123{
1124	my ($onefile) = @_;
1125
1126	for ( my $i = 0; $i <= $#{$onefile}; $i++ )
1127	{
1128		${$onefile}[$i] =~ s/\r?\n$/\r\n/;
1129	}
1130}
1131
1132##########################################################################
1133# Generation the file setup.ini, that is used by the loader setup.exe.
1134##########################################################################
1135
1136sub create_setup_ini
1137{
1138	my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_;
1139
1140	installer::logger::include_header_into_logfile("Creating setup.ini");
1141
1142	my $setupinifilename = $installdir . $installer::globals::separator . "setup.ini";
1143
1144	my @setupinifile = ();
1145	my $setupinifile = \@setupinifile;
1146
1147	my $line = "\[setup\]\n";
1148	push(@setupinifile, $line);
1149
1150	put_databasename_into_setupini($setupinifile, $allvariableshashref);
1151	put_msiversion_into_setupini($setupinifile);
1152	put_productname_into_setupini($setupinifile, $allvariableshashref);
1153	put_productcode_into_setupini($setupinifile);
1154	put_productversion_into_setupini($setupinifile);
1155	put_upgradekey_into_setupini($setupinifile);
1156
1157	$line = "\[languages\]\n";
1158	push(@setupinifile, $line);
1159
1160	put_languagecount_into_setupini($setupinifile, $languagesarray);
1161	put_defaultlanguage_into_setupini($setupinifile, $defaultlanguage);
1162
1163	if ( $#{$languagesarray} > 0 )	# writing the transforms information
1164	{
1165		my $counter = 1;
1166
1167		for ( my $i = 0; $i <= $#{$languagesarray}; $i++ )
1168		{
1169			if ( ${$languagesarray}[$i] eq $defaultlanguage ) { next; }
1170
1171			put_transforms_into_setupini($setupinifile, ${$languagesarray}[$i], $counter);
1172			$counter++;
1173		}
1174	}
1175
1176	if ( $installer::globals::iswin && $installer::globals::plat =~ /cygwin/i)		# Windows line ends only for Cygwin
1177	{
1178		include_windows_lineends($setupinifile);
1179	}
1180
1181	installer::files::save_file($setupinifilename, $setupinifile);
1182
1183	$infoline = "Generated file $setupinifilename !\n";
1184	$installer::logger::Lang->print($infoline);
1185}
1186
1187#################################################################
1188# Copying the files defined as ScpActions into the
1189# installation set.
1190#################################################################
1191
1192sub copy_scpactions_into_installset
1193{
1194	my ($defaultlanguage, $installdir, $allscpactions) = @_;
1195
1196	installer::logger::include_header_into_logfile("Copying ScpAction files into installation set");
1197
1198	for ( my $i = 0; $i <= $#{$allscpactions}; $i++ )
1199	{
1200		my $onescpaction = ${$allscpactions}[$i];
1201
1202		if ( $onescpaction->{'Name'} eq "loader.exe" ) { next; }	# do not copy this ScpAction loader
1203
1204		# only copying language independent files or files with the correct language (the defaultlanguage)
1205
1206		my $filelanguage = $onescpaction->{'specificlanguage'};
1207
1208		if ( ($filelanguage eq $defaultlanguage) || ($filelanguage eq "") )
1209		{
1210			my $sourcefile = $onescpaction->{'sourcepath'};
1211			my $destfile = $installdir . $installer::globals::separator . $onescpaction->{'DestinationName'};
1212
1213			installer::systemactions::copy_one_file($sourcefile, $destfile);
1214		}
1215	}
1216}
1217
1218#################################################################
1219# Copying the files for the Windows installer into the
1220# installation set (setup.exe).
1221#################################################################
1222
1223sub copy_windows_installer_files_into_installset
1224{
1225	my ($installdir, $includepatharrayref, $allvariables) = @_;
1226
1227    installer::logger::include_header_into_logfile("Copying Windows installer files into installation set");
1228
1229	@copyfile = ();
1230	push(@copyfile, "loader2.exe");
1231
1232	if ( $allvariables->{'NOLOADERREQUIRED'} ) { @copyfile = (); }
1233
1234	for ( my $i = 0; $i <= $#copyfile; $i++ )
1235	{
1236		my $filename = $copyfile[$i];
1237		my $sourcefileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1);
1238
1239		if ( ! -f $$sourcefileref ) { installer::exiter::exit_program("ERROR: msi file not found: $$sourcefileref !", "copy_windows_installer_files_into_installset"); }
1240
1241		my $destfile;
1242		if ( $copyfile[$i] eq "loader2.exe" ) { $destfile = "setup.exe"; }	# renaming the loader
1243		else { $destfile = $copyfile[$i]; }
1244
1245		$destfile = $installdir . $installer::globals::separator . $destfile;
1246
1247		installer::systemactions::copy_one_file($$sourcefileref, $destfile);
1248	}
1249}
1250
1251#################################################################
1252# Copying MergeModules for the Windows installer into the
1253# installation set. The list of MergeModules is located
1254# in %installer::globals::copy_msm_files
1255#################################################################
1256
1257sub copy_merge_modules_into_installset
1258{
1259	my ($installdir) = @_;
1260
1261	installer::logger::include_header_into_logfile("Copying Merge files into installation set");
1262
1263	my $cabfile;
1264	foreach $cabfile ( keys  %installer::globals::copy_msm_files )
1265	{
1266		my $sourcefile  = $installer::globals::copy_msm_files{$cabfile};
1267		my $destfile = $installdir . $installer::globals::separator . $cabfile;
1268
1269		installer::systemactions::copy_one_file($sourcefile, $destfile);
1270	}
1271}
1272
1273#################################################################
1274# Copying the child projects into the
1275# installation set
1276#################################################################
1277
1278sub copy_child_projects_into_installset
1279{
1280	my ($installdir, $allvariables) = @_;
1281
1282	my $sourcefile = "";
1283	my $destdir = "";
1284
1285	# adding Java
1286
1287	if ( $allvariables->{'JAVAPRODUCT'} )
1288	{
1289		$sourcefile = $installer::globals::javafile->{'sourcepath'};
1290		$destdir = $installdir . $installer::globals::separator . $installer::globals::javafile->{'Subdir'};
1291		if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); }
1292		installer::systemactions::copy_one_file($sourcefile, $destdir);
1293	}
1294
1295	if ( $allvariables->{'UREPRODUCT'} )
1296	{
1297		$sourcefile = $installer::globals::urefile->{'sourcepath'};
1298		$destdir = $installdir . $installer::globals::separator . $installer::globals::urefile->{'Subdir'};
1299		if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); }
1300		installer::systemactions::copy_one_file($sourcefile, $destdir);
1301	}
1302}
1303
1304#################################################################
1305# Getting a list of GUID using uuidgen.exe.
1306# This works only on Windows
1307#################################################################
1308
1309sub get_guid_list
1310{
1311	my ($number, $log) = @_;
1312
1313	if ( $log ) { installer::logger::include_header_into_logfile("Generating $number GUID"); }
1314
1315	my $uuidgen = "uuidgen.exe";		# Has to be in the path
1316
1317	# "-c" for uppercase output
1318
1319	# my $systemcall = "$uuidgen -n$number -c |";
1320	my $systemcall = "$uuidgen -n$number |";
1321	open (UUIDGEN, "$systemcall" ) or die("uuidgen is missing.");
1322	my @uuidlist = <UUIDGEN>;
1323	close (UUIDGEN);
1324
1325	my $infoline = "Systemcall: $systemcall\n";
1326	if ( $log ) { $installer::logger::Lang->print($infoline); }
1327
1328	my $comparenumber = $#uuidlist + 1;
1329
1330	if ( $comparenumber == $number )
1331	{
1332		$infoline = "Success: Executed $uuidgen successfully!\n";
1333		if ( $log ) { $installer::logger::Lang->print($infoline); }
1334	}
1335	else
1336	{
1337		$infoline = "ERROR: Could not execute $uuidgen successfully!\n";
1338		if ( $log ) { $installer::logger::Lang->print($infoline); }
1339	}
1340
1341	# uppercase, no longer "-c", because this is only supported in uuidgen.exe v.1.01
1342	for ( my $i = 0; $i <= $#uuidlist; $i++ ) { $uuidlist[$i] = uc($uuidlist[$i]); }
1343
1344	return \@uuidlist;
1345}
1346
1347#################################################################
1348# Calculating a GUID with a string using md5.
1349#################################################################
1350
1351sub calculate_guid
1352{
1353	my ( $string ) = @_;
1354
1355	my $guid = "";
1356
1357    my $md5 = Digest::MD5->new;
1358    $md5->add($string);
1359    my $digest = $md5->hexdigest;
1360    $digest = uc($digest);
1361
1362	# my $id = pack("A32", $digest);
1363	my ($first, $second, $third, $fourth, $fifth) = unpack ('A8 A4 A4 A4 A12', $digest);
1364	$guid = "$first-$second-$third-$fourth-$fifth";
1365
1366	return $guid;
1367}
1368
1369#################################################################
1370# Calculating a ID with a string using md5 (very fast).
1371#################################################################
1372
1373sub calculate_id
1374{
1375	my ( $string, $length ) = @_;
1376
1377	my $id = "";
1378
1379    my $md5 = Digest::MD5->new;
1380    $md5->add($string);
1381    my $digest = lc($md5->hexdigest);
1382	$id = substr($digest, 0, $length);
1383
1384	return $id;
1385}
1386
1387#################################################################
1388# Filling the component hash with the values of the
1389# component file.
1390#################################################################
1391
1392sub fill_component_hash
1393{
1394	my ($componentfile) = @_;
1395
1396	my %components = ();
1397
1398	for ( my $i = 0; $i <= $#{$componentfile}; $i++ )
1399	{
1400		my $line = ${$componentfile}[$i];
1401
1402		if ( $line =~ /^\s*(.*?)\t(.*?)\s*$/ )
1403		{
1404			my $key = $1;
1405			my $value = $2;
1406
1407			$components{$key} = $value;
1408		}
1409	}
1410
1411	return \%components;
1412}
1413
1414#################################################################
1415# Creating a new component file, if new guids were generated.
1416#################################################################
1417
1418sub create_new_component_file
1419{
1420	my ($componenthash) = @_;
1421
1422	my @componentfile = ();
1423
1424	my $key;
1425
1426	foreach $key (keys %{$componenthash})
1427	{
1428		my $value = $componenthash->{$key};
1429		my $input = "$key\t$value\n";
1430		push(@componentfile ,$input);
1431	}
1432
1433	return \@componentfile;
1434}
1435
1436#################################################################
1437# Filling real component GUID into the component table.
1438# This works only on Windows
1439#################################################################
1440
1441sub set_uuid_into_component_table
1442{
1443	my ($idtdirbase, $allvariables) = @_;
1444
1445	my $componenttablename  = $idtdirbase . $installer::globals::separator . "Componen.idt";
1446
1447	my $componenttable = installer::files::read_file($componenttablename);
1448
1449	# For update and patch reasons (small update) the GUID of an existing component must not change!
1450	# The collection of component GUIDs is saved in the directory $installer::globals::idttemplatepath in the file "components.txt"
1451
1452	my $infoline = "";
1453	my $counter = 0;
1454	# my $componentfile = installer::files::read_file($installer::globals::componentfilename);
1455	# my $componenthash = fill_component_hash($componentfile);
1456
1457	for ( my $i = 3; $i <= $#{$componenttable}; $i++ )	# ignoring the first three lines
1458	{
1459		my $oneline = ${$componenttable}[$i];
1460		my $componentname = "";
1461		if ( $oneline =~ /^\s*(\S+?)\t/ ) { $componentname = $1; }
1462
1463		my $uuid = "";
1464
1465	#	if ( $componenthash->{$componentname} )
1466	#	{
1467	#		$uuid = $componenthash->{$componentname};
1468	#	}
1469	#	else
1470	#	{
1471
1472			if ( exists($installer::globals::calculated_component_guids{$componentname}))
1473			{
1474				$uuid = $installer::globals::calculated_component_guids{$componentname};
1475			}
1476			else
1477			{
1478				# Calculating new GUID with the help of the component name.
1479				my $useooobaseversion = 1;
1480				if ( exists($installer::globals::base_independent_components{$componentname})) { $useooobaseversion = 0; }
1481				my $sourcestring = $componentname;
1482
1483				if ( $useooobaseversion )
1484				{
1485					if ( ! exists($allvariables->{'OOOBASEVERSION'}) ) { installer::exiter::exit_program("ERROR: Could not find variable \"OOOBASEVERSION\" (required value for GUID creation)!", "set_uuid_into_component_table"); }
1486					$sourcestring = $sourcestring . "_" . $allvariables->{'OOOBASEVERSION'};
1487				}
1488				$uuid = calculate_guid($sourcestring);
1489				$counter++;
1490
1491				# checking, if there is a conflict with an already created guid
1492				if ( exists($installer::globals::allcalculated_guids{$uuid}) ) { installer::exiter::exit_program("ERROR: \"$uuid\" was already created before!", "set_uuid_into_component_table"); }
1493				$installer::globals::allcalculated_guids{$uuid} = 1;
1494				$installer::globals::calculated_component_guids{$componentname} = $uuid;
1495
1496				# Setting new uuid
1497				# $componenthash->{$componentname} = $uuid;
1498
1499				# Setting flag
1500				# $installer::globals::created_new_component_guid = 1;	# this is very important!
1501			}
1502	#	}
1503
1504		${$componenttable}[$i] =~ s/COMPONENTGUID/$uuid/;
1505	}
1506
1507	installer::files::save_file($componenttablename, $componenttable);
1508
1509#	if ( $installer::globals::created_new_component_guid )
1510#	{
1511#		# create new component file!
1512#		$componentfile = create_new_component_file($componenthash);
1513#		installer::worker::sort_array($componentfile);
1514#
1515#		# To avoid conflict the components file cannot be saved at the same place
1516#		# All important data have to be saved in the directory: $installer::globals::infodirectory
1517#		my $localcomponentfilename = $installer::globals::componentfilename;
1518#		installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$localcomponentfilename);
1519#		$localcomponentfilename = $installer::globals::infodirectory . $installer::globals::separator . $localcomponentfilename;
1520#		installer::files::save_file($localcomponentfilename, $componentfile);
1521#
1522#		# installer::files::save_file($installer::globals::componentfilename, $componentfile);	# version using new file in solver
1523#
1524#		$infoline = "COMPONENTCODES: Created $counter new GUIDs for components ! \n";
1525#		$installer::logger::Lang->print($infoline);
1526#	}
1527#	else
1528#	{
1529#		$infoline = "SUCCESS COMPONENTCODES: All component codes exist! \n";
1530#		$installer::logger::Lang->print($infoline);
1531#	}
1532
1533}
1534
1535#########################################################################
1536# Adding final 64 properties into msi database, if required.
1537# RegLocator : +16 in type column to search in 64 bit registry.
1538# All conditions: "VersionNT" -> "VersionNT64" (several tables).
1539# Already done: "+256" in Attributes column of table "Component".
1540# Still following: Setting "x64" instead of "Intel" in Summary
1541# Information Stream of msi database in "get_template_for_sis".
1542#########################################################################
1543
1544sub prepare_64bit_database
1545{
1546	my ($basedir, $allvariables) = @_;
1547
1548	my $infoline = "";
1549
1550	if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 ))
1551	{
1552		# 1. Beginning with table "RegLocat.idt". Adding "16" to the type.
1553
1554		my $reglocatfile = "";
1555		my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
1556
1557		if ( -f $reglocatfilename )
1558		{
1559			my $saving_required = 0;
1560			$reglocatfile = installer::files::read_file($reglocatfilename);
1561
1562			for ( my $i = 3; $i <= $#{$reglocatfile}; $i++ ) 	# ignoring the first three lines
1563			{
1564				my $oneline = ${$reglocatfile}[$i];
1565
1566				if ( $oneline =~ /^\s*\#/ ) { next; }	# this is a comment line
1567				if ( $oneline =~ /^\s*$/ ) { next; }
1568
1569				if ( $oneline =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(\d+)\s*$/ )
1570				{
1571					# Syntax: Signature_ Root Key Name Type
1572					my $sig = $1;
1573					my $root = $2;
1574					my $key = $3;
1575					my $name = $4;
1576					my $type = $5;
1577
1578					$type = $type + 16;
1579
1580					my $newline = $sig . "\t" . $root . "\t" . $key . "\t" . $name . "\t" . $type . "\n";
1581					${$reglocatfile}[$i] = $newline;
1582
1583					$saving_required = 1;
1584				}
1585			}
1586
1587			if ( $saving_required )
1588			{
1589				# Saving the files
1590				installer::files::save_file($reglocatfilename ,$reglocatfile);
1591				$infoline = "Making idt file 64 bit conform: $reglocatfilename\n";
1592				$installer::logger::Lang->print($infoline);
1593			}
1594		}
1595
1596		# 2. Replacing all occurences of "VersionNT" by "VersionNT64"
1597
1598		my @versionnt_files = ("Componen.idt", "InstallE.idt", "InstallU.idt", "LaunchCo.idt");
1599
1600		foreach my $onefile ( @versionnt_files )
1601		{
1602			my $fullfilename = $basedir . $installer::globals::separator . $onefile;
1603
1604			if ( -f $fullfilename )
1605			{
1606				my $saving_required = 0;
1607				$filecontent = installer::files::read_file($fullfilename);
1608
1609				for ( my $i = 3; $i <= $#{$filecontent}; $i++ ) 	# ignoring the first three lines
1610				{
1611					my $oneline = ${$filecontent}[$i];
1612
1613					if ( $oneline =~ /\bVersionNT\b/ )
1614					{
1615						${$filecontent}[$i] =~ s/\bVersionNT\b/VersionNT64/g;
1616						$saving_required = 1;
1617					}
1618				}
1619
1620				if ( $saving_required )
1621				{
1622					# Saving the files
1623					installer::files::save_file($fullfilename ,$filecontent);
1624					$infoline = "Making idt file 64 bit conform: $fullfilename\n";
1625					$installer::logger::Lang->print($infoline);
1626				}
1627			}
1628		}
1629	}
1630
1631}
1632
1633#################################################################
1634# Include all cab files into the msi database.
1635# This works only on Windows
1636#################################################################
1637
1638sub include_cabs_into_msi
1639{
1640	my ($installdir) = @_;
1641
1642	installer::logger::include_header_into_logfile("Including cabs into msi database");
1643
1644	my $from = cwd();
1645	my $to = $installdir;
1646
1647	chdir($to);
1648
1649	my $infoline = "Changing into directory: $to";
1650	$installer::logger::Lang->print($infoline);
1651
1652	my $msidb = "msidb.exe";	# Has to be in the path
1653	my $extraslash = "";		# Has to be set for non-ActiveState perl
1654
1655	my $msifilename = $installer::globals::msidatabasename;
1656
1657	$msifilename = installer::converter::make_path_conform($msifilename);
1658
1659	# msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
1660	$msifilename =~ s/\//\\\\/g;
1661	$extraslash = "\\";
1662
1663	my $allcabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir);
1664
1665	for ( my $i = 0; $i <= $#{$allcabfiles}; $i++ )
1666	{
1667		my $systemcall = $msidb . " -d " . $msifilename . " -a " . ${$allcabfiles}[$i];
1668
1669		my $returnvalue = system($systemcall);
1670
1671		$infoline = "Systemcall: $systemcall\n";
1672		$installer::logger::Lang->print($infoline);
1673
1674		if ($returnvalue)
1675		{
1676			$infoline = "ERROR: Could not execute $systemcall !\n";
1677			$installer::logger::Lang->print($infoline);
1678		}
1679		else
1680		{
1681			$infoline = "Success: Executed $systemcall successfully!\n";
1682			$installer::logger::Lang->print($infoline);
1683		}
1684
1685		# deleting the cab file
1686
1687		unlink(${$allcabfiles}[$i]);
1688
1689		$infoline = "Deleted cab file: ${$allcabfiles}[$i]\n";
1690		$installer::logger::Lang->print($infoline);
1691	}
1692
1693	$infoline = "Changing back into directory: $from";
1694	$installer::logger::Lang->print($infoline);
1695
1696	chdir($from);
1697}
1698
1699#################################################################
1700# Executing the created batch file to pack all files.
1701# This works only on Windows
1702#################################################################
1703
1704sub execute_packaging
1705{
1706	my ($localpackjobref, $loggingdir, $allvariables) = @_;
1707
1708	installer::logger::include_header_into_logfile("Packaging process");
1709
1710	$installer::logger::Lang->add_timestamp("Performance Info: Execute packaging start");
1711
1712	my $infoline = "";
1713	my $from = cwd();
1714	my $to = $loggingdir;
1715
1716	chdir($to);
1717	$infoline = "chdir: $to \n";
1718	$installer::logger::Lang->print($infoline);
1719
1720	# if the ddf file contains relative pathes, it is necessary to change into the temp directory
1721	if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} )
1722	{
1723		$to = $installer::globals::temppath;
1724		chdir($to);
1725		$infoline = "chdir: $to \n";
1726		$installer::logger::Lang->print($infoline);
1727	}
1728
1729	# changing the tmp directory, because makecab.exe generates temporary cab files
1730	my $origtemppath = "";
1731	if ( $ENV{'TMP'} ) { $origtemppath = $ENV{'TMP'}; }
1732	$ENV{'TMP'} = $installer::globals::temppath;	# setting TMP to the new unique directory!
1733
1734	my $maxmakecabcalls = 3;
1735	my $allmakecabcalls = $#{$localpackjobref} + 1;
1736
1737	for ( my $i = 0; $i <= $#{$localpackjobref}; $i++ )
1738	{
1739		my $systemcall = ${$localpackjobref}[$i];
1740
1741		my $callscounter = $i + 1;
1742
1743		$installer::logger::Info->printf("... makecab.exe (%s/%s) ... \n", $callscounter, $allmakecabcalls);
1744
1745		# my $returnvalue = system($systemcall);
1746
1747		for ( my $n = 1; $n <= $maxmakecabcalls; $n++ )
1748		{
1749			my @ddfoutput = ();
1750
1751			$infoline = "Systemcall: $systemcall";
1752			$installer::logger::Lang->print($infoline);
1753
1754			open (DDF, "$systemcall");
1755			while (<DDF>) {push(@ddfoutput, $_); }
1756			close (DDF);
1757
1758			my $returnvalue = $?;	# $? contains the return value of the systemcall
1759
1760			if ($returnvalue)
1761			{
1762				if ( $n < $maxmakecabcalls )
1763				{
1764                    $installer::logger::Info->printf("makecab_error (Try %s): Trying again\n", $n);
1765                    $installer::logger::Lang->printf("makecab_error (Try %s): Trying again\n", $n);
1766				}
1767				else
1768				{
1769                    $installer::logger::Info->printf("ERROR (Try %s): Abort packing \n", $n);
1770                    $installer::logger::Lang->printf("ERROR (Try %s): Abort packing \n", $n);
1771				}
1772
1773				for ( my $m = 0; $m <= $#ddfoutput; $m++ )
1774				{
1775					if ( $ddfoutput[$m] =~ /(ERROR\:.*?)\s*$/ )
1776					{
1777						$infoline = $1 . "\n";
1778						if ( $n < $maxmakecabcalls )
1779                        {
1780                            $infoline =~ s/ERROR\:/makecab_error\:/i;
1781                        }
1782						$installer::logger::Info->print($infoline);
1783						$installer::logger::Lang->print($infoline);
1784					}
1785				}
1786
1787				if ( $n == $maxmakecabcalls ) { installer::exiter::exit_program("ERROR: \"$systemcall\"!", "execute_packaging"); }
1788			}
1789			else
1790			{
1791				$infoline = "Success (Try $n): $systemcall";
1792				$installer::logger::Lang->print($infoline);
1793				last;
1794			}
1795		}
1796	}
1797
1798	$installer::logger::Lang->add_timestamp("Performance Info: Execute packaging end");
1799
1800	# setting back to the original tmp directory
1801	$ENV{'TMP'} = $origtemppath;
1802
1803	chdir($from);
1804	$infoline = "chdir: $from \n";
1805	$installer::logger::Lang->print($infoline);
1806}
1807
1808###############################################################
1809# Setting the global variables ProductCode and the UpgradeCode
1810###############################################################
1811
1812sub set_global_code_variables
1813{
1814	my ( $languagesref, $languagestringref, $allvariableshashref, $alloldproperties ) = @_;
1815
1816	# In the msi template directory a files "codes.txt" has to exist, in which the ProductCode
1817	# and the UpgradeCode for the product are defined.
1818	# The name "codes.txt" can be overwritten in Product definition with CODEFILENAME .
1819	# Default $installer::globals::codefilename is defined in parameter.pm.
1820
1821	if ( $allvariableshashref->{'CODEFILENAME'} )
1822	{
1823		$installer::globals::codefilename = $installer::globals::idttemplatepath  . $installer::globals::separator . $allvariableshashref->{'CODEFILENAME'};
1824		installer::files::check_file($installer::globals::codefilename);
1825	}
1826
1827	my $infoline = "Using Codes file: $installer::globals::codefilename \n";
1828	$installer::logger::Lang->print($infoline);
1829
1830	my $codefile = installer::files::read_file($installer::globals::codefilename);
1831
1832	my $isopensource = 0;
1833	if ( $allvariableshashref->{'OPENSOURCE'} ) { $isopensource = $allvariableshashref->{'OPENSOURCE'}; }
1834
1835	my $onelanguage = "";
1836
1837	if ( $#{$languagesref} > 0 )	# more than one language
1838	{
1839		if (( $installer::globals::added_english ) && ( $#{$languagesref} == 1 )) # only multilingual because of added English
1840		{
1841			$onelanguage = ${$languagesref}[1];  # setting the first language, that is not english
1842		}
1843		else
1844		{
1845			if (( ${$languagesref}[1] =~ /jp/ ) ||
1846				( ${$languagesref}[1] =~ /ko/ ) ||
1847				( ${$languagesref}[1] =~ /zh/ ))
1848			{
1849				$onelanguage = "multiasia";
1850			}
1851			else
1852			{
1853				$onelanguage = "multiwestern";
1854			}
1855		}
1856	}
1857	else	# only one language
1858	{
1859		$onelanguage = ${$languagesref}[0];
1860	}
1861
1862	# ProductCode must not change, if Windows patches shall be applied
1863	if ( $installer::globals::updatedatabase )
1864	{
1865		$installer::globals::productcode = $alloldproperties->{'ProductCode'};
1866	}
1867	elsif ( $installer::globals::prepare_winpatch )
1868	{
1869		# ProductCode has to be specified in each language
1870		my $searchstring = "PRODUCTCODE";
1871		my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile);
1872		$installer::globals::productcode = installer::windows::idtglobal::get_code_from_code_block($codeblock, $onelanguage);
1873	} else {
1874		my $guidref = get_guid_list(1, 1);	# only one GUID shall be generated
1875		${$guidref}[0] =~ s/\s*$//;		# removing ending spaces
1876		$installer::globals::productcode = "\{" . ${$guidref}[0] . "\}";
1877	}
1878
1879	if ( $installer::globals::patch ) # patch upgrade codes are defined in soffice.lst
1880	{
1881		if ( $allvariableshashref->{'PATCHUPGRADECODE'} ) { $installer::globals::upgradecode = $allvariableshashref->{'PATCHUPGRADECODE'}; }
1882		else { installer::exiter::exit_program("ERROR: PATCHUPGRADECODE not defined in list file!", "set_global_code_variables"); }
1883	}
1884	else
1885	{
1886		# UpgradeCode can take english as default, if not defined in specified language
1887
1888		$searchstring = "UPGRADECODE";	# searching in the codes.txt file
1889		$codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile);
1890		$installer::globals::upgradecode = installer::windows::idtglobal::get_language_string_from_language_block($codeblock, $onelanguage, "");
1891	}
1892
1893	# if (( $installer::globals::productcode eq "" ) && ( ! $isopensource )) { installer::exiter::exit_program("ERROR: ProductCode for language $onelanguage not defined in $installer::globals::codefilename !", "set_global_code_variables"); }
1894	if ( $installer::globals::upgradecode eq "" ) { installer::exiter::exit_program("ERROR: UpgradeCode not defined in $installer::globals::codefilename !", "set_global_code_variables"); }
1895
1896	$infoline = "Setting ProductCode to: $installer::globals::productcode \n";
1897	$installer::logger::Lang->print($infoline);
1898	$infoline = "Setting UpgradeCode to: $installer::globals::upgradecode \n";
1899	$installer::logger::Lang->print($infoline);
1900
1901	# Adding both variables into the variables array
1902
1903	$allvariableshashref->{'PRODUCTCODE'} = $installer::globals::productcode;
1904	$allvariableshashref->{'UPGRADECODE'} = $installer::globals::upgradecode;
1905
1906	$infoline = "Defined variable PRODUCTCODE: $installer::globals::productcode \n";
1907	$installer::logger::Lang->print($infoline);
1908
1909	$infoline = "Defined variable UPGRADECODE: $installer::globals::upgradecode \n";
1910	$installer::logger::Lang->print($infoline);
1911
1912}
1913
1914###############################################################
1915# Setting the product version used in property table and
1916# upgrade table. Saving in global variable $msiproductversion
1917###############################################################
1918
1919sub set_msiproductversion
1920{
1921	my ( $allvariables ) = @_;
1922
1923	my $productversion = $allvariables->{'PRODUCTVERSION'};
1924
1925	if (( $productversion =~ /^\s*\d+\s*$/ ) && ( $productversion > 255 )) { $productversion = $productversion%256; }
1926
1927	if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
1928	{
1929		$productversion = $1 . "\." . $2 . $3 . "\." . $installer::globals::buildid;
1930	}
1931	elsif  ( $productversion =~ /^\s*(\d+)\.(\d+)\s*$/ )
1932	{
1933		$productversion = $1 . "\." . $2 . "\." . $installer::globals::buildid;
1934	}
1935	else
1936	{
1937		my $productminor = "00";
1938		if (( $allvariables->{'PACKAGEVERSION'} ) && ( $allvariables->{'PACKAGEVERSION'} ne "" ))
1939		{
1940			if ( $allvariables->{'PACKAGEVERSION'} =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) { $productminor = $2; }
1941		}
1942
1943		$productversion = $productversion . "\." . $productminor . "\." . $installer::globals::buildid;
1944	}
1945
1946	$installer::globals::msiproductversion = $productversion;
1947
1948	# Setting $installer::globals::msimajorproductversion, to differ between old version in upgrade table
1949
1950	if ( $installer::globals::msiproductversion =~ /^\s*(\d+)\./ )
1951	{
1952		my $major = $1;
1953		$installer::globals::msimajorproductversion = $major . "\.0\.0";
1954	}
1955}
1956
1957#################################################################################
1958# Including the msi product version into the bootstrap.ini, Windows only
1959#################################################################################
1960
1961sub put_msiproductversion_into_bootstrapfile
1962{
1963	my ($filesref) = @_;
1964
1965	for ( my $i = 0; $i <= $#{$filesref}; $i++ )
1966	{
1967		my $onefile = ${$filesref}[$i];
1968
1969		if ( $onefile->{'gid'} eq "gid_Profile_Version_Ini" )
1970		{
1971			my $file = installer::files::read_file($onefile->{'sourcepath'});
1972
1973			for ( my $j = 0; $j <= $#{$file}; $j++ )
1974			{
1975				${$file}[$j] =~ s/\<msiproductversion\>/$installer::globals::msiproductversion/;
1976			}
1977
1978			installer::files::save_file($onefile->{'sourcepath'}, $file);
1979
1980			last;
1981		}
1982	}
1983}
1984
1985####################################################################################
1986# Updating the file Property.idt dynamically
1987# Content:
1988# Property Value
1989####################################################################################
1990
1991sub update_reglocat_table
1992{
1993	my ($basedir, $allvariables) = @_;
1994
1995	my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
1996
1997	# Only do something, if this file exists
1998
1999	if ( -f $reglocatfilename )
2000	{
2001		my $reglocatfile = installer::files::read_file($reglocatfilename);
2002
2003		my $layername = "";
2004		if ( $allvariables->{'REGISTRYLAYERNAME'} )
2005		{
2006			$layername = $allvariables->{'REGISTRYLAYERNAME'};
2007		}
2008		else
2009		{
2010			for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
2011			{
2012				if ( ${$reglocatfile}[$i] =~ /\bLAYERNAMETEMPLATE\b/ )
2013				{
2014					installer::exiter::exit_program("ERROR: Variable \"REGISTRYLAYERNAME\" has to be defined", "update_reglocat_table");
2015				}
2016			}
2017		}
2018
2019		if ( $layername ne "" )
2020		{
2021			# Updating the layername in
2022
2023			for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
2024			{
2025				${$reglocatfile}[$i] =~ s/\bLAYERNAMETEMPLATE\b/$layername/;
2026			}
2027
2028			# Saving the file
2029			installer::files::save_file($reglocatfilename ,$reglocatfile);
2030			my $infoline = "Updated idt file: $reglocatfilename\n";
2031			$installer::logger::Lang->print($infoline);
2032		}
2033	}
2034}
2035
2036
2037
2038####################################################################################
2039# Updating the file RemoveRe.idt dynamically (RemoveRegistry.idt)
2040# The name of the component has to be replaced.
2041####################################################################################
2042
2043sub update_removere_table
2044{
2045	my ($basedir) = @_;
2046
2047	my $removeregistryfilename = $basedir . $installer::globals::separator . "RemoveRe.idt";
2048
2049	# Only do something, if this file exists
2050
2051	if ( -f $removeregistryfilename )
2052	{
2053		my $removeregistryfile = installer::files::read_file($removeregistryfilename);
2054
2055		for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
2056		{
2057			for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
2058			{
2059				${$removeregistryfile}[$i] =~ s/\bREGISTRYROOTCOMPONENT\b/$installer::globals::registryrootcomponent/;
2060			}
2061		}
2062
2063		# Saving the file
2064		installer::files::save_file($removeregistryfilename ,$removeregistryfile);
2065		my $infoline = "Updated idt file: $removeregistryfilename \n";
2066		$installer::logger::Lang->print($infoline);
2067	}
2068}
2069
2070##########################################################################
2071# Reading saved mappings in Files.idt and Director.idt.
2072# This is required, if installation sets shall be created,
2073# that can be used for creation of msp files.
2074##########################################################################
2075
2076sub read_saved_mappings
2077{
2078	installer::logger::include_header_into_logfile("Reading saved mappings from older installation sets:");
2079
2080	$installer::logger::Lang->add_timestamp("Performance Info: Reading saved mappings start");
2081
2082	if ( $installer::globals::previous_idt_dir )
2083	{
2084		my @errorlines = ();
2085		my $errorstring = "";
2086		my $error_occured = 0;
2087		my $file_error_occured = 0;
2088		my $dir_error = 0;
2089
2090		my $idtdir = $installer::globals::previous_idt_dir;
2091		$idtdir =~ s/\Q$installer::globals::separator\E\s*$//;
2092
2093		# Reading File.idt
2094
2095		my $idtfile = $idtdir . $installer::globals::separator . "File.idt";
2096        $installer::logger::Global->print("\n");
2097        $installer::logger::Global->printf("Analyzing file: %s\n", $idtfile);
2098		if ( ! -f $idtfile )
2099        {
2100            $installer::logger::Global->printf("Warning: File %s does not exist!\n", $idtfile);
2101        }
2102
2103		my $n = 0;
2104		open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings");
2105		<F>; <F>; <F>;
2106		while (<F>)
2107		{
2108			m/^([^\t]+)\t([^\t]+)\t((.*)\|)?([^\t]*)/;
2109			print "OUT1: \$1: $1, \$2: $2, \$3: $3, \$4: $4, \$5: $5\n";
2110			next if ("$1" eq "$5") && (!defined($3));
2111			my $lc1 = lc($1);
2112
2113			if ( exists($installer::globals::savedmapping{"$2/$5"}))
2114			{
2115				if ( ! $file_error_occured )
2116				{
2117					$errorstring = "\nErrors in $idtfile: \n";
2118					push(@errorlines, $errorstring);
2119				}
2120				$errorstring = "Duplicate savedmapping{" . "$2/$5}\n";
2121				push(@errorlines, $errorstring);
2122				$error_occured = 1;
2123				$file_error_occured = 1;
2124			}
2125
2126			if ( exists($installer::globals::savedrevmapping{$lc1}))
2127			{
2128				if ( ! $file_error_occured )
2129				{
2130					$errorstring = "\nErrors in $idtfile: \n";
2131					push(@errorlines, $errorstring);
2132				}
2133				$errorstring = "Duplicate savedrevmapping{" . "$lc1}\n";
2134				push(@errorlines, $errorstring);
2135				$error_occured = 1;
2136				$file_error_occured = 1;
2137			}
2138
2139			my $shortname = $4 || '';
2140
2141			# Don't reuse illegal 8.3 mappings that we used to generate in 2.0.4
2142			if (index($shortname, '.') > 8 ||
2143			    (index($shortname, '.') == -1 && length($shortname) > 8))
2144			{
2145			    $shortname = '';
2146			}
2147
2148			if (( $shortname ne '' ) && ( index($shortname, '~') > 0 ) && ( exists($installer::globals::savedrev83mapping{$shortname}) ))
2149			{
2150				if ( ! $file_error_occured )
2151				{
2152					$errorstring = "\nErrors in $idtfile: \n";
2153					push(@errorlines, $errorstring);
2154				}
2155				$errorstring = "Duplicate savedrev83mapping{" . "$shortname}\n";
2156				push(@errorlines, $errorstring);
2157				$error_occured = 1;
2158				$file_error_occured = 1;
2159			}
2160
2161			$installer::globals::savedmapping{"$2/$5"} = "$1;$shortname";
2162			$installer::globals::savedrevmapping{lc($1)} = "$2/$5";
2163			$installer::globals::savedrev83mapping{$shortname} = "$2/$5" if $shortname ne '';
2164			$n++;
2165		}
2166
2167		close (F);
2168
2169        $installer::logger::Global->printf("Read %s old file table key or 8.3 name mappings from %s\n",
2170            $n, $idtfile);
2171
2172		# Reading Director.idt
2173
2174		$idtfile = $idtdir . $installer::globals::separator . "Director.idt";
2175        $installer::logger::Global->print("\n");
2176        $installer::logger::Global->printf("Analyzing file %s\n", $idtfile);
2177		if ( ! -f $idtfile )
2178        {
2179            $installer::logger::Global->printf("Warning: File %s does not exist!\n", $idtfile);
2180        }
2181
2182		$n = 0;
2183		open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings");
2184		<F>; <F>; <F>;
2185		while (<F>)
2186		{
2187			m/^([^\t]+)\t([^\t]+)\t(([^~]+~\d.*)\|)?([^\t]*)/;
2188			next if (!defined($3));
2189			my $lc1 = lc($1);
2190
2191			print "OUT2: \$1: $1, \$2: $2, \$3: $3\n";
2192
2193			if ( exists($installer::globals::saved83dirmapping{$1}) )
2194			{
2195				if ( ! $dir_error_occured )
2196				{
2197					$errorstring = "\nErrors in $idtfile: \n";
2198					push(@errorlines, $errorstring);
2199				}
2200				$errorstring = "Duplicate saved83dirmapping{" . "$1}\n";
2201				push(@errorlines, $errorstring);
2202				$error_occured = 1;
2203				$dir_error_occured = 1;
2204			}
2205
2206			$installer::globals::saved83dirmapping{$1} = $4;
2207			$n++;
2208		}
2209		close (F);
2210
2211        $installer::logger::Global->printf("Read %s old directory 8.3 name mappings from %s\n",
2212            $n, $idtfile);
2213
2214		# Analyzing errors
2215
2216		if ( $error_occured )
2217		{
2218			for my $line (@errorlines)
2219			{
2220                $installer::logger::Info->print($line);
2221                $installer::logger::Global->print($line);
2222			}
2223			installer::exiter::exit_program("ERROR: Duplicate entries in saved mappings!", "read_saved_mappings");
2224		}
2225	} else {
2226		installer::exiter::exit_program("ERROR: Windows patch shall be prepared, but environment variable PREVIOUS_IDT_DIR is not set!", "read_saved_mappings");
2227	}
2228
2229	$installer::logger::Lang->add_timestamp("Performance Info: Reading saved mappings end");
2230}
2231
22321;
2233
2234