Add support for basic quoting in the DNS zone file 0.0.6
authorEvan Broder <broder@mit.edu>
Sat, 1 Nov 2008 08:21:47 +0000 (04:21 -0400)
committerEvan Broder <broder@mit.edu>
Sat, 1 Nov 2008 08:21:47 +0000 (04:21 -0400)
svn path=/trunk/packages/invirt-dns/; revision=1490

debian/changelog
invirt-dns

index b34b96a..ff67d88 100644 (file)
@@ -1,3 +1,9 @@
+invirt-dns (0.0.6) unstable; urgency=low
+
+  * Add support for basic quoting in the zone file
+
+ -- Evan Broder <broder@mit.edu>  Sat, 01 Nov 2008 04:01:09 -0400
+
 invirt-dns (0.0.5) unstable; urgency=low
 
   * Instead of hacking in support for prod.xvm.mit.edu, support zone files
index 976a9a7..4193be1 100755 (executable)
@@ -12,6 +12,7 @@ import invirt.database
 import psycopg2
 import sqlalchemy
 import time
+import re
 
 class DatabaseAuthority(common.ResolverBase):
     """An Authority that is loaded from a file."""
@@ -113,11 +114,77 @@ class DatabaseAuthority(common.ResolverBase):
             #Doesn't exist
             return defer.fail(failure.Failure(dns.AuthoritativeDomainError(name)))
 
+class QuotingBindAuthority(authority.BindAuthority):
+    """
+    A BindAuthority that (almost) deals with quoting correctly
+    
+    This will catch double quotes as marking the start or end of a
+    quoted phrase, unless the double quote is escaped by a backslash
+    """
+    # Grab everything up to the first whitespace character or
+    # quotation mark not proceeded by a backslash
+    whitespace_re = re.compile(r'(.*?)([\t\n\x0b\x0c\r ]+|(?<!\\)")')
+    def collapseContinuations(self, lines):
+        L = []
+        state = 0
+        for line in lines:
+            if state == 0:
+                if line.find('(') == -1:
+                    L.append(line)
+                else:
+                    L.append(line[:line.find('(')])
+                    state = 1
+            else:
+                if line.find(')') != -1:
+                    L[-1] += ' ' + line[:line.find(')')]
+                    state = 0
+                else:
+                    L[-1] += ' ' + line
+        lines = L
+        L = []
+        for line in lines:
+            in_quote = False
+            split_line = []
+            while len(line) > 0:
+                match = self.whitespace_re.match(line)
+                if match is None:
+                    # If there's no match, that means that there's no
+                    # whitespace in the rest of the line, so it should
+                    # be treated as a single entity, quoted or not
+                    #
+                    # This also means that a closing quote isn't
+                    # strictly necessary if the line ends the quote
+                    substr = line
+                    end = ''
+                else:
+                    substr, end = match.groups()
+                
+                if in_quote:
+                    # If we're in the middle of the quote, the string
+                    # we just grabbed belongs at the end of the
+                    # previous string
+                    #
+                    # Including the whitespace! Unless it's not
+                    # whitespace and is actually a closequote instead
+                    split_line[-1] += substr + (end if end != '"' else '')
+                else:
+                    # If we're not in the middle of a quote, than this
+                    # is the next new string
+                    split_line.append(substr)
+                
+                if end == '"':
+                    in_quote = not in_quote
+                
+                # Then strip off what we just processed
+                line = line[len(substr + end):]
+            L.append(split_line)
+        return filter(None, L)
+
 if '__main__' == __name__:
     resolvers = []
     for zone in config.dns.zone_files:
         for origin in config.dns.domains:
-            r = authority.BindAuthority(zone)
+            r = QuotingBindAuthority(zone)
             # This sucks, but if I want a generic zone file, I have to
             # reload the information by hand
             r.origin = origin