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# Calculating a ID with a string using md5 (very fast).
1531#################################################################
1532
1533sub calculate_id
1534{
1535	my ( $string, $length ) = @_;
1536
1537	my $id = "";
1538
1539    my $md5 = Digest::MD5->new;
1540    $md5->add($string);
1541    my $digest = lc($md5->hexdigest);
1542	$id = substr($digest, 0, $length);
1543
1544	return $id;
1545}
1546
1547#################################################################
1548# Filling the component hash with the values of the
1549# component file.
1550#################################################################
1551
1552sub fill_component_hash
1553{
1554	my ($componentfile) = @_;
1555
1556	my %components = ();
1557
1558	for ( my $i = 0; $i <= $#{$componentfile}; $i++ )
1559	{
1560		my $line = ${$componentfile}[$i];
1561
1562		if ( $line =~ /^\s*(.*?)\t(.*?)\s*$/ )
1563		{
1564			my $key = $1;
1565			my $value = $2;
1566
1567			$components{$key} = $value;
1568		}
1569	}
1570
1571	return \%components;
1572}
1573
1574#################################################################
1575# Creating a new component file, if new guids were generated.
1576#################################################################
1577
1578sub create_new_component_file
1579{
1580	my ($componenthash) = @_;
1581
1582	my @componentfile = ();
1583
1584	my $key;
1585
1586	foreach $key (keys %{$componenthash})
1587	{
1588		my $value = $componenthash->{$key};
1589		my $input = "$key\t$value\n";
1590		push(@componentfile ,$input);
1591	}
1592
1593	return \@componentfile;
1594}
1595
1596#################################################################
1597# Filling real component GUID into the component table.
1598# This works only on Windows
1599#################################################################
1600
1601sub set_uuid_into_component_table
1602{
1603	my ($idtdirbase, $allvariables) = @_;
1604
1605	my $componenttablename  = $idtdirbase . $installer::globals::separator . "Componen.idt";
1606
1607	my $componenttable = installer::files::read_file($componenttablename);
1608
1609	# For update and patch reasons (small update) the GUID of an existing component must not change!
1610	# The collection of component GUIDs is saved in the directory $installer::globals::idttemplatepath in the file "components.txt"
1611
1612	my $infoline = "";
1613	my $counter = 0;
1614	# my $componentfile = installer::files::read_file($installer::globals::componentfilename);
1615	# my $componenthash = fill_component_hash($componentfile);
1616
1617	for ( my $i = 3; $i <= $#{$componenttable}; $i++ )	# ignoring the first three lines
1618	{
1619		my $oneline = ${$componenttable}[$i];
1620		my $componentname = "";
1621		if ( $oneline =~ /^\s*(\S+?)\t/ ) { $componentname = $1; }
1622
1623		my $uuid = "";
1624
1625	#	if ( $componenthash->{$componentname} )
1626	#	{
1627	#		$uuid = $componenthash->{$componentname};
1628	#	}
1629	#	else
1630	#	{
1631
1632			if ( exists($installer::globals::calculated_component_guids{$componentname}))
1633			{
1634				$uuid = $installer::globals::calculated_component_guids{$componentname};
1635			}
1636			else
1637			{
1638				# Calculating new GUID with the help of the component name.
1639				my $useooobaseversion = 1;
1640				if ( exists($installer::globals::base_independent_components{$componentname})) { $useooobaseversion = 0; }
1641				my $sourcestring = $componentname;
1642
1643				if ( $useooobaseversion )
1644				{
1645					if ( ! exists($allvariables->{'OOOBASEVERSION'}) ) { installer::exiter::exit_program("ERROR: Could not find variable \"OOOBASEVERSION\" (required value for GUID creation)!", "set_uuid_into_component_table"); }
1646					$sourcestring = $sourcestring . "_" . $allvariables->{'OOOBASEVERSION'};
1647				}
1648				$uuid = calculate_guid($sourcestring);
1649				$counter++;
1650
1651				# checking, if there is a conflict with an already created guid
1652				if ( exists($installer::globals::allcalculated_guids{$uuid}) ) { installer::exiter::exit_program("ERROR: \"$uuid\" was already created before!", "set_uuid_into_component_table"); }
1653				$installer::globals::allcalculated_guids{$uuid} = 1;
1654				$installer::globals::calculated_component_guids{$componentname} = $uuid;
1655
1656				# Setting new uuid
1657				# $componenthash->{$componentname} = $uuid;
1658
1659				# Setting flag
1660				# $installer::globals::created_new_component_guid = 1;	# this is very important!
1661			}
1662	#	}
1663
1664		${$componenttable}[$i] =~ s/COMPONENTGUID/$uuid/;
1665	}
1666
1667	installer::files::save_file($componenttablename, $componenttable);
1668
1669#	if ( $installer::globals::created_new_component_guid )
1670#	{
1671#		# create new component file!
1672#		$componentfile = create_new_component_file($componenthash);
1673#		installer::worker::sort_array($componentfile);
1674#
1675#		# To avoid conflict the components file cannot be saved at the same place
1676#		# All important data have to be saved in the directory: $installer::globals::infodirectory
1677#		my $localcomponentfilename = $installer::globals::componentfilename;
1678#		installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$localcomponentfilename);
1679#		$localcomponentfilename = $installer::globals::infodirectory . $installer::globals::separator . $localcomponentfilename;
1680#		installer::files::save_file($localcomponentfilename, $componentfile);
1681#
1682#		# installer::files::save_file($installer::globals::componentfilename, $componentfile);	# version using new file in solver
1683#
1684#		$infoline = "COMPONENTCODES: Created $counter new GUIDs for components ! \n";
1685#		push( @installer::globals::logfileinfo, $infoline);
1686#	}
1687#	else
1688#	{
1689#		$infoline = "SUCCESS COMPONENTCODES: All component codes exist! \n";
1690#		push( @installer::globals::logfileinfo, $infoline);
1691#	}
1692
1693}
1694
1695#########################################################################
1696# Adding final 64 properties into msi database, if required.
1697# RegLocator : +16 in type column to search in 64 bit registry.
1698# All conditions: "VersionNT" -> "VersionNT64" (several tables).
1699# Already done: "+256" in Attributes column of table "Component".
1700# Still following: Setting "x64" instead of "Intel" in Summary
1701# Information Stream of msi database in "get_template_for_sis".
1702#########################################################################
1703
1704sub prepare_64bit_database
1705{
1706	my ($basedir, $allvariables) = @_;
1707
1708	my $infoline = "";
1709
1710	if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 ))
1711	{
1712		# 1. Beginning with table "RegLocat.idt". Adding "16" to the type.
1713
1714		my $reglocatfile = "";
1715		my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
1716
1717		if ( -f $reglocatfilename )
1718		{
1719			my $saving_required = 0;
1720			$reglocatfile = installer::files::read_file($reglocatfilename);
1721
1722			for ( my $i = 3; $i <= $#{$reglocatfile}; $i++ ) 	# ignoring the first three lines
1723			{
1724				my $oneline = ${$reglocatfile}[$i];
1725
1726				if ( $oneline =~ /^\s*\#/ ) { next; }	# this is a comment line
1727				if ( $oneline =~ /^\s*$/ ) { next; }
1728
1729				if ( $oneline =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(\d+)\s*$/ )
1730				{
1731					# Syntax: Signature_ Root Key Name Type
1732					my $sig = $1;
1733					my $root = $2;
1734					my $key = $3;
1735					my $name = $4;
1736					my $type = $5;
1737
1738					$type = $type + 16;
1739
1740					my $newline = $sig . "\t" . $root . "\t" . $key . "\t" . $name . "\t" . $type . "\n";
1741					${$reglocatfile}[$i] = $newline;
1742
1743					$saving_required = 1;
1744				}
1745			}
1746
1747			if ( $saving_required )
1748			{
1749				# Saving the files
1750				installer::files::save_file($reglocatfilename ,$reglocatfile);
1751				$infoline = "Making idt file 64 bit conform: $reglocatfilename\n";
1752				push(@installer::globals::logfileinfo, $infoline);
1753			}
1754		}
1755
1756		# 2. Replacing all occurences of "VersionNT" by "VersionNT64"
1757
1758		my @versionnt_files = ("Componen.idt", "InstallE.idt", "InstallU.idt", "LaunchCo.idt");
1759
1760		foreach my $onefile ( @versionnt_files )
1761		{
1762			my $fullfilename = $basedir . $installer::globals::separator . $onefile;
1763
1764			if ( -f $fullfilename )
1765			{
1766				my $saving_required = 0;
1767				$filecontent = installer::files::read_file($fullfilename);
1768
1769				for ( my $i = 3; $i <= $#{$filecontent}; $i++ ) 	# ignoring the first three lines
1770				{
1771					my $oneline = ${$filecontent}[$i];
1772
1773					if ( $oneline =~ /\bVersionNT\b/ )
1774					{
1775						${$filecontent}[$i] =~ s/\bVersionNT\b/VersionNT64/g;
1776						$saving_required = 1;
1777					}
1778				}
1779
1780				if ( $saving_required )
1781				{
1782					# Saving the files
1783					installer::files::save_file($fullfilename ,$filecontent);
1784					$infoline = "Making idt file 64 bit conform: $fullfilename\n";
1785					push(@installer::globals::logfileinfo, $infoline);
1786				}
1787			}
1788		}
1789	}
1790
1791}
1792
1793#################################################################
1794# Include all cab files into the msi database.
1795# This works only on Windows
1796#################################################################
1797
1798sub include_cabs_into_msi
1799{
1800	my ($installdir) = @_;
1801
1802	installer::logger::include_header_into_logfile("Including cabs into msi database");
1803
1804	my $from = cwd();
1805	my $to = $installdir;
1806
1807	chdir($to);
1808
1809	my $infoline = "Changing into directory: $to";
1810	push( @installer::globals::logfileinfo, $infoline);
1811
1812	my $msidb = "msidb.exe";	# Has to be in the path
1813	my $extraslash = "";		# Has to be set for non-ActiveState perl
1814
1815	my $msifilename = $installer::globals::msidatabasename;
1816
1817	$msifilename = installer::converter::make_path_conform($msifilename);
1818
1819	# msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
1820	$msifilename =~ s/\//\\\\/g;
1821	$extraslash = "\\";
1822
1823	my $allcabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir);
1824
1825	for ( my $i = 0; $i <= $#{$allcabfiles}; $i++ )
1826	{
1827		my $systemcall = $msidb . " -d " . $msifilename . " -a " . ${$allcabfiles}[$i];
1828
1829		my $returnvalue = system($systemcall);
1830
1831		$infoline = "Systemcall: $systemcall\n";
1832		push( @installer::globals::logfileinfo, $infoline);
1833
1834		if ($returnvalue)
1835		{
1836			$infoline = "ERROR: Could not execute $systemcall !\n";
1837			push( @installer::globals::logfileinfo, $infoline);
1838		}
1839		else
1840		{
1841			$infoline = "Success: Executed $systemcall successfully!\n";
1842			push( @installer::globals::logfileinfo, $infoline);
1843		}
1844
1845		# deleting the cab file
1846
1847		unlink(${$allcabfiles}[$i]);
1848
1849		$infoline = "Deleted cab file: ${$allcabfiles}[$i]\n";
1850		push( @installer::globals::logfileinfo, $infoline);
1851	}
1852
1853	$infoline = "Changing back into directory: $from";
1854	push( @installer::globals::logfileinfo, $infoline);
1855
1856	chdir($from);
1857}
1858
1859#################################################################
1860# Executing the created batch file to pack all files.
1861# This works only on Windows
1862#################################################################
1863
1864sub execute_packaging
1865{
1866	my ($localpackjobref, $loggingdir, $allvariables) = @_;
1867
1868	installer::logger::include_header_into_logfile("Packaging process");
1869
1870	installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging start");
1871
1872	my $infoline = "";
1873	my $from = cwd();
1874	my $to = $loggingdir;
1875
1876	chdir($to);
1877	$infoline = "chdir: $to \n";
1878	push( @installer::globals::logfileinfo, $infoline);
1879
1880	# if the ddf file contains relative pathes, it is necessary to change into the temp directory
1881	if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} )
1882	{
1883		$to = $installer::globals::temppath;
1884		chdir($to);
1885		$infoline = "chdir: $to \n";
1886		push( @installer::globals::logfileinfo, $infoline);
1887	}
1888
1889	# changing the tmp directory, because makecab.exe generates temporary cab files
1890	my $origtemppath = "";
1891	if ( $ENV{'TMP'} ) { $origtemppath = $ENV{'TMP'}; }
1892	$ENV{'TMP'} = $installer::globals::temppath;	# setting TMP to the new unique directory!
1893
1894	my $maxmakecabcalls = 3;
1895	my $allmakecabcalls = $#{$localpackjobref} + 1;
1896
1897	for ( my $i = 0; $i <= $#{$localpackjobref}; $i++ )
1898	{
1899		my $systemcall = ${$localpackjobref}[$i];
1900
1901		my $callscounter = $i + 1;
1902
1903		installer::logger::print_message( "... makecab.exe ($callscounter/$allmakecabcalls) ... \n" );
1904
1905		# my $returnvalue = system($systemcall);
1906
1907		for ( my $n = 1; $n <= $maxmakecabcalls; $n++ )
1908		{
1909			my @ddfoutput = ();
1910
1911			$infoline = "Systemcall: $systemcall";
1912			push( @installer::globals::logfileinfo, $infoline);
1913
1914			open (DDF, "$systemcall");
1915			while (<DDF>) {push(@ddfoutput, $_); }
1916			close (DDF);
1917
1918			my $returnvalue = $?;	# $? contains the return value of the systemcall
1919
1920			if ($returnvalue)
1921			{
1922				if ( $n < $maxmakecabcalls )
1923				{
1924					installer::logger::print_message( "makecab_error (Try $n): Trying again \n" );
1925					$infoline = "makecab_error (Try $n): $systemcall !";
1926				}
1927				else
1928				{
1929					installer::logger::print_message( "ERROR (Try $n): Abort packing \n" );
1930					$infoline = "ERROR (Try $n): $systemcall !";
1931				}
1932
1933				push( @installer::globals::logfileinfo, $infoline);
1934				# for ( my $j = 0; $j <= $#ddfoutput; $j++ ) { push( @installer::globals::logfileinfo, "$ddfoutput[$j]"); }
1935
1936				for ( my $m = 0; $m <= $#ddfoutput; $m++ )
1937				{
1938					if ( $ddfoutput[$m] =~ /(ERROR\:.*?)\s*$/ )
1939					{
1940						$infoline = $1 . "\n";
1941						if ( $n < $maxmakecabcalls ) { $infoline =~ s/ERROR\:/makecab_error\:/i; }
1942						installer::logger::print_message( $infoline );
1943						push( @installer::globals::logfileinfo, $infoline);
1944					}
1945				}
1946
1947				if ( $n == $maxmakecabcalls ) { installer::exiter::exit_program("ERROR: \"$systemcall\"!", "execute_packaging"); }
1948			}
1949			else
1950			{
1951				# installer::logger::print_message( "Success (Try $n): \"$systemcall\"\n" );
1952				$infoline = "Success (Try $n): $systemcall";
1953				push( @installer::globals::logfileinfo, $infoline);
1954				last;
1955			}
1956		}
1957	}
1958
1959	installer::logger::include_timestamp_into_logfile("Performance Info: Execute packaging end");
1960
1961	# setting back to the original tmp directory
1962	$ENV{'TMP'} = $origtemppath;
1963
1964	chdir($from);
1965	$infoline = "chdir: $from \n";
1966	push( @installer::globals::logfileinfo, $infoline);
1967}
1968
1969###############################################################
1970# Setting the global variables ProductCode and the UpgradeCode
1971###############################################################
1972
1973sub set_global_code_variables
1974{
1975	my ( $languagesref, $languagestringref, $allvariableshashref, $alloldproperties ) = @_;
1976
1977	# In the msi template directory a files "codes.txt" has to exist, in which the ProductCode
1978	# and the UpgradeCode for the product are defined.
1979	# The name "codes.txt" can be overwritten in Product definition with CODEFILENAME .
1980	# Default $installer::globals::codefilename is defined in parameter.pm.
1981
1982	if ( $allvariableshashref->{'CODEFILENAME'} )
1983	{
1984		$installer::globals::codefilename = $installer::globals::idttemplatepath  . $installer::globals::separator . $allvariableshashref->{'CODEFILENAME'};
1985		installer::files::check_file($installer::globals::codefilename);
1986	}
1987
1988	my $infoline = "Using Codes file: $installer::globals::codefilename \n";
1989	push( @installer::globals::logfileinfo, $infoline);
1990
1991	my $codefile = installer::files::read_file($installer::globals::codefilename);
1992
1993	my $isopensource = 0;
1994	if ( $allvariableshashref->{'OPENSOURCE'} ) { $isopensource = $allvariableshashref->{'OPENSOURCE'}; }
1995
1996	my $onelanguage = "";
1997
1998	if ( $#{$languagesref} > 0 )	# more than one language
1999	{
2000		if (( $installer::globals::added_english ) && ( $#{$languagesref} == 1 )) # only multilingual because of added English
2001		{
2002			$onelanguage = ${$languagesref}[1];  # setting the first language, that is not english
2003		}
2004		else
2005		{
2006			if (( ${$languagesref}[1] =~ /jp/ ) ||
2007				( ${$languagesref}[1] =~ /ko/ ) ||
2008				( ${$languagesref}[1] =~ /zh/ ))
2009			{
2010				$onelanguage = "multiasia";
2011			}
2012			else
2013			{
2014				$onelanguage = "multiwestern";
2015			}
2016		}
2017	}
2018	else	# only one language
2019	{
2020		$onelanguage = ${$languagesref}[0];
2021	}
2022
2023	# ProductCode must not change, if Windows patches shall be applied
2024	if ( $installer::globals::updatedatabase )
2025	{
2026		$installer::globals::productcode = $alloldproperties->{'ProductCode'};
2027	}
2028	elsif ( $installer::globals::prepare_winpatch )
2029	{
2030		# ProductCode has to be specified in each language
2031		my $searchstring = "PRODUCTCODE";
2032		my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile);
2033		$installer::globals::productcode = installer::windows::idtglobal::get_code_from_code_block($codeblock, $onelanguage);
2034	} else {
2035		my $guidref = get_guid_list(1, 1);	# only one GUID shall be generated
2036		${$guidref}[0] =~ s/\s*$//;		# removing ending spaces
2037		$installer::globals::productcode = "\{" . ${$guidref}[0] . "\}";
2038	}
2039
2040	if ( $installer::globals::patch ) # patch upgrade codes are defined in soffice.lst
2041	{
2042		if ( $allvariableshashref->{'PATCHUPGRADECODE'} ) { $installer::globals::upgradecode = $allvariableshashref->{'PATCHUPGRADECODE'}; }
2043		else { installer::exiter::exit_program("ERROR: PATCHUPGRADECODE not defined in list file!", "set_global_code_variables"); }
2044	}
2045	else
2046	{
2047		# UpgradeCode can take english as default, if not defined in specified language
2048
2049		$searchstring = "UPGRADECODE";	# searching in the codes.txt file
2050		$codeblock = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $codefile);
2051		$installer::globals::upgradecode = installer::windows::idtglobal::get_language_string_from_language_block($codeblock, $onelanguage, "");
2052	}
2053
2054	# 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"); }
2055	if ( $installer::globals::upgradecode eq "" ) { installer::exiter::exit_program("ERROR: UpgradeCode not defined in $installer::globals::codefilename !", "set_global_code_variables"); }
2056
2057	$infoline = "Setting ProductCode to: $installer::globals::productcode \n";
2058	push( @installer::globals::logfileinfo, $infoline);
2059	$infoline = "Setting UpgradeCode to: $installer::globals::upgradecode \n";
2060	push( @installer::globals::logfileinfo, $infoline);
2061
2062	# Adding both variables into the variables array
2063
2064	$allvariableshashref->{'PRODUCTCODE'} = $installer::globals::productcode;
2065	$allvariableshashref->{'UPGRADECODE'} = $installer::globals::upgradecode;
2066
2067	$infoline = "Defined variable PRODUCTCODE: $installer::globals::productcode \n";
2068	push( @installer::globals::logfileinfo, $infoline);
2069
2070	$infoline = "Defined variable UPGRADECODE: $installer::globals::upgradecode \n";
2071	push( @installer::globals::logfileinfo, $infoline);
2072
2073}
2074
2075###############################################################
2076# Setting the product version used in property table and
2077# upgrade table. Saving in global variable $msiproductversion
2078###############################################################
2079
2080sub set_msiproductversion
2081{
2082	my ( $allvariables ) = @_;
2083
2084	my $productversion = $allvariables->{'PRODUCTVERSION'};
2085
2086	if (( $productversion =~ /^\s*\d+\s*$/ ) && ( $productversion > 255 )) { $productversion = $productversion%256; }
2087
2088	if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
2089	{
2090		$productversion = $1 . "\." . $2 . $3 . "\." . $installer::globals::buildid;
2091	}
2092	elsif  ( $productversion =~ /^\s*(\d+)\.(\d+)\s*$/ )
2093	{
2094		$productversion = $1 . "\." . $2 . "\." . $installer::globals::buildid;
2095	}
2096	else
2097	{
2098		my $productminor = "00";
2099		if (( $allvariables->{'PACKAGEVERSION'} ) && ( $allvariables->{'PACKAGEVERSION'} ne "" ))
2100		{
2101			if ( $allvariables->{'PACKAGEVERSION'} =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) { $productminor = $2; }
2102		}
2103
2104		$productversion = $productversion . "\." . $productminor . "\." . $installer::globals::buildid;
2105	}
2106
2107	$installer::globals::msiproductversion = $productversion;
2108
2109	# Setting $installer::globals::msimajorproductversion, to differ between old version in upgrade table
2110
2111	if ( $installer::globals::msiproductversion =~ /^\s*(\d+)\./ )
2112	{
2113		my $major = $1;
2114		$installer::globals::msimajorproductversion = $major . "\.0\.0";
2115	}
2116}
2117
2118#################################################################################
2119# Including the msi product version into the bootstrap.ini, Windows only
2120#################################################################################
2121
2122sub put_msiproductversion_into_bootstrapfile
2123{
2124	my ($filesref) = @_;
2125
2126	for ( my $i = 0; $i <= $#{$filesref}; $i++ )
2127	{
2128		my $onefile = ${$filesref}[$i];
2129
2130		if ( $onefile->{'gid'} eq "gid_Profile_Version_Ini" )
2131		{
2132			my $file = installer::files::read_file($onefile->{'sourcepath'});
2133
2134			for ( my $j = 0; $j <= $#{$file}; $j++ )
2135			{
2136				${$file}[$j] =~ s/\<msiproductversion\>/$installer::globals::msiproductversion/;
2137			}
2138
2139			installer::files::save_file($onefile->{'sourcepath'}, $file);
2140
2141			last;
2142		}
2143	}
2144}
2145
2146####################################################################################
2147# Updating the file Property.idt dynamically
2148# Content:
2149# Property Value
2150####################################################################################
2151
2152sub update_reglocat_table
2153{
2154	my ($basedir, $allvariables) = @_;
2155
2156	my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
2157
2158	# Only do something, if this file exists
2159
2160	if ( -f $reglocatfilename )
2161	{
2162		my $reglocatfile = installer::files::read_file($reglocatfilename);
2163
2164		my $layername = "";
2165		if ( $allvariables->{'REGISTRYLAYERNAME'} )
2166		{
2167			$layername = $allvariables->{'REGISTRYLAYERNAME'};
2168		}
2169		else
2170		{
2171			for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
2172			{
2173				if ( ${$reglocatfile}[$i] =~ /\bLAYERNAMETEMPLATE\b/ )
2174				{
2175					installer::exiter::exit_program("ERROR: Variable \"REGISTRYLAYERNAME\" has to be defined", "update_reglocat_table");
2176				}
2177			}
2178		}
2179
2180		if ( $layername ne "" )
2181		{
2182			# Updating the layername in
2183
2184			for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
2185			{
2186				${$reglocatfile}[$i] =~ s/\bLAYERNAMETEMPLATE\b/$layername/;
2187			}
2188
2189			# Saving the file
2190			installer::files::save_file($reglocatfilename ,$reglocatfile);
2191			my $infoline = "Updated idt file: $reglocatfilename\n";
2192			push(@installer::globals::logfileinfo, $infoline);
2193		}
2194	}
2195}
2196
2197
2198
2199####################################################################################
2200# Updating the file RemoveRe.idt dynamically (RemoveRegistry.idt)
2201# The name of the component has to be replaced.
2202####################################################################################
2203
2204sub update_removere_table
2205{
2206	my ($basedir) = @_;
2207
2208	my $removeregistryfilename = $basedir . $installer::globals::separator . "RemoveRe.idt";
2209
2210	# Only do something, if this file exists
2211
2212	if ( -f $removeregistryfilename )
2213	{
2214		my $removeregistryfile = installer::files::read_file($removeregistryfilename);
2215
2216		for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
2217		{
2218			for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
2219			{
2220				${$removeregistryfile}[$i] =~ s/\bREGISTRYROOTCOMPONENT\b/$installer::globals::registryrootcomponent/;
2221			}
2222		}
2223
2224		# Saving the file
2225		installer::files::save_file($removeregistryfilename ,$removeregistryfile);
2226		my $infoline = "Updated idt file: $removeregistryfilename \n";
2227		push(@installer::globals::logfileinfo, $infoline);
2228	}
2229}
2230
2231##########################################################################
2232# Reading saved mappings in Files.idt and Director.idt.
2233# This is required, if installation sets shall be created,
2234# that can be used for creation of msp files.
2235##########################################################################
2236
2237sub read_saved_mappings
2238{
2239	installer::logger::include_header_into_logfile("Reading saved mappings from older installation sets:");
2240
2241	installer::logger::include_timestamp_into_logfile("Performance Info: Reading saved mappings start");
2242
2243	if ( $installer::globals::previous_idt_dir )
2244	{
2245		my @errorlines = ();
2246		my $errorstring = "";
2247		my $error_occured = 0;
2248		my $file_error_occured = 0;
2249		my $dir_error = 0;
2250
2251		my $idtdir = $installer::globals::previous_idt_dir;
2252		$idtdir =~ s/\Q$installer::globals::separator\E\s*$//;
2253
2254		# Reading File.idt
2255
2256		my $idtfile = $idtdir . $installer::globals::separator . "File.idt";
2257		push( @installer::globals::globallogfileinfo, "\nAnalyzing file: $idtfile\n" );
2258		if ( ! -f $idtfile ) { push( @installer::globals::globallogfileinfo, "Warning: File $idtfile does not exist!\n" ); }
2259
2260		my $n = 0;
2261		open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings");
2262		<F>; <F>; <F>;
2263		while (<F>)
2264		{
2265			m/^([^\t]+)\t([^\t]+)\t((.*)\|)?([^\t]*)/;
2266			print "OUT1: \$1: $1, \$2: $2, \$3: $3, \$4: $4, \$5: $5\n";
2267			next if ("$1" eq "$5") && (!defined($3));
2268			my $lc1 = lc($1);
2269
2270			if ( exists($installer::globals::savedmapping{"$2/$5"}))
2271			{
2272				if ( ! $file_error_occured )
2273				{
2274					$errorstring = "\nErrors in $idtfile: \n";
2275					push(@errorlines, $errorstring);
2276				}
2277				$errorstring = "Duplicate savedmapping{" . "$2/$5}\n";
2278				push(@errorlines, $errorstring);
2279				$error_occured = 1;
2280				$file_error_occured = 1;
2281			}
2282
2283			if ( exists($installer::globals::savedrevmapping{$lc1}))
2284			{
2285				if ( ! $file_error_occured )
2286				{
2287					$errorstring = "\nErrors in $idtfile: \n";
2288					push(@errorlines, $errorstring);
2289				}
2290				$errorstring = "Duplicate savedrevmapping{" . "$lc1}\n";
2291				push(@errorlines, $errorstring);
2292				$error_occured = 1;
2293				$file_error_occured = 1;
2294			}
2295
2296			my $shortname = $4 || '';
2297
2298			# Don't reuse illegal 8.3 mappings that we used to generate in 2.0.4
2299			if (index($shortname, '.') > 8 ||
2300			    (index($shortname, '.') == -1 && length($shortname) > 8))
2301			{
2302			    $shortname = '';
2303			}
2304
2305			if (( $shortname ne '' ) && ( index($shortname, '~') > 0 ) && ( exists($installer::globals::savedrev83mapping{$shortname}) ))
2306			{
2307				if ( ! $file_error_occured )
2308				{
2309					$errorstring = "\nErrors in $idtfile: \n";
2310					push(@errorlines, $errorstring);
2311				}
2312				$errorstring = "Duplicate savedrev83mapping{" . "$shortname}\n";
2313				push(@errorlines, $errorstring);
2314				$error_occured = 1;
2315				$file_error_occured = 1;
2316			}
2317
2318			$installer::globals::savedmapping{"$2/$5"} = "$1;$shortname";
2319			$installer::globals::savedrevmapping{lc($1)} = "$2/$5";
2320			$installer::globals::savedrev83mapping{$shortname} = "$2/$5" if $shortname ne '';
2321			$n++;
2322		}
2323
2324		close (F);
2325
2326		push( @installer::globals::globallogfileinfo, "Read $n old file table key or 8.3 name mappings from $idtfile\n" );
2327
2328		# Reading Director.idt
2329
2330		$idtfile = $idtdir . $installer::globals::separator . "Director.idt";
2331		push( @installer::globals::globallogfileinfo, "\nAnalyzing file $idtfile\n" );
2332		if ( ! -f $idtfile ) { push( @installer::globals::globallogfileinfo, "Warning: File $idtfile does not exist!\n" ); }
2333
2334		$n = 0;
2335		open (F, "<$idtfile") || installer::exiter::exit_program("ERROR: Cannot open file $idtfile for reading", "read_saved_mappings");
2336		<F>; <F>; <F>;
2337		while (<F>)
2338		{
2339			m/^([^\t]+)\t([^\t]+)\t(([^~]+~\d.*)\|)?([^\t]*)/;
2340			next if (!defined($3));
2341			my $lc1 = lc($1);
2342
2343			print "OUT2: \$1: $1, \$2: $2, \$3: $3\n";
2344
2345			if ( exists($installer::globals::saved83dirmapping{$1}) )
2346			{
2347				if ( ! $dir_error_occured )
2348				{
2349					$errorstring = "\nErrors in $idtfile: \n";
2350					push(@errorlines, $errorstring);
2351				}
2352				$errorstring = "Duplicate saved83dirmapping{" . "$1}\n";
2353				push(@errorlines, $errorstring);
2354				$error_occured = 1;
2355				$dir_error_occured = 1;
2356			}
2357
2358			$installer::globals::saved83dirmapping{$1} = $4;
2359			$n++;
2360		}
2361		close (F);
2362
2363		push( @installer::globals::globallogfileinfo, "Read $n old directory 8.3 name mappings from $idtfile\n" );
2364
2365		# Analyzing errors
2366
2367		if ( $error_occured )
2368		{
2369			for ( my $i = 0; $i <= $#errorlines; $i++ )
2370			{
2371				print "$errorlines[$i]";
2372				push( @installer::globals::globallogfileinfo, "$errorlines[$i]");
2373			}
2374			installer::exiter::exit_program("ERROR: Duplicate entries in saved mappings!", "read_saved_mappings");
2375		}
2376	} else {
2377		# push( @installer::globals::globallogfileinfo, "WARNING: Windows patch shall be prepared, but PREVIOUS_IDT_DIR is not set!\n" );
2378		installer::exiter::exit_program("ERROR: Windows patch shall be prepared, but environment variable PREVIOUS_IDT_DIR is not set!", "read_saved_mappings");
2379	}
2380
2381	installer::logger::include_timestamp_into_logfile("Performance Info: Reading saved mappings end");
2382}
2383
23841;
2385
2386