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