]> git.decadent.org.uk Git - dak.git/blob - daklib/binary.py
back to ./ for tarballs
[dak.git] / daklib / binary.py
1 #!/usr/bin/python
2
3 """
4 Functions related debian binary packages
5
6 @contact: Debian FTPMaster <ftpmaster@debian.org>
7 @copyright: 2009  Mike O'Connor <stew@debian.org>
8 @license: GNU General Public License version 2 or later
9 """
10
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24
25 ################################################################################
26
27 # <Ganneff> are we going the xorg way?
28 # <Ganneff> a dak without a dak.conf?
29 # <stew> automatically detect the wrong settings at runtime?
30 # <Ganneff> yes!
31 # <mhy> well, we'll probably always need dak.conf (how do you get the database setting
32 # <mhy> but removing most of the config into the database seems sane
33 # <Ganneff> mhy: dont spoil the fun
34 # <Ganneff> mhy: and i know how. we nmap localhost and check all open ports
35 # <Ganneff> maybe one answers to sql
36 # <stew> we will discover projectb via avahi
37 # <mhy> you're both sick
38 # <mhy> really fucking sick
39
40 ################################################################################
41
42 import os
43 import shutil
44 import tempfile
45 import tarfile
46 import commands
47 import traceback
48 import atexit
49 from debian_bundle import deb822
50 from dbconn import DBConn
51
52 class Binary(object):
53     def __init__(self, filename):
54         self.filename = filename
55         self.tmpdir = None
56         self.chunks = None
57
58     def __del__(self):
59         """
60         make sure we cleanup when we are garbage collected.
61         """
62         self._cleanup()
63
64     def _cleanup(self):
65         """
66         we need to remove the temporary directory, if we created one
67         """
68         if self.tmpdir and os.path.exists(self.tmpdir):
69             shutil.rmtree(self.tmpdir)
70             self.tmpdir = None
71
72     def __scan_ar(self):
73         # get a list of the ar contents
74         if not self.chunks:
75
76             cmd = "ar t %s" % (self.filename)
77
78             (result, output) = commands.getstatusoutput(cmd)
79             if result != 0:
80                 rejected = True
81                 reject("%s: 'ar t' invocation failed." % (self.filename))
82                 reject(utils.prefix_multi_line_string(output, " [ar output:] "), "")
83             self.chunks = output.split('\n')
84
85
86
87     def __unpack(self):
88         # Internal function which extracts the contents of the .ar to
89         # a temporary directory
90
91         if not self.tmpdir:
92             tmpdir = tempfile.mkdtemp()
93             cwd = os.getcwd()
94             try:
95                 os.chdir( tmpdir )
96                 cmd = "ar x %s %s %s" % (os.path.join(cwd,self.filename), self.chunks[1], self.chunks[2])
97                 (result, output) = commands.getstatusoutput(cmd)
98                 if result != 0:
99                     reject("%s: '%s' invocation failed." % (filename, cmd))
100                     reject(utils.prefix_multi_line_string(output, " [ar output:] "), "")
101                 else:
102                     self.tmpdir = tmpdir
103                     atexit.register( self._cleanup )
104
105             finally:
106                 os.chdir( cwd )
107
108     def valid_deb(self):
109         """
110         Check deb contents making sure the .deb contains:
111           1. debian-binary
112           2. control.tar.gz
113           3. data.tar.gz or data.tar.bz2
114         in that order, and nothing else.
115         """
116         self.__scan_ar()
117         rejected = not self.chunks
118         if len(self.chunks) != 3:
119             rejected = True
120             reject("%s: found %d chunks, expected 3." % (self.filename, len(self.chunks)))
121         if self.chunks[0] != "debian-binary":
122             rejected = True
123             reject("%s: first chunk is '%s', expected 'debian-binary'." % (self.filename, self.chunks[0]))
124         if self.chunks[1] != "control.tar.gz":
125             rejected = True
126             reject("%s: second chunk is '%s', expected 'control.tar.gz'." % (self.filename, self.chunks[1]))
127         if self.chunks[2] not in [ "data.tar.bz2", "data.tar.gz" ]:
128             rejected = True
129             reject("%s: third chunk is '%s', expected 'data.tar.gz' or 'data.tar.bz2'." % (self.filename, self.chunks[2]))
130
131         return not rejected
132
133     def scan_package(self, bootstrap_id=0):
134         """
135         Unpack the .deb, do sanity checking, and gather info from it.
136
137         Currently information gathering consists of getting the contents list. In
138         the hopefully near future, it should also include gathering info from the
139         control file.
140
141         @ptype bootstrap_id: int
142         @param bootstrap_id: the id of the binary these packages
143           should be associated or zero meaning we are not bootstrapping
144           so insert into a temporary table
145
146         @return True if the deb is valid and contents were imported
147         """
148         rejected = not self.valid_deb()
149         self.__unpack()
150
151         result = False
152
153         if not rejected and self.tmpdir:
154             cwd = os.getcwd()
155             try:
156                 os.chdir(self.tmpdir)
157                 if self.chunks[1] == "control.tar.gz":
158                     control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
159                     control.extract('./control', self.tmpdir )
160                 if self.chunks[2] == "data.tar.gz":
161                     data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
162                 elif self.chunks[2] == "data.tar.bz2":
163                     data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
164
165                 if bootstrap_id:
166                     result = DBConn().insert_content_paths(bootstrap_id, [tarinfo.name for tarinfo in data if not tarinfo.isdir()])
167                 else:
168                     pkg = deb822.Packages.iter_paragraphs(file(os.path.join(self.tmpdir,'control'))).next()
169                     result = DBConn().insert_pending_content_paths(pkg, [tarinfo.name for tarinfo in data if not tarinfo.isdir()])
170
171             except:
172                 traceback.print_exc()
173                 result = False
174
175         os.chdir(cwd)
176         return result
177
178 if __name__ == "__main__":
179     Binary( "/srv/ftp.debian.org/queue/accepted/halevt_0.1.3-2_amd64.deb" ).scan_package()
180