configurationengine/source/plugins/symbian/ConeCRMLPlugin/CRMLPlugin/crml_writer.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
equal deleted inserted replaced
-1:000000000000 0:2e8eeb919028
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description:
       
    15 #
       
    16 import re
       
    17 from cone.public import exceptions
       
    18 import crml_reader
       
    19 from crml_model import *
       
    20 
       
    21 class CenRepEntry(object):
       
    22     """
       
    23     Class representing an entry in a CenRep text file.
       
    24     """
       
    25     def __init__(self, **kwargs):
       
    26         self.int            = kwargs.get('int')
       
    27         self.crml_type      = kwargs.get('crml_type')
       
    28         self.confml_type    = kwargs.get('confml_type')
       
    29         self.value          = kwargs.get('value')
       
    30         self.orig_value     = kwargs.get('orig_value')
       
    31         self.access         = kwargs.get('access')
       
    32         self.backup         = kwargs.get('backup', False)
       
    33     
       
    34     @property
       
    35     def metadata(self):
       
    36         return _get_metadata(self.backup)
       
    37     
       
    38     def __lt__(self, other):
       
    39         return crml_reader.convert_num(self.int) < crml_reader.convert_num(other.int)
       
    40 
       
    41 class CenRepRfsRecord(object):
       
    42     """
       
    43     Class representing an entry in the CenRep RFS text file.
       
    44     """
       
    45     def __init__(self, repo_uid, key_uids=None):
       
    46         self.repo_uid = repo_uid
       
    47         self.key_uids = key_uids or []
       
    48     
       
    49     def __eq__(self, other):
       
    50         if type(self) == type(other):   return self.repo_uid == other.repo_uid
       
    51         else:                           return False
       
    52     
       
    53     def __ne__(self, other):
       
    54         return not (self == other)
       
    55     
       
    56     def __lt__(self, other):
       
    57         return  self.repo_uid < other.repo_uid
       
    58     
       
    59     def __repr__(self):
       
    60         return "CenRepRfsRecord(repo_uid=%s, key_uids=%r)" % (self.repo_uid, self.key_uids)
       
    61         
       
    62 
       
    63 class CrmlTxtWriter(object):
       
    64     """
       
    65     Writer class for generating CenRep .txt files based on a CRML model.
       
    66     """
       
    67     
       
    68     def __init__(self, configuration, log):
       
    69         self.configuration = configuration
       
    70         self.log = log
       
    71     
       
    72     def get_cenrep_txt_data(self, repository):
       
    73         """
       
    74         Return the text data for the CenRep txt generated based on the given
       
    75         CRML repository model.
       
    76         @return: Text data for the CenRep text file.
       
    77         """
       
    78         data = []
       
    79         
       
    80         # Generate header lines 
       
    81         data.extend(self.get_header_lines(repository))
       
    82         
       
    83         self._check_repository_attrs(repository)
       
    84         
       
    85         # Generate CenRep entries for all keys
       
    86         cenrep_entries = []
       
    87         for key in repository.keys:
       
    88             cenrep_entries.extend(self.get_cenrep_entries(key))
       
    89         
       
    90         # Generate entry lines based on the entries
       
    91         cenrep_entries.sort()
       
    92         for entry in cenrep_entries:
       
    93             data.append(self.get_cenrep_entry_line(entry))
       
    94         
       
    95         data.append('')
       
    96         
       
    97         # Remove Nones from the line list
       
    98         data = filter(lambda val: val is not None, data)
       
    99         
       
   100         return '\r\n'.join(data)
       
   101     
       
   102     def get_cenrep_rfs_txt_data(self, rfs_records):
       
   103         """
       
   104         Return the text data for the CenRep RFS txt generated based on the given
       
   105         CenRep RFS record list.
       
   106         """
       
   107         data = []
       
   108         
       
   109         # Make a distinct and sorted array of the records
       
   110         records = []
       
   111         for r in rfs_records:
       
   112             if r not in records: records.append(r)
       
   113         records.sort()
       
   114         
       
   115         for record in records:
       
   116             repo_uid = record.repo_uid
       
   117             
       
   118             # Add padding zeros to the UID
       
   119             if len(repo_uid) < 8:
       
   120                 repo_uid = (8 - (len(repo_uid) % 8)) * '0' + repo_uid
       
   121             temp = "CR %s" % repo_uid
       
   122             if record.key_uids:
       
   123                 temp += " %s" % ' '.join(record.key_uids)
       
   124             data.append(temp)
       
   125         
       
   126         return '\r\n'.join(data)
       
   127     
       
   128     def get_cenrep_rfs_record(self, repository):
       
   129         """
       
   130         Return the RFS record for the given CRML repository.
       
   131         
       
   132         @return: A CenRepRfsRecord object if the repository should be listed
       
   133             in cenrep_rfs.txt, None if not.
       
   134         """
       
   135         # Get the UID as a hex value without the leading 0x
       
   136         repo_uid = _translate_key_uid(repository.uid_value)[2:]
       
   137         
       
   138         # Check if the whole repository has RFS=true
       
   139         if repository.rfs:
       
   140             return CenRepRfsRecord(repo_uid)
       
   141         
       
   142         # Collect the UIDs of the keys that should be listed
       
   143         rfs_key_uids = []
       
   144         for key in repository.keys:
       
   145             if self._key_is_rfs(key) and key.int:
       
   146                 # Get the UID as a hex value without the leading 0x
       
   147                 uid = _translate_key_uid(key.int)[2:]
       
   148                 rfs_key_uids.append(uid)
       
   149                     
       
   150         if rfs_key_uids:
       
   151             return CenRepRfsRecord(repo_uid, rfs_key_uids)
       
   152         else:
       
   153             return None
       
   154     
       
   155     def _key_is_rfs(self, key):
       
   156         """
       
   157         @return: True if the key UID should be listed in cenrep_rfs.txt
       
   158         """
       
   159         if isinstance(key, CrmlSimpleKey):
       
   160             return bool(self._get_rfs_value(key.ref))
       
   161         elif isinstance(key, CrmlBitmaskKey):
       
   162             for bit in key.bits:
       
   163                 if self._get_rfs_value(bit.ref):
       
   164                     return True
       
   165         else:
       
   166             return False
       
   167     
       
   168     def _get_rfs_value(self, ref):
       
   169         """
       
   170         @return: The RFS value for the given setting, or None if not available.
       
   171         """
       
   172         if ref is None: return
       
   173         
       
   174         try:
       
   175             feature = self._get_feature(ref)
       
   176         except exceptions.NotFound:
       
   177             # Feature not found in the configuration
       
   178             return None
       
   179         
       
   180         return feature.get_value(attr='rfs')
       
   181     
       
   182     def get_header_lines(self, repository):
       
   183         """
       
   184         Return a list of lines to be written in the header section of the CenRep text file.
       
   185         """
       
   186         data = ['cenrep',
       
   187                 'version %s' % repository.version]
       
   188         
       
   189         if repository.owner:
       
   190             data.append('[owner]')
       
   191             data.append(repository.owner)
       
   192         
       
   193         data.append('[defaultmeta]')
       
   194         data.append(' %d' % _get_metadata(repository.backup))
       
   195         for key in repository.keys:
       
   196             data.append(self.get_defaultmeta_line(key))
       
   197         
       
   198         data.append('[platsec]')
       
   199         acc_text = self.get_access_line(repository.access)
       
   200         if acc_text: acc_text = ' ' + acc_text
       
   201         data.append(acc_text)
       
   202         for key in repository.keys:
       
   203                 data.append(self.get_platsec_line(key, repository))
       
   204         
       
   205         
       
   206         data.append('[Main]')
       
   207         return data
       
   208     
       
   209     def get_cenrep_entries(self, key):
       
   210         """
       
   211         Generate CenRep entries based on the given CRML key object.
       
   212         @return: A list of CenRepEntry objects.
       
   213         """
       
   214         if isinstance(key, CrmlSimpleKey):
       
   215             feature = self._get_feature(key.ref)
       
   216             entry = CenRepEntry(int          = key.int,
       
   217                                 crml_type    = key.type,
       
   218                                 confml_type  = feature.get_type(),
       
   219                                 value        = feature.get_value(),
       
   220                                 orig_value   = feature.get_original_value(),
       
   221                                 backup       = key.backup,
       
   222                                 access       = key.access)
       
   223             return [entry]
       
   224         elif isinstance(key, CrmlBitmaskKey):
       
   225             return self.get_bitmask_key_cenrep_entries(key)
       
   226         elif isinstance(key, CrmlKeyRange):
       
   227             return self.get_key_range_cenrep_entries(key)
       
   228         else:
       
   229             raise TypeError("Unsupported CRML key object type %s" % type(key))
       
   230     
       
   231     def get_key_range_cenrep_entries(self, key_range):
       
   232         """
       
   233         Generate CenRep entries based on the given CrmlKeyRange object.
       
   234         @return: A list of CenRepEntry objects.
       
   235         """
       
   236         entries = []
       
   237         count = 0
       
   238         
       
   239         # Generate the countInt entry if necessary
       
   240         if key_range.count_int is not None and key_range.ref is not None:
       
   241             try:
       
   242                 feature = self._get_feature(key_range.ref)
       
   243                 
       
   244                 # For CT2 output compatibility
       
   245                 if feature.get_type() != 'sequence':
       
   246                     return []
       
   247                 
       
   248                 values = feature.get_value()
       
   249             except exceptions.NotFound:
       
   250                 values = []
       
   251             
       
   252             count = len(values)
       
   253                 
       
   254             entry = CenRepEntry(int         = key_range.count_int,
       
   255                                 crml_type   = 'int',
       
   256                                 confml_type = 'int',
       
   257                                 value       = count,
       
   258                                 backup      = key_range.backup,
       
   259                                 access      = key_range.access)
       
   260             entries.append(entry)
       
   261         
       
   262         # Generate entries based on the sequence values
       
   263         for subkey in key_range.subkeys:
       
   264             full_ref = "%s.%s"% (key_range.ref, subkey.ref)
       
   265             
       
   266             try:
       
   267                 feature = self._get_feature(full_ref)
       
   268                 values = feature.get_value()
       
   269                 confml_type = feature.get_type()
       
   270                 backup = key_range.backup
       
   271             except exceptions.NotFound:
       
   272                 # For CT2 output compatibility
       
   273                 values = ['null' for i in xrange(count)]
       
   274                 confml_type = None
       
   275                 backup = False
       
   276                 
       
   277             for i, value in enumerate(values):
       
   278                 # Calculate the index of the entry
       
   279                 index = self.get_index(crml_reader.convert_num(key_range.first_int),
       
   280                                        crml_reader.convert_num(key_range.first_index),
       
   281                                        crml_reader.convert_num(key_range.index_bits),
       
   282                                        i,
       
   283                                        crml_reader.convert_num(subkey.int))
       
   284                 
       
   285                 entry = CenRepEntry(int         = "0x%x" % index,
       
   286                                     crml_type   = subkey.type,
       
   287                                     confml_type = confml_type,
       
   288                                     value       = value,
       
   289                                     orig_value  = value,
       
   290                                     backup      = backup,
       
   291                                     access      = key_range.access)
       
   292                 entries.append(entry)
       
   293         
       
   294         return entries
       
   295     
       
   296     def get_bitmask_key_cenrep_entries(self, key):
       
   297         """
       
   298         Generate CenRep entries based on the given CrmlBitmaskKey object.
       
   299         @return: A list of CenRepEntry objects.
       
   300         """
       
   301         # Calculate the value based on the bit values
       
   302         # -------------------------------------------
       
   303         value = 0
       
   304         for bit in key.bits:
       
   305             feature = self._get_feature(bit.ref)
       
   306             bit_value = feature.get_value()
       
   307             if bit.invert:  bit_value = not bit_value
       
   308             if bit_value:   value |= 1 << (bit.index - 1)
       
   309         
       
   310         # Generate the textual representation of the bitmask value.
       
   311         # This is done at this point because in get_cenrep_entry_line()
       
   312         # we don't know anymore if the key was a bitmask key or a
       
   313         # simple key.
       
   314         # -------------------------------------------------------------
       
   315         if key.type == 'binary':
       
   316             orig_value = "%X" % value
       
   317             # Add padding zeroes so that the number of digits
       
   318             # is divisible by 8 (done manually since the length
       
   319             # of a binary bitmask is unbounded).
       
   320             padding_zeroes = (8 - len(orig_value) % 8) * '0' 
       
   321             # 4 is a special case for CT2 output compatibility
       
   322             if len(orig_value) != 4:
       
   323                 orig_value = padding_zeroes + orig_value
       
   324         else:
       
   325             orig_value = str(value)
       
   326         
       
   327         entry = CenRepEntry(int         = key.int,
       
   328                             crml_type   = key.type,
       
   329                             confml_type = 'int',
       
   330                             value       = value,
       
   331                             orig_value  = orig_value,
       
   332                             backup      = key.backup,
       
   333                             access      = key.access)
       
   334         return [entry]
       
   335     
       
   336     def get_defaultmeta_line(self, key):
       
   337         """
       
   338         Return the defaultmeta section line for the given CRML key object.
       
   339         """
       
   340         if not isinstance(key, CrmlKeyRange): return None
       
   341         
       
   342         return "%s %s %d" % (key.first_int,
       
   343                              key.last_int,
       
   344                              _get_metadata(key.backup))
       
   345     
       
   346     def get_platsec_line(self, key, repository):
       
   347         """
       
   348         Return the platsec section line for the given CRML key object.
       
   349         """
       
   350         if not isinstance(key, CrmlKeyRange): return None
       
   351         
       
   352         # In a key range platsec entry something must be present, so if
       
   353         # the access object is empty, use cap_rd and cap_wr from the repository's
       
   354         # global access definition
       
   355         access = key.access.copy()
       
   356         is_empty = True
       
   357         for attrname in ('sid_rd', 'cap_rd', 'sid_wr', 'cap_wr'):
       
   358             if getattr(access, attrname) not in ('', None):
       
   359                 is_empty = False
       
   360         if is_empty:
       
   361             access.cap_rd = repository.access.cap_rd
       
   362             access.cap_wr = repository.access.cap_wr
       
   363         
       
   364         acc_text = self.get_access_line(access)
       
   365         if acc_text: acc_text = ' ' + acc_text
       
   366         
       
   367         return "%s %s%s" % (key.first_int,
       
   368                              key.last_int,
       
   369                              acc_text)
       
   370         
       
   371     
       
   372     def get_cenrep_entry_line(self, entry):
       
   373         """
       
   374         Return the text line for a CenRepEntry object.
       
   375         """
       
   376         value = None
       
   377         if entry.crml_type in ('string', 'string8'):
       
   378             if entry.confml_type is None:
       
   379                 pass
       
   380             else:
       
   381                 if entry.orig_value is None:
       
   382                     value = '""'
       
   383                 else:
       
   384                     value = '"%s"' % entry.orig_value
       
   385         elif entry.crml_type == 'int':
       
   386             if entry.confml_type == 'boolean':
       
   387                 if entry.value: value = '1'
       
   388                 else:           value = '0'
       
   389             else:
       
   390                 value = entry.orig_value
       
   391         elif entry.crml_type == 'real':
       
   392             value = entry.orig_value or ''
       
   393         elif entry.crml_type == 'binary':
       
   394             # Empty binary values are denoted by a single dash
       
   395             value = entry.orig_value or '-'
       
   396             
       
   397             if value != '-':
       
   398                 # Make sure that the number of digits is divisible by two
       
   399                 if len(value) % 2 != 0:
       
   400                     value = '0' + value
       
   401         
       
   402         if value is None:
       
   403             value = unicode(entry.value)
       
   404         
       
   405         self._check_value(entry, value)
       
   406         
       
   407         acc_text = self.get_access_line(entry.access)
       
   408         if acc_text: acc_text = ' ' + acc_text
       
   409         
       
   410         return '%s %s %s %d%s' % (_translate_key_uid(entry.int),
       
   411                                   entry.crml_type,
       
   412                                   value,
       
   413                                   entry.metadata,
       
   414                                   acc_text)
       
   415     def _check_value(self, entry, value):
       
   416         """
       
   417         Check that the given value is valid for the given CenRep entry,
       
   418         and log a warning if it is not.
       
   419         """
       
   420         if entry.crml_type == 'int':
       
   421             # Check if the value is a string, since it may already
       
   422             # be an integer
       
   423             if not isinstance(value, basestring):
       
   424                 return
       
   425             
       
   426             try:
       
   427                 value = value.strip()
       
   428                 if value.lower().startswith('0x'):
       
   429                     long(value, 16)
       
   430                 else:
       
   431                     long(value)
       
   432             except ValueError:
       
   433                 self.log.warn("Key %s: Invalid integer value '%s'" % (entry.int, value))
       
   434         elif entry.crml_type == 'real':
       
   435             try:
       
   436                 float(value)
       
   437             except ValueError:
       
   438                 self.log.warn("Key %s: Invalid real value '%s'" % (entry.int, value))
       
   439         elif entry.crml_type == 'binary':
       
   440             if value != '-' and re.match(r'^(0[xX])?[0-9a-fA-F]+$', value) is None:
       
   441                 self.log.warn("Key %s: Invalid binary value '%s'" % (entry.int, value))
       
   442     
       
   443     def _check_repository_attrs(self, repository):
       
   444         """
       
   445         Check that the attributes of the given repository are valid and
       
   446         log warnings if not.
       
   447         """
       
   448         if repository.owner is not None:
       
   449             owner = repository.owner.strip()
       
   450             # An empty owner UID is OK, it doesn't generate anything
       
   451             # invalid into the output
       
   452             if owner != '':
       
   453                 try:
       
   454                     if owner.lower().startswith('0x'):
       
   455                         long(owner, 16)
       
   456                     else:
       
   457                         long(owner)
       
   458                 except ValueError:
       
   459                     self.log.warn("Invalid owner UID '%s'" % owner)
       
   460     
       
   461     def get_access_line(self, access):
       
   462         """
       
   463         Generate a line containing access information based on a CrmlAccess object.
       
   464         """
       
   465         # Write the access information in a specific order, because it
       
   466         # won't work otherwise
       
   467         var_order = ['sid_rd', 'cap_rd', 'sid_wr', 'cap_wr']
       
   468         data = []
       
   469         for varname in var_order:
       
   470             val = getattr(access, varname)
       
   471             if val not in ('', None):
       
   472                 # Using _translate_capability_string() on all, since a SID should
       
   473                 # not contain anything that could be messed up by the function
       
   474                 data.append('%s=%s' % (varname, _translate_capability_string(val)))
       
   475         
       
   476         return ' '.join(data)
       
   477     
       
   478     def _get_feature(self, ref):
       
   479         return self.configuration.get_default_view().get_feature(ref)
       
   480     
       
   481     @classmethod
       
   482     def get_index(cls,firstInt,firstIndex,indexBits,seqIndex, subIndex):
       
   483         """
       
   484         @param firstIndex: The first value available in the keyrange 
       
   485         @param lastInt: The last value available in the keyrange 
       
   486         @param indexBits: The index bits or mask for sequence index
       
   487         @param seqIndex: The sequence index
       
   488         @param subIndex: The sequence sub element index
       
   489         @return: an numeric value for the encoded index
       
   490         """
       
   491         rangeshift = cls.get_range_shift(indexBits)
       
   492         return (((seqIndex+firstIndex) << rangeshift) + firstInt) + subIndex
       
   493 
       
   494     @classmethod
       
   495     def get_seqid(cls,firstInt,firstIndex,indexBits,cenrepkey):
       
   496         """
       
   497         @param firstIndex: The first value available in the keyrange 
       
   498         @param lastInt: The last value available in the keyrange 
       
   499         @param indexBits: The index bits or mask for sequence index
       
   500         @param cenrepkey: Crml key id
       
   501         @return: an numeric value for the encoded index
       
   502         """
       
   503         rangeshift = cls.get_range_shift(indexBits)
       
   504         return (((cenrepkey & indexBits) -firstInt) >> rangeshift)-firstIndex
       
   505 
       
   506     @classmethod
       
   507     def get_subseqid(cls,firstInt,firstIndex,indexBits,cenrepkey):
       
   508         """
       
   509         @param firstIndex: The first value available in the keyrange 
       
   510         @param lastInt: The last value available in the keyrange 
       
   511         @param indexBits: The index bits or mask for sequence index
       
   512         @param cenrepkey: Crml key id
       
   513         @return: an numeric value for the encoded index
       
   514         """
       
   515         range = cls.get_range(indexBits)
       
   516         return (cenrepkey - firstInt) & range
       
   517 
       
   518     @classmethod
       
   519     def get_range_shift(cls,indexBits):
       
   520         """ Get the bit left to the """
       
   521         seqrange = cls.get_range(indexBits)
       
   522         shiftamount = 0
       
   523         for i in range(0,32):
       
   524             if (seqrange >> i)  == 0:
       
   525                 shiftamount = i
       
   526                 break
       
   527         return shiftamount
       
   528 
       
   529     @classmethod
       
   530     def get_range(cls,indexBits):
       
   531         """ Get the bit left to the """
       
   532         indexshift = 0 
       
   533         for i in range(0,32):
       
   534             if (indexBits >> i)  == 0:
       
   535                 indexshift = i
       
   536                 break
       
   537         return ((0x1 << indexshift) - indexBits)-1
       
   538 
       
   539 
       
   540 # =============================================================================
       
   541 # Utility functions
       
   542 # =============================================================================
       
   543 
       
   544 def _get_metadata(backup):
       
   545     metadata = 0
       
   546     if backup: metadata |= 0x1000000
       
   547     return metadata
       
   548         
       
   549 def _translate_key_uid(uid):
       
   550     """Translate a key UID given in CRML so that it matches the output of CT2."""
       
   551     if uid.lower().startswith("0x"):
       
   552         prefix = uid[:2]
       
   553         temp = uid[2:]
       
   554         if int(temp, 16) == 0:  return prefix + "0"
       
   555         else:                   return prefix + uid[2:].lstrip('0')
       
   556     else:
       
   557         if int(uid) == 0:       return "0"
       
   558         else:                   return uid.lstrip('0')
       
   559 
       
   560 def _translate_capability_string(cap_str):
       
   561     """
       
   562     Translate a capability string so that it is
       
   563     suitable for writing to a CenRep txt file.
       
   564     """
       
   565     cap_str = cap_str.replace('AlwaysPass', 'alwayspass').replace('AlwaysFail', 'alwaysfail')
       
   566     
       
   567     # The capability string can be a list separated either by
       
   568     # whitespace or commas
       
   569     if ',' in cap_str:  caps = cap_str.split(',')
       
   570     else:               caps = cap_str.split()
       
   571     
       
   572     # The output must always be comma-separated
       
   573     return ','.join(caps)
       
   574