Add torrent and magnet links for all downloads.
[people/shoehn/ipfire.org.git] / www / webapp / backend / releases.py
CommitLineData
940227cb
MT
1#!/usr/bin/python
2
3c4f2edc 3import hashlib
940227cb 4import logging
3c4f2edc
MT
5import os
6import re
fadcfd00 7import urllib
940227cb
MT
8import urlparse
9
fadcfd00
MT
10import tracker
11
940227cb
MT
12from databases import Databases
13from misc import Singleton
14from settings import Settings
15
16class File(object):
17 def __init__(self, release, id):
18 self.release = release
19
20 # get all data from database
21 self.__data = self.db.get("SELECT * FROM files WHERE id = %s", id)
22
23 @property
24 def db(self):
25 return self.release.db
26
27 @property
28 def type(self):
fadcfd00
MT
29 filename = self.filename
30
31 if filename.endswith(".iso"):
32 return "iso"
33
34 elif filename.endswith(".torrent"):
35 return "torrent"
36
37 elif "xen" in filename:
38 return "xen"
39
40 elif "sources" in filename:
41 return "source"
42
43 elif "usb-fdd" in filename:
44 return "usbfdd"
45
46 elif "usb-hdd" in filename:
47 return "usbhdd"
48
49 elif "armv5tel" in filename:
50 return "armv5tel"
51
52 elif "scon" in filename:
53 return "alix"
54
55 elif filename.endswith(".img.gz"):
56 return "flash"
57
58 else:
59 return "unknown"
940227cb
MT
60
61 @property
62 def url(self):
63 baseurl = Settings().get("download_url")
64
65 return urlparse.urljoin(baseurl, self.filename)
66
67 @property
68 def desc(self):
69 _ = lambda x: x
70
71 descriptions = {
fadcfd00 72 "armv5tel" : _("Image for the armv5tel architecture"),
940227cb
MT
73 "iso" : _("Installable CD image"),
74 "torrent" : _("Torrent file"),
75 "flash" : _("Flash image"),
76 "alix" : _("Alix image"),
77 "usbfdd" : _("USB FDD Image"),
78 "usbhdd" : _("USB HDD Image"),
79 "xen" : _("Pregenerated Xen image"),
80 }
81
82 try:
83 return descriptions[self.type]
84 except KeyError:
85 return _("Unknown image type")
86
87 @property
88 def prio(self):
89 priorities = {
90 "iso" : 10,
91 "torrent" : 20,
92 "flash" : 40,
93 "alix" : 41,
94 "usbfdd" : 31,
95 "usbhdd" : 30,
fadcfd00 96 "armv5tel" : 40,
940227cb
MT
97 "xen" : 50,
98 }
99
100 try:
101 return priorities[self.type]
102 except KeyError:
103 return 999
104
105 @property
106 def rem(self):
107 _ = lambda x: x
108
109 remarks = {
fadcfd00 110 "armv5tel" : _("This image runs on many ARM-based boards"),
940227cb
MT
111 "iso" : _("Use this image to burn a CD and install IPFire from it."),
112 "torrent" : _("Download the CD image from the torrent network."),
113 "flash" : _("An image that is meant to run on embedded devices."),
114 "alix" : _("Flash image where a serial console is enabled by default."),
115 "usbfdd" : _("Install IPFire from a floppy-formated USB key."),
116 "usbhdd" : _("If the floppy image doesn't work, use this image instead."),
117 "xen" : _("A ready-to-run image for Xen."),
118 }
119
120 try:
121 return remarks[self.type]
122 except KeyError:
123 return _("Unknown image type")
124
125 @property
126 def sha1(self):
127 return self.__data.get("sha1")
128
129 @property
130 def filename(self):
131 return self.__data.get("filename")
132
c9698a0b
MT
133 @property
134 def basename(self):
135 return os.path.basename(self.filename)
136
60024cc8
MT
137 @property
138 def size(self):
139 return self.__data.get("filesize")
140
141 @property
142 def arch(self):
143 known_arches = ("i586", "arm")
144
145 for arch in known_arches:
146 if arch in self.basename:
147 return arch
148
fadcfd00
MT
149 return "N/A"
150
151 @property
152 def torrent_hash(self):
153 return self.__data.get("torrent_hash", None)
154
155 @property
156 def magnet_link(self):
157 # Don't return anything if we have no torrent hash.
158 if self.torrent_hash is None:
159 return
160
161 s = "magnet:?xt=urn:btih:%s" % self.torrent_hash
162
163 #s += "&xl=%d" % self.size
164 s += "&dn=%s" % urllib.quote(self.basename)
165
166 # Add our tracker.
167 s += "&tr=http://tracker.ipfire.org:6969/announce"
168
169 return s
60024cc8 170
940227cb
MT
171
172class Release(object):
173 @property
174 def db(self):
175 return Releases().db
176
177 def __init__(self, id):
178 self.id = id
179
180 # get all data from database
181 self.__data = \
182 self.db.get("SELECT * FROM releases WHERE id = %s", self.id)
183 assert self.__data
184
185 self.__files = []
186
187 def __repr__(self):
188 return "<%s %s>" % (self.__class__.__name__, self.name)
189
190 @property
191 def files(self):
192 if not self.__files:
fadcfd00
MT
193 files = self.db.query("SELECT id, filename FROM files WHERE releases = %s \
194 AND loadable = 'Y'", self.id)
940227cb 195
fadcfd00 196 self.__files = [File(self, f.id) for f in files if not f.filename.endswith(".torrent")]
940227cb
MT
197 self.__files.sort(lambda a, b: cmp(a.prio, b.prio))
198
199 return self.__files
200
201 @property
202 def name(self):
203 return self.__data.get("name")
204
205 @property
206 def stable(self):
207 return self.__data.get("stable")
208
209 @property
210 def published(self):
211 return self.__data.get("published")
212
213 @property
214 def date(self):
215 return self.__data.get("date")
216
3c4f2edc
MT
217 @property
218 def path(self):
219 return self.__data.get("path")
220
940227cb
MT
221 def get_file(self, type):
222 for file in self.files:
223 if file.type == type:
224 return file
225
3c4f2edc
MT
226 def __file_hash(self, filename):
227 sha1 = hashlib.sha1()
228
229 with open(filename) as f:
b5f4eef0
MT
230 buf_size = 1024
231 buf = f.read(buf_size)
232 while buf:
233 sha1.update(buf)
234 buf = f.read(buf_size)
3c4f2edc
MT
235
236 return sha1.hexdigest()
237
3c4f2edc
MT
238 def scan_files(self, basepath="/srv/mirror0"):
239 if not self.path:
240 return
241
242 path = os.path.join(basepath, self.path)
921d98cc
MT
243 if not os.path.exists(path):
244 return
245
fadcfd00
MT
246 files = self.db.query("SELECT filename FROM files WHERE releases = %s", self.id)
247 files = [f.filename for f in files]
3c4f2edc
MT
248
249 # Make files that do not exists not loadable.
250 for filename in files:
251 _filename = os.path.join(basepath, filename)
252 if not os.path.exists(_filename):
253 self.db.execute("UPDATE files SET loadable='N' WHERE filename = %s", filename)
254
255 for filename in os.listdir(path):
256 filename = os.path.join(path, filename)
257
258 if os.path.isdir(filename):
259 continue
260
261 _filename = re.match(".*(releases/.*)", filename).group(1)
262 if _filename in files:
263 continue
264
265 if filename.endswith(".md5"):
266 continue
267
268 filehash = self.__file_hash(filename)
269 filesize = os.path.getsize(filename)
3c4f2edc 270
fadcfd00
MT
271 # Check if there is a torrent download available for this file:
272 torrent_hash = ""
273 torrent_file = "%s.torrent" % filename
274 if os.path.exists(torrent_file):
275 torrent_hash = self.torrent_read_hash(torrent_file)
276
277 self.db.execute("INSERT INTO files(releases, filename, filesize, \
278 sha1, torrent_hash) VALUES(%s, %s, %s, %s, %s)",
279 self.id, _filename, filesize, filehash, torrent_hash)
280
281 # Search for all files that miss a torrent hash.
282 files = self.db.query("SELECT id, filename FROM files \
283 WHERE releases = %s AND torrent_hash IS NULL", self.id)
284
285 for file in files:
286 path = os.path.join(basepath, file.filename)
287
288 torrent_file = "%s.torrent" % path
289 if os.path.exists(torrent_file):
290 torrent_hash = self.torrent_read_hash(torrent_file)
291
292 self.db.execute("UPDATE files SET torrent_hash = %s WHERE id = %s",
293 torrent_hash, file.id)
294
295 def torrent_read_hash(self, filename):
296 f = None
297 try:
298 f = open(filename, "rb")
299
300 metainfo = tracker.bdecode(f.read())
301 metainfo = tracker.bencode(metainfo["info"])
302
303 hash = hashlib.sha1()
304 hash.update(metainfo)
305
306 return hash.hexdigest()
307
308 finally:
309 if f:
310 f.close()
3c4f2edc 311
940227cb
MT
312
313class Releases(object):
314 __metaclass__ = Singleton
315
316 @property
317 def db(self):
318 return Databases().webapp
319
320 def list(self):
321 return [Release(r.id) for r in self.db.query("SELECT id FROM releases ORDER BY date DESC")]
322
323 def get_by_id(self, id):
324 id = int(id)
325 if id in [r.id for r in self.db.query("SELECT id FROM releases")]:
326 return Release(id)
327
328 def get_latest(self, stable=1):
329 query = "SELECT id FROM releases WHERE published='Y' AND"
330 if stable:
331 query += " stable='Y'"
332 else:
333 query += " stable='N'"
334
335 query += " ORDER BY date DESC LIMIT 1"
336
337 release = self.db.get(query)
338 if release:
339 return Release(release.id)
340
341 def get_stable(self):
342 releases = self.db.query("""SELECT id FROM releases
343 WHERE published='Y' AND stable='Y'
344 ORDER BY date DESC""")
345
346 return [Release(r.id) for r in releases]
347
348 def get_unstable(self):
349 releases = self.db.query("""SELECT id FROM releases
350 WHERE published='Y' AND stable='N'
351 ORDER BY date DESC""")
352
353 return [Release(r.id) for r in releases]
354
355 def get_all(self):
356 releases = self.db.query("""SELECT id FROM releases
357 WHERE published='Y' ORDER BY date DESC""")
358
359 return [Release(r.id) for r in releases]
360
fadcfd00
MT
361 def get_filename_for_torrent_hash(self, torrent_hash):
362 file = self.db.get("SELECT filename FROM files WHERE torrent_hash = %s LIMIT 1",
363 torrent_hash)
364
365 if not file:
366 return
367
368 return file.filename
369
940227cb
MT
370
371if __name__ == "__main__":
372 r = Releases()
373
374 for release in r.get_all():
375 print release.name
376
377 print r.get_latest()