]> git.donarmstrong.com Git - dak.git/blobdiff - daklib/dbconn.py
Add replay protection for uploaded changes files.
[dak.git] / daklib / dbconn.py
index 31caa902dcd8f50ca06cde5f31c9ab16252e768f..c2621e36d150a38b042fd25182e15ff7374cbb87 100644 (file)
 ################################################################################
 
 import apt_pkg
+import daklib.daksubprocess
 import os
 from os.path import normpath
 import re
 import psycopg2
+import subprocess
 import traceback
-import commands
-import signal
 
 try:
     # python >= 2.6
@@ -52,7 +52,6 @@ except:
 from datetime import datetime, timedelta
 from errno import ENOENT
 from tempfile import mkstemp, mkdtemp
-from subprocess import Popen, PIPE
 from tarfile import TarFile
 
 from inspect import getargspec
@@ -110,11 +109,11 @@ class DebVersion(UserDefinedType):
         return None
 
 sa_major_version = sqlalchemy.__version__[0:3]
-if sa_major_version in ["0.5", "0.6", "0.7", "0.8"]:
+if sa_major_version in ["0.5", "0.6", "0.7", "0.8", "0.9"]:
     from sqlalchemy.databases import postgres
     postgres.ischema_names['debversion'] = DebVersion
 else:
-    raise Exception("dak only ported to SQLA versions 0.5 to 0.8.  See daklib/dbconn.py")
+    raise Exception("dak only ported to SQLA versions 0.5 to 0.9.  See daklib/dbconn.py")
 
 ################################################################################
 
@@ -311,7 +310,7 @@ class ORMObject(object):
         return object_session(self)
 
     def clone(self, session = None):
-        '''
+        """
         Clones the current object in a new session and returns the new clone. A
         fresh session is created if the optional session parameter is not
         provided. The function will fail if a session is provided and has
@@ -324,8 +323,8 @@ class ORMObject(object):
         WARNING: Only persistent (committed) objects can be cloned. Changes
         made to the original object that are not committed yet will get lost.
         The session of the new object will always be rolled back to avoid
-        ressource leaks.
-        '''
+        resource leaks.
+        """
 
         if self.session() is None:
             raise RuntimeError( \
@@ -433,27 +432,6 @@ def get_architecture(architecture, session=None):
 
 __all__.append('get_architecture')
 
-# TODO: should be removed because the implementation is too trivial
-@session_wrapper
-def get_architecture_suites(architecture, session=None):
-    """
-    Returns list of Suite objects for given C{architecture} name
-
-    @type architecture: str
-    @param architecture: Architecture name to search for
-
-    @type session: Session
-    @param session: Optional SQL session object (a temporary one will be
-    generated if not supplied)
-
-    @rtype: list
-    @return: list of Suite objects for the given name (may be empty)
-    """
-
-    return get_architecture(architecture, session).suites
-
-__all__.append('get_architecture_suites')
-
 ################################################################################
 
 class Archive(object):
@@ -519,11 +497,6 @@ __all__.append('BinContents')
 
 ################################################################################
 
-def subprocess_setup():
-    # Python installs a SIGPIPE handler by default. This is usually not what
-    # non-Python subprocesses expect.
-    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
-
 class DBBinary(ORMObject):
     def __init__(self, package = None, source = None, version = None, \
         maintainer = None, architecture = None, poolfile = None, \
@@ -560,8 +533,8 @@ class DBBinary(ORMObject):
         package does not contain any regular file.
         '''
         fullpath = self.poolfile.fullpath
-        dpkg = Popen(['dpkg-deb', '--fsys-tarfile', fullpath], stdout = PIPE,
-            preexec_fn = subprocess_setup)
+        dpkg_cmd = ('dpkg-deb', '--fsys-tarfile', fullpath)
+        dpkg = daklib.daksubprocess.Popen(dpkg_cmd, stdout=subprocess.PIPE)
         tar = TarFile.open(fileobj = dpkg.stdout, mode = 'r|')
         for member in tar.getmembers():
             if not member.isdir():
@@ -585,11 +558,8 @@ class DBBinary(ORMObject):
         '''
         import utils
         fullpath = self.poolfile.fullpath
-        deb_file = open(fullpath, 'r')
-        stanza = utils.deb_extract_control(deb_file)
-        deb_file.close()
-
-        return stanza
+        with open(fullpath, 'r') as deb_file:
+            return utils.deb_extract_control(deb_file)
 
     def read_control_fields(self):
         '''
@@ -599,10 +569,15 @@ class DBBinary(ORMObject):
         @rtype: dict
         @return: fields of the control section as a dictionary.
         '''
-        import apt_pkg
         stanza = self.read_control()
         return apt_pkg.TagSection(stanza)
 
+    @property
+    def proxy(self):
+        session = object_session(self)
+        query = session.query(BinaryMetadata).filter_by(binary=self)
+        return MetadataProxy(session, query)
+
 __all__.append('DBBinary')
 
 @session_wrapper
@@ -1186,9 +1161,6 @@ def get_ldap_name(entry):
 ################################################################################
 
 class Keyring(object):
-    gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
-                     " --with-colons --fingerprint --fingerprint"
-
     keys = {}
     fpr_lookup = {}
 
@@ -1218,11 +1190,14 @@ class Keyring(object):
         if not self.keyring_id:
             raise Exception('Must be initialized with database information')
 
-        k = os.popen(self.gpg_invocation % keyring, "r")
+        cmd = ["gpg", "--no-default-keyring", "--keyring", keyring,
+               "--with-colons", "--fingerprint", "--fingerprint"]
+        p = daklib.daksubprocess.Popen(cmd, stdout=subprocess.PIPE)
+
         key = None
-        signingkey = False
+        need_fingerprint = False
 
-        for line in k:
+        for line in p.stdout:
             field = line.split(":")
             if field[0] == "pub":
                 key = field[4]
@@ -1231,18 +1206,20 @@ class Keyring(object):
                 if "@" in addr:
                     self.keys[key]["email"] = addr
                     self.keys[key]["name"] = name
-                self.keys[key]["fingerprints"] = []
-                signingkey = True
-            elif key and field[0] == "sub" and len(field) >= 12:
-                signingkey = ("s" in field[11])
+                need_fingerprint = True
             elif key and field[0] == "uid":
                 (name, addr) = self.parse_address(field[9])
                 if "email" not in self.keys[key] and "@" in addr:
                     self.keys[key]["email"] = addr
                     self.keys[key]["name"] = name
-            elif signingkey and field[0] == "fpr":
-                self.keys[key]["fingerprints"].append(field[9])
+            elif need_fingerprint and field[0] == "fpr":
+                self.keys[key]["fingerprints"] = [field[9]]
                 self.fpr_lookup[field[9]] = key
+                need_fingerprint = False
+
+        r = p.wait()
+        if r != 0:
+            raise subprocess.CalledProcessError(r, cmd)
 
     def import_users_from_ldap(self, session):
         import ldap
@@ -1882,6 +1859,9 @@ class SignatureHistory(ORMObject):
         self.contents_sha1 = signed_file.contents_sha1()
         return self
 
+    def query(self, session):
+        return session.query(SignatureHistory).filter_by(fingerprint=self.fingerprint, signature_timestamp=self.signature_timestamp, contents_sha1=self.contents_sha1).first()
+
 __all__.append('SignatureHistory')
 
 ################################################################################
@@ -2008,6 +1988,12 @@ class DBSource(ORMObject):
             fileset.add(name)
         return fileset
 
+    @property
+    def proxy(self):
+        session = object_session(self)
+        query = session.query(SourceMetadata).filter_by(source=self)
+        return MetadataProxy(session, query)
+
 __all__.append('DBSource')
 
 @session_wrapper
@@ -2119,27 +2105,28 @@ __all__.append('get_sources_from_name')
 # FIXME: This function fails badly if it finds more than 1 source package and
 # its implementation is trivial enough to be inlined.
 @session_wrapper
-def get_source_in_suite(source, suite, session=None):
+def get_source_in_suite(source, suite_name, session=None):
     """
