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