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