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