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