]> git.decadent.org.uk Git - dak.git/blob - daklib/binary.py
fix things I broke yesterday with the contents merge
[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 sys
44 import shutil
45 import tempfile
46 import tarfile
47 import commands
48 import traceback
49 import atexit
50 from debian_bundle import deb822
51 from dbconn import DBConn
52 from config import Config
53 import logging
54 import utils
55
56 class Binary(object):
57     def __init__(self, filename, reject=None):
58         """
59         @ptype filename: string
60         @param filename: path of a .deb
61
62         @ptype reject: function
63         @param reject: a function to log reject messages to
64         """
65         self.filename = filename
66         self.tmpdir = None
67         self.chunks = None
68         self.wrapped_reject = reject
69
70     def reject(self, message):
71         """
72         if we were given a reject function, send the reject message,
73         otherwise send it to stderr.
74         """
75         print >> sys.stderr, message
76         if self.wrapped_reject:
77             self.wrapped_reject(message)
78
79     def __del__(self):
80         """
81         make sure we cleanup when we are garbage collected.
82         """
83         self._cleanup()
84
85     def _cleanup(self):
86         """
87         we need to remove the temporary directory, if we created one
88         """
89         if self.tmpdir and os.path.exists(self.tmpdir):
90             shutil.rmtree(self.tmpdir)
91             self.tmpdir = None
92
93     def __scan_ar(self):
94         # get a list of the ar contents
95         if not self.chunks:
96
97             cmd = "ar t %s" % (self.filename)
98             (result, output) = commands.getstatusoutput(cmd)
99             if result != 0:
100                 rejected = True
101                 print("%s: 'ar t' invocation failed." % (self.filename))
102                 self.reject("%s: 'ar t' invocation failed." % (self.filename))
103                 self.reject(utils.prefix_multi_line_string(output, " [ar output:] "))
104             self.chunks = output.split('\n')
105
106
107
108     def __unpack(self):
109         # Internal function which extracts the contents of the .ar to
110         # a temporary directory
111
112         if not self.tmpdir:
113             tmpdir = utils.temp_dirname()
114             cwd = os.getcwd()
115             try:
116                 os.chdir( tmpdir )
117                 cmd = "ar x %s %s %s" % (os.path.join(cwd,self.filename), self.chunks[1], self.chunks[2])
118                 (result, output) = commands.getstatusoutput(cmd)
119                 if result != 0:
120                     print("%s: '%s' invocation failed." % (self.filename, cmd))
121                     self.reject("%s: '%s' invocation failed." % (self.filename, cmd))
122                     self.reject(utils.prefix_multi_line_string(output, " [ar output:] "))
123                 else:
124                     self.tmpdir = tmpdir
125                     atexit.register( self._cleanup )
126
127             finally:
128                 os.chdir( cwd )
129
130     def valid_deb(self, relaxed=False):
131         """
132         Check deb contents making sure the .deb contains:
133           1. debian-binary
134           2. control.tar.gz
135           3. data.tar.gz or data.tar.bz2
136         in that order, and nothing else.
137         """
138         self.__scan_ar()
139         rejected = not self.chunks
140         if relaxed:
141             if en(self.chunks) < 3:
142                 rejected = True
143                 self.reject("%s: found %d chunks, expected at least 3." % (self.filename, len(self.chunks)))
144         else
145             if en(self.chunks) != 3:
146                 rejected = True
147                 self.reject("%s: found %d chunks, expected 3." % (self.filename, len(self.chunks)))
148         if self.chunks[0] != "debian-binary":
149             rejected = True
150             self.reject("%s: first chunk is '%s', expected 'debian-binary'." % (self.filename, self.chunks[0]))
151         if not rejected and self.chunks[1] != "control.tar.gz":
152             rejected = True
153             self.reject("%s: second chunk is '%s', expected 'control.tar.gz'." % (self.filename, self.chunks[1]))
154         if not rejected and self.chunks[2] not in [ "data.tar.bz2", "data.tar.gz" ]:
155             rejected = True
156             self.reject("%s: third chunk is '%s', expected 'data.tar.gz' or 'data.tar.bz2'." % (self.filename, self.chunks[2]))
157
158         return not rejected
159
160     def scan_package(self, bootstrap_id=0, relaxed=False):
161         """
162         Unpack the .deb, do sanity checking, and gather info from it.
163
164         Currently information gathering consists of getting the contents list. In
165         the hopefully near future, it should also include gathering info from the
166         control file.
167
168         @ptype bootstrap_id: int
169         @param bootstrap_id: the id of the binary these packages
170           should be associated or zero meaning we are not bootstrapping
171           so insert into a temporary table
172
173         @return True if the deb is valid and contents were imported
174         """
175         result = False
176         rejected = not self.valid_deb(relaxed)
177         if not rejected:
178             self.__unpack()
179
180
181             cwd = os.getcwd()
182             if not rejected and self.tmpdir:
183                 try:
184                     os.chdir(self.tmpdir)
185                     if self.chunks[1] == "control.tar.gz":
186                         control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
187                         control.extract('./control', self.tmpdir )
188                     if self.chunks[2] == "data.tar.gz":
189                         data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
190                     elif self.chunks[2] == "data.tar.bz2":
191                         data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
192
193                     if bootstrap_id:
194                         result = DBConn().insert_content_paths(bootstrap_id, [tarinfo.name for tarinfo in data if not tarinfo.isdir()])
195                     else:
196                         pkgs = deb822.Packages.iter_paragraphs(file(os.path.join(self.tmpdir,'control')))
197                         pkg = pkgs.next()
198                         result = DBConn().insert_pending_content_paths(pkg, [tarinfo.name for tarinfo in data if not tarinfo.isdir()])
199
200                 except:
201                     traceback.print_exc()
202
203             os.chdir(cwd)
204         self._cleanup()
205         return result
206
207     def check_utf8_package(self, package):
208         """
209         Unpack the .deb, do sanity checking, and gather info from it.
210
211         Currently information gathering consists of getting the contents list. In
212         the hopefully near future, it should also include gathering info from the
213         control file.
214
215         @ptype bootstrap_id: int
216         @param bootstrap_id: the id of the binary these packages
217           should be associated or zero meaning we are not bootstrapping
218           so insert into a temporary table
219
220         @return True if the deb is valid and contents were imported
221         """
222         rejected = not self.valid_deb(True)
223         self.__unpack()
224
225         if not rejected and self.tmpdir:
226             cwd = os.getcwd()
227             try:
228                 os.chdir(self.tmpdir)
229                 if self.chunks[1] == "control.tar.gz":
230                     control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
231                     control.extract('control', self.tmpdir )
232                 if self.chunks[2] == "data.tar.gz":
233                     data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
234                 elif self.chunks[2] == "data.tar.bz2":
235                     data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
236
237                 for tarinfo in data:
238                     try:
239                         unicode( tarinfo.name )
240                     except:
241                         print >> sys.stderr, "E: %s has non-unicode filename: %s" % (package,tarinfo.name)
242
243             except:
244                 traceback.print_exc()
245                 result = False
246
247             os.chdir(cwd)
248
249