/[secure-testing]/bin/tracker_service.py
ViewVC logotype

Contents of /bin/tracker_service.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 13517 - (hide annotations) (download) (as text)
Sat Dec 12 07:43:50 2009 UTC (3 years, 6 months ago) by geissert
File MIME type: text/script
File size: 52774 byte(s)
Fix link to the svn repo
1 fw 2482 #!/usr/bin/python
2    
3     import sys
4     sys.path.insert(0,'../lib/python')
5     import bugs
6     import re
7     import security_db
8     from web_support import *
9    
10 fw 12985 if len(sys.argv) not in (3, 5):
11     print "usage: python tracker_service.py SOCKET-PATH DATABASE-PATH"
12     print " python tracker_service.py URL HOST PORT DATABASE-PATH"
13     sys.exit(1)
14     if len(sys.argv) == 3:
15     socket_name = sys.argv[1]
16     db_name = sys.argv[2]
17     webservice_base_class = WebService
18     else:
19     server_base_url = sys.argv[1]
20     server_address = sys.argv[2]
21     server_port = int(sys.argv[3])
22     socket_name = (server_base_url, server_address, server_port)
23     db_name = sys.argv[4]
24     webservice_base_class = WebServiceHTTP
25    
26 fw 2491 class BugFilter:
27 fw 3859 default_action_list = [("hide_medium_urgency", "lower urgencies"),
28     ("hide_non_remote", "local vulnerabilities")]
29     def __init__(self, params, action_list=None):
30     if action_list is None:
31     self.action_list = self.default_action_list
32     else:
33     self.action_list = action_list
34     self.params = {}
35 fw 3902 for (prop, desc) in self.action_list:
36 fw 3859 self.params[prop] = int(params.get(prop, (0,))[0])
37 fw 2491
38     def actions(self, url):
39     """Returns a HTML snippet which can be used to change the filter."""
40    
41 fw 3859 l = []
42     for (prop, desc) in self.action_list:
43     if self.params[prop]:
44     a = A(url.updateParamsDict({prop : None}),
45     'Show ' + desc)
46     else:
47     a = A(url.updateParamsDict({prop : '1'}),
48     'Hide ' + desc)
49     l.append(a)
50     l.append(' ')
51    
52     return apply(P, l[:-1])
53    
54 fw 2491 def urgencyFiltered(self, urg):
55     """Returns True if the urgency urg is filtered."""
56 fw 3859 return self.params['hide_medium_urgency'] \
57     and urg not in ("high", "unknown", "")
58 fw 2491
59     def remoteFiltered(self, remote):
60     """Returns True if the attack range is filtered."""
61 fw 3859 return remote is not None and self.params['hide_non_remote'] \
62     and not remote
63 fw 2491
64 fw 3859 class BugFilterNoDSA(BugFilter):
65     def __init__(self, params):
66     BugFilter.__init__(self, params, self.default_action_list
67     + [('hide_nodsa', 'non-DSA vulnerabilities')])
68     self.hide_nodsa = int(params.get('hide_nodsa',(0,))[0])
69    
70     def nodsaFiltered(self, nodsa):
71     """Returns True if no DSA will be issued for the bug."""
72     return nodsa and self.params['hide_nodsa']
73    
74 fw 12985 class TrackerService(webservice_base_class):
75 fw 2482 head_contents = compose(STYLE(
76     """h1 { font-size : 144%; }
77     h2 { font-size : 120%; }
78     h3 { font-size : 100%; }
79    
80     table { padding-left : 1.5em }
81     td, th { text-align : left;
82     padding-left : 0.25em;
83     padding-right : 0.25em; }
84     td { vertical-align: baseline }
85     span.red { color: red; }
86     span.dangerous { color: rgb(191,127,0); }
87     """), SCRIPT('''var old_query_value = "";
88    
89     function selectSearch() {
90     document.searchForm.query.focus();
91     }
92    
93     function onSearch(query) {
94     if (old_query_value == "") {
95     if (query.length > 5) {
96     old_query_value = query;
97     document.searchForm.submit();
98     } else {
99     old_query_value = query;
100     }
101     }
102     }
103     ''')).toHTML()
104    
105     def __init__(self, socket_name, db_name):
106 fw 12985 webservice_base_class.__init__(self, socket_name)
107 fw 2482 self.db = security_db.DB(db_name)
108     self.register('', self.page_home)
109     self.register('*', self.page_object)
110 fw 2485 self.register('redirect/*', self.page_redirect)
111 fw 2482 self.register('source-package/*', self.page_source_package)
112     self.register('binary-package/*', self.page_binary_package)
113 thijs 11216 self.register('status/release/oldstable',
114     self.page_status_release_oldstable)
115 fw 2482 self.register('status/release/stable', self.page_status_release_stable)
116 fw 10607 self.register('status/release/stable-backports',
117     self.page_status_release_stable_backports)
118 fw 11223 self.register('status/release/oldstable-backports',
119     self.page_status_release_oldstable_backports)
120 fw 2482 self.register('status/release/testing',
121     self.page_status_release_testing)
122     self.register('status/release/unstable',
123     self.page_status_release_unstable)
124     self.register('status/dtsa-candidates',
125     self.page_status_dtsa_candidates)
126     self.register('status/todo', self.page_status_todo)
127     self.register('status/itp', self.page_status_itp)
128     self.register('data/unknown-packages', self.page_data_unknown_packages)
129     self.register('data/missing-epochs', self.page_data_missing_epochs)
130 fw 3136 self.register('data/latently-vulnerable',
131     self.page_data_latently_vulnerable)
132 fw 2482 self.register('data/releases', self.page_data_releases)
133     self.register('data/funny-versions', self.page_data_funny_versions)
134 fw 2554 self.register('data/fake-names', self.page_data_fake_names)
135 fw 12999 self.register('data/pts/1', self.page_data_pts)
136 fw 3051 self.register('debsecan/**', self.page_debsecan)
137 fw 3594 self.register('data/report', self.page_report)
138 fw 2482
139     def page_home(self, path, params, url):
140     query = params.get('query', ('',))[0]
141     if query:
142     if '/' in query:
143     return self.page_not_found(url, query)
144     else:
145     return RedirectResult(url.scriptRelativeFull(query))
146    
147     return self.create_page(
148     url, 'Security Bug Tracker',
149     [P(
150 fw 3594 """The data in this tracker comes solely from the bug database maintained
151     by Debian's security team located in the testing-security Subversion """,
152 geissert 13517 A("http://svn.debian.org/wsvn/secure-testing/data/", "repository"),
153 fw 3594 """. The data represented here is derived from: """,
154     A("http://www.debian.org/security/#DSAS", "DSAs"),
155     """ issued by the Security Team; issues tracked in the """,
156 stef-guest 6467 A("http://cve.mitre.org/cve/", "CVE database"),
157 fw 3594 """, issues tracked in the """,
158     A("http://nvd.nist.gov/", "National Vulnerability Database"),
159     """ (NVD), maintained by NIST; and security issues
160     discovered in Debian packages as reported in the BTS."""),
161 stef-guest 4662 P("""All external data (including Debian bug reports and official Debian
162 fw 3594 security advisories) must be added to this database before it appears
163 fw 3595 here. Please help us keep this information up-to-date by """,
164 fw 3594 A(url.scriptRelative("data/report"), "reporting"),
165     """ any discrepancies or change of states that you are
166     aware of and/or help us improve the quality of this information by """,
167 seanius 4570 A(url.scriptRelative("data/report"), "participating"),
168 fw 3594 "."),
169     make_menu(
170 fw 2482 url.scriptRelative,
171 fw 3071 ('status/release/unstable',
172     'Vulnerable packages in the unstable suite'),
173     ('status/release/testing',
174     'Vulnerable packages in the testing suite'),
175 fw 2482 ('status/release/stable',
176     'Vulnerable packages in the stable suite'),
177 fw 11220 ('status/release/oldstable',
178     'Vulnerable packages in the old stable suite'),
179 fw 10607 ('status/release/stable-backports',
180     'Vulnerable packages in backports for stable'),
181 fw 11223 ('status/release/oldstable-backports',
182     'Vulnerable packages in backports for oldstable'),
183 fw 2482 ('status/dtsa-candidates', "Candidates for DTSAs"),
184     ('status/todo', 'TODO items'),
185     ('status/itp', 'ITPs with potential security issues'),
186     ('data/unknown-packages',
187     'Packages names not found in the archive'),
188 fw 2554 ('data/fake-names', 'Tracked issues without a CVE name'),
189 fw 2482 ('data/missing-epochs',
190     'Package versions which might lack an epoch'),
191 fw 3136 ('data/latently-vulnerable',
192     'Packages which are latently vulnerable in unstable'),
193 fw 2482 ('data/funny-versions',
194     'Packages with strange version numbers'),
195     ('data/releases',
196     'Covered Debian releases and architectures (slow)'),
197     self.make_search_button(url)),
198 fw 2495 P("""(You can enter CVE names, Debian bug numbers and package
199 fw 2488 names in the search forms.)"""),
200 fw 2482
201 fw 2488 H2("External interfaces"),
202     P("""If you want to automatically open a relevant web page for
203     some object, use the """,
204     CODE(str(url.scriptRelative("redirect/")), EM("object")),
205     """ URL. If no information is contained in this database,
206     the browser is automatically redirected to the corresponding external
207     data source.""")],
208 fw 2482 search_in_page=True)
209    
210     def page_object(self, path, params, url):
211     obj = path[0]
212 fw 2485 return self.page_object_or_redirect(url, obj, False)
213 fw 2482
214 fw 2485 def page_redirect(self, path, params, url):
215 fw 12993 if path == ():
216     obj = ''
217     else:
218     obj = path[0]
219 fw 2485 return self.page_object_or_redirect(url, obj, True)
220    
221     def page_object_or_redirect(self, url, obj, redirect):
222     c = self.db.cursor()
223    
224 fw 2482 if not obj:
225     # Redirect to start page.
226     return RedirectResult(url.scriptRelativeFull(""))
227    
228 fw 5103 # Attempt to decode a bug number. TEMP-nnn bugs (but not
229     # TEMP-nnn-mmm bugs) are treated as bug references, too.
230 fw 2482 bugnumber = 0
231 fw 3311 fake_bug = False
232 fw 2482 try:
233 fw 5103 if obj[0:5] == 'FAKE-' or obj[0:5] == 'TEMP-':
234 fw 3311 bugnumber = int(obj[5:])
235     fake_bug = True
236     else:
237     bugnumber = int(obj)
238 fw 2482 except ValueError:
239     pass
240     if bugnumber:
241 fw 2485 buglist = list(self.db.getBugsFromDebianBug(c, bugnumber))
242     if buglist:
243 fw 3311 return self.page_debian_bug(url, bugnumber, buglist, fake_bug)
244 fw 2485 if redirect:
245     return RedirectResult(self.url_debian_bug(url, str(bugnumber)),
246     permanent=False)
247 fw 2482
248 fw 3311 if 'A' <= obj[0] <= 'Z':
249     # Bug names start with a capital letter.
250     return self.page_bug(url, obj, redirect)
251    
252 fw 2482 if self.db.isSourcePackage(c, obj):
253     return RedirectResult(self.url_source_package(url, obj, full=True))
254     if self.db.isBinaryPackage(c, obj):
255     return RedirectResult(self.url_binary_package(url ,obj, full=True))
256    
257     return self.page_not_found(url, obj)
258    
259 fw 2485 def page_bug(self, url, name, redirect):
260 fw 2511 # FIXME: Normalize CAN-* to CVE-* when redirecting. Too many
261     # people still use CAN.
262     if redirect and name[0:4] == 'CAN-':
263     name = 'CVE-' + name[4:]
264    
265 fw 2482 cursor = self.db.cursor()
266     try:
267     bug = bugs.BugFromDB(cursor, name)
268     except ValueError:
269 fw 2485 if redirect:
270 fw 2495 if name[0:4] == 'CVE-':
271 fw 2485 return RedirectResult(self.url_cve(url, name),
272     permanent=False)
273 fw 2482 return self.page_not_found(url, name)
274 fw 2485 if bug.name <> name or redirect:
275 fw 2482 # Show the normalized bug name in the browser address bar.
276     return RedirectResult(url.scriptRelativeFull(bug.name))
277    
278     page = []
279    
280     def gen_header():
281     yield B("Name"), bug.name
282    
283     source = bug.name.split('-')[0]
284 fw 2495 if source == 'CVE':
285 fw 2488 source_xref = compose(self.make_cve_ref(url, bug.name, 'CVE'),
286     " (",
287     self.make_nvd_ref(url, bug.name,
288     'in NVD'),
289     ")")
290 fw 2482 elif source == 'DSA':
291     source_xref = self.make_dsa_ref(url, bug.name, 'Debian')
292     elif source == 'DTSA':
293     source_xref = 'Debian Testing Security Team'
294 fw 5103 elif source == 'TEMP':
295 fw 2482 source_xref = (
296     'Automatically generated temporary name. Not for external reference.')
297     else:
298     source_xref = None
299    
300     if source_xref:
301     yield B("Source"), source_xref
302    
303 fw 3078 nvd = self.db.getNVD(cursor, bug.name)
304    
305     if nvd and nvd.cve_desc:
306     yield B("Description"), nvd.cve_desc
307     elif bug.description:
308 fw 2482 yield B("Description"), bug.description
309    
310     xref = list(self.db.getBugXrefs(cursor, bug.name))
311     if xref:
312     yield B("References"), self.make_xref_list(url, xref)
313 fw 2488
314     if nvd:
315 fw 3079 nvd_range = nvd.rangeString()
316 fw 2488 if nvd.severity:
317 fw 3079 nvd_severity = nvd.severity.lower()
318     if nvd_range:
319     nvd_severity = "%s (attack range: %s)" \
320     % (nvd_severity, nvd_range)
321     yield B("NVD severity"), nvd_severity
322 fw 2482
323     debian_bugs = bug.getDebianBugs(cursor)
324     if debian_bugs:
325     yield (B("Debian Bugs"),
326     self.make_debian_bug_list(url, debian_bugs))
327    
328     if not bug.not_for_us:
329     for (release, status, reason) in bug.getStatus(cursor):
330     if status <> 'fixed':
331     reason = self.make_red(reason)
332 fw 3080 yield B('Debian/%s' % release), reason
333 fw 2482
334     page.append(make_table(gen_header()))
335    
336     if bug.notes:
337     page.append(H2("Vulnerable and fixed packages"))
338    
339     def gen_source():
340     old_pkg = ''
341     for (package, release, version, vulnerable) \
342     in self.db.getSourcePackages(cursor, bug.name):
343     if package == old_pkg:
344     package = ''
345     else:
346     old_pkg = package
347     package = compose(
348     self.make_source_package_ref(url, package),
349     " (", self.make_pts_ref(url, package, 'PTS'), ")")
350     if vulnerable:
351     vuln = self.make_red('vulnerable')
352     version = self.make_red(version)
353     else:
354     vuln = 'fixed'
355    
356     yield package, ', '.join(release), version, vuln
357    
358     page.append(make_table(gen_source(),
359     caption=("Source Package", "Release", "Version", "Status"),
360     introduction=P('The table below lists information on source packages.')))
361    
362     def gen_binary():
363     old_pkg = ''
364     for (packages, releases, version, archs, vulnerable) \
365     in self.db.getBinaryPackages(cursor, bug.name):
366     pkg = ', '.join(packages)
367     if pkg == old_pkg:
368     packages = ''
369     else:
370     old_pkg = pkg
371     packages = self.make_binary_packages_ref(url, packages)
372    
373     if vulnerable:
374     vuln = self.make_red('vulnerable')
375     version = self.make_red(version)
376     else:
377     vuln = 'fixed'
378     yield (packages,
379     ', '.join(releases),
380     version, vuln,
381     ', '.join(archs))
382    
383     page.append(make_table(gen_binary(),
384     caption=("Binary Package", "Release", "Version", "Status",
385     "Architecures"),
386     introduction=P("The next table lists affected binary packages.")))
387    
388     def gen_data():
389     notes_sorted = bug.notes[:]
390     notes_sorted.sort(lambda a, b: cmp(a.package, b.package))
391     for n in notes_sorted:
392     if n.release:
393     rel = str(n.release)
394     else:
395     rel = '(unstable)'
396     urgency = str(n.urgency)
397     if n.fixed_version:
398     ver = str(n.fixed_version)
399     if ver == '0':
400     ver = '(not affected)'
401     urgency = ''
402     else:
403     ver = self.make_red('(unfixed)')
404    
405     pkg = n.package
406     pkg_kind = n.package_kind
407     if pkg_kind == 'source':
408     pkg = self.make_source_package_ref(url, pkg)
409     elif pkg_kind == 'binary':
410     pkg = self.make_binary_package_ref(url, pkg)
411     elif pkg_kind == 'itp':
412     pkg_kind = 'ITP'
413     rel = ''
414     ver = ''
415     urgency = ''
416    
417     bugs = n.bugs
418     bugs.sort()
419     bugs = make_list(
420     map(lambda x: self.make_debian_bug(url, x), bugs))
421     if n.bug_origin:
422     origin = self.make_xref(url, n.bug_origin)
423     else:
424     origin = ''
425     yield (pkg, pkg_kind, rel, ver, urgency, origin, bugs)
426    
427     page.append(
428     make_table(gen_data(),
429     caption=("Package", "Type", "Release", "Fixed Version",
430     "Urgency", "Origin", "Debian Bugs"),
431     introduction=P("The information above is based on the following data on fixed versions.")))
432    
433     if bug.comments:
434     page.append(H2("Notes"))
435     def gen_comments():
436     for (t, c) in bug.comments:
437     yield c
438     page.append(make_pre(gen_comments()))
439    
440     return self.create_page(url, bug.name, page)
441    
442 fw 3311 def page_debian_bug(self, url, bugnumber, buglist, fake_bug):
443     if fake_bug:
444     new_buglist = []
445     for b in buglist:
446     (bug_name, urgency, description) = b
447 fw 5103 if bug_name[0:5] == 'FAKE-' or bug_name[0:5] == 'TEMP-':
448 fw 3311 new_buglist.append(b)
449     if len(new_buglist) > 0:
450     # Only replace the bug list if there are still fake
451     # bug reports.
452     buglist = new_buglist
453    
454 fw 2485 if len(buglist) == 1:
455     # Single issue, redirect.
456     return RedirectResult(url.scriptRelativeFull(buglist[0][0]))
457 fw 2482
458 fw 2485 def gen():
459     for (name, urgency, description) in buglist:
460     if urgency == "unknown":
461     urgency = ""
462     yield self.make_xref(url, name), urgency, description
463 fw 2482
464 fw 3311 if fake_bug:
465     intro = """The URL you used contained a non-stable name
466     based on a Debian bug number. This name cannot be mapped to a specific
467     issue. """
468     else:
469     intro = ""
470    
471 fw 2485 return self.create_page(
472     url, "Information related to Debian bug #%d" % bugnumber,
473 fw 3311 [P(intro + "The following issues reference to Debian bug ",
474 fw 2485 self.make_debian_bug(url, bugnumber), ":"),
475     make_table(gen(),
476     caption=("Name", "Urgency", "Description"))])
477 fw 2482
478     def page_not_found(self, url, query):
479     return self.create_page(url, 'Not found',
480     [P('Your query ',
481     CODE(query),
482     ' matched no results.')],
483     status=404)
484    
485 fw 3594 def page_report(self, path, params, url):
486     return self.create_page(
487     url, 'Reporting discrepancies in the data',
488     [P("""The data in this tracker is always in flux, as bugs are fixed and new
489     issues disclosed, the data contained herein is updated. We strive to
490     maintain complete and accurate state information, and appreciate any
491     updates in status, information or new issues."""),
492     P("There are three ways that you can report updates to this information:"),
493     make_numbered_list(
494     [P("""IRC: We can be found at """,
495     CODE("irc.oftc.net"),
496     ", ",
497     CODE("#debian-security"),
498     """. If you have information to report, please go ahead and join
499     the channel and tell us. Please feel free to state the issue,
500     regardless if there is someone who has acknowledged you. Many of us
501     idle on this channel and may not be around when you join, but we read
502     the backlog and will see what you have said. If you require a
503     response, do not forget to let us know how to get a hold of you."""),
504     P("Mailing list: Our mailing list is: ",
505 stef-guest 6277 A("mailto:debian-security-tracker@lists.debian.org",
506     "debian-security-tracker@lists.debian.org")),
507 fw 3594 P("""Helping out: We welcome people who wish to join us in tracking
508     issues. The process is designed to be easy to learn and participate,
509     please read our """,
510 fw 3595 A("http://svn.debian.org/wsvn/secure-testing/doc/narrative_introduction?op=file&rev=0&sc=0",
511 fw 3594 "Introduction"),
512     """ to get familiar with how things work. Join us on
513     our mailing list, and on IRC and request to be added to the Alioth """,
514     A("http://alioth.debian.org/projects/secure-testing/", "project"),
515     """. We are really quite friendly. If you have a
516     question about how things work, don't be afraid to ask, we would like
517     to improve our documentation and procedures, so feedback is welcome.""")])])
518    
519 fw 2482 def page_source_package(self, path, params, url):
520     pkg = path[0]
521    
522     def gen_versions():
523     for (releases, version) in self.db.getSourcePackageVersions(
524     self.db.cursor(), pkg):
525     yield ', '.join(releases), version
526     def gen_binary():
527     for (packages, releases, archs, version) \
528     in self.db.getBinaryPackagesForSource(
529     self.db.cursor(), pkg):
530     yield (self.make_binary_packages_ref(url, packages),
531     ', '.join(releases), version, ', '.join(archs))
532     def gen_bug_list(lst):
533     for (bug, description) in lst:
534     yield self.make_xref(url, bug), description
535    
536     return self.create_page(
537     url, "Information on source package " + pkg,
538     [make_menu(lambda x: x,
539     (self.url_pts(url, pkg),
540     pkg + ' in the Package Tracking System'),
541     (self.url_debian_bug_pkg(url, pkg),
542     pkg + ' in the Bug Tracking System'),
543     (self.url_testing_status(url, pkg),
544     pkg + ' in the testing migration checker')),
545     H2("Available versions"),
546     make_table(gen_versions(), caption=("Release", "Version")),
547    
548     H2("Available binary packages"),
549     make_table(gen_binary(),
550     caption=('Package', 'Release', 'Version', 'Architectures'),
551     replacement="""No binary packages are recorded in this database.
552     This probably means that the package is architecture-specific, and the
553     architecture is currently not tracked."""),
554    
555     H2("Open issues"),
556     make_table(gen_bug_list(self.db.getBugsForSourcePackage
557     (self.db.cursor(), pkg, True)),
558     caption=('Bug', 'Description'),
559     replacement='No known open issues.'),
560    
561     H2("Resolved issues"),
562     make_table(gen_bug_list(self.db.getBugsForSourcePackage
563     (self.db.cursor(), pkg, False)),
564     caption=('Bug', 'Description'),
565     replacement='No known resolved issues.')])
566    
567     def page_binary_package(self, path, params, url):
568     pkg = path[0]
569    
570     def gen_versions():
571     for (releases, source, version, archs) \
572     in self.db.getBinaryPackageVersions(self.db.cursor(), pkg):
573     yield (', '.join(releases),
574     self.make_source_package_ref(url, source),
575     version, ', '.join(archs))
576     def gen_bug_list(lst):
577     for (bug, description) in lst:
578     yield self.make_xref(url, bug), description
579    
580     return self.create_page(
581     url, "Information on binary package " + pkg,
582     [make_menu(lambda x: x,
583     (self.url_debian_bug_pkg(url, pkg),
584     pkg + ' in the Bug Tracking System')),
585     H2("Available versions"),
586     make_table(gen_versions(),
587     caption=("Release", "Source", "Version", "Architectures")),
588    
589     H2("Open issues"),
590     make_table(gen_bug_list(self.db.getBugsForBinaryPackage
591     (self.db.cursor(), pkg, True)),
592     caption=('Bug', 'Description'),
593     replacement='No known open issues.'),
594    
595     H2("Resolved issues"),
596     make_table(gen_bug_list(self.db.getBugsForBinaryPackage
597     (self.db.cursor(), pkg, False)),
598     caption=('Bug', 'Description'),
599     replacement='No known resolved issues.'),
600    
601     H2("Non-issues"),
602     make_table(gen_bug_list(self.db.getNonBugsForBinaryPackage
603     (self.db.cursor(), pkg)),
604     caption=('Bug', 'Description'),
605     replacement="""No known issues which do not affect
606     this package, but still reference it.""")])
607    
608 fw 3069 def page_status_release_stable_oldstable(self, release, params, url):
609     assert release in ('stable', 'oldstable')
610    
611 fw 3859 bf = BugFilterNoDSA(params)
612 fw 2491
613 fw 2482 def gen():
614     old_pkg_name = ''
615 fw 3859 for (pkg_name, bug_name, archive, urgency, remote, no_dsa) in \
616 fw 2482 self.db.cursor().execute(
617 fw 3859 """SELECT package, bug, section, urgency, remote, no_dsa
618 fw 3069 FROM %s_status""" % release):
619 fw 2491 if bf.urgencyFiltered(urgency):
620     continue
621     if bf.remoteFiltered(remote):
622     continue
623 fw 3859 if bf.nodsaFiltered(no_dsa):
624     continue
625 fw 2491
626 fw 2482 if pkg_name == old_pkg_name:
627     pkg_name = ''
628     else:
629     old_pkg_name = pkg_name
630     if archive <> 'main':
631     pkg_name = "%s (%s)" % (pkg_name, archive)
632    
633 fw 2488 if remote is None:
634     remote = ''
635     elif remote:
636     remote = 'yes'
637     else:
638     remote = 'no'
639    
640 fw 2482 if urgency == 'unknown':
641 fw 3859 if no_dsa:
642     urgency = 'no DSA'
643     else:
644     urgency = ''
645 fw 2482 elif urgency == 'high':
646     urgency = self.make_red(urgency)
647 fw 3859 else:
648     if no_dsa:
649     urgency = urgency + '*'
650 fw 2482
651 fw 2488 yield pkg_name, self.make_xref(url, bug_name), urgency, remote
652 fw 2482
653     return self.create_page(
654 fw 3069 url, 'Vulnerable source packages in the %s suite' % release,
655 fw 2491 [bf.actions(url),
656     make_table(gen(), caption=("Package", "Bug", "Urgency",
657 fw 3859 "Remote")),
658     P('''(If a "*" is included in the urgency field, no DSA is planned
659     for this vulnerability.)''')])
660 fw 3069
661     def page_status_release_stable(self, path, params, url):
662     return self.page_status_release_stable_oldstable('stable', params, url)
663 thijs 11216 def page_status_release_oldstable(self, path, params, url):
664     return self.page_status_release_stable_oldstable('oldstable',
665     params, url)
666 fw 2482
667     def page_status_release_testing(self, path, params, url):
668 fw 5100 bf = BugFilterNoDSA(params)
669 fw 2491
670 fw 2482 def gen():
671     old_pkg_name = ''
672     for (pkg_name, bug_name, archive, urgency,
673 fw 5100 sid_vulnerable, ts_fixed, remote, no_dsa) \
674     in self.db.cursor().execute(
675 fw 2482 """SELECT package, bug, section, urgency, unstable_vulnerable,
676 fw 5100 testing_security_fixed, remote, no_dsa
677 fw 2482 FROM testing_status"""):
678 fw 2491 if bf.urgencyFiltered(urgency):
679     continue
680     if bf.remoteFiltered(remote):
681     continue
682 fw 5100 if bf.nodsaFiltered(no_dsa):
683     continue
684 fw 2491
685 fw 2482 if pkg_name == old_pkg_name:
686     pkg_name = ''
687     else:
688     old_pkg_name = pkg_name
689     if archive <> 'main':
690     pkg_name = "%s (%s)" % (pkg_name, archive)
691    
692 fw 2488 if remote is None:
693     remote = ''
694     elif remote:
695     remote = 'yes'
696     else:
697     remote = 'no'
698    
699 fw 2482 if ts_fixed:
700     status = 'fixed in testing-security'
701     else:
702     if sid_vulnerable:
703     status = self.make_red('unstable is vulnerable')
704     else:
705     status = self.make_dangerous('fixed in unstable')
706    
707     if urgency == 'unknown':
708     urgency = ''
709    
710     yield (pkg_name, self.make_xref(url, bug_name),
711 fw 2488 urgency, remote, status)
712 fw 2482
713     return self.create_page(
714     url, 'Vulnerable source packages in the testing suite',
715     [make_menu(url.scriptRelative,
716     ("status/dtsa-candidates", "Candidates for DTSAs")),
717 fw 2491 bf.actions(url),
718 fw 2488 make_table(gen(), caption=("Package", "Bug", "Urgency",
719     "Remote"))])
720 fw 2482
721 fw 10607 def page_status_release_unstable_like(self, path, params, url,
722     rel, title):
723 fw 2491 bf = BugFilter(params)
724    
725 fw 2482 def gen():
726     old_pkg_name = ''
727 fw 2491 for (pkg_name, bug_name, section, urgency, remote) \
728 fw 2482 in self.db.cursor().execute(
729     """SELECT DISTINCT sp.name, st.bug_name,
730 fw 2491 sp.archive, st.urgency,
731     (SELECT range_remote FROM nvd_data
732     WHERE cve_name = st.bug_name)
733 fw 2482 FROM source_package_status AS st, source_packages AS sp
734     WHERE st.vulnerable AND st.urgency <> 'unimportant'
735 fw 10607 AND sp.rowid = st.package AND sp.release = ?
736 fw 2482 AND sp.subrelease = ''
737 fw 10607 ORDER BY sp.name, st.bug_name""", (rel,)):
738 fw 2491 if bf.urgencyFiltered(urgency):
739     continue
740     if bf.remoteFiltered(remote):
741     continue
742    
743 fw 2482 if pkg_name == old_pkg_name:
744     pkg_name = ''
745     else:
746     old_pkg_name = pkg_name
747     if section <> 'main':
748     pkg_name = "%s (%s)" % (pkg_name, section)
749     else:
750     pkg_name = self.make_xref(url, pkg_name)
751    
752 fw 2491 if remote is None:
753     remote = ''
754     elif remote:
755     remote = 'yes'
756     else:
757     remote = 'no'
758    
759 fw 2482 if urgency == 'unknown':
760     urgency = ''
761     elif urgency == 'high':
762     urgency = self.make_red(urgency)
763    
764 fw 2491 yield pkg_name, self.make_xref(url, bug_name), urgency, remote
765 fw 2482 return self.create_page(
766 fw 10607 url, title,
767 fw 2482 [P("""Note that the list below is based on source packages.
768     This means that packages are not listed here once a new,
769     fixed source version has been uploaded to the archive, even
770     if there are still some vulnerably binary packages present
771     in the archive."""),
772 fw 2491 bf.actions(url),
773     make_table(gen(), caption=('Package', 'Bug', 'Urgency',
774     'Remote'))])
775 fw 2482
776 fw 10607 def page_status_release_unstable(self, path, params, url):
777     return self.page_status_release_unstable_like(
778     path, params, url,
779     title='Vulnerable source packages in the unstable suite',
780     rel='sid')
781    
782     def page_status_release_stable_backports(self, path, params, url):
783     return self.page_status_release_unstable_like(
784     path, params, url,
785     title='Vulnerable source packages among backports for stable',
786 thijs 11215 rel='lenny-backports')
787 fw 10607
788 fw 11223 def page_status_release_oldstable_backports(self, path, params, url):
789     return self.page_status_release_unstable_like(
790     path, params, url,
791     title='Vulnerable source packages among backports for oldstable',
792     rel='etch-backports')
793    
794 fw 2482 def page_status_dtsa_candidates(self, path, params, url):
795 fw 2491 bf = BugFilter(params)
796    
797 fw 2482 def gen():
798     old_pkg_name = ''
799 fw 2491 for (pkg_name, bug_name, archive, urgency, stable_later,
800     remote) \
801 fw 2482 in self.db.cursor().execute(
802     """SELECT package, bug, section, urgency,
803     (SELECT testing.version_id < stable.version_id
804     FROM source_packages AS testing, source_packages AS stable
805     WHERE testing.name = testing_status.package
806 thijs 11215 AND testing.release = 'squeeze'
807 fw 2482 AND testing.subrelease = ''
808     AND testing.archive = testing_status.section
809     AND stable.name = testing_status.package
810 thijs 11215 AND stable.release = 'lenny'
811 fw 2482 AND stable.subrelease = 'security'
812 fw 2491 AND stable.archive = testing_status.section),
813     (SELECT range_remote FROM nvd_data
814     WHERE cve_name = bug)
815 fw 2482 FROM testing_status
816     WHERE (NOT unstable_vulnerable)
817     AND (NOT testing_security_fixed)"""):
818 fw 2491 if bf.urgencyFiltered(urgency):
819     continue
820     if bf.remoteFiltered(remote):
821     continue
822    
823 fw 2482 if pkg_name == old_pkg_name:
824     pkg_name = ''
825     migration = ''
826     else:
827     old_pkg_name = pkg_name
828     migration = A(self.url_testing_status(url, pkg_name),
829     "check")
830     if archive <> 'main':
831     pkg_name = "%s (%s)" % (pkg_name, archive)
832     else:
833     pkg_name = self.make_source_package_ref(url, pkg_name)
834    
835 fw 2491 if remote is None:
836     remote = ''
837     elif remote:
838     remote = 'yes'
839     else:
840     remote = 'no'
841    
842 fw 2482 if urgency == 'unknown':
843     urgency = ''
844     elif urgency == 'high':
845     urgency = self.make_red(urgency)
846    
847     if stable_later:
848     notes = "(fixed in stable?)"
849     else:
850     notes = ''
851    
852     yield (pkg_name, migration, self.make_xref(url, bug_name),
853 fw 2491 urgency, remote, notes)
854 fw 2482
855     return self.create_page(
856     url, "Candidates for DTSAs",
857     [P("""The table below lists packages which are fixed
858     in unstable, but unfixed in testing. Use the testing migration
859 fw 2679 checker to find out why they have not entered testing yet."""),
860 fw 2482 make_menu(url.scriptRelative,
861     ("status/release/testing",
862     "List of vulnerable packages in testing")),
863 fw 2491 bf.actions(url),
864 fw 2482 make_table(gen(),
865 fw 2491 caption=("Package", "Migration", "Bug", "Urgency",
866     "Remote"))])
867 fw 2482
868     def page_status_todo(self, path, params, url):
869 fw 4005 hide_check = params.get('hide_check', False)
870     if hide_check:
871     flags = A(url.updateParamsDict({'hide_check' : None}),
872     'Show "check" TODOs')
873     else:
874     flags = A(url.updateParamsDict({'hide_check' : '1'}),
875     'Hide "check" TODOs')
876    
877 fw 2482 def gen():
878 fw 4005 for (bug, description) in self.db.getTODOs(hide_check=hide_check):
879 fw 2482 yield self.make_xref(url, bug), description
880     return self.create_page(
881     url, "Bugs with TODO items",
882 fw 4005 [P(flags),
883     make_table(gen(),
884 fw 2482 caption=("Bug", "Description"))])
885    
886     def page_status_itp(self, path, params, url):
887     def gen():
888     old_pkg = ''
889     for pkg, bugs, debian_bugs in self.db.getITPs(self.db.cursor()):
890     if pkg == old_pkg:
891     pkg = ''
892     else:
893     old_pkg = pkg
894     yield (pkg, self.make_xref_list(url, bugs),
895     self.make_debian_bug_list(url, debian_bugs))
896     return self.create_page(
897     url, "ITPs with potential security issues",
898     [make_table(gen(), caption=("Package", "Issue", "Debian Bugs"),
899     replacement="No ITP bugs are currently known.")])
900    
901     def page_data_unknown_packages(self, path, params, url):
902     def gen():
903     for name, bugs in self.db.getUnknownPackages(self.db.cursor()):
904     yield name, self.make_xref_list(url, bugs)
905     return self.create_page(
906     url, "Unknown packages",
907     [P("""Sometimes, a package referenced in a bug report
908     cannot be found in the database. This can be the result of a spelling
909 thijs 6389 error, or a historic entry refers to a
910     package which is no longer in the archive."""),
911 fw 2482 make_table(gen(), caption=("Package", "Bugs"),
912     replacement="No unknown packages are referenced in the database.")])
913    
914     def page_data_missing_epochs(self, path, params, url):
915     def gen():
916     old_bug = ''
917     old_pkg = ''
918     for bug, pkg, ver1, ver2 in self.db.cursor().execute(
919     """SELECT DISTINCT bug_name, n.package,
920     n.fixed_version, sp.version
921     FROM package_notes AS n, source_packages AS sp
922     WHERE n.package_kind = 'source'
923     AND n.fixed_version NOT LIKE '%:%'
924     AND n.fixed_version <> '0'
925     AND n.bug_origin = ''
926     AND sp.name = n.package
927     AND sp.version LIKE '%:%'
928     ORDER BY bug_name, package"""):
929     if bug == old_bug:
930     bug = ''
931     else:
932     old_bug = bug
933     old_pkg = ''
934     bug = self.make_xref(url, bug)
935     if pkg == old_pkg:
936     pkg = ''
937     else:
938     old_pkg = pkg
939     pkg = self.make_source_package_ref(url, pkg)
940     yield bug, pkg, ver1, ver2
941    
942     return self.create_page(
943     url, "Missing epochs in package versions",
944     [make_table(gen(),
945     caption=("Bug", "Package", "Version 1", "Version 2"),
946     replacement="No source package version with missing epochs.")])
947    
948 fw 3136 def page_data_latently_vulnerable(self, path, params, url):
949     def gen():
950     for pkg, bugs in self.db.cursor().execute(
951     """SELECT package, string_set(bug_name)
952     FROM package_notes AS p1
953     WHERE release <> ''
954 fw 5103 AND (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
955 fw 3136 AND NOT EXISTS (SELECT 1 FROM package_notes AS p2
956     WHERE p2.bug_name = p1.bug_name
957     AND p2.package = p1.package
958     AND release = '')
959     AND EXISTS (SELECT 1 FROM source_packages
960     WHERE name = p1.package AND release = 'sid')
961     GROUP BY package
962     ORDER BY package"""):
963     pkg = self.make_source_package_ref(url, pkg)
964     bugs = bugs.split(',')
965     yield pkg, self.make_xref_list(url, bugs)
966    
967 fw 3166 def gen_unimportant():
968     for pkg, bugs in self.db.cursor().execute(
969     """SELECT package, string_set(bug_name)
970     FROM package_notes AS p1
971     WHERE release <> ''
972     AND urgency <> 'unimportant'
973 fw 5103 AND (bug_name LIKE 'CVE-%' OR bug_name LIKE 'TEMP-%')
974 fw 3166 AND EXISTS (SELECT 1 FROM package_notes AS p2
975     WHERE p2.bug_name = p1.bug_name
976     AND p2.package = p1.package
977     AND release = '')
978     AND NOT EXISTS (SELECT 1 FROM package_notes AS p2
979     WHERE p2.bug_name = p1.bug_name
980     AND p2.package = p1.package
981     AND urgency <> 'unimportant'
982     AND release = '')
983     AND EXISTS (SELECT 1 FROM source_packages
984     WHERE name = p1.package AND release = 'sid')
985     GROUP BY package
986     ORDER BY package"""):
987     pkg = self.make_source_package_ref(url, pkg)
988     bugs = bugs.split(',')
989     yield pkg, self.make_xref_list(url, bugs)
990    
991 fw 3136 return self.create_page(
992     url, "Latently vulnerable packages in unstable",
993     [P(
994     """A package is latently vulnerable in unstable if it is vulnerable in
995     any release, and there is no package note for the same vulnerability
996     and package in unstable (and the package is still available in
997     unstable, of course)."""),
998     make_table(gen(),
999     caption=("Package", "Bugs"),
1000 fw 3166 replacement="No latently vulnerable packages were found."),
1001     P(
1002     """The next table lists issues which are marked unimportant for
1003     unstable, but for which release-specific annotations exist which are
1004     not unimportant."""),
1005     make_table(gen_unimportant(),
1006     caption=("Package", "Bugs"),
1007     replacement=
1008     "No packages with unimportant latent vulnerabilities were found."),
1009     ])
1010 fw 3136
1011 fw 2482 def page_data_releases(self, path, params, url):
1012     def gen():
1013     for (rel, subrel, archive, sources, archs) \
1014     in self.db.availableReleases():
1015     if sources:
1016     sources = 'yes'
1017     else:
1018     sources = 'no'
1019     yield rel, subrel, archive, sources, make_list(archs)
1020     return self.create_page(
1021     url, "Available releases",
1022     [P("""The security issue database is checked against
1023     the Debian releases listed in the table below."""),
1024     make_table(gen(),
1025     caption=("Release", "Subrelease", "Archive",
1026     "Sources", "Architectures"))])
1027    
1028     def page_data_funny_versions(self, path, params, url):
1029     def gen():
1030     for name, release, archive, version, source_version \
1031     in self.db.getFunnyPackageVersions():
1032     yield name, release, archive, source_version, version
1033    
1034     return self.create_page(
1035     url, "Version conflicts between source/binary packages",
1036     [P("""The table below lists source packages
1037     which have a binary package of the same name, but with a different
1038     version. This means that extra care is necessary to determine
1039     the version of a package which has been fixed. (Note that
1040     the bug tracker prefers source versions to binary versions
1041     in this case.)"""),
1042     make_table(gen(),
1043     caption=("Package",
1044     "Release",
1045     "Archive",
1046     "Source Version",
1047     "Binary Version")),
1048     P("""Technically speaking, these version numbering is fine,
1049     but it makes version-based bug tracking quite difficult for these packages."""),
1050     P("""There are many binary packages which are built from source
1051     packages with different version numbering schemes. However, as
1052     long as none of the binary packages carries the same name as the
1053     source package, most confusion is avoided or can be easily
1054     explained.""")])
1055    
1056 fw 2554 def page_data_fake_names(self, path, params, url):
1057     def gen():
1058     for (bug, description) in self.db.getFakeBugs():
1059     yield self.make_xref(url, bug), description
1060     return self.create_page(
1061     url, "Automatically generated issue names",
1062     [P("""Some issues have not been assigned CVE names, but are still
1063     tracked by this database. In this case, the system automatically assigns
1064     a unique name. These names are not stable and can change when the database
1065     is updated, so they should not be used in external references."""),
1066     P('''The automatically generated names come in two flavors:
1067 fw 5103 the first kind starts with the string "''', CODE("TEMP-000000-"),
1068 fw 2554 '''". This means that no Debian bug has been assigned to this
1069     issue (or a bug has been created and is not recorded in this database).
1070     In the second kind of names, there is a Debian bug for the issue, and the "''',
1071     CODE("000000"), '''"part of the name is replaced with the
1072     Debian bug number.'''),
1073     make_table(gen(),
1074     caption=("Bug", "Description"))])
1075    
1076 fw 12999 def page_data_pts(self, path, params, url):
1077     data = []
1078     for pkg, bugs in self.db.cursor().execute(
1079     """SELECT package, COUNT(DISTINCT bug) FROM
1080     (SELECT package, bug FROM stable_status
1081     UNION ALL SELECT package, bug FROM oldstable_status
1082     UNION ALL SELECT DISTINCT sp.name, st.bug_name
1083     FROM source_package_status AS st, source_packages AS sp
1084     WHERE st.vulnerable AND st.urgency <> 'unimportant'
1085     AND sp.rowid = st.package AND sp.release = 'sid'
1086     AND sp.subrelease = '') x
1087     GROUP BY package ORDER BY package"""):
1088     data.append(pkg)
1089     data.append(':')
1090     data.append(str(bugs))
1091     data.append('\n')
1092     return BinaryResult(''.join(data))
1093    
1094 fw 3051 def page_debsecan(self, path, params, url):
1095     obj = '/'.join(path)
1096     data = self.db.getDebsecan(obj)
1097     if data:
1098     return BinaryResult(data)
1099     else:
1100     return self.create_page(
1101     url, "Object not found",
1102     [P("The requested debsecan object has not been found.")],
1103     status=404)
1104    
1105 fw 2482 def create_page(self, url, title, body, search_in_page=False, status=200):
1106     append = body.append
1107     append(HR())
1108     if not search_in_page:
1109     append(self.make_search_button(url))
1110     append(P(A(url.scriptRelative(""), "Home"),
1111     " - ", A(url.absolute("http://secure-testing.debian.net/"),
1112     "Testing Security Team"),
1113     " - ", A(url.absolute("http://www.debian.org/security/"),
1114     "Debian Security"),
1115     " - ", A(url.absolute
1116     ("http://www.enyo.de/fw/impressum.html"),
1117     "Imprint")))
1118     if search_in_page:
1119     on_load = "selectSearch()"
1120     else:
1121     on_load = None
1122     return HTMLResult(self.add_title(title, body,
1123     head_contents=self.head_contents,
1124     body_attribs={'onload': on_load}),
1125     doctype=self.html_dtd(),
1126     status=status)
1127    
1128     def make_search_button(self, url):
1129     return FORM("Search for package or bug name: ",
1130     INPUT(type='text', name='query',
1131     onkeyup="onSearch(this.value)",
1132     onmousemove="onSearch(this.value)"),
1133     INPUT(type='submit', value='Go'),
1134 fw 3597 ' ',
1135 fw 3596 A(url.scriptRelative("data/report"), "Reporting problems"),
1136 fw 2482 method='get',
1137     action=url.scriptRelative(''))
1138    
1139     def url_cve(self, url, name):
1140     return url.absolute("http://cve.mitre.org/cgi-bin/cvename.cgi",
1141     name=name)
1142 fw 2488 def url_nvd(self, url, name):
1143 thijs 9808 return url.absolute("http://web.nvd.nist.gov/view/vuln/detail",
1144     vulnId=name)
1145 fw 2488
1146 fw 2482 def url_dsa(self, url, dsa, re_dsa=re.compile(r'^DSA-(\d+)(?:-\d+)?$')):
1147     match = re_dsa.match(dsa)
1148     if match:
1149     # We must determine the year because there is no generic URL.
1150     (number,) = match.groups()
1151     for (date,) in self.db.cursor().execute(
1152     "SELECT release_date FROM bugs WHERE name = ?", (dsa,)):
1153     (y, m, d) = date.split('-')
1154     return url.absolute("http://www.debian.org/security/%d/dsa-%d"
1155     % (int(y), int(number)))
1156     return None
1157    
1158     def url_debian_bug(self, url, debian):
1159     return url.absolute("http://bugs.debian.org/cgi-bin/bugreport.cgi",
1160     bug=str(debian))
1161     def url_debian_bug_pkg(self, url, debian):
1162     return url.absolute("http://bugs.debian.org/cgi-bin/pkgreport.cgi",
1163     pkg=debian)
1164     def url_pts(self, url, package):
1165     return url.absolute("http://packages.qa.debian.org/common/index.html",
1166     src=package)
1167     def url_testing_status(self, url, package):
1168 thijs 8112 return url.absolute("http://release.debian.org/migration/testing.pl",
1169 fw 2482 package=package)
1170     def url_source_package(self, url, package, full=False):
1171     if full:
1172     return url.scriptRelativeFull("source-package/" + package)
1173     else:
1174     return url.scriptRelative("source-package/" + package)
1175     def url_binary_package(self, url, package, full=False):
1176     if full:
1177     return url.scriptRelativeFull("binary-package/" + package)
1178     else:
1179     return url.scriptRelative("binary-package/" + package)
1180    
1181     def make_xref(self, url, name):
1182     return A(url.scriptRelative(name), name)
1183    
1184     def make_xref_list(self, url, lst, separator=', '):
1185     return make_list(map(lambda x: self.make_xref(url, x), lst), separator)
1186    
1187     def make_debian_bug(self, url, debian):
1188     return A(self.url_debian_bug(url, debian), str(debian))
1189     def make_debian_bug_list(self, url, lst):
1190     return make_list(map(lambda x: self.make_debian_bug(url, x), lst))
1191    
1192     def make_cve_ref(self, url, cve, name=None):
1193     if name is None:
1194     name = cve
1195     return A(self.url_cve(url, cve), name)
1196    
1197 fw 2488 def make_nvd_ref(self, url, cve, name=None):
1198     if name is None:
1199     name = cve
1200     return A(self.url_nvd(url, cve), name)
1201    
1202 fw 2482 def make_dsa_ref(self, url, dsa, name=None):
1203     if name is None:
1204     name = dsa
1205     u = self.url_dsa(url, dsa)
1206     if u:
1207     return A(u, name)
1208     else:
1209     return name
1210    
1211     def make_pts_ref(self, url, pkg, name=None):
1212     if name is None:
1213     name = pkg
1214     return A(self.url_pts(url, pkg), name)
1215    
1216     def make_source_package_ref(self, url, pkg, title=None):
1217     if title is None:
1218     title = pkg
1219     return A(self.url_source_package(url, pkg), title)
1220     def make_binary_package_ref(self, url, pkg, title=None):
1221     if title is None:
1222     title = pkg
1223     return A(self.url_binary_package(url, pkg), title)
1224     def make_binary_packages_ref(self, url, lst):
1225     assert type(lst) <> types.StringType
1226     return make_list(map(lambda x: self.make_binary_package_ref(url, x),
1227     lst))
1228    
1229     def make_red(self, contents):
1230     return SPAN(contents, _class="red")
1231    
1232     def make_dangerous(self, contents):
1233     return SPAN(contents, _class="dangerous")
1234    
1235     def pre_dispatch(self):
1236     self.db.refresh()
1237    
1238     TrackerService(socket_name, db_name).run()

Properties

Name Value
svn:mime-type text/script

  ViewVC Help
Powered by ViewVC 1.1.5