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