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