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