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