Add a flexible packet-parsing framework
authorW. Trevor King <wking@tremily.us>
Thu, 19 Dec 2013 05:09:42 +0000 (21:09 -0800)
committerW. Trevor King <wking@tremily.us>
Fri, 20 Dec 2013 05:20:49 +0000 (21:20 -0800)
Shunting packet-type processing out to type-specific methods.  For
example, 'public-key packet' packets will be parsed by
PGPPacket._parse_public_key_packet.

From the re docs [1]:

  '+'
    Causes the resulting RE to match 1 or more repetitions of the
    preceding RE. ab+ will match 'a' followed by any non-zero number
    of 'b's; it will not match just 'a'.

  ...

  \W
    For Unicode (str) patterns:
      Matches Unicode word characters; this includes most characters
      that can be part of a word in any language, as well as numbers
      and the underscore. If the ASCII flag is used, only [a-zA-Z0-9_]
      is matched (but the flag affects the entire regular expression,
      so in such cases using an explicit [a-zA-Z0-9_] may be a better
      choice).

    For 8-bit (bytes) patterns:
      Matches characters considered alphanumeric in the ASCII
      character set; this is equivalent to [a-zA-Z0-9_].

[1]: http://docs.python.org/3/library/re.html#regular-expression-syntax

gpg-migrate.py

index c41ddd46a7a2252e120395e8796e372fbf61715b..f754aef8b6498935d8e1f43c4e672ba19c39a669 100755 (executable)
@@ -1,5 +1,6 @@
 #!/usr/bin/python
 
+import re as _re
 import subprocess as _subprocess
 import struct as _struct
 
@@ -50,6 +51,11 @@ class PGPPacket (dict):
         63: 'private',
         }
 
+    _clean_type_regex = _re.compile('\W+')
+
+    def _clean_type(self):
+        return self._clean_type_regex.sub('_', self['type'])
+
     def from_bytes(self, data):
         offset = self._parse_header(data=data)
         packet = data[offset:offset + self['length']]
@@ -57,6 +63,12 @@ class PGPPacket (dict):
             raise ValueError('packet too short ({} < {})'.format(
                 len(packet), self['length']))
         offset += self['length']
+        method_name = '_parse_{}'.format(self._clean_type())
+        method = getattr(self, method_name, None)
+        if not method:
+            raise NotImplementedError(
+                'cannot parse packet type {!r}'.format(self['type']))
+        method(data=packet)
         return offset
 
     def _parse_header(self, data):