Fixed an argument parsing bug in syncqt.
[profile/ivi/qtbase.git] / bin / patch_capabilities.pl
1 #!/usr/bin/perl
2 #############################################################################
3 ##
4 ## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ## All rights reserved.
6 ## Contact: Nokia Corporation (qt-info@nokia.com)
7 ##
8 ## This file is part of the S60 port of the Qt Toolkit.
9 ##
10 ## $QT_BEGIN_LICENSE:LGPL$
11 ## GNU Lesser General Public License Usage
12 ## This file may be used under the terms of the GNU Lesser General Public
13 ## License version 2.1 as published by the Free Software Foundation and
14 ## appearing in the file LICENSE.LGPL included in the packaging of this
15 ## file. Please review the following information to ensure the GNU Lesser
16 ## General Public License version 2.1 requirements will be met:
17 ## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ##
19 ## In addition, as a special exception, Nokia gives you certain additional
20 ## rights. These rights are described in the Nokia Qt LGPL Exception
21 ## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ##
23 ## GNU General Public License Usage
24 ## Alternatively, this file may be used under the terms of the GNU General
25 ## Public License version 3.0 as published by the Free Software Foundation
26 ## and appearing in the file LICENSE.GPL included in the packaging of this
27 ## file. Please review the following information to ensure the GNU General
28 ## Public License version 3.0 requirements will be met:
29 ## http://www.gnu.org/copyleft/gpl.html.
30 ##
31 ## Other Usage
32 ## Alternatively, this file may be used in accordance with the terms and
33 ## conditions contained in a signed written agreement between you and Nokia.
34 ##
35 ##
36 ##
37 ##
38 ##
39 ## $QT_END_LICENSE$
40 ##
41 #############################################################################
42
43 #######################################################################
44 #
45 # A script for setting binary capabilities based on .pkg file contents.
46 #
47 #######################################################################
48
49 #
50 # Note: Please make sure to output all changes done to the pkg file in a print statements
51 #       starting with "Patching: " to ease integration into IDEs!
52 #
53
54 use File::Copy;
55 use File::Spec;
56 use File::Path;
57
58 sub Usage() {
59     print("This script can be used to set capabilities of all binaries\n");
60     print("specified for deployment in a .pkg file.\n");
61     print("If no capabilities are given, the binaries will be given the\n");
62     print("capabilities supported by self-signed certificates.\n\n");
63     print(" *** NOTE: If *_template.pkg file is given and one is using symbian-abld or\n");
64     print(" symbian-sbsv2 platform, 'target-platform' is REQUIRED. ***\n\n");
65     print(" *** NOTE2: When patching gcce binaries built with symbian-sbsv2 toolchain,\n");
66     print(" armv5 must be specified as platform.\n");
67     print("\nUsage: patch_capabilities.pl [-c|-t tmp_path] pkg_filename [target-platform [capability list]]\n");
68     print("\nE.g. patch_capabilities.pl myapp_template.pkg release-armv5 \"All -TCB\"\n");
69     print("\nThe parameter -c can be used to just check if package is compatible with self-signing\n");
70     print("without actually doing any patching.\n");
71     print("Explicit capability list cannot be used with -c parameter.\n");
72     print("\nThe parameter -t can be used to specify a dir under which the temporary files are created.\n");
73     print("Defaults to 'patch_capabilities_tmp' under the path to pkg file.\n");
74     exit();
75 }
76
77 sub trim($) {
78     my $string = shift;
79     $string =~ s/^\s+//;
80     $string =~ s/\s+$//;
81     return $string;
82 }
83
84 my $epocroot = $ENV{EPOCROOT};
85 my $epocToolsDir = "";
86 if ($epocroot ne "") {
87     $epocroot =~ s,\\,/,g;
88     if ($epocroot =~ m,[^/]$,) {
89         $epocroot = $epocroot."/";
90     }
91     $epocToolsDir = "${epocroot}epoc32/tools/";
92 }
93
94 my $nullDevice = "/dev/null";
95 $nullDevice = "NUL" if ($^O =~ /MSWin/);
96
97 my @capabilitiesToAllow = ("LocalServices", "NetworkServices", "ReadUserData", "UserEnvironment", "WriteUserData", "Location");
98 my @capabilitiesSpecified = ();
99
100 # If arguments were given to the script,
101 if (@ARGV)
102 {
103     # Parse the first given script argument as a ".pkg" file name.
104     my $pkgFileName = shift(@ARGV);
105     my $justCheck = "";
106     my $msgPrefix = "Patching:";
107     my $tempPatchPath = "";
108
109     if ($pkgFileName eq "-c") {
110         $pkgFileName = shift(@ARGV);
111         $justCheck = true;
112         $msgPrefix = "Warning:";
113     }
114
115     if ($pkgFileName eq "-t") {
116         $tempPatchPath = shift(@ARGV);
117         $pkgFileName = shift(@ARGV);
118     }
119
120     my ($pkgVolume, $pkgPath, $pkgPlainFileName) = File::Spec->splitpath($pkgFileName);
121     if ($tempPatchPath eq "") {
122         $tempPatchPath = File::Spec->catpath($pkgVolume, $pkgPath."patch_capabilities_tmp", "");
123     }
124
125     mkpath($tempPatchPath);
126
127     # These variables will only be set for template .pkg files.
128     my $target;
129     my $platform;
130
131     # Check if using template .pkg and set target/platform variables
132     if (($pkgFileName =~ m|_template\.pkg$|i) && -r($pkgFileName))
133     {
134         my $targetplatform;
135         my $templateFile;
136         my $templateContents;
137         open($templateFile, "< $pkgFileName") or die ("Could not open $pkgFileName");
138         $templateContents = <$templateFile>;
139         close($templateFile);
140         unless (($targetplatform = shift(@ARGV)) || $templateContents !~ /\$\(PLATFORM\)/)
141         {
142             Usage();
143         }
144         $targetplatform = "-" if (!$targetplatform);
145         my @tmpvalues = split('-', $targetplatform);
146         $target = $tmpvalues[0];
147         $platform = $tmpvalues[1];
148
149         # Convert visual target to real target (debug->udeb and release->urel)
150         $target =~ s/debug/udeb/i;
151         $target =~ s/release/urel/i;
152
153         if (($platform =~ m/^gcce$/i) && ($ENV{SBS_HOME})) {
154             # Print a informative note in case suspected misuse is detected.
155             print "\nNote: You must use armv5 as platform when packaging gcce binaries built using symbian-sbsv2 mkspec.\n";
156         }
157     }
158
159     # If the specified ".pkg" file exists (and can be read),
160     if (($pkgFileName =~ m|\.pkg$|i) && -r($pkgFileName))
161     {
162         print ("\n");
163         if ($justCheck) {
164             print ("Checking");
165         } else {
166             print ("Patching");
167         }
168         print (" package file and relevant binaries...\n");
169
170         if (!$justCheck) {
171             # If there are more arguments given, parse them as capabilities.
172             if (@ARGV)
173             {
174                 @capabilitiesSpecified = ();
175                 while (@ARGV)
176                 {
177                     push (@capabilitiesSpecified, pop(@ARGV));
178                 }
179             }
180         }
181
182         # Start with no binaries listed.
183         my @binaries = ();
184         my $binariesDelimeter = "///";
185
186         my $tempPkgFileName = $tempPatchPath."/__TEMP__".$pkgPlainFileName;
187
188         if (!$justCheck) {
189             unlink($tempPkgFileName);
190             open (NEW_PKG, ">>".$tempPkgFileName);
191         }
192         open (PKG, "<".$pkgFileName);
193
194         my $checkFailed = "";
195         my $somethingPatched = "";
196
197         # Parse each line.
198         while (<PKG>)
199         {
200             my $line = $_;
201             my $newLine = $line;
202
203             # Patch pkg UID if it's in protected range
204             if ($line =~ m/^\#.*\((0x[0-7][0-9a-fA-F]*)\).*$/)
205             {
206                 my $oldUID = $1;
207                 print ("$msgPrefix UID $oldUID is not compatible with self-signing!\n");
208
209                 if ($justCheck) {
210                     $checkFailed = true;
211                 } else {
212                     my $newUID = $oldUID;
213                     $newUID =~ s/0x./0xE/i;
214                     $newLine =~ s/$oldUID/$newUID/;
215                     print ("$msgPrefix Package UID changed to: $newUID.\n");
216                     $somethingPatched = true;
217                 }
218             }
219
220             # If the line specifies a file, parse the source and destination locations.
221             if ($line =~ m|^ *\"([^\"]+)\"\s*\-\s*\"([^\"]+)\"|)
222             {
223                 my $sourcePath = $1;
224
225                 # If the given file is a binary, check the target and binary type (+ the actual filename) from its path.
226                 if ($sourcePath =~ m:\w+(\.dll|\.exe)$:i)
227                 {
228                     # Do preprocessing for template pkg,
229                     # In case of template pkg target and platform variables are set
230                     if(length($target) && length($platform))
231                     {
232                         $sourcePath =~ s/\$\(PLATFORM\)/$platform/gm;
233                         $sourcePath =~ s/\$\(TARGET\)/$target/gm;
234                     }
235
236                     my ($dummy1, $dummy2, $binaryBaseName) = File::Spec->splitpath($sourcePath);
237
238                     if ($justCheck) {
239                         push (@binaries, $binaryBaseName.$binariesDelimeter.$sourcePath);
240                     } else {
241                         # Copy original files over to patching dir
242                         # Patching dir will be flat to make it cleanable with QMAKE_CLEAN, so path
243                         # will be collapsed into the file name to avoid name collisions in the rare
244                         # case where custom pkg rules are used to install files with same names from
245                         # different directories (probably using platform checks to choose only one of them.)
246                         my $patchedSourcePath = $sourcePath;
247                         $patchedSourcePath =~ s/[\/\\:]/_/g;
248                         $patchedSourcePath = "$tempPatchPath/$patchedSourcePath";
249                         $newLine =~ s/^.*(\.dll|\.exe)(.*)(\.dll|\.exe)/\"$patchedSourcePath$2$3/i;
250
251                         copy($sourcePath, $patchedSourcePath) or die "$sourcePath cannot be copied for patching.";
252                         push (@binaries, $binaryBaseName.$binariesDelimeter.$patchedSourcePath);
253                     }
254                 }
255             }
256
257             print NEW_PKG $newLine;
258
259             chomp ($line);
260         }
261
262         close (PKG);
263         if (!$justCheck) {
264             close (NEW_PKG);
265
266             unlink($pkgFileName);
267             rename($tempPkgFileName, $pkgFileName);
268         }
269         print ("\n");
270
271         my $baseCommandToExecute = "${epocToolsDir}elftran -vid 0x0 -capability \"%s\" ";
272
273         # Actually set the capabilities of the listed binaries.
274         foreach my $binariesItem(@binaries)
275         {
276             $binariesItem =~ m|^(.*)$binariesDelimeter(.*)$|;
277             my $binaryBaseName = $1;
278             my $binaryPath = $2;
279
280             # Create the command line for setting the capabilities.
281             my $commandToExecute = $baseCommandToExecute;
282             my $executeNeeded = "";
283             if (@capabilitiesSpecified)
284             {
285                 $commandToExecute = sprintf($baseCommandToExecute, join(" ", @capabilitiesSpecified));
286                 $executeNeeded = true;
287                 my $capString = join(" ", @capabilitiesSpecified);
288                 print ("$msgPrefix Patching the the Vendor ID to 0 and the capabilities used to: \"$capString\" in \"$binaryBaseName\".\n");
289             } else {
290                 # Test which capabilities are present and then restrict them to the allowed set.
291                 # This avoid raising the capabilities of apps that already have none.
292                 my $dllCaps;
293                 open($dllCaps, "${epocToolsDir}elftran -dump s $binaryPath |") or die ("ERROR: Could not execute elftran");
294                 my $capsFound = 0;
295                 my $originalVid;
296                 my @capabilitiesToSet;
297                 my $capabilitiesToAllow = join(" ", @capabilitiesToAllow);
298                 my @capabilitiesToDrop;
299                 while (<$dllCaps>) {
300                     if (/^Secure ID: ([0-7][0-9a-fA-F]*)$/) {
301                         my $exeSid = $1;
302                         if ($binaryBaseName =~ /\.exe$/) {
303                             # Installer refuses to install protected executables in a self signed package, so abort if one is detected.
304                             # We can't simply just patch the executable SID, as any registration resources executable uses will be linked to it via SID.
305                             print ("$msgPrefix Executable with SID in the protected range (0x$exeSid) detected: \"$binaryBaseName\". A self-signed sis with protected executables is not supported.\n\n");
306                             $checkFailed = true;
307                         }
308                     }
309                     if (/^Vendor ID: ([0-9a-fA-F]*)$/) {
310                         $originalVid = "$1";
311                     }
312                     if (!$capsFound) {
313                         $capsFound = 1 if (/Capabilities:/);
314                     } else {
315                         $_ = trim($_);
316                         if ($capabilitiesToAllow =~ /$_/) {
317                             push(@capabilitiesToSet, $_);
318                             if (Location =~ /$_/i) {
319                                 print ("$msgPrefix \"Location\" capability detected for binary: \"$binaryBaseName\". This capability is not self-signable for S60 3rd edition feature pack 1 devices, so installing this package on those devices will most likely not work.\n\n");
320                             }
321                         } else {
322                             push(@capabilitiesToDrop, $_);
323                         }
324                     }
325                 }
326                 close($dllCaps);
327                 if ($originalVid !~ "00000000") {
328                     print ("$msgPrefix Non-zero vendor ID (0x$originalVid) is incompatible with self-signed packages in \"$binaryBaseName\"");
329                     if ($justCheck) {
330                         print (".\n\n");
331                         $checkFailed = true;
332                     } else {
333                         print (", setting it to zero.\n\n");
334                         $executeNeeded = true;
335                     }
336                 }
337                 if ($#capabilitiesToDrop) {
338                     my $capsToDropStr = join("\", \"", @capabilitiesToDrop);
339                     $capsToDropStr =~ s/\", \"$//;
340
341                     if ($justCheck) {
342                         print ("$msgPrefix The following capabilities used in \"$binaryBaseName\" are not compatible with a self-signed package: \"$capsToDropStr\".\n\n");
343                         $checkFailed = true;
344                     } else {
345                         if ($binaryBaseName =~ /\.exe$/) {
346                             # While libraries often have capabilities they do not themselves need just to enable them to be loaded by wider variety of processes,
347                             # executables are more likely to need every capability they have been assigned or they won't function correctly.
348                             print ("$msgPrefix Executable with capabilities incompatible with self-signing detected: \"$binaryBaseName\". (Incompatible capabilities: \"$capsToDropStr\".) Reducing capabilities is only supported for libraries.\n");
349                             $checkFailed = true;
350                         } else {
351                             print ("$msgPrefix The following capabilities used in \"$binaryBaseName\" are not compatible with a self-signed package and will be removed: \"$capsToDropStr\".\n");
352                             $executeNeeded = true;
353                         }
354                     }
355                 }
356                 $commandToExecute = sprintf($baseCommandToExecute, join(" ", @capabilitiesToSet));
357             }
358             $commandToExecute .= $binaryPath;
359
360             if ($executeNeeded) {
361                 # Actually execute the elftran command to set the capabilities.
362                 print ("\n");
363                 system ("$commandToExecute > $nullDevice");
364                 $somethingPatched = true;
365             }
366         }
367
368         if ($checkFailed) {
369             print ("\n");
370             if ($justCheck) {
371                 print ("$msgPrefix The package is not compatible with self-signing.\n");
372             } else {
373                 print ("$msgPrefix Unable to patch the package for self-singing.\n");
374             }
375             print ("Use a proper developer certificate for signing this package.\n\n");
376             exit(1);
377         }
378
379         if ($justCheck) {
380             print ("Package is compatible with self-signing.\n");
381         } else {
382             if ($somethingPatched) {
383                 print ("NOTE: A patched package may not work as expected due to reduced capabilities and other modifications,\n");
384                 print ("      so it should not be used for any kind of Symbian signing or distribution!\n");
385                 print ("      Use a proper certificate to avoid the need to patch the package.\n");
386             } else {
387                 print ("No patching was required!\n");
388             }
389         }
390         print ("\n");
391     } else {
392         Usage();
393     }
394 }
395 else
396 {
397     Usage();
398 }