xref: /AOO41X/main/solenv/bin/modules/installer/windows/msiglobal.pm (revision e01a4f84dd7eb12162e0e927290b16aa319e46ae)
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        $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 pathes
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 occured.
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 occured.
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 occured.\n";
632                        $installer::logger::Lang->print($infoline);
633                    }
634                }
635                else
636                {
637                    $infoline = "File $transformfile does not exist -> An error occured.\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 occurences 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 pathes, 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 perparation for building a Windows patch, certain conditions have to be fullfilled.
1571     - The upgrade code changes from old to new version
1572     - The product code remains the same
1573     In order to inforce that we have to access information about the source version.
1574
1575    The resulting values are stored as global variables
1576        $installer::globals::productcode
1577        $installer::globals::upgradecode
1578    and as variables in the given hash
1579        $allvariableshashref->{'PRODUCTCODE'}
1580        $allvariableshashref->{'UPGRADECODE'}
1581
1582=cut
1583sub set_global_code_variables ($$)
1584{
1585    my ($languagesref, $allvariableshashref) = @_;
1586
1587    my ($source_product_code, $source_upgrade_code) = get_source_codes($languagesref);
1588    my ($target_product_code, $target_upgrade_code) = (undef, undef);
1589
1590    if (defined $source_product_code && defined $source_upgrade_code)
1591    {
1592        if ($installer::globals::is_major_release)
1593        {
1594            # For a major release we have to change the product code.
1595            $target_product_code = "{" . create_guid() . "}";
1596            $installer::logger::Lang->printf("building a major release, created new product code %s\n",
1597                $target_product_code);
1598
1599            # Let's do a paranoia check that the new and the old guids are
1600            # different.  In reality the new guid really has to be
1601            # different from all other guids used for * codes, components,
1602            # etc.
1603            if ($target_product_code eq $source_product_code)
1604            {
1605                installer::logger::PrintError(
1606                    "new GUID for product code is the same as the old product code but should be different.");
1607            }
1608        }
1609        else
1610        {
1611            # For minor or micro releases we have to keeep the old product code.
1612            $target_product_code = "{" . $source_product_code . "}";
1613            $installer::logger::Lang->printf("building a minor or micro release, reusing product code %s\n",
1614                $target_product_code);
1615        }
1616
1617        $target_upgrade_code = "{" . create_guid() . "}";
1618        # Again, just one test for paranoia.
1619        if ($target_upgrade_code eq $source_upgrade_code)
1620        {
1621            installer::logger::PrintError(
1622                "new GUID for upgrade code is the same as the old upgrade code but should be different.");
1623        }
1624    }
1625    else
1626    {
1627        # There is no previous version with which to compare the product code.
1628        # Just create two new uuids.
1629        $target_product_code = "{" . create_guid() . "}";
1630        $target_upgrade_code = "{" . create_guid() . "}";
1631        $installer::logger::Lang->printf("there is no source version => created new guids\n");
1632    }
1633
1634    # Keep the upgrade code constant between versions.  Read it from the codes.txt file.
1635    # Note that this handles regular installation sets and language packs.
1636    my $onelanguage = ${$languagesref}[0];
1637    $installer::logger::Lang->printf("reading upgrade code for language %s from %s\n",
1638        $onelanguage,
1639        $installer::globals::codefilename);
1640    if (defined $installer::globals::codefilename)
1641    {
1642        my $code_filename = $installer::globals::codefilename;
1643        installer::files::check_file($code_filename);
1644        my $codefile = installer::files::read_file($code_filename);
1645        my $searchstring = "UPGRADECODE";
1646        my $codeblock = installer::windows::idtglobal::get_language_block_from_language_file(
1647            $searchstring,
1648            $codefile);
1649        $target_upgrade_code = installer::windows::idtglobal::get_language_string_from_language_block(
1650            $codeblock,
1651            $onelanguage,
1652            "");
1653    }
1654    # else use the previously generated upgrade code.
1655
1656    $installer::globals::productcode = $target_product_code;
1657    $installer::globals::upgradecode = $target_upgrade_code;
1658    $allvariableshashref->{'PRODUCTCODE'} = $target_product_code;
1659    $allvariableshashref->{'UPGRADECODE'} = $target_upgrade_code;
1660
1661    $installer::logger::Lang->printf("target product code is %s\n", $target_product_code);
1662    $installer::logger::Lang->printf("target upgrade code is %s\n", $target_upgrade_code);
1663}
1664
1665
1666
1667
1668###############################################################
1669# Setting the product version used in property table and
1670# upgrade table. Saving in global variable $msiproductversion
1671###############################################################
1672
1673sub set_msiproductversion
1674{
1675    my ( $allvariables ) = @_;
1676
1677    my $productversion = $allvariables->{'PRODUCTVERSION'};
1678
1679    if (( $productversion =~ /^\s*\d+\s*$/ ) && ( $productversion > 255 )) { $productversion = $productversion%256; }
1680
1681    if ( $productversion =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ )
1682    {
1683        $productversion = $1 . "\." . $2 . $3 . "\." . $installer::globals::buildid;
1684    }
1685    elsif  ( $productversion =~ /^\s*(\d+)\.(\d+)\s*$/ )
1686    {
1687        $productversion = $1 . "\." . $2 . "\." . $installer::globals::buildid;
1688    }
1689    else
1690    {
1691        my $productminor = "00";
1692        if (( $allvariables->{'PACKAGEVERSION'} ) && ( $allvariables->{'PACKAGEVERSION'} ne "" ))
1693        {
1694            if ( $allvariables->{'PACKAGEVERSION'} =~ /^\s*(\d+)\.(\d+)\.(\d+)\s*$/ ) { $productminor = $2; }
1695        }
1696
1697        $productversion = $productversion . "\." . $productminor . "\." . $installer::globals::buildid;
1698    }
1699
1700    $installer::globals::msiproductversion = $productversion;
1701
1702    # Setting $installer::globals::msimajorproductversion, to differ between old version in upgrade table
1703
1704    if ( $installer::globals::msiproductversion =~ /^\s*(\d+)\./ )
1705    {
1706        my $major = $1;
1707        $installer::globals::msimajorproductversion = $major . "\.0\.0";
1708    }
1709}
1710
1711#################################################################################
1712# Including the msi product version into the bootstrap.ini, Windows only
1713#################################################################################
1714
1715sub put_msiproductversion_into_bootstrapfile
1716{
1717    my ($filesref) = @_;
1718
1719    for ( my $i = 0; $i <= $#{$filesref}; $i++ )
1720    {
1721        my $onefile = ${$filesref}[$i];
1722
1723        if ( $onefile->{'gid'} eq "gid_Profile_Version_Ini" )
1724        {
1725            my $file = installer::files::read_file($onefile->{'sourcepath'});
1726
1727            for ( my $j = 0; $j <= $#{$file}; $j++ )
1728            {
1729                ${$file}[$j] =~ s/\<msiproductversion\>/$installer::globals::msiproductversion/;
1730            }
1731
1732            installer::files::save_file($onefile->{'sourcepath'}, $file);
1733
1734            last;
1735        }
1736    }
1737}
1738
1739####################################################################################
1740# Updating the file Property.idt dynamically
1741# Content:
1742# Property Value
1743####################################################################################
1744
1745sub update_reglocat_table
1746{
1747    my ($basedir, $allvariables) = @_;
1748
1749    my $reglocatfilename = $basedir . $installer::globals::separator . "RegLocat.idt";
1750
1751    # Only do something, if this file exists
1752
1753    if ( -f $reglocatfilename )
1754    {
1755        my $reglocatfile = installer::files::read_file($reglocatfilename);
1756
1757        my $layername = "";
1758        if ( $allvariables->{'REGISTRYLAYERNAME'} )
1759        {
1760            $layername = $allvariables->{'REGISTRYLAYERNAME'};
1761        }
1762        else
1763        {
1764            for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
1765            {
1766                if ( ${$reglocatfile}[$i] =~ /\bLAYERNAMETEMPLATE\b/ )
1767                {
1768                    installer::exiter::exit_program("ERROR: Variable \"REGISTRYLAYERNAME\" has to be defined", "update_reglocat_table");
1769                }
1770            }
1771        }
1772
1773        if ( $layername ne "" )
1774        {
1775            # Updating the layername in
1776
1777            for ( my $i = 0; $i <= $#{$reglocatfile}; $i++ )
1778            {
1779                ${$reglocatfile}[$i] =~ s/\bLAYERNAMETEMPLATE\b/$layername/;
1780            }
1781
1782            # Saving the file
1783            installer::files::save_file($reglocatfilename ,$reglocatfile);
1784            my $infoline = "Updated idt file: $reglocatfilename\n";
1785            $installer::logger::Lang->print($infoline);
1786        }
1787    }
1788}
1789
1790
1791
1792####################################################################################
1793# Updating the file RemoveRe.idt dynamically (RemoveRegistry.idt)
1794# The name of the component has to be replaced.
1795####################################################################################
1796
1797sub update_removere_table
1798{
1799    my ($basedir) = @_;
1800
1801    my $removeregistryfilename = $basedir . $installer::globals::separator . "RemoveRe.idt";
1802
1803    # Only do something, if this file exists
1804
1805    if ( -f $removeregistryfilename )
1806    {
1807        my $removeregistryfile = installer::files::read_file($removeregistryfilename);
1808
1809        for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
1810        {
1811            for ( my $i = 0; $i <= $#{$removeregistryfile}; $i++ )
1812            {
1813                ${$removeregistryfile}[$i] =~ s/\bREGISTRYROOTCOMPONENT\b/$installer::globals::registryrootcomponent/;
1814            }
1815        }
1816
1817        # Saving the file
1818        installer::files::save_file($removeregistryfilename ,$removeregistryfile);
1819        my $infoline = "Updated idt file: $removeregistryfilename \n";
1820        $installer::logger::Lang->print($infoline);
1821    }
1822}
1823
1824
18251;
1826
1827