-    Returns a DBSource object for a combination of C{source} and C{suite}.
+    Returns a DBSource object for a combination of C{source} and C{suite_name}.
 
       - B{source} - source package name, eg. I{mailfilter}, I{bbdb}, I{glibc}
-      - B{suite} - a suite name, eg. I{unstable}
+      - B{suite_name} - a suite name, eg. I{unstable}
 
     @type source: string
     @param source: source package name
 
-    @type suite: string
+    @type suite_name: string
     @param suite: the suite name
 
     @rtype: string
     @return: the version for I{source} in I{suite}
 
     """
-
-    q = get_suite(suite, session).get_sources(source)
+    suite = get_suite(suite_name, session)
+    if suite is None:
+        return None
     try:
-        return q.one()
+        return suite.get_sources(source).one()
     except NoResultFound:
         return None
 
@@ -2501,6 +2488,37 @@ __all__.append('SourceMetadata')
 
 ################################################################################
 
+class MetadataProxy(object):
+    def __init__(self, session, query):
+        self.session = session
+        self.query = query
+
+    def _get(self, key):
+        metadata_key = self.session.query(MetadataKey).filter_by(key=key).first()
+        if metadata_key is None:
+            return None
+        metadata = self.query.filter_by(key=metadata_key).first()
+        return metadata
+
+    def __contains__(self, key):
+        if self._get(key) is not None:
+            return True
+        return False
+
+    def __getitem__(self, key):
+        metadata = self._get(key)
+        if metadata is None:
+            raise KeyError
+        return metadata.value
+
+    def get(self, key, default=None):
+        try:
+            return self[key]
+        except KeyError:
+            return default
+
+################################################################################
+
 class VersionCheck(ORMObject):
     def __init__(self, *args, **kwargs):
        pass
@@ -2560,6 +2578,7 @@ class DBConn(object):
             'changelogs_text',
             'changes',
             'component',
+            'component_suite',
             'config',
             'dsc_files',
             'external_overrides',
@@ -2835,7 +2854,10 @@ class DBConn(object):
                                  srcformats = relation(SrcFormat, secondary=self.tbl_suite_src_formats,
                                      backref=backref('suites', lazy='dynamic')),
                                  archive = relation(Archive, backref='suites'),
-                                 acls = relation(ACL, secondary=self.tbl_suite_acl_map, collection_class=set)),
+                                 acls = relation(ACL, secondary=self.tbl_suite_acl_map, collection_class=set),
+                                 components = relation(Component, secondary=self.tbl_component_suite,
+                                                   order_by=self.tbl_component.c.ordering,
+                                                   backref=backref('suites'))),
                 extension = validator)
 
         mapper(Uid, self.tbl_uid,
@@ -2958,5 +2980,3 @@ class DBConn(object):
         return session
 
 __all__.append('DBConn')
-
-