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