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