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