-
Notifications
You must be signed in to change notification settings - Fork 63
/
packages.py
235 lines (216 loc) · 9.19 KB
/
packages.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#!/usr/bin/env python
# -*- Encoding: utf-8 -*-
###
# Copyright (c) 2006-2007 Dennis Kaarsemaker
# Copyright (c) 2008-2010 Terence Simpson
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
###
import sys
import warnings
warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
import subprocess, os, apt
from email.feedparser import FeedParser
if sys.version_info[0] >= 3:
import urllib.parse
urlquote = urllib.parse.quote
else:
import urllib
urlquote = urllib.quote
def component(arg):
if '/' in arg: return arg[:arg.find('/')]
return 'main'
def description(pkg):
if not pkg:
return None
if 'Description-en' in pkg:
return pkg['Description-en'].split('\n')[0]
elif 'Description' in pkg:
return pkg['Description'].split('\n')[0]
return None
def apt_cache(aptdir, distro, extra):
return subprocess.check_output(['apt-cache',
'-oDir::State::Lists=%s/%s' % (aptdir, distro),
'-oDir::etc::sourcelist=%s/%s.list' % (aptdir, distro),
'-oDir::etc::SourceParts=%s/%s.list.d' % (aptdir, distro),
'-oDir::State::status=%s/%s.status' % (aptdir, distro),
'-oDir::Cache=%s/cache' % aptdir,
'-oAPT::Architecture=i386'] +
extra).decode('utf8')
def apt_file(aptdir, distro, pkg):
return subprocess.check_output(['apt-file',
'-s', '%s/%s.list' % (aptdir, distro),
'-c', '%s/apt-file/%s' % (aptdir, distro),
'-l', '-a', 'i386',
'search', pkg]).decode('utf8')
class Apt:
def __init__(self, plugin):
self.aptdir = plugin.registryValue('aptdir')
self.distros = []
self.plugin = plugin
self.log = plugin.log
os.environ["LANG"] = "C"
if self.aptdir:
self.distros = [x[:-5] for x in os.listdir(self.aptdir) if x.endswith('.list')]
self.distros.sort()
def find(self, pkg, chkdistro, filelookup=True):
_pkg = ''.join([x for x in pkg.strip().split(None,1)[0] if x.isalnum() or x in '.-_+/'])
distro = ''
if len(pkg.strip().split()) > 1:
distro = ''.join([x for x in pkg.strip().split(None,2)[1] if x.isalnum() or x in '.-_+'])
if not distro:
distro = chkdistro
if distro not in self.distros:
return "%s is not a valid distribution: %s" % (distro, ", ".join(self.distros))
pkg = _pkg
try:
data = apt_cache(self.aptdir, distro, ['search', '-n', pkg])
except subprocess.CalledProcessError as e:
data = e.output
if not data:
if filelookup:
try:
data = apt_file(self.aptdir, distro, pkg).split()
except subprocess.CalledProcessError as e:
data = e.output
if data:
if data[0] == 'sh:': # apt-file isn't installed
self.log.error("PackageInfo/packages: apt-file is not installed")
return "Please use http://packages.ubuntu.com/ to search for files"
if data[0] == 'E:': # No files in the cache dir
self.log.error("PackageInfo/packages: Please run the 'update_apt_file' script")
return "Cache out of date, please contact the administrator"
if data[0] == "Use" and data[1] == "of":
url = "http://packages.ubuntu.com/search?searchon=contents&keywords=%s&mode=&suite=%s&arch=any" % (urlquote(pkg), distro)
return url
if len(data) > 10:
return "File %s found in %s (and %d others) http://packages.ubuntu.com/search?searchon=contents&keywords=%s&mode=&suite=%s&arch=any" % (pkg, ', '.join(data[:10]), len(data)-10, urlquote(pkg), distro)
return "File %s found in %s" % (pkg, ', '.join(data))
return 'Package/file %s does not exist in %s' % (pkg, distro)
return "No packages matching '%s' could be found" % pkg
pkgs = [x.split()[0] for x in data.split('\n') if x.split()]
if len(pkgs) > 10:
return "Found: %s (and %d others) http://packages.ubuntu.com/search?keywords=%s&searchon=names&suite=%s§ion=all" % (', '.join(pkgs[:10]), len(pkgs)-10, urlquote(pkg), distro)
else:
return "Found: %s" % ', '.join(pkgs[:5])
def raw_info(self, pkg, chkdistro):
if not pkg.strip():
return ''
_pkg = ''.join([x for x in pkg.strip().split(None,1)[0] if x.isalnum() or x in '.-_+'])
distro = chkdistro
if len(pkg.strip().split()) > 1:
distro = ''.join([x for x in pkg.strip().split(None,2)[1] if x.isalnum() or x in '-._+'])
if distro not in self.distros:
return "%r is not a valid distribution: %s" % (distro, ", ".join(self.distros))
pkg = _pkg
try:
data = apt_cache(self.aptdir, distro, ['show', pkg])
except subprocess.CalledProcessError as e:
data = e.output
try:
data2 = apt_cache(self.aptdir, distro, ['showsrc', pkg])
except subprocess.CalledProcessError as e:
data2 = e.output
if not data or 'E: No packages found' in data:
return 'Package %s does not exist in %s' % (pkg, distro)
maxp = {'Version': '0~'}
packages = [x.strip() for x in data.split('\n\n')]
for p in packages:
if not p.strip():
continue
parser = FeedParser()
parser.feed(p)
p = parser.close()
if type(p) == type(""):
self.log.error("PackageInfo/packages: apt returned an error, do you have the deb-src URLs in %s.list?" % distro)
return "Package lookup faild"
if not p.get("Version", None):
continue
if apt.apt_pkg.version_compare(maxp['Version'], p['Version']) <= 0:
maxp = p
del parser
maxp2 = {'Version': '0~'}
packages2 = [x.strip() for x in data2.split('\n\n')]
for p in packages2:
if not p.strip():
continue
parser = FeedParser()
parser.feed(p)
p = parser.close()
if type(p) == type(""):
self.log.error("PackageInfo/packages: apt returned an error, do you have the deb-src URLs in %s.list?" % distro)
return "Package lookup faild"
if not p['Version']:
continue
if apt.apt_pkg.version_compare(maxp2['Version'], p['Version']) <= 0:
maxp2 = p
del parser
archs = ''
if 'Architecture' in maxp2:
archs = [_.strip() for _ in maxp2['Architecture'].split() if _.strip()]
for arch in archs:
if arch not in ('any', 'all'):
continue
else:
archs = ''
break
if archs:
archs = ' (Only available for %s)' % '; '.join(archs)
maxp["Distribution"] = distro
maxp["Architectures"] = archs
return maxp
def info(self, pkg, chkdistro):
maxp = self.raw_info(pkg, chkdistro)
if isinstance(maxp, str):
return maxp
return("%s (source: %s): %s. In component %s, is %s. Version %s (%s), package size %s kB, installed size %s kB%s" %
(maxp['Package'], maxp['Source'] or maxp['Package'], description(maxp), component(maxp['Section']),
maxp['Priority'], maxp['Version'], maxp["Distribution"], int(maxp['Size'])/1024, maxp['Installed-Size'], maxp["Architectures"]))
def depends(self, pkg, chkdistro):
maxp = self.raw_info(pkg, chkdistro)
if isinstance(maxp, str):
return maxp
return("%s (version %s in %s) depends on: %s" %
(maxp['Package'], maxp["Version"], maxp["Distribution"], maxp["Depends"]))
# Simple test
if __name__ == "__main__":
import sys
argv = sys.argv
argc = len(argv)
if argc == 1:
print("Need at least one arg")
sys.exit(1)
if argc > 3:
print("Only takes 2 args")
sys.exit(1)
class FakePlugin:
class FakeLog:
def error(*args, **kwargs):
pass
def __init__(self):
self.log = self.FakeLog()
def registryValue(self, *args, **kwargs):
return "/home/bot/aptdir"
command = argv[1].split(None, 1)[0]
try:
lookup = argv[1].split(None, 1)[1]
except:
print("Need something to lookup")
sys.exit(1)
dists = "hardy"
if argc == 3:
dists = argv[2]
plugin = FakePlugin()
aptlookup = Apt(plugin)
if command == "find":
print(aptlookup.find(lookup, dists))
else:
print(aptlookup.info(lookup, dists))