installationservices/swi/source/certstoretobin/AppGen.pm
changeset 0 ba25891c3a9e
child 25 98b66e4fb0be
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/installationservices/swi/source/certstoretobin/AppGen.pm	Thu Dec 17 08:51:10 2009 +0200
@@ -0,0 +1,1222 @@
+#
+# Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description: 
+#
+
+package AppGen;
+use strict;
+
+my $BuildSystemVersion = $ENV{'SBS_VERSION'};
+
+##############################################
+## the object constructor                   ##
+##############################################
+sub new
+{
+    my $self = {};
+    
+    shift @_;
+    
+    my $actual_num = scalar(@_);
+    $self->error("Expected 9 args, got $actual_num") if ($actual_num != 9);
+    
+    $self->{PLATFORM} = shift @_;
+    $self->{VARIANT} = shift @_;
+    $self->{TARGET_NAME} = shift @_;
+    $self->{OUTPUT_DIR} = shift @_;
+
+    $self->{UTIL} = shift @_;
+    die "util not defined\n" if not $self->{UTIL};
+
+    $self->{INPUT_FILE} = shift @_;
+    $self->{UID} = shift @_;
+    $self->{VID} = shift @_;
+    $self->{INSTALL_DRIVE} = shift @_;
+    
+    $self->{GENERATE_PKGFILE} = 0;
+    $self->{GENERATE_SISFILE} = 0;
+    
+    $self->{PKGFILE} = ($self->{OUTPUT_DIR} . "/" . $self->{TARGET_NAME} 
+			. ".pkg");
+
+    my $input_file = $self->{INPUT_FILE};
+    
+    if ($input_file)
+    {
+	-e $input_file or 
+	    $self->{UTIL}->error("Input file '$input_file' does not exist\n");
+    }
+
+    $self->{UTIL}->detailed("variant: ", $self->{VARIANT}, "\n");
+    $self->{UTIL}->detailed("target_name: ", $self->{TARGET_NAME}, "\n");
+    $self->{UTIL}->detailed("output_dir: ", $self->{OUTPUT_DIR}, "\n");
+    $self->{UTIL}->detailed("platform: ", $self->{PLATFORM}, "\n");
+    $self->{UTIL}->detailed("variant: ", $self->{VARIANT}, "\n");
+    
+    $self->{OUTPUT_DIR} =~ m|(.*)[/\\]+.*$|;
+    $self->{ROOT} = "$1\\..";
+
+    bless($self);
+    return $self;
+}
+
+##############################################
+## Class methods to access per-object data  ##
+##                                          ##
+##############################################
+
+#
+# Perform environment checking
+#
+sub generate_prerequisites()
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+ 
+    my $util = $self->{UTIL};
+    $util->detailed("Checking environment for source generation\n");
+
+    my $output_dir = $self->{OUTPUT_DIR};
+
+    $util->error("$output_dir already exists") if -e $output_dir;
+    mkpath($output_dir, 1, 0777);
+    $util->error("Could not create $output_dir") if not -d $output_dir;
+    
+    my $input_file = $self->{INPUT_FILE};
+    $util->error("'$input_file' does not exist") if not -e $input_file;
+    
+    $util->error("A UID is mandatory for generating sources") 
+	if (not $self->{UID});
+    
+    my $uid = $self->{UID};
+    my $vid = '';
+    if (defined($self->{VID}))
+    {
+	my $vid = $self->{VID};
+    }
+    
+    $util->detailed("Using UID and VID {$uid,$vid} respectively\n");
+}
+
+#
+# Perform environment checking
+#
+sub pkg_prerequisites()
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+
+    my $util = $self->{UTIL};
+
+    my $localised_vendor = shift;
+    my $non_localised_vendor = shift;
+    my $suid = shift;
+
+    if ($localised_vendor or $non_localised_vendor or $suid)
+    {
+	$util->detailed("Checking environment for pkg file generation\n");
+
+	if (not ($localised_vendor and $non_localised_vendor and $suid))
+	{
+
+	    if (not $localised_vendor)
+	    {
+		$util->error("localised_vendor must be specified");
+	    }
+
+	    if (not $non_localised_vendor)
+	    {
+		$util->error("non_localised_vendor must be specified");
+	    }
+	    
+	    if (not $suid)
+	    {
+		$util->error("SIS UID must be specified");
+	    }
+
+	    # This is a weak check since source generation and
+	    # compilation (including SIS file creation) may occur at
+	    # different times.  Thus the uid may be NULL
+	    
+	    my $uid = $self->{UID};
+	    
+	    if ($uid eq $suid)
+	    {
+		$util->error("SIS file UID and Application UID ", 
+			     "should not be the same");
+	    }
+	} 
+
+	$self->{GENERATE_PKGFILE} = 1;
+    } 
+}
+
+#
+# Generate sources
+#
+sub generate(@)
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+    
+    my $output_dir = $self->{OUTPUT_DIR};
+    my $util = $self->{UTIL};
+    
+    my $major = shift;
+    my $minor = shift;
+    my $buildnum = shift;
+    my $suid = shift;
+    my $localised_vendor = shift;
+    my $non_localised_vendor = shift;
+
+    $self->generate_prerequisites();
+    $self->pkg_prerequisites($localised_vendor, $non_localised_vendor, $suid);
+
+    $util->detailed("Output will be generated in $output_dir\n");
+    
+    
+    
+    $self->make_source_src();
+    $self->make_make_src();
+    $self->make_doc_src($major, $minor, $buildnum);
+    
+    if ($self->{GENERATE_PKGFILE})
+    {
+	$self->make_pkg_src($major, $minor, $buildnum, $suid, 
+			    $localised_vendor,
+			    $non_localised_vendor);
+    }
+}
+
+#
+# Perform environment checking
+#
+sub make_prerequisites(@)
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+
+    my $util = $self->{UTIL};
+
+    $util->detailed("Checking environment for building\n");
+    
+    my $output_dir = $self->{OUTPUT_DIR};
+    $util->error("$output_dir does not exist") if (not -d $output_dir);
+    
+    my $platform = $self->{PLATFORM};
+    $util->warning("Platform '$platform' untested.")
+	if ($platform =~ /"winscw"/i and $platform =~ /"armv5"/i);
+    
+    my $variant = $self->{VARIANT};
+    $util->error("$variant not supported") 
+	if ($variant =~ /"urel"/i and $variant =~ /"udeb"/i);
+}
+
+#
+# Perform environment checking
+#
+sub sis_prerequisites()
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+
+    my $key = shift;
+    my $certificate = shift;
+    
+    my $util = $self->{UTIL};
+
+    if ($key or $certificate)
+    {
+    	$util->detailed("Checking environment for SIS file creation\n");
+
+	# Passphrase is optional
+	$util->error("Both Key and certificate must be specified") 
+	    if (not ($key and $certificate));
+	
+	-e $key or $util->error("$key does not exist");    
+	-e $certificate or $util->error("$certificate does not exist");
+
+	$self->{GENERATE_SISFILE} = 1;
+    }
+}
+
+#
+# Build the generated source file.
+#
+sub make(@)
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+    
+    my $target_name = $self->{TARGET_NAME};
+    my $output_dir = $self->{OUTPUT_DIR};
+    my $platform = $self->{PLATFORM};
+    my $variant = $self->{VARIANT};
+    my $util = $self->{UTIL};
+
+    my $key = shift;
+    my $certificate = shift;
+    my $passphrase = shift;
+    
+    $self->make_prerequisites();
+    $self->sis_prerequisites($key, $certificate);
+
+    $util->info("Building...\n");
+    
+    $util->info("Target Executable: ");
+    $util->info("\\epoc32\\RELEASE\\$platform\\$variant\\${target_name}.exe\n");
+    use Cwd;
+    my $whence = Cwd::cwd();
+    
+    chdir "$output_dir" or$util->error("chdir $output_dir failed");
+    
+ 	if ($BuildSystemVersion == "2") 
+ 	{
+ 		my $platformConfig = "$platform"."_"."$variant";
+ 		$util->invoke("sbs -c $platformConfig");
+ 	}
+ 	else
+ 	{
+ 		$util->invoke("bldmake bldfiles");
+ 		$util->invoke("abld build $platform $variant");
+ 		$util->invoke("abld -check build $platform $variant");
+ 	}
+ 	
+   
+    chdir "$whence" or $util->error("chdir $whence failed");
+
+    if ($self->{GENERATE_SISFILE})
+    {
+	$self->make_sis($key, $certificate, $passphrase);
+    }
+}
+
+sub make_doc($)
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+
+    use Cwd;
+    my $whence = Cwd::cwd();
+    
+    my $util = $self->{UTIL};
+    my $output_dir = $self->{OUTPUT_DIR};
+    
+    chdir "$output_dir" or $util->error("chdir $output_dir failed");
+    
+    $util->invoke("doxygen");
+    chdir "$whence" or $util->error("chdir $whence failed");
+}
+
+#
+# Generate the Doxygen configuration file.
+#
+sub make_doc_src()
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+    
+    my $major = shift;
+    my $minor = shift;
+    my $buildnum = shift;
+
+    my $target_name = $self->{TARGET_NAME};
+    my $output_dir = $self->{OUTPUT_DIR};
+    
+    my $util = $self->{UTIL};
+    
+    $util->info("Generating Doxygen configuration files\n");
+    
+    open(DOXYFILE, "> ${output_dir}/Doxyfile") or 
+	$util->error("Could not open Doxyfile!");
+    
+    print DOXYFILE <<_____VERBATIM_____;
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME		   = $target_name
+PROJECT_NUMBER		   = "V${major}.${minor}.${buildnum}"
+OUTPUT_DIRECTORY	   = ./
+DETAILS_AT_TOP		   = YES
+TAB_SIZE		   = 8
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL		   = YES
+EXTRACT_STATIC		   = YES
+CASE_SENSE_NAMES	   = YES
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT			   = ${target_name}.cpp
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML		   = YES
+GENERATE_TREEVIEW	   = YES
+TREEVIEW_WIDTH		   = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LATEX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX		   = NO
+_____VERBATIM_____
+
+	close DOXYFILE;
+}
+
+#
+# Generate .pkg file.
+#
+sub make_pkg_src()
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+
+    my $util = $self->{UTIL};
+    my $pkgfile = $self->{PKGFILE};
+    my $platform = $self->{PLATFORM};
+    my $variant = $self->{VARIANT};
+    
+    my $major = shift;
+    my $minor = shift;
+    my $buildnum = shift;
+    my $suid = shift;
+    my $localised_vendor = shift;
+    my $non_localised_vendor = shift;
+    
+    $util->detailed("Generating SIS .pkg file\n");
+    
+    my $output_dir = $self->{OUTPUT_DIR};
+    my $target_name = $self->{TARGET_NAME};
+    my $install_drive = $self->{INSTALL_DRIVE};
+
+    $pkgfile = "$output_dir/${target_name}.pkg";
+
+    if (not $major and $minor and $buildnum)
+    {
+	$util->warning("Major Minor and build number not specifed.");
+    }
+    
+    $util->detailed("Using Major Minor and build number ",
+		    "{$major, $minor, $buildnum} respectively\n");
+    
+    open(PKGFILEWRITE, "> $pkgfile") or $util->error("Could not open $pkgfile!");
+    
+    print PKGFILEWRITE <<_____VERBATIM_____;
+;Languages
+&EN
+
+; Header
+
+; The application\'s major and minor version numbers are required for
+; version control (e.g., AppName 3.1 specifies a major build 3, and
+; minor build 1.)
+
+; The build-number replaces the variant value which was unimplemented
+; in previous versions of makesis.
+
+; All SIS files require a UID, even if the installed components do not
+; strictly require one.
+
+; The UID value should be unique for SISAPP (SA) types of SIS file
+
+#{"${target_name}-EN"}, ($suid), $major, $minor, $buildnum, TYPE=SA
+
+;FILERUN, RUNINSTALL
+"\\epoc32\\RELEASE\\$platform\\$variant\\${target_name}.exe" - 
+"$install_drive:\\sys\\bin\\${target_name}.exe", FR, RI
+
+; Localised vendor
+%{"$localised_vendor"}
+
+; Non-localised vendor
+:"$non_localised_vendor"
+_____VERBATIM_____
+
+	close PKGFILEWRITE;
+}
+
+#
+# Clean up generated files.
+#
+sub clean($)
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+
+    my $reallyclean = (shift @_ > 1);
+    
+    my $platform = $self->{PLATFORM};
+    my $variant  = $self->{VARIANT};
+    my $target_name = $self->{TARGET_NAME};
+    my $output_dir = $self->{OUTPUT_DIR};
+    my $util = $self->{UTIL};
+
+    $util->error("Unknown target") if not $target_name;
+
+    $util->info("Attempting to clean for '$target_name'\n");
+    
+    use Cwd;
+    my $whence = Cwd::cwd();
+
+    if ($reallyclean)
+    {
+		if ($BuildSystemVersion == "2") 
+		{
+			if (not -e "$output_dir/bld.inf")
+			{
+				if (not -d $output_dir)
+				{
+					mkpath($output_dir, 1, 0777);
+					-d $output_dir or $self->error("Could not create $output_dir");
+				}
+				
+				## Generate the project files so we can clean for the
+				## specifed platform and variant							
+				$self->make_make_src();				
+			}
+			
+			$util->info("Cleaning for platform: $platform, variant: $variant\n");
+			
+			Cwd::cwd() eq "$output_dir" or 
+				chdir "$output_dir" or $util->error("chdir $output_dir failed");
+			
+			my $platformConfig = "$platform"."_"."$variant";
+			$util->invoke("sbs -c $platformConfig reallyclean");			
+			
+			$util->info("System cleaned for platform: $platform, ",
+					"variant: $variant\n");
+		}
+		else
+		{
+			if (not -e "$output_dir/abld.bat")
+			{
+				if (not -d $output_dir)
+				{
+					mkpath($output_dir, 1, 0777);
+					-d $output_dir or $self->error("Could not create $output_dir");
+				}
+						
+				## Generate the project files so we can clean for the
+				## specifed platform and variant			
+				
+				$self->make_make_src();
+			
+				chdir "$output_dir" or $util->error("chdir $output_dir failed");
+				$util->invoke("bldmake bldfiles");
+			}
+			
+			$util->info("Cleaning for platform: $platform, variant: $variant\n");
+			
+			Cwd::cwd() eq "$output_dir" or 
+				chdir "$output_dir" or $util->error("chdir $output_dir failed");
+
+			$util->invoke("abld reallyclean $platform $variant");
+			$util->invoke("bldmake clean");
+			
+			$util->info("System cleaned for platform: $platform, ",
+					"variant: $variant\n");
+		}
+   	}
+    
+    if (-d $output_dir)
+    {
+	$util->info("Removing $output_dir\n");
+	
+	my $root = $self->{ROOT};
+	chdir $root or $util->error("chdir $root failed");
+	
+	use File::Path;
+	
+	my $deleted_count = rmtree($output_dir, $util->diagnostic(), 1);
+	
+	$util->detailed("Deleted $deleted_count files\n");
+	
+	$util->error("Couldn't remove $output_dir") if -d $output_dir;
+    }
+
+    chdir "$whence" or $util->error("chdir $whence failed");
+}
+
+#
+# Generate .SIS file.
+#
+sub make_sis()
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+    
+    my $util = $self->{UTIL};
+    my $output_dir = $self->{OUTPUT_DIR};
+    my $target_name = $self->{TARGET_NAME};
+
+    $util->info("Generating sis file\n");
+    
+    my $args = "";
+    
+    $args = "-v" if ($util->diagnostic());
+    
+    use Cwd;
+    my $whence = Cwd::cwd();
+    
+    my $root = $self->{ROOT};
+    chdir $root or $util->error("chdir $root failed");
+
+    my $sis_file = "${output_dir}/${target_name}";
+
+    my $pkg_file = $self->{PKGFILE};
+    $util->invoke("makesis $args $pkg_file ${sis_file}.unsigned.SIS");
+    
+    my $key = shift;
+    my $certificate = shift;
+    my $passphrase = shift;
+    
+    $util->invoke("signsis", "-s $args", "${sis_file}.unsigned.SIS",	 
+		  "${sis_file}.SIS", "$certificate", "$key", "$passphrase");
+    
+    $util->invoke("signsis", "-o", "${sis_file}.SIS")
+	if ($util->level() >= $util->info());
+
+    chdir "$whence" or $util->error("chdir $whence failed");
+}
+
+#
+# Generate the make and project files.
+#
+sub make_make_src
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+
+    my $util = $self->{UTIL};
+    my $output_dir = $self->{OUTPUT_DIR};
+    my $target_name = $self->{TARGET_NAME};
+
+    $util->info("Generating project files\n");
+
+    open(BLDFILEWRITE, "> ${output_dir}/bld.inf") or 
+	$util->error("Could not open bld.inf!");
+    
+    print BLDFILEWRITE <<_____VERBATIM_____;
+// Project files
+prj_mmpfiles
+${target_name}.mmp
+_____VERBATIM_____
+
+	close BLDFILEWRITE;
+
+    open(MMPFILEWRITE, "> ${output_dir}/${target_name}.mmp") or 
+	$util->error("Could not open ${output_dir}/${target_name}.mmp!");
+    
+    print MMPFILEWRITE <<_____VERBATIM_____;
+CAPABILITY	  TCB
+TARGET		  ${target_name}.exe
+TARGETTYPE	  exe
+UID		  0x1000007A $self->{UID}
+_____VERBATIM_____
+
+if (defined($self->{VID}))
+{
+    print MMPFILEWRITE <<_____VERBATIM_____;
+VENDORID	  $self->{VID}
+_____VERBATIM_____
+}
+
+    print MMPFILEWRITE <<_____VERBATIM_____;
+SOURCEPATH	  .
+SOURCE		  ${target_name}.cpp
+MW_LAYER_SYSTEMINCLUDE_SYMBIAN
+OS_LAYER_SYSTEMINCLUDE_SYMBIAN
+LIBRARY		  euser.lib
+LIBRARY		  EFSRV.lib
+LIBRARY		  hash.lib 
+LIBRARY		  hal.lib
+_____VERBATIM_____
+
+    close MMPFILEWRITE;
+}
+
+#
+# Generate the application code.
+#
+sub make_source_src()
+{
+    my $self = shift;
+    die "self not defined" if (not defined($self));
+
+    my $platform = $self->{PLATFORM};
+    my $variant  = $self->{VARIANT};
+    my $target_name = $self->{TARGET_NAME};
+    my $util = $self->{UTIL};
+    my $output_dir = $self->{OUTPUT_DIR};
+
+    $util->info("Generating source code\n");
+
+    open(FILEWRITE, "> ${output_dir}/${target_name}.cpp") or 
+	log->error("Could not open $target_name!");
+    
+    my $datentime = $util->timestamp();
+    my $storesize = 0;
+
+    print FILEWRITE <<_____VERBATIM_____;
+// ${target_name}.cpp
+//
+// Copyright (c) 2005 Symbian Ltd. All rights reserved.
+//
+
+/** \@file ${target_name}.cpp
+* Deliver an update to the writable SwiCertstore on a device.
+*
+* The certificate store is embedded in the executable.
+*
+* This source code was generated on $datentime
+* by SwiCertStoretobin.pl
+*/
+
+/** \@mainpage
+*
+* The Writable SWI certstore forms part of the security subsystem. The
+* certificates in the SWI certstore are used by SWIS to perform checks
+* when installing software on the device and indirectly by clients of
+* the unified certstore.
+*
+* Updates to the certstore are atomic.	No partial updates to the
+* writable certstore are permitted.
+*
+* The writable SWI certstore will be written to a TCB only writable
+* area on the system drive, "\\\\Resource\\\\SwiCertstore\\\\dat\\\\", 
+* and will complement the SWI certstore, 
+* "Z:\\\\resource\\\\swicertstore.dat", in ROM.
+*
+* Once the certstore update is installed in the handset, dependent
+* processes (processes that have loaded Swicertstore.dll) will each
+* synchronise their internal state at the earliest opportunity.
+*
+* The generated source code varies with the certstore and therefore
+* the generated executable cannot be used to deliver other updates to
+* the device.  
+* 
+* $target_name.exe takes no command line options and is not run-time
+* configurable in any way.
+*/
+
+#include <e32def.h>
+#include <e32err.h>
+#include <f32file.h>
+#include <e32debug.h>
+#include <e32property.h>
+#include <hash.h>
+#include <unifiedcertstore.h>
+#include <f32file.h>
+#include <hal.h>
+
+namespace 
+{
+    const TInt KTimeOut(2000000);
+
+_____VERBATIM_____
+
+my $input_file = $self->{INPUT_FILE};
+if (-z $input_file)
+{
+    print FILEWRITE <<_____VERBATIM_____;
+    TUint8 *rawdata(0);
+_____VERBATIM_____
+}
+else
+{
+print FILEWRITE <<_____VERBATIM_____;
+    TUint8 rawdata[] = 
+    {
+_____VERBATIM_____
+	    
+        open(FILEREAD, "< $input_file") or $util->error("Could not open $input_file!");
+	
+	binmode FILEREAD;
+	
+        $storesize = (stat FILEREAD)[7];
+
+	my $byte;
+	my $rv;
+
+        $util->detailed("Embedding data.\n");
+
+        my $prefix = $util->prefix();
+        $util->prefix('');
+
+	print FILEWRITE "\t";
+	for (my $count = 1;; ++$count)
+	{
+	    $util->detailed(".");
+	    
+	    # Restrict width of dot pattern
+	    $util->detailed("\n") if ($count != 0 and ($count % 70) == 0);
+	    
+	    read(FILEREAD, $byte, 1) or last;
+	    my $v = unpack "C", $byte;
+	    
+	    if ($v < 0x10)
+	    {
+		# Aesthetics
+		printf FILEWRITE "0x0%x", $v;
+	    }
+	    else
+	    {
+		printf FILEWRITE "0x%x", $v;
+	    }
+
+	    print FILEWRITE ","	 if ($count != $storesize);
+	    print FILEWRITE "\n\t" if (($count % 15) == 0);
+	}
+	print "\n";
+        
+        $util->prefix($prefix);
+
+    print FILEWRITE <<_____VERBATIM_____;
+    };
+_____VERBATIM_____
+}
+
+my $digest = $self->{UTIL}->certstore_digest($self->{INPUT_FILE});
+my $tmp_name = $self->{UTIL}->tmp_name($target_name, $self->{UID}, $digest);
+
+print FILEWRITE <<_____VERBATIM_____;
+	const TInt KRawDataLength($storesize);
+
+	_LIT(KDriveSpec, "!:\\\\");
+	_LIT(KSwiCertstoreDatDir, "\\\\Resource\\\\SwiCertstore\\\\dat\\\\");
+	_LIT(KSwiCertstoreTmpDir, "\\\\Resource\\\\SwiCertstore\\\\tmp\\\\");
+	_LIT(KSwiCertstoreTmpFile, 
+	 "\\\\Resource\\\\SwiCertstore\\\\tmp\\\\$tmp_name");
+	_LIT8(KSwiCertstoreDigest, "$digest");
+	_LIT(KUpdate, "Swicertstore Update error");
+}
+
+EXPORT_C TDriveNumber GetSystemDrive()
+	{
+	TDriveNumber driveNo = RFs::GetSystemDrive();
+	return driveNo;
+	}
+
+EXPORT_C TChar GetSystemDriveChar(TDriveNumber aDriveNumber)
+	{
+	TChar driveChar;	
+	RFs::DriveToChar(aDriveNumber, driveChar);
+	return driveChar;
+	}
+
+/**\@brief Ensure that the temporary file is cleaned up if a leave
+ *	occurs.
+ */ 
+void CleanUpTmpFile(TAny *)
+	{
+	RFs rfssession;
+
+	if (KErrNone != rfssession.Connect())
+		{
+		return;
+		}
+    
+	TBuf<3> driveSpec(KDriveSpec);
+	driveSpec[0] = GetSystemDriveChar(GetSystemDrive());
+	rfssession.SetSessionPath(driveSpec);
+	
+	// Ignore errors
+	rfssession.Delete(KSwiCertstoreTmpFile);
+	rfssession.Close();
+	}
+
+/**\@brief Create the writable SwiCertStore\'s temp and data
+ * directories.
+ * 
+ * \@param aFs A file server session.
+ */
+void CreateDirsL(RFs &aFs)
+	{
+	// Create the temp directory where writers ($target_name.exe)
+	// will write their temporary files prior to renaming
+	
+	TInt result(aFs.MkDirAll(KSwiCertstoreTmpDir));
+	
+	if (KErrAlreadyExists != result and KErrNone != result)
+		{
+		User::Leave(result);
+		}
+	
+	// Writers ($target_name.exe) will atomically rename (move) their
+	// temporary files into KSwiCertstoreDatDir
+	
+	result = aFs.MkDirAll(KSwiCertstoreDatDir);
+	
+	if (KErrAlreadyExists != result and KErrNone != result)
+		{
+		User::Leave(result);
+		}
+	}
+
+/**\@brief List the contents of the specified directory.
+ *
+ * \@note The caller is responsible for deleting the memory pointed to
+ * by the returned pointer.
+ *
+ * \@param aFs A file server session.
+ * \@param aFn The directory to list.
+ *
+ * \@return A list of directory entries.
+ */
+CDir *LsLC(RFs &aFs, const TDesC &aFn)
+	{
+	CDir *entry_list(0);
+	
+	User::LeaveIfError(aFs.GetDir(aFn, KEntryAttNormal,
+								  ESortByName | EDescending, entry_list));
+	
+	// The caller is responsible for cleaning up the entry_list
+	CleanupStack::PushL(entry_list);
+	
+	return entry_list;
+	}
+
+/**\@brief Get the target filename for the update.
+ *
+ * The returned filename is generated by incrementing the highest
+ * numbered filename in directory
+ * "\\\\Resource\\\\SwiCertstore\\\\dat".
+ *
+ * \@note The caller is responsible for deleting the memory pointed to
+ * by the returned pointer.
+ *
+ * \@param aFs A file server session.
+ * \@param aFn A preallocated buffer to hold the filename.
+ *
+ * \@return A list of directory entries. 
+ */
+CDir *GetNextFileNameLC(RFs &aFs, TFileName &aFn)
+	{
+	CDir *entry_list(LsLC(aFs, KSwiCertstoreDatDir));
+	
+	aFn = KSwiCertstoreDatDir;
+	
+	TInt number(1);
+	
+	if (entry_list->Count() > 0)
+		{
+		const TEntry &item((*entry_list)[0]);
+		
+		__ASSERT_ALWAYS(EFalse == item.IsDir(), User::Panic(KUpdate,
+															KErrCorrupt));
+		
+		TLex lexer(item.iName);
+		const TInt result(lexer.Val(number));
+		
+		if (KErrNone != result)
+			{
+			User::Panic(KUpdate, result);
+			}
+		
+		number += 1;
+		}
+	
+	aFn.AppendNumFixedWidth(number, EDecimal, 8);
+	
+	return entry_list;
+	}
+
+/**Convert a hex char to its decimal representation.
+ *
+ * \@param aCh The char to convert.
+ *
+ * \@return The integer representation of the supplied char.
+ */
+TInt ToIntL(const TChar aCh)
+	{
+	TInt a(-1);
+	
+	if ((aCh >= 'A') and (aCh <= 'F'))
+		{
+		a = aCh - 'A' + 10;
+		}
+	if ((aCh >= 'a') and (aCh <= 'f'))
+		{
+		a = aCh - 'a' + 10;
+		}
+	else if (aCh >= '0' and aCh <= '9')
+		{
+		a = aCh - '0';
+		}
+	else
+		{
+		User::Leave(KErrCorrupt);
+		}
+	
+	__ASSERT_ALWAYS((a >= 0 and a < 16), 
+			User::Panic(KUpdate, KErrCorrupt)); // four bits
+	
+	return a;
+	}
+
+/**\@brief Convert two hex characters to a single byte.
+ *		
+ * \@param aChars A pointer to the char sequence to convert.
+ *
+ * \@param aBuf A reference to a buffer that will contain the
+ * binary representation of the char sequence.	The buffer must be be
+ * at least half the length of the char sequence.
+ */
+template <TInt length>
+void Hex2BinL(TBuf8<length> &aBuf, TPtrC8 aChars)
+	{
+	__ASSERT_ALWAYS((aChars.Length() == 2 * length), User::Panic(KUpdate, 
+																 KErrArgument));
+	__ASSERT_ALWAYS((length % 2 == 0), User::Panic(KUpdate, KErrArgument));
+	
+	for (int idx(0), appends(0); appends < length; ++appends, idx += 2)
+		{			
+		aBuf.Append((ToIntL(aChars[idx]) << 4) | ToIntL(aChars[idx + 1]));
+		}
+	}
+
+/**\@brief Verify the integrity of the writable cerstore on disk.
+ *
+ * \@param aFs A file server session.
+ * \@param aName The name of the file to verify.
+ */
+void VerifyL(RFs &aFs, const TDesC &aName)
+	{
+	RFile file;
+
+	User::LeaveIfError(file.Open(aFs, aName,
+								 EFileRead | EFileShareExclusive));
+	CleanupClosePushL(file);
+
+	TBuf8<1024> block;
+	
+	CMD5* md5 = CMD5::NewL();
+	CleanupStack::PushL(md5);
+	
+	for (;;)
+		{
+		User::LeaveIfError(file.Read(block));
+		
+		if (block.Size() > 0)
+			{
+			md5->Update(block);
+			continue;
+			}
+		
+		break;
+		}
+	
+	TBuf8<MD5_HASH> hash(md5->Final());
+	CleanupStack::PopAndDestroy(md5);
+	
+	TBuf8<MD5_HASH> referencehash;
+	
+	TPtrC8 digest(KSwiCertstoreDigest);
+	
+	// Convert the hex digest to binary.  Convert each hex character
+	// to four bits
+	Hex2BinL(referencehash, digest);
+	
+	if (referencehash != hash)
+		{
+		User::Leave(KErrCorrupt);
+		}
+	
+	CleanupStack::PopAndDestroy(); // file
+	}
+
+/**\@brief Start the writable SwiCertstore update.
+ * 
+ * The update is atomic.
+ */
+void StartL(void)
+	{
+	TPtr8 data(rawdata, KRawDataLength, KRawDataLength);
+	
+	RFs rfssession;
+	
+	User::LeaveIfError(rfssession.Connect());
+	CleanupClosePushL(rfssession);
+		
+	TBuf<3> driveSpec(KDriveSpec);
+	driveSpec[0] = GetSystemDriveChar(GetSystemDrive());
+	User::LeaveIfError(rfssession.SetSessionPath(driveSpec));		
+	
+	CreateDirsL(rfssession);
+	
+	// Ensure that the tmp file is cleaned up if a leave occurs
+	CleanupStack::PushL(TCleanupItem(CleanUpTmpFile, 0));
+	
+	RFile file;	
+	
+	// If the temp file already exists we will overwrite it.  UIDs are
+	// unique after all.
+	User::LeaveIfError(file.Replace(rfssession, KSwiCertstoreTmpFile, 
+									EFileWrite | EFileShareExclusive));
+	
+	CleanupClosePushL(file);
+
+	// write is not atomic
+	User::LeaveIfError(file.Write(data, KRawDataLength));
+	
+	User::LeaveIfError(file.Flush());
+	CleanupStack::PopAndDestroy(); // file
+	
+	VerifyL(rfssession, KSwiCertstoreTmpFile);
+	
+	TFileName fullpath;
+	
+	CDir *entry_list(0);
+	for (;;)
+		{
+		entry_list = GetNextFileNameLC(rfssession, fullpath);
+		
+		CleanupStack::Check(entry_list);
+		
+		// RFs::Rename is atomic for non-removeable media even in the
+		// event of power failure
+		TInt result(rfssession.Rename(KSwiCertstoreTmpFile, fullpath));
+	
+		if (KErrInUse == result or KErrAlreadyExists == result)
+			{
+			// It might be that another writer wrote fullpath first.
+			// We will need to scan the directory again
+			CleanupStack::PopAndDestroy(entry_list);
+			continue;
+			}
+		
+		if (KErrNone == result)
+			{
+			// entry_list remains on the cleanup stack and is used
+			// later
+			CleanupStack::Check(entry_list); 
+			break;
+			}
+		
+		User::Leave(result);
+		}
+	
+	// We can now notify all parties that are interested in the
+	// update.
+	const TInt result(RProperty::Set(KUnifiedCertStorePropertyCat, 
+									 EUnifiedCertStoreFlag, 1));
+
+	// If property has not been defined then no one is interested in
+	// the update
+	
+	if (KErrNotFound != result and KErrNone != result)
+		{
+		User::Leave(result);
+		}
+	
+	// Wait for an arbitrary amount of time, in the hope that readers
+	// will have closed any other files in KSwiCertstoreDatDir,
+	// before attempting to delete all files in KSwiCertstoreDatDir
+	// apart from the update we have just placed there.
+	
+	User::After(KTimeOut);
+	
+	for (TInt idx(0), sz(entry_list->Count()); idx < sz; ++idx)
+		{
+		const TEntry &item((*entry_list)[idx]);
+		
+		fullpath = KSwiCertstoreDatDir;
+		fullpath.Append(item.iName);
+	
+		// Ignore errors - delete as much as possible.	 It may be
+		// that a legacy file is currently being read (KErrInUse)
+		rfssession.Delete(fullpath);
+		}
+	
+	CleanupStack::PopAndDestroy(entry_list);
+	
+	// We pop this here because it is now on the top of the stack.
+	// It could in fact has been dealt with following a successful
+	// rename except that the cleanupitem was not on the top of the
+	// stack at that point.
+	CleanupStack::Pop(); // cleanupfile
+	
+	// Now delete any temporary files in the temp directory. There
+	// may be other simultaneous update attempts in which case we may
+	// be about to defenestrate these.	 This race condition is
+	// considered acceptable since there is no agreed protocol
+	// between writers and we must clean up ealier failed updates
+	// attempts at some point.
+	entry_list = LsLC(rfssession, KSwiCertstoreTmpDir);
+	
+	for (TInt idx(0), sz(entry_list->Count()); idx < sz; ++idx)
+		{
+		const TEntry &item((*entry_list)[idx]);
+		
+		fullpath = KSwiCertstoreTmpDir;
+		fullpath.Append(item.iName);
+	
+		// Ignore errors - delete as much as possible.	 It may be
+		// that a legacy file is currently being read (KErrInUse)
+		rfssession.Delete(fullpath);
+		}
+	
+	
+	CleanupStack::PopAndDestroy(2, &rfssession);
+	}
+
+/**\@brief Main.
+ *
+ * \@return A status code.
+ */
+TInt E32Main(void)
+	// No arguments are permitted
+	{
+	__UHEAP_MARK;
+	
+	const CTrapCleanup *const stack(CTrapCleanup::New());
+	TInt result(KErrNoMemory);
+	
+	if (stack)
+		{
+		TRAP(result, StartL());
+		
+		if (KErrNone != result)
+			{
+			// Register with the task scheduler and try again later? 
+			}
+		
+		delete stack;
+		}
+	
+	__UHEAP_MARKEND;
+
+	return result;
+	}
+
+_____VERBATIM_____
+    
+    close FILEWRITE;
+}
+
+#
+# Set the logging level
+#
+sub level
+{
+    my $self = shift;
+    die "self not defined\n" if (not defined($self));
+
+    if (@_) { $self->{LEVEL} = shift }
+    return $self->{LEVEL};
+}
+
+1;  # so the require or use succeeds
+