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