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;
40use installer::patch::ReleasesList;
41
42use strict;
43
44###########################################################################
45# Generating the header of the ddf file.
46# The usage of ddf files is needed, because makecab.exe can only include
47# one sourcefile into a cab file
48###########################################################################
49
50sub write_ddf_file_header
51{
52	my ($ddffileref, $cabinetfile, $installdir) = @_;
53
54	my $oneline;
55
56	$oneline = ".Set CabinetName1=" . $cabinetfile . "\n";
57	push(@{$ddffileref} ,$oneline);
58	$oneline = ".Set ReservePerCabinetSize=128\n";	# This reserves space for a digital signature.
59	push(@{$ddffileref} ,$oneline);
60	$oneline = ".Set MaxDiskSize=2147483648\n";		# This allows the .cab file to get a size of 2 GB.
61	push(@{$ddffileref} ,$oneline);
62	$oneline = ".Set CompressionType=LZX\n";
63	push(@{$ddffileref} ,$oneline);
64	$oneline = ".Set Compress=ON\n";
65	push(@{$ddffileref} ,$oneline);
66	$oneline = ".Set CompressionLevel=$installer::globals::cabfilecompressionlevel\n";
67	push(@{$ddffileref} ,$oneline);
68	$oneline = ".Set Cabinet=ON\n";
69	push(@{$ddffileref} ,$oneline);
70	$oneline = ".Set DiskDirectoryTemplate=" . $installdir . "\n";
71	push(@{$ddffileref} ,$oneline);
72}
73
74##########################################################################
75# Lines in ddf files must not contain more than 256 characters
76##########################################################################
77
78sub check_ddf_file
79{
80	my ( $ddffile, $ddffilename ) = @_;
81
82	my $maxlength = 0;
83	my $maxline = 0;
84	my $linelength = 0;
85	my $linenumber = 0;
86
87	for ( my $i = 0; $i <= $#{$ddffile}; $i++ )
88	{
89		my $oneline = ${$ddffile}[$i];
90
91		$linelength = length($oneline);
92		$linenumber = $i + 1;
93
94		if ( $linelength > 256 )
95		{
96			installer::exiter::exit_program("ERROR \"$ddffilename\" line $linenumber: Lines in ddf files must not contain more than 256 characters!", "check_ddf_file");
97		}
98
99		if ( $linelength > $maxlength )
100		{
101			$maxlength = $linelength;
102			$maxline = $linenumber;
103		}
104	}
105
106	my $infoline = "Check of ddf file \"$ddffilename\": Maximum length \"$maxlength\" in line \"$maxline\" (allowed line length: 256 characters)\n";
107	$installer::logger::Lang->print($infoline);
108}
109
110##########################################################################
111# Lines in ddf files must not be longer than 256 characters.
112# Therefore it can be useful to use relative pathes. Then it is
113# necessary to change into temp directory before calling
114# makecab.exe.
115##########################################################################
116
117sub make_relative_ddf_path
118{
119	my ( $sourcepath ) = @_;
120
121	my $windowstemppath = $installer::globals::temppath;
122
123	if ( $^O =~ /cygwin/i )
124	{
125		$windowstemppath = $installer::globals::cyg_temppath;
126	}
127
128	$sourcepath =~ s/\Q$windowstemppath\E//;
129	$sourcepath =~ s/^\\//;
130
131	return $sourcepath;
132}
133
134
135##########################################################################
136# Generation the list, in which the source of the files is connected
137# with the cabinet destination file. Because more than one file needs
138# to be included into a cab file, this has to be done via ddf files.
139##########################################################################
140
141sub generate_cab_file_list ($$$$)
142{
143	my ($filesref, $installdir, $ddfdir, $allvariables) = @_;
144
145	installer::logger::include_header_into_logfile("Generating ddf files");
146
147	if ( $^O =~ /cygwin/i )
148    {
149        installer::worker::generate_cygwin_pathes($filesref);
150    }
151
152    # Make sure that all files point to the same cabinet file.
153    # Multiple cabinet files are not supported anymore.
154    my $cabinetfile = $filesref->[0]->{'cabinet'};
155    foreach my $onefile (@$filesref)
156    {
157        if ($onefile->{'cabinet'} ne $cabinetfile)
158        {
159            installer::exiter::exit_program(
160                "ERROR: multiple cabinet files are not supported",
161                "generate_cab_file_list");
162        }
163    }
164
165    # Sort files on the sequence number.
166    my @sorted_files = sort {$a->{'sequencenumber'} <=> $b->{'sequencenumber'}} @$filesref;
167
168    my @ddffile = ();
169    write_ddf_file_header(\@ddffile, $cabinetfile, $installdir);
170    foreach my $onefile (@sorted_files)
171    {
172        my $styles = $onefile->{'Styles'};
173        $styles = "" unless defined $styles;
174        if ($styles =~ /\bDONT_PACK\b/)
175        {
176            $installer::logger::Lang->printf("    excluding '%s' from ddf\n", $onefile->{'uniquename'});
177        }
178
179        my $uniquename = $onefile->{'uniquename'};
180        my $sourcepath = $onefile->{'sourcepath'};
181        if ( $^O =~ /cygwin/i )
182        {
183            $sourcepath = $onefile->{'cyg_sourcepath'};
184        }
185
186        # to avoid lines with more than 256 characters, it can be useful to use relative pathes
187        if ($allvariables->{'RELATIVE_PATHES_IN_DDF'})
188        {
189            $sourcepath = make_relative_ddf_path($sourcepath);
190        }
191
192        my $ddfline = "\"" . $sourcepath . "\"" . " " . $uniquename . "\n";
193        push(@ddffile, $ddfline);
194
195        $installer::logger::Lang->printf("    adding '%s' with sequence %d to ddf\n",
196            $onefile->{'uniquename'},
197            $onefile->{'sequencenumber'});
198    }
199    # creating the DDF file
200
201    my $ddffilename = $cabinetfile;
202    $ddffilename =~ s/.cab/.ddf/;
203    $ddfdir =~ s/\Q$installer::globals::separator\E\s*$//;
204    $ddffilename = $ddfdir . $installer::globals::separator . $ddffilename;
205
206    installer::files::save_file($ddffilename ,\@ddffile);
207    $installer::logger::Lang->print("Created ddf file: %s\n", $ddffilename);
208
209    # lines in ddf files must not be longer than 256 characters
210    check_ddf_file(\@ddffile, $ddffilename);
211
212    # collecting all ddf files
213    push(@installer::globals::allddffiles, $ddffilename);
214
215    # Writing the makecab system call
216    # Return a list with all system calls for packaging process.
217	my @cabfilelist = ("makecab.exe /V3 /F " . $ddffilename . " 2\>\&1 |" . "\n");
218	return \@cabfilelist;
219}
220
221
222
223#################################################################
224# Returning the name of the msi database
225#################################################################
226
227sub get_msidatabasename
228{
229	my ($allvariableshashref, $language) = @_;
230
231	my $databasename = $allvariableshashref->{'PRODUCTNAME'} . $allvariableshashref->{'PRODUCTVERSION'};
232	$databasename = lc($databasename);
233	$databasename =~ s/\.//g;
234	$databasename =~ s/\-//g;
235	$databasename =~ s/\s//g;
236
237	# possibility to overwrite the name with variable DATABASENAME
238	if ( $allvariableshashref->{'DATABASENAME'} )
239	{
240		$databasename = $allvariableshashref->{'DATABASENAME'};
241	}
242
243	if ( $language )
244	{
245		if (!($language eq ""))
246		{
247			$databasename .= "_$language";
248		}
249	}
250
251	$databasename .= ".msi";
252
253	return $databasename;
254}
255
256#################################################################
257# Creating the msi database
258# This works only on Windows
259#################################################################
260
261sub create_msi_database
262{
263	my ($idtdirbase ,$msifilename) = @_;
264
265	# -f : path containing the idt files
266	# -d : msi database, including path
267	# -c : create database
268	# -i : include the following tables ("*" includes all available tables)
269
270	my $msidb = "msidb.exe";	# Has to be in the path
271	my $extraslash = "";		# Has to be set for non-ActiveState perl
272
273	installer::logger::include_header_into_logfile("Creating msi database");
274
275	$idtdirbase = installer::converter::make_path_conform($idtdirbase);
276
277	$msifilename = installer::converter::make_path_conform($msifilename);
278
279	if ( $^O =~ /cygwin/i ) {
280		# msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
281		$idtdirbase =~ s/\//\\\\/g;
282		$msifilename =~ s/\//\\\\/g;
283		$extraslash = "\\";
284	}
285	my $systemcall = $msidb . " -f " . $idtdirbase . " -d " . $msifilename . " -c " . "-i " . $extraslash . "*";
286
287	my $returnvalue = system($systemcall);
288
289	my $infoline = "Systemcall: $systemcall\n";
290	$installer::logger::Lang->print($infoline);
291
292	if ($returnvalue)
293	{
294		$infoline = "ERROR: Could not execute $msidb!\n";
295		$installer::logger::Lang->print($infoline);
296	}
297	else
298	{
299		$infoline = "Success: Executed $msidb successfully!\n";
300		$installer::logger::Lang->print($infoline);
301	}
302}
303
304#####################################################################
305# Returning the value from sis.mlf for Summary Information Stream
306#####################################################################
307
308sub get_value_from_sis_lng
309{
310	my ($language, $languagefile, $searchstring) = @_;
311
312	my $language_block = installer::windows::idtglobal::get_language_block_from_language_file($searchstring, $languagefile);
313	my $newstring = installer::windows::idtglobal::get_language_string_from_language_block($language_block, $language, $searchstring);
314	$newstring = "\"" . $newstring . "\"";
315
316	return $newstring;
317}
318
319#################################################################
320# Returning the msi version for the Summary Information Stream
321#################################################################
322
323sub get_msiversion_for_sis
324{
325	my $msiversion = "200";
326	return $msiversion;
327}
328
329#################################################################
330# Returning the word count for the Summary Information Stream
331#################################################################
332
333sub get_wordcount_for_sis
334{
335	my $wordcount = "0";
336	return $wordcount;
337}
338
339#################################################################
340# Returning the codepage for the Summary Information Stream
341#################################################################
342
343sub get_codepage_for_sis
344{
345	my ( $language ) = @_;
346
347	my $codepage = installer::windows::language::get_windows_encoding($language);
348
349	# Codepage 65001 does not work in Summary Information Stream
350	if ( $codepage == 65001 ) { $codepage = 0; }
351
352	# my $codepage = "1252";	# determine dynamically in a function
353	# my $codepage = "65001";		# UTF-8
354	return $codepage;
355}
356
357#################################################################
358# Returning the template for the Summary Information Stream
359#################################################################
360
361sub get_template_for_sis
362{
363	my ( $language, $allvariables ) = @_;
364
365	my $windowslanguage = installer::windows::language::get_windows_language($language);
366
367	my $architecture = "Intel";
368
369	# Adding 256, if this is a 64 bit installation set.
370	if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 )) { $architecture = "x64"; }
371
372	my $value = "\"" . $architecture . ";" . $windowslanguage;	# adding the Windows language
373
374	$value = $value . "\"";						# adding ending '"'
375
376	return $value ;
377}
378
379#################################################################
380# Returning the PackageCode for the Summary Information Stream
381#################################################################
382
383sub get_packagecode_for_sis
384{
385	# always generating a new package code for each package
386
387	my $guid = "\{" . create_guid() . "\}";
388
389	my $infoline = "PackageCode: $guid\n";
390	$installer::logger::Lang->print($infoline);
391
392	return $guid;
393}
394
395#################################################################
396# Returning the title for the Summary Information Stream
397#################################################################
398
399sub get_title_for_sis
400{
401	my ( $language, $languagefile, $searchstring ) = @_;
402
403	my $title = get_value_from_sis_lng($language, $languagefile, $searchstring );
404
405	return $title;
406}
407
408#################################################################
409# Returning the author for the Summary Information Stream
410#################################################################
411
412sub get_author_for_sis
413{
414	my $author = $installer::globals::longmanufacturer;
415
416	$author = "\"" . $author . "\"";
417
418	return $author;
419}
420
421#################################################################
422# Returning the subject for the Summary Information Stream
423#################################################################
424
425sub get_subject_for_sis
426{
427	my ( $allvariableshashref ) = @_;
428
429	my $subject = $allvariableshashref->{'PRODUCTNAME'} . " " . $allvariableshashref->{'PRODUCTVERSION'};
430
431	$subject = "\"" . $subject . "\"";
432
433	return $subject;
434}
435
436#################################################################
437# Returning the comment for the Summary Information Stream
438#################################################################
439
440sub get_comment_for_sis
441{
442	my ( $language, $languagefile, $searchstring ) = @_;
443
444	my $comment = get_value_from_sis_lng($language, $languagefile, $searchstring );
445
446	return $comment;
447}
448
449#################################################################
450# Returning the keywords for the Summary Information Stream
451#################################################################
452
453sub get_keywords_for_sis
454{
455	my ( $language, $languagefile, $searchstring ) = @_;
456
457	my $keywords = get_value_from_sis_lng($language, $languagefile, $searchstring );
458
459	return $keywords;
460}
461
462######################################################################
463# Returning the application name for the Summary Information Stream
464######################################################################
465
466sub get_appname_for_sis
467{
468	my ( $language, $languagefile, $searchstring ) = @_;
469
470	my $appname = get_value_from_sis_lng($language, $languagefile, $searchstring );
471
472	return $appname;
473}
474
475######################################################################
476# Returning the security for the Summary Information Stream
477######################################################################
478
479sub get_security_for_sis
480{
481	my $security = "0";
482	return $security;
483}
484
485#################################################################
486# Writing the Summary information stream into the msi database
487# This works only on Windows
488#################################################################
489
490sub write_summary_into_msi_database
491{
492	my ($msifilename, $language, $languagefile, $allvariableshashref) = @_;
493
494	# -g : requrired msi version
495	# -c : codepage
496	# -p : template
497
498	installer::logger::include_header_into_logfile("Writing summary information stream");
499
500	my $msiinfo = "msiinfo.exe";	# Has to be in the path
501
502	my $sislanguage = "en-US";	# title, comment, keyword and appname alway in english
503
504	my $msiversion = get_msiversion_for_sis();
505	my $codepage = get_codepage_for_sis($language);
506	my $template = get_template_for_sis($language, $allvariableshashref);
507	my $guid = get_packagecode_for_sis();
508	my $title = get_title_for_sis($sislanguage,$languagefile, "OOO_SIS_TITLE");
509	my $author = get_author_for_sis();
510	my $subject = get_subject_for_sis($allvariableshashref);
511	my $comment = get_comment_for_sis($sislanguage,$languagefile, "OOO_SIS_COMMENT");
512	my $keywords = get_keywords_for_sis($sislanguage,$languagefile, "OOO_SIS_KEYWORDS");
513	my $appname = get_appname_for_sis($sislanguage,$languagefile, "OOO_SIS_APPNAME");
514	my $security = get_security_for_sis();
515	my $wordcount = get_wordcount_for_sis();
516
517	$msifilename = installer::converter::make_path_conform($msifilename);
518
519	my $systemcall = $msiinfo . " " . $msifilename . " -g " . $msiversion . " -c " . $codepage
520					. " -p " . $template . " -v " . $guid . " -t " . $title . " -a " . $author
521					. " -j " . $subject . " -o " . $comment . " -k " . $keywords . " -n " . $appname
522					. " -u " . $security . " -w " . $wordcount;
523
524	my $returnvalue = system($systemcall);
525
526	my $infoline = "Systemcall: $systemcall\n";
527	$installer::logger::Lang->print($infoline);
528
529	if ($returnvalue)
530	{
531		$infoline = "ERROR: Could not execute $msiinfo!\n";
532		$installer::logger::Lang->print($infoline);
533	}
534	else
535	{
536		$infoline = "Success: Executed $msiinfo successfully!\n";
537		$installer::logger::Lang->print($infoline);
538	}
539}
540
541#########################################################################
542# For more than one language in the installation set:
543# Use one database and create Transformations for all other languages
544#########################################################################
545
546sub create_transforms
547{
548	my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_;
549
550	installer::logger::include_header_into_logfile("Creating Transforms");
551
552	my $msitran = "msitran.exe";	# Has to be in the path
553
554	$installdir = installer::converter::make_path_conform($installdir);
555
556	# Syntax for creating a transformation
557	# msitran.exe -g <baseDB> <referenceDB> <transformfile> [<errorhandling>}
558
559	my $basedbname = get_msidatabasename($allvariableshashref, $defaultlanguage);
560	$basedbname = $installdir . $installer::globals::separator . $basedbname;
561
562	my $errorhandling = "f";	# Suppress "change codepage" error
563
564	# Iterating over all files
565
566	foreach ( @{$languagesarray} )
567	{
568		my $onelanguage = $_;
569
570		if ( $onelanguage eq $defaultlanguage ) { next; }
571
572		my $referencedbname = get_msidatabasename($allvariableshashref, $onelanguage);
573		$referencedbname = $installdir . $installer::globals::separator . $referencedbname;
574
575		my $transformfile = $installdir . $installer::globals::separator . "trans_" . $onelanguage . ".mst";
576
577		my $systemcall = $msitran . " " . " -g " . $basedbname . " " . $referencedbname . " " . $transformfile . " " . $errorhandling;
578
579		my $returnvalue = system($systemcall);
580
581		my $infoline = "Systemcall: $systemcall\n";
582		$installer::logger::Lang->print($infoline);
583
584		# Problem: msitran.exe in version 4.0 always returns "1", even if no failure occured.
585		# Therefore it has to be checked, if this is version 4.0. If yes, if the mst file
586		# exists and if it is larger than 0 bytes. If this is true, then no error occured.
587		# File Version of msitran.exe: 4.0.6000.16384 has checksum: "b66190a70145a57773ec769e16777b29".
588		# Same for msitran.exe from wntmsci12: "aa25d3445b94ffde8ef0c1efb77a56b8"
589
590		if ($returnvalue)
591		{
592			$infoline = "WARNING: Returnvalue of $msitran is not 0. Checking version of $msitran!\n";
593			$installer::logger::Lang->print($infoline);
594
595			open(FILE, "<$installer::globals::msitranpath") or die "ERROR: Can't open $installer::globals::msitranpath for creating file hash";
596			binmode(FILE);
597			my $digest = Digest::MD5->new->addfile(*FILE)->hexdigest;
598			close(FILE);
599
600			my @problemchecksums = ("b66190a70145a57773ec769e16777b29", "aa25d3445b94ffde8ef0c1efb77a56b8");
601			my $isproblemchecksum = 0;
602
603			foreach my $problemchecksum ( @problemchecksums )
604			{
605				$infoline = "Checksum of problematic MsiTran.exe: $problemchecksum\n";
606				$installer::logger::Lang->print($infoline);
607				$infoline = "Checksum of used MsiTran.exe: $digest\n";
608				$installer::logger::Lang->print($infoline);
609				if ( $digest eq $problemchecksum ) { $isproblemchecksum = 1; }
610			}
611
612			if ( $isproblemchecksum )
613			{
614				# Check existence of mst
615				if ( -f $transformfile )
616				{
617					$infoline = "File $transformfile exists.\n";
618					$installer::logger::Lang->print($infoline);
619					my $filesize = ( -s $transformfile );
620					$infoline = "Size of $transformfile: $filesize\n";
621					$installer::logger::Lang->print($infoline);
622
623					if ( $filesize > 0 )
624					{
625						$infoline = "Info: Returnvalue $returnvalue of $msitran is no problem :-) .\n";
626						$installer::logger::Lang->print($infoline);
627						$returnvalue = 0; # reset the error
628					}
629					else
630					{
631						$infoline = "Filesize indicates that an error occured.\n";
632						$installer::logger::Lang->print($infoline);
633					}
634				}
635				else
636				{
637					$infoline = "File $transformfile does not exist -> An error occured.\n";
638					$installer::logger::Lang->print($infoline);
639				}
640			}
641			else
642			{
643				$infoline = "This is not a problematic version of msitran.exe. Therefore the error is not caused by problematic msitran.exe.\n";
644				$installer::logger::Lang->print($infoline);
645			}
646		}
647
648		if ($returnvalue)
649		{
650			$infoline = "ERROR: Could not execute $msitran!\n";
651			$installer::logger::Lang->print($infoline);
652		}
653		else
654		{
655			$infoline = "Success: Executed $msitran successfully!\n";
656			$installer::logger::Lang->print($infoline);
657		}
658
659		# The reference database can be deleted
660
661		my $result = unlink($referencedbname);
662		# $result contains the number of deleted files
663
664		if ( $result == 0 )
665		{
666			$infoline = "ERROR: Could not remove file $$referencedbname !\n";
667			$installer::logger::Lang->print($infoline);
668			installer::exiter::exit_program($infoline, "create_transforms");
669		}
670	}
671}
672
673#########################################################################
674# The default language msi database does not need to contain
675# the language in the database name. Therefore the file
676# is renamed. Example: "openofficeorg20_01.msi" to "openofficeorg20.msi"
677#########################################################################
678
679sub rename_msi_database_in_installset
680{
681	my ($defaultlanguage, $installdir, $allvariableshashref) = @_;
682
683	installer::logger::include_header_into_logfile("Renaming msi database");
684
685	my $olddatabasename = get_msidatabasename($allvariableshashref, $defaultlanguage);
686	$olddatabasename = $installdir . $installer::globals::separator . $olddatabasename;
687
688	my $newdatabasename = get_msidatabasename($allvariableshashref);
689
690	$installer::globals::shortmsidatabasename = $newdatabasename;
691
692	$newdatabasename = $installdir . $installer::globals::separator . $newdatabasename;
693
694	installer::systemactions::rename_one_file($olddatabasename, $newdatabasename);
695
696	$installer::globals::msidatabasename = $newdatabasename;
697}
698
699#########################################################################
700# Adding the language to the name of the msi databasename,
701# if this is required (ADDLANGUAGEINDATABASENAME)
702#########################################################################
703
704sub add_language_to_msi_database
705{
706	my ($defaultlanguage, $installdir, $allvariables) = @_;
707
708	my $languagestring = $defaultlanguage;
709	if ( $allvariables->{'USELANGUAGECODE'} ) { $languagestring = installer::windows::language::get_windows_language($defaultlanguage); }
710	my $newdatabasename = $installer::globals::shortmsidatabasename;
711	$newdatabasename =~ s/\.msi\s*$/_$languagestring\.msi/;
712	$installer::globals::shortmsidatabasename = $newdatabasename;
713	$newdatabasename = $installdir . $installer::globals::separator . $newdatabasename;
714
715	my $olddatabasename = $installer::globals::msidatabasename;
716
717	installer::systemactions::rename_one_file($olddatabasename, $newdatabasename);
718
719	$installer::globals::msidatabasename = $newdatabasename;
720}
721
722##########################################################################
723# Writing the databasename into the setup.ini.
724##########################################################################
725
726sub put_databasename_into_setupini
727{
728	my ($setupinifile, $allvariableshashref) = @_;
729
730	my $databasename = get_msidatabasename($allvariableshashref);
731	my $line = "database=" . $databasename . "\n";
732
733	push(@{$setupinifile}, $line);
734}
735
736##########################################################################
737# Writing the required msi version into setup.ini
738##########################################################################
739
740sub put_msiversion_into_setupini
741{
742	my ($setupinifile) = @_;
743
744	my $msiversion = "2.0";
745	my $line = "msiversion=" . $msiversion . "\n";
746
747	push(@{$setupinifile}, $line);
748}
749
750##########################################################################
751# Writing the productname into setup.ini
752##########################################################################
753
754sub put_productname_into_setupini
755{
756	my ($setupinifile, $allvariableshashref) = @_;
757
758	my $productname = $allvariableshashref->{'PRODUCTNAME'};
759	my $line = "productname=" . $productname . "\n";
760
761	push(@{$setupinifile}, $line);
762}
763
764##########################################################################
765# Writing the productcode into setup.ini
766##########################################################################
767
768sub put_productcode_into_setupini
769{
770	my ($setupinifile) = @_;
771
772	my $productcode = $installer::globals::productcode;
773	my $line = "productcode=" . $productcode . "\n";
774
775	push(@{$setupinifile}, $line);
776}
777
778##########################################################################
779# Writing the ProductVersion from Property table into setup.ini
780##########################################################################
781
782sub put_productversion_into_setupini
783{
784	my ($setupinifile) = @_;
785
786	my $line = "productversion=" . $installer::globals::msiproductversion . "\n";
787	push(@{$setupinifile}, $line);
788}
789
790##########################################################################
791# Writing the key for Minor Upgrades into setup.ini
792##########################################################################
793
794sub put_upgradekey_into_setupini
795{
796	my ($setupinifile) = @_;
797
798	if ( $installer::globals::minorupgradekey ne "" )
799	{
800		my $line = "upgradekey=" . $installer::globals::minorupgradekey . "\n";
801		push(@{$setupinifile}, $line);
802	}
803}
804
805##########################################################################
806# Writing the number of languages into setup.ini
807##########################################################################
808
809sub put_languagecount_into_setupini
810{
811	my ($setupinifile, $languagesarray) = @_;
812
813	my $languagecount = $#{$languagesarray} + 1;
814	my $line = "count=" . $languagecount . "\n";
815
816	push(@{$setupinifile}, $line);
817}
818
819##########################################################################
820# Writing the defaultlanguage into setup.ini
821##########################################################################
822
823sub put_defaultlanguage_into_setupini
824{
825	my ($setupinifile, $defaultlanguage) = @_;
826
827	my $windowslanguage = installer::windows::language::get_windows_language($defaultlanguage);
828	my $line = "default=" . $windowslanguage . "\n";
829	push(@{$setupinifile}, $line);
830}
831
832##########################################################################
833# Writing the information about transformations into setup.ini
834##########################################################################
835
836sub put_transforms_into_setupini
837{
838	my ($setupinifile, $onelanguage, $counter) = @_;
839
840	my $windowslanguage = installer::windows::language::get_windows_language($onelanguage);
841	my $transformfilename = "trans_" . $onelanguage . ".mst";
842
843	my $line = "lang" . $counter . "=" . $windowslanguage . "," . $transformfilename . "\n";
844
845	push(@{$setupinifile}, $line);
846}
847
848###################################################
849# Including Windows line ends in ini files
850# Profiles on Windows shall have \r\n line ends
851###################################################
852
853sub include_windows_lineends
854{
855	my ($onefile) = @_;
856
857	for ( my $i = 0; $i <= $#{$onefile}; $i++ )
858	{
859		${$onefile}[$i] =~ s/\r?\n$/\r\n/;
860	}
861}
862
863##########################################################################
864# Generation the file setup.ini, that is used by the loader setup.exe.
865##########################################################################
866
867sub create_setup_ini
868{
869	my ($languagesarray, $defaultlanguage, $installdir, $allvariableshashref) = @_;
870
871	installer::logger::include_header_into_logfile("Creating setup.ini");
872
873	my $setupinifilename = $installdir . $installer::globals::separator . "setup.ini";
874
875	my @setupinifile = ();
876	my $setupinifile = \@setupinifile;
877
878	my $line = "\[setup\]\n";
879	push(@setupinifile, $line);
880
881	put_databasename_into_setupini($setupinifile, $allvariableshashref);
882	put_msiversion_into_setupini($setupinifile);
883	put_productname_into_setupini($setupinifile, $allvariableshashref);
884	put_productcode_into_setupini($setupinifile);
885	put_productversion_into_setupini($setupinifile);
886	put_upgradekey_into_setupini($setupinifile);
887
888	$line = "\[languages\]\n";
889	push(@setupinifile, $line);
890
891	put_languagecount_into_setupini($setupinifile, $languagesarray);
892	put_defaultlanguage_into_setupini($setupinifile, $defaultlanguage);
893
894	if ( $#{$languagesarray} > 0 )	# writing the transforms information
895	{
896		my $counter = 1;
897
898		for ( my $i = 0; $i <= $#{$languagesarray}; $i++ )
899		{
900			if ( ${$languagesarray}[$i] eq $defaultlanguage ) { next; }
901
902			put_transforms_into_setupini($setupinifile, ${$languagesarray}[$i], $counter);
903			$counter++;
904		}
905	}
906
907	if ( $installer::globals::iswin && $installer::globals::plat =~ /cygwin/i)		# Windows line ends only for Cygwin
908	{
909		include_windows_lineends($setupinifile);
910	}
911
912	installer::files::save_file($setupinifilename, $setupinifile);
913
914	$installer::logger::Lang->printf("Generated file %s\n", $setupinifilename);
915}
916
917#################################################################
918# Copying the files defined as ScpActions into the
919# installation set.
920#################################################################
921
922sub copy_scpactions_into_installset
923{
924	my ($defaultlanguage, $installdir, $allscpactions) = @_;
925
926	installer::logger::include_header_into_logfile("Copying ScpAction files into installation set");
927
928	for ( my $i = 0; $i <= $#{$allscpactions}; $i++ )
929	{
930		my $onescpaction = ${$allscpactions}[$i];
931
932		if ( $onescpaction->{'Name'} eq "loader.exe" ) { next; }	# do not copy this ScpAction loader
933
934		# only copying language independent files or files with the correct language (the defaultlanguage)
935
936		my $filelanguage = $onescpaction->{'specificlanguage'};
937
938		if ( ($filelanguage eq $defaultlanguage) || ($filelanguage eq "") )
939		{
940			my $sourcefile = $onescpaction->{'sourcepath'};
941			my $destfile = $installdir . $installer::globals::separator . $onescpaction->{'DestinationName'};
942
943			installer::systemactions::copy_one_file($sourcefile, $destfile);
944		}
945	}
946}
947
948#################################################################
949# Copying the files for the Windows installer into the
950# installation set (setup.exe).
951#################################################################
952
953sub copy_windows_installer_files_into_installset
954{
955	my ($installdir, $includepatharrayref, $allvariables) = @_;
956
957    installer::logger::include_header_into_logfile("Copying Windows installer files into installation set");
958
959	my @copyfile = ();
960	push(@copyfile, "loader2.exe");
961
962	if ( $allvariables->{'NOLOADERREQUIRED'} ) { @copyfile = (); }
963
964	for ( my $i = 0; $i <= $#copyfile; $i++ )
965	{
966		my $filename = $copyfile[$i];
967		my $sourcefileref = installer::scriptitems::get_sourcepath_from_filename_and_includepath(\$filename, $includepatharrayref, 1);
968
969		if ( ! -f $$sourcefileref ) { installer::exiter::exit_program("ERROR: msi file not found: $$sourcefileref !", "copy_windows_installer_files_into_installset"); }
970
971		my $destfile;
972		if ( $copyfile[$i] eq "loader2.exe" ) { $destfile = "setup.exe"; }	# renaming the loader
973		else { $destfile = $copyfile[$i]; }
974
975		$destfile = $installdir . $installer::globals::separator . $destfile;
976
977		installer::systemactions::copy_one_file($$sourcefileref, $destfile);
978	}
979}
980
981#################################################################
982# Copying the child projects into the
983# installation set
984#################################################################
985
986sub copy_child_projects_into_installset
987{
988	my ($installdir, $allvariables) = @_;
989
990	my $sourcefile = "";
991	my $destdir = "";
992
993	# adding Java
994
995	if ( $allvariables->{'JAVAPRODUCT'} )
996	{
997		$sourcefile = $installer::globals::javafile->{'sourcepath'};
998		$destdir = $installdir . $installer::globals::separator . $installer::globals::javafile->{'Subdir'};
999		if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); }
1000		installer::systemactions::copy_one_file($sourcefile, $destdir);
1001	}
1002
1003	if ( $allvariables->{'UREPRODUCT'} )
1004	{
1005		$sourcefile = $installer::globals::urefile->{'sourcepath'};
1006		$destdir = $installdir . $installer::globals::separator . $installer::globals::urefile->{'Subdir'};
1007		if ( ! -d $destdir) { installer::systemactions::create_directory($destdir); }
1008		installer::systemactions::copy_one_file($sourcefile, $destdir);
1009	}
1010}
1011
1012
1013
1014=head2 create_guid ()
1015
1016    Create a single UUID aka GUID via calling the external executable 'uuidgen'.
1017    There are Perl modules for that, but do they exist on the build bots?
1018
1019=cut
1020sub create_guid ()
1021{
1022	my $uuid = qx("uuidgen");
1023    $uuid =~ s/\s*$//;
1024	return uc($uuid);
1025}
1026
1027#################################################################
1028# Calculating a GUID with a string using md5.
1029#################################################################
1030
1031sub calculate_guid
1032{
1033	my ( $string ) = @_;
1034
1035	my $guid = "";
1036
1037    my $md5 = Digest::MD5->new;
1038    $md5->add($string);
1039    my $digest = $md5->hexdigest;
1040    $digest = uc($digest);
1041
1042	# my $id = pack("A32", $digest);
1043	my ($first, $second, $third, $fourth, $fifth) = unpack ('A8 A4 A4 A4 A12', $digest);
1044	$guid = "$first-$second-$third-$fourth-$fifth";
1045
1046    $installer::logger::Lang->printf("guid for '%s' is %s\n",
1047        $string, $guid);
1048
1049	return $guid;
1050}
1051
1052#################################################################
1053# Calculating a ID with a string using md5 (very fast).
1054#################################################################
1055
1056sub calculate_id
1057{
1058	my ( $string, $length ) = @_;
1059
1060	my $id = "";
1061
1062    my $md5 = Digest::MD5->new;
1063    $md5->add($string);
1064    my $digest = lc($md5->hexdigest);
1065	$id = substr($digest, 0, $length);
1066
1067	return $id;
1068}
1069
1070#################################################################
1071# Filling the component hash with the values of the
1072# component file.
1073#################################################################
1074
1075sub fill_component_hash
1076{
1077	my ($componentfile) = @_;
1078
1079	my %components = ();
1080
1081	for ( my $i = 0; $i <= $#{$componentfile}; $i++ )
1082	{
1083		my $line = ${$componentfile}[$i];
1084
1085		if ( $line =~ /^\s*(.*?)\t(.*?)\s*$/ )
1086		{
1087			my $key = $1;
1088			my $value = $2;
1089
1090			$components{$key} = $value;
1091		}
1092	}
1093
1094	return \%components;
1095}
1096
1097#################################################################
1098# Creating a new component file, if new guids were generated.
1099#################################################################
1100
1101sub create_new_component_file
1102{
1103	my ($componenthash) = @_;
1104
1105	my @componentfile = ();
1106
1107	my $key;
1108
1109	foreach $key (keys %{$componenthash})
1110	{
1111		my $value = $componenthash->{$key};
1112		my $input = "$key\t$value\n";
1113		push(@componentfile ,$input);
1114	}
1115
1116	return \@componentfile;
1117}
1118
1119#################################################################
1120# Filling real component GUID into the component table.
1121# This works only on Windows
1122#################################################################
1123
1124sub __set_uuid_into_component_table
1125{
1126	my ($idtdirbase, $allvariables) = @_;
1127
1128	my $componenttablename  = $idtdirbase . $installer::globals::separator . "Componen.idt";
1129
1130	my $componenttable = installer::files::read_file($componenttablename);
1131
1132	# For update and patch reasons (small update) the GUID of an existing component must not change!
1133	# The collection of component GUIDs is saved in the directory $installer::globals::idttemplatepath in the file "components.txt"
1134
1135	my $infoline = "";
1136	my $counter = 0;
1137	# my $componentfile = installer::files::read_file($installer::globals::componentfilename);
1138	# my $componenthash = fill_component_hash($componentfile);
1139
1140	for ( my $i = 3; $i <= $#{$componenttable}; $i++ )	# ignoring the first three lines
1141	{
1142		my $oneline = ${$componenttable}[$i];
1143		my $componentname = "";
1144		if ( $oneline =~ /^\s*(\S+?)\t/ ) { $componentname = $1; }
1145
1146		my $uuid = "";
1147
1148	#	if ( $componenthash->{$componentname} )
1149	#	{
1150	#		$uuid = $componenthash->{$componentname};
1151	#	}
1152	#	else
1153	#	{
1154
1155			if ( exists($installer::globals::calculated_component_guids{$componentname}))
1156			{
1157				$uuid = $installer::globals::calculated_component_guids{$componentname};
1158			}
1159			else
1160			{
1161				# Calculating new GUID with the help of the component name.
1162				my $useooobaseversion = 1;
1163				if ( exists($installer::globals::base_independent_components{$componentname}))
1164                {
1165                    $useooobaseversion = 0;
1166                }
1167				my $sourcestring = $componentname;
1168
1169				if ( $useooobaseversion )
1170				{
1171					if ( ! exists($allvariables->{'OOOBASEVERSION'}) )
1172                    {
1173                        installer::exiter::exit_program(
1174                            "ERROR: Could not find variable \"OOOBASEVERSION\" (required value for GUID creation)!",
1175                            "set_uuid_into_component_table");
1176                    }
1177					$sourcestring = $sourcestring . "_" . $allvariables->{'OOOBASEVERSION'};
1178				}
1179				$uuid = calculate_guid($sourcestring);
1180				$counter++;
1181
1182				# checking, if there is a conflict with an already created guid
1183				if ( exists($installer::globals::allcalculated_guids{$uuid}) )
1184                {
1185                    installer::exiter::exit_program(
1186                        "ERROR: \"$uuid\" was already created before!",
1187                        "set_uuid_into_component_table");
1188                }
1189				$installer::globals::allcalculated_guids{$uuid} = 1;
1190				$installer::globals::calculated_component_guids{$componentname} = $uuid;
1191
1192				# Setting new uuid
1193				# $componenthash->{$componentname} = $uuid;
1194
1195				# Setting flag
1196				# $installer::globals::created_new_component_guid = 1;	# this is very important!
1197			}
1198	#	}
1199
1200		${$componenttable}[$i] =~ s/COMPONENTGUID/$uuid/;
1201	}
1202
1203	installer::files::save_file($componenttablename, $componenttable);
1204
1205#	if ( $installer::globals::created_new_component_guid )
1206#	{
1207#		# create new component file!
1208#		$componentfile = create_new_component_file($componenthash);
1209#		installer::worker::sort_array($componentfile);
1210#
1211#		# To avoid conflict the components file cannot be saved at the same place
1212#		# All important data have to be saved in the directory: $installer::globals::infodirectory
1213#		my $localcomponentfilename = $installer::globals::componentfilename;
1214#		installer::pathanalyzer::make_absolute_filename_to_relative_filename(\$localcomponentfilename);
1215#		$localcomponentfilename = $installer::globals::infodirectory . $installer::globals::separator . $localcomponentfilename;
1216#		installer::files::save_file($localcomponentfilename, $componentfile);
1217#
1218#		# installer::files::save_file($installer::globals::componentfilename, $componentfile);	# version using new file in solver
1219#
1220#		$infoline = "COMPONENTCODES: Created $counter new GUIDs for components ! \n";
1221#		$installer::logger::Lang->print($infoline);
1222#	}
1223#	else
1224#	{
1225#		$infoline = "SUCCESS COMPONENTCODES: All component codes exist! \n";
1226#		$installer::logger::Lang->print($infoline);
1227#	}
1228
1229}
1230
1231#########################################################################
1232# Adding final 64 properties into msi database, if required.
1233# RegLocator : +16 in type column to search in 64 bit registry.
1234# All conditions: "VersionNT" -> "VersionNT64" (several tables).
1235# Already done: "+256" in Attributes column of table "Component".
1236# Still following: Setting "x64" instead of "Intel" in Summary
1237# Information Stream of msi database in "get_template_for_sis".
1238#########################################################################
1239
1240sub prepare_64bit_database
1241{
1242	my ($basedir, $allvariables) = @_;
1243
1244	my $infoline = "";
1245
1246	if (( $allvariables->{'64BITPRODUCT'} ) && ( $allvariables->{'64BITPRODUCT'} == 1 ))
1247	{
1248		# 1. Beginning with table "RegLocat.idt". Adding "16" to the type.
1249
1250		my $reglocatfile = "";
1251		my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
1252
1253		if ( -f $reglocatfilename )
1254		{
1255			my $saving_required = 0;
1256			$reglocatfile = installer::files::read_file($reglocatfilename);
1257
1258			for ( my $i = 3; $i <= $#{$reglocatfile}; $i++ ) 	# ignoring the first three lines
1259			{
1260				my $oneline = ${$reglocatfile}[$i];
1261
1262				if ( $oneline =~ /^\s*\#/ ) { next; }	# this is a comment line
1263				if ( $oneline =~ /^\s*$/ ) { next; }
1264
1265				if ( $oneline =~ /^\s*(.*?)\t(.*?)\t(.*?)\t(.*?)\t(\d+)\s*$/ )
1266				{
1267					# Syntax: Signature_ Root Key Name Type
1268					my $sig = $1;
1269					my $root = $2;
1270					my $key = $3;
1271					my $name = $4;
1272					my $type = $5;
1273
1274					$type = $type + 16;
1275
1276					my $newline = $sig . "\t" . $root . "\t" . $key . "\t" . $name . "\t" . $type . "\n";
1277					${$reglocatfile}[$i] = $newline;
1278
1279					$saving_required = 1;
1280				}
1281			}
1282
1283			if ( $saving_required )
1284			{
1285				# Saving the files
1286				installer::files::save_file($reglocatfilename ,$reglocatfile);
1287				$infoline = "Making idt file 64 bit conform: $reglocatfilename\n";
1288				$installer::logger::Lang->print($infoline);
1289			}
1290		}
1291
1292		# 2. Replacing all occurences of "VersionNT" by "VersionNT64"
1293
1294		my @versionnt_files = ("Componen.idt", "InstallE.idt", "InstallU.idt", "LaunchCo.idt");
1295
1296		foreach my $onefile ( @versionnt_files )
1297		{
1298			my $fullfilename = $basedir . $installer::globals::separator . $onefile;
1299
1300			if ( -f $fullfilename )
1301			{
1302				my $saving_required = 0;
1303				my $filecontent = installer::files::read_file($fullfilename);
1304
1305				for ( my $i = 3; $i <= $#{$filecontent}; $i++ ) 	# ignoring the first three lines
1306				{
1307					my $oneline = ${$filecontent}[$i];
1308
1309					if ( $oneline =~ /\bVersionNT\b/ )
1310					{
1311						${$filecontent}[$i] =~ s/\bVersionNT\b/VersionNT64/g;
1312						$saving_required = 1;
1313					}
1314				}
1315
1316				if ( $saving_required )
1317				{
1318					# Saving the files
1319					installer::files::save_file($fullfilename ,$filecontent);
1320					$infoline = "Making idt file 64 bit conform: $fullfilename\n";
1321					$installer::logger::Lang->print($infoline);
1322				}
1323			}
1324		}
1325	}
1326
1327}
1328
1329#################################################################
1330# Include all cab files into the msi database.
1331# This works only on Windows
1332#################################################################
1333
1334sub include_cabs_into_msi
1335{
1336	my ($installdir) = @_;
1337
1338	installer::logger::include_header_into_logfile("Including cabs into msi database");
1339
1340	my $from = cwd();
1341	my $to = $installdir;
1342
1343	chdir($to);
1344
1345	my $infoline = "Changing into directory: $to";
1346	$installer::logger::Lang->print($infoline);
1347
1348	my $msidb = "msidb.exe";	# Has to be in the path
1349	my $extraslash = "";		# Has to be set for non-ActiveState perl
1350
1351	my $msifilename = $installer::globals::msidatabasename;
1352
1353	$msifilename = installer::converter::make_path_conform($msifilename);
1354
1355	# msidb.exe really wants backslashes. (And double escaping because system() expands the string.)
1356	$msifilename =~ s/\//\\\\/g;
1357	$extraslash = "\\";
1358
1359	my $allcabfiles = installer::systemactions::find_file_with_file_extension("cab", $installdir);
1360
1361	for ( my $i = 0; $i <= $#{$allcabfiles}; $i++ )
1362	{
1363		my $systemcall = $msidb . " -d " . $msifilename . " -a " . ${$allcabfiles}[$i];
1364
1365		my $returnvalue = system($systemcall);
1366
1367		$infoline = "Systemcall: $systemcall\n";
1368		$installer::logger::Lang->print($infoline);
1369
1370		if ($returnvalue)
1371		{
1372			$infoline = "ERROR: Could not execute $systemcall !\n";
1373			$installer::logger::Lang->print($infoline);
1374		}
1375		else
1376		{
1377			$infoline = "Success: Executed $systemcall successfully!\n";
1378			$installer::logger::Lang->print($infoline);
1379		}
1380
1381		# deleting the cab file
1382
1383		unlink(${$allcabfiles}[$i]);
1384
1385		$infoline = "Deleted cab file: ${$allcabfiles}[$i]\n";
1386		$installer::logger::Lang->print($infoline);
1387	}
1388
1389	$infoline = "Changing back into directory: $from";
1390	$installer::logger::Lang->print($infoline);
1391
1392	chdir($from);
1393}
1394
1395#################################################################
1396# Executing the created batch file to pack all files.
1397# This works only on Windows
1398#################################################################
1399
1400sub execute_packaging
1401{
1402	my ($localpackjobref, $loggingdir, $allvariables) = @_;
1403
1404	installer::logger::include_header_into_logfile("Packaging process");
1405
1406	$installer::logger::Lang->add_timestamp("Performance Info: Execute packaging start");
1407
1408	my $infoline = "";
1409	my $from = cwd();
1410	my $to = $loggingdir;
1411
1412	chdir($to);
1413	$infoline = "chdir: $to \n";
1414	$installer::logger::Lang->print($infoline);
1415
1416	# if the ddf file contains relative pathes, it is necessary to change into the temp directory
1417	if ( $allvariables->{'RELATIVE_PATHES_IN_DDF'} )
1418	{
1419		$to = $installer::globals::temppath;
1420		chdir($to);
1421		$infoline = "chdir: $to \n";
1422		$installer::logger::Lang->print($infoline);
1423	}
1424
1425	# changing the tmp directory, because makecab.exe generates temporary cab files
1426	my $origtemppath = "";
1427	if ( $ENV{'TMP'} ) { $origtemppath = $ENV{'TMP'}; }
1428	$ENV{'TMP'} = $installer::globals::temppath;	# setting TMP to the new unique directory!
1429
1430	my $maxmakecabcalls = 3;
1431	my $allmakecabcalls = $#{$localpackjobref} + 1;
1432
1433	for ( my $i = 0; $i <= $#{$localpackjobref}; $i++ )
1434	{
1435		my $systemcall = ${$localpackjobref}[$i];
1436
1437		my $callscounter = $i + 1;
1438
1439		$installer::logger::Info->printf("... makecab.exe (%s/%s) ... \n", $callscounter, $allmakecabcalls);
1440
1441		# my $returnvalue = system($systemcall);
1442
1443		for ( my $n = 1; $n <= $maxmakecabcalls; $n++ )
1444		{
1445			my @ddfoutput = ();
1446
1447			$infoline = "Systemcall: $systemcall";
1448			$installer::logger::Lang->print($infoline);
1449
1450			open (DDF, "$systemcall");
1451			while (<DDF>) {push(@ddfoutput, $_); }
1452			close (DDF);
1453
1454			my $returnvalue = $?;	# $? contains the return value of the systemcall
1455
1456			if ($returnvalue)
1457			{
1458				if ( $n < $maxmakecabcalls )
1459				{
1460                    $installer::logger::Info->printf("makecab_error (Try %s): Trying again\n", $n);
1461                    $installer::logger::Lang->printf("makecab_error (Try %s): Trying again\n", $n);
1462				}
1463				else
1464				{
1465                    $installer::logger::Info->printf("ERROR (Try %s): Abort packing \n", $n);
1466                    $installer::logger::Lang->printf("ERROR (Try %s): Abort packing \n", $n);
1467				}
1468
1469				for ( my $m = 0; $m <= $#ddfoutput; $m++ )
1470				{
1471					if ( $ddfoutput[$m] =~ /(ERROR\:.*?)\s*$/ )
1472					{
1473						$infoline = $1 . "\n";
1474						if ( $n < $maxmakecabcalls )
1475                        {
1476                            $infoline =~ s/ERROR\:/makecab_error\:/i;
1477                        }
1478						$installer::logger::Info->print($infoline);
1479						$installer::logger::Lang->print($infoline);
1480					}
1481				}
1482
1483				if ( $n == $maxmakecabcalls ) { installer::exiter::exit_program("ERROR: \"$systemcall\"!", "execute_packaging"); }
1484			}
1485			else
1486			{
1487				$infoline = "Success (Try $n): $systemcall";
1488				$installer::logger::Lang->print($infoline);
1489				last;
1490			}
1491		}
1492	}
1493
1494	$installer::logger::Lang->add_timestamp("Performance Info: Execute packaging end");
1495
1496	# setting back to the original tmp directory
1497	$ENV{'TMP'} = $origtemppath;
1498
1499	chdir($from);
1500	$infoline = "chdir: $from \n";
1501	$installer::logger::Lang->print($infoline);
1502}
1503
1504
1505=head2 get_source_codes($languagesref)
1506
1507    Return product code and upgrade code from the source version.
1508    When no source version is defined then return undef for both.
1509
1510=cut
1511sub get_source_codes ($)
1512{
1513    my ($languagesref) = @_;
1514
1515    if ( ! $installer::globals::is_release)
1516    {
1517        return (undef, undef);
1518    }
1519    elsif ( ! defined $installer::globals::source_version)
1520    {
1521        $installer::logger::Lang->printf("no source version defined\n");
1522        return (undef, undef);
1523    }
1524
1525    my $onelanguage = installer::languages::get_key_language($languagesref);
1526
1527    my $release_data = installer::patch::ReleasesList::Instance()
1528        ->{$installer::globals::source_version}
1529        ->{$installer::globals::packageformat};
1530    if (defined $release_data)
1531    {
1532        my $normalized_language = installer::languages::get_normalized_language($languagesref);
1533        my $language_data = $release_data->{$normalized_language};
1534        if (defined $language_data)
1535        {
1536            $installer::logger::Lang->printf("source product code is %s\n", $language_data->{'product-code'});
1537            $installer::logger::Lang->printf("source upgrade code is %s\n", $release_data->{'upgrade-code'});
1538
1539            return (
1540                $language_data->{'product-code'},
1541                $release_data->{'upgrade-code'}
1542                );
1543        }
1544        else
1545        {
1546            $installer::logger::Info->printf(
1547                "Warning: can not access information about previous version %s and language %s/%s/%s\n",
1548                $installer::globals::source_version,
1549                $onelanguage,
1550                join(", ",@$languagesref),
1551                $normalized_language);
1552            return (undef,undef);
1553        }
1554    }
1555    else
1556    {
1557        $installer::logger::Info->printf("Warning: can not access information about previous version %s\n",
1558            $installer::globals::source_version);
1559        return (undef,undef);
1560    }
1561}
1562
1563
1564
1565
1566=head2 set_global_code_variables ($languagesref, $allvariableshashref)
1567
1568    Determine values for the product code and upgrade code of the target version.
1569
1570    As perparation for building a Windows patch, certain conditions have to be fullfilled.
1571     - The upgrade code changes from old to new version
1572     - The product code remains the same
1573     In order to inforce that we have to access information about the source version.
1574
1575    The resulting values are stored as global variables
1576        $installer::globals::productcode
1577        $installer::globals::upgradecode
1578    and as variables in the given hash
1579    	$allvariableshashref->{'PRODUCTCODE'}
1580        $allvariableshashref->{'UPGRADECODE'}
1581
1582=cut
1583sub set_global_code_variables ($$)
1584{
1585    my ($languagesref, $allvariableshashref) = @_;
1586
1587    my ($source_product_code, $source_upgrade_code) = get_source_codes($languagesref);
1588    my ($target_product_code, $target_upgrade_code) = (undef, undef);
1589
1590    if (defined $source_product_code && defined $source_upgrade_code)
1591    {
1592        if ($installer::globals::is_major_release)
1593        {
1594            # For a major release we have to change the product code.
1595            $target_product_code = "{" . create_guid() . "}";
1596            $installer::logger::Lang->printf("building a major release, created new product code %s\n",
1597                $target_product_code);
1598
1599            # Let's do a paranoia check that the new and the old guids are
1600            # different.  In reality the new guid really has to be
1601            # different from all other guids used for * codes, components,
1602            # etc.
1603            if ($target_product_code eq $source_product_code)
1604            {
1605                installer::logger::PrintError(
1606                    "new GUID for product code is the same as the old product code but should be different.");
1607            }
1608        }
1609        else
1610        {
1611            # For minor or micro releases we have to keeep the old product code.
1612            $target_product_code = "{" . $source_product_code . "}";
1613            $installer::logger::Lang->printf("building a minor or micro release, reusing product code %s\n",
1614                $target_product_code);
1615        }
1616
1617        $target_upgrade_code = "{" . create_guid() . "}";
1618        # Again, just one test for paranoia.
1619        if ($target_upgrade_code eq $source_upgrade_code)
1620        {
1621            installer::logger::PrintError(
1622                "new GUID for upgrade code is the same as the old upgrade code but should be different.");
1623        }
1624    }
1625    else
1626    {
1627        # There is no previous version with which to compare the product code.
1628        # Just create two new uuids.
1629        $target_product_code = "{" . create_guid() . "}";
1630        $target_upgrade_code = "{" . create_guid() . "}";
1631        $installer::logger::Lang->printf("there is no source version => created new guids\n");
1632    }
1633
1634    # Keep the upgrade code constant between versions.  Read it from the codes.txt file.
1635    # Note that this handles regular installation sets and language packs.
1636    my $onelanguage = ${$languagesref}[0];
1637    $installer::logger::Lang->printf("reading upgrade code for language %s from %s\n",
1638        $onelanguage,
1639        $installer::globals::codefilename);
1640    if (defined $installer::globals::codefilename)
1641    {
1642        my $code_filename = $installer::globals::codefilename;
1643        installer::files::check_file($code_filename);
1644        my $codefile = installer::files::read_file($code_filename);
1645        my $searchstring = "UPGRADECODE";
1646        my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file(
1647            $searchstring,
1648            $codefile);
1649        $target_upgrade_code = installer::windows::idtglobal::get_language_string_from_language_block(
1650            $codeblock,
1651            $onelanguage,
1652            "");
1653    }
1654    # else use the previously generated upgrade code.
1655
1656    $installer::globals::productcode = $target_product_code;
1657    $installer::globals::upgradecode = $target_upgrade_code;
1658    $allvariableshashref->{'PRODUCTCODE'} = $target_product_code;
1659    $allvariableshashref->{'UPGRADECODE'} = $target_upgrade_code;
1660
1661    $installer::logger::Lang->printf("target product code is %s\n", $target_product_code);
1662    $installer::logger::Lang->printf("target upgrade code is %s\n", $target_upgrade_code);
1663}
1664
1665
1666
1667
1668###############################################################
1669# Setting the product version used in property table and
1670# upgrade table. Saving in global variable $msiproductversion
1671###############################################################
1672
1673sub set_msiproductversion
1674{
1675	my ( $allvariables ) = @_;
1676
1677	my $productversion = $allvariables->{'PRODUCTVERSION'};
1678
1679	if (( $productversion =~ /^\s*\d+\s*$/ ) && ( $productversion > 255 )) { $productversion = $productversion%256; }
1680
1681	if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
1682	{
1683		$productversion = $1 . "\." . $2 . $3 . "\." . $installer::globals::buildid;
1684	}
1685	elsif  ( $productversion =~ /^\s*(\d+)\.(\d+)\s*$/ )
1686	{
1687		$productversion = $1 . "\." . $2 . "\." . $installer::globals::buildid;
1688	}
1689	else
1690	{
1691		my $productminor = "00";
1692		if (( $allvariables->{'PACKAGEVERSION'} ) && ( $allvariables->{'PACKAGEVERSION'} ne "" ))
1693		{
1694			if ( $allvariables->{'PACKAGEVERSION'} =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) { $productminor = $2; }
1695		}
1696
1697		$productversion = $productversion . "\." . $productminor . "\." . $installer::globals::buildid;
1698	}
1699
1700	$installer::globals::msiproductversion = $productversion;
1701
1702	# Setting $installer::globals::msimajorproductversion, to differ between old version in upgrade table
1703
1704	if ( $installer::globals::msiproductversion =~ /^\s*(\d+)\./ )
1705	{
1706		my $major = $1;
1707		$installer::globals::msimajorproductversion = $major . "\.0\.0";
1708	}
1709}
1710
1711#################################################################################
1712# Including the msi product version into the bootstrap.ini, Windows only
1713#################################################################################
1714
1715sub put_msiproductversion_into_bootstrapfile
1716{
1717	my ($filesref) = @_;
1718
1719	for ( my $i = 0; $i <= $#{$filesref}; $i++ )
1720	{
1721		my $onefile = ${$filesref}[$i];
1722
1723		if ( $onefile->{'gid'} eq "gid_Profile_Version_Ini" )
1724		{
1725			my $file = installer::files::read_file($onefile->{'sourcepath'});
1726
1727			for ( my $j = 0; $j <= $#{$file}; $j++ )
1728			{
1729				${$file}[$j] =~ s/\<msiproductversion\>/$installer::globals::msiproductversion/;
1730			}
1731
1732			installer::files::save_file($onefile->{'sourcepath'}, $file);
1733
1734			last;
1735		}
1736	}
1737}
1738
1739####################################################################################
1740# Updating the file Property.idt dynamically
1741# Content:
1742# Property Value
1743####################################################################################
1744
1745sub update_reglocat_table
1746{
1747	my ($basedir, $allvariables) = @_;
1748
1749	my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
1750
1751	# Only do something, if this file exists
1752
1753	if ( -f $reglocatfilename )
1754	{
1755		my $reglocatfile = installer::files::read_file($reglocatfilename);
1756
1757		my $layername = "";
1758		if ( $allvariables->{'REGISTRYLAYERNAME'} )
1759		{
1760			$layername = $allvariables->{'REGISTRYLAYERNAME'};
1761		}
1762		else
1763		{
1764			for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
1765			{
1766				if ( ${$reglocatfile}[$i] =~ /\bLAYERNAMETEMPLATE\b/ )
1767				{
1768					installer::exiter::exit_program("ERROR: Variable \"REGISTRYLAYERNAME\" has to be defined", "update_reglocat_table");
1769				}
1770			}
1771		}
1772
1773		if ( $layername ne "" )
1774		{
1775			# Updating the layername in
1776
1777			for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
1778			{
1779				${$reglocatfile}[$i] =~ s/\bLAYERNAMETEMPLATE\b/$layername/;
1780			}
1781
1782			# Saving the file
1783			installer::files::save_file($reglocatfilename ,$reglocatfile);
1784			my $infoline = "Updated idt file: $reglocatfilename\n";
1785			$installer::logger::Lang->print($infoline);
1786		}
1787	}
1788}
1789
1790
1791
1792####################################################################################
1793# Updating the file RemoveRe.idt dynamically (RemoveRegistry.idt)
1794# The name of the component has to be replaced.
1795####################################################################################
1796
1797sub update_removere_table
1798{
1799	my ($basedir) = @_;
1800
1801	my $removeregistryfilename = $basedir . $installer::globals::separator . "RemoveRe.idt";
1802
1803	# Only do something, if this file exists
1804
1805	if ( -f $removeregistryfilename )
1806	{
1807		my $removeregistryfile = installer::files::read_file($removeregistryfilename);
1808
1809		for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
1810		{
1811			for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
1812			{
1813				${$removeregistryfile}[$i] =~ s/\bREGISTRYROOTCOMPONENT\b/$installer::globals::registryrootcomponent/;
1814			}
1815		}
1816
1817		# Saving the file
1818		installer::files::save_file($removeregistryfilename ,$removeregistryfile);
1819		my $infoline = "Updated idt file: $removeregistryfilename \n";
1820		$installer::logger::Lang->print($infoline);
1821	}
1822}
1823
1824
18251;
1826
1827