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