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