PackageInfo: Various improvements (by Valentin Lorentz)
* Add depends command. * Improve handling of find and info. * Fix crash by double reloading. * Fix shell code injection vulnerability.
This commit is contained in:
@ -22,7 +22,7 @@ import supybot
|
||||
import supybot.world as world
|
||||
from imp import reload
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__version__ = "1.1.0"
|
||||
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
|
||||
__contributors__ = {
|
||||
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Concept'],
|
||||
|
@ -35,6 +35,23 @@ def description(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::State::Status=%s/%s.status' % (aptdir, distro),
|
||||
'-oDir::Etc::SourceList=%s/%s.list' % (aptdir, distro),
|
||||
'-oDir::Etc::SourceParts=""',
|
||||
'-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')
|
||||
@ -44,15 +61,6 @@ class Apt:
|
||||
os.environ["LANG"] = "C"
|
||||
if self.aptdir:
|
||||
self.distros = sorted([x[:-5] for x in os.listdir(self.aptdir) if x.endswith('.list')])
|
||||
self.aptcommand = """apt-cache\\
|
||||
-o"Dir::State::Lists=%s/%%s"\\
|
||||
-o"Dir::etc::sourcelist=%s/%%s.list"\\
|
||||
-o"Dir::etc::SourceParts=%s/%%s.list.d"\\
|
||||
-o"Dir::State::status=%s/%%s.status"\\
|
||||
-o"Dir::Cache=%s/cache"\\
|
||||
-o"APT::Architecture=i386"\\
|
||||
%%s %%s""" % tuple([self.aptdir]*5)
|
||||
self.aptfilecommand = """apt-file -s %s/%%s.list -c %s/apt-file/%%s -l -a i386 search %%s""" % (self.aptdir, self.aptdir)
|
||||
|
||||
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 '.-_+/'])
|
||||
@ -65,10 +73,16 @@ class Apt:
|
||||
return "%s is not a valid distribution: %s" % (distro, ", ".join(self.distros))
|
||||
pkg = _pkg
|
||||
|
||||
data = subprocess.getoutput(self.aptcommand % (distro, distro, distro, distro, 'search -n', pkg))
|
||||
try:
|
||||
data = apt_cache(self.aptdir, distro, ['search', '-n', pkg])
|
||||
except subprocess.CalledProcessError as e:
|
||||
data = e.output
|
||||
if not data:
|
||||
if filelookup:
|
||||
data = subprocess.getoutput(self.aptfilecommand % (distro, distro, pkg)).split()
|
||||
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")
|
||||
@ -84,13 +98,13 @@ class Apt:
|
||||
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')]
|
||||
pkgs = [x.split()[0] for x in data.split('\n') if x]
|
||||
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, utils.web.urlquote(pkg), distro)
|
||||
else:
|
||||
return "Found: %s" % ', '.join(pkgs[:5])
|
||||
|
||||
def info(self, pkg, chkdistro):
|
||||
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 '.-_+'])
|
||||
@ -102,8 +116,14 @@ class Apt:
|
||||
|
||||
pkg = _pkg
|
||||
|
||||
data = subprocess.getoutput(self.aptcommand % (distro, distro, distro, distro, 'show', pkg))
|
||||
data2 = subprocess.getoutput(self.aptcommand % (distro, distro, distro, distro, 'showsrc', 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~'}
|
||||
@ -151,10 +171,24 @@ class Apt:
|
||||
if archs:
|
||||
archs = ' (Only available for %s)' % '; '.join(archs)
|
||||
|
||||
maxp["Distrobution"] = distro
|
||||
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'], distro, int(maxp['Size'])/1024, maxp['Installed-Size'], archs))
|
||||
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__":
|
||||
@ -189,6 +223,8 @@ if __name__ == "__main__":
|
||||
aptlookup = Apt(plugin)
|
||||
if command == "find":
|
||||
print(aptlookup.find(lookup, dists))
|
||||
elif command == "depends":
|
||||
print(aptlookup.depends(lookup, dists))
|
||||
else:
|
||||
print(aptlookup.info(lookup, dists))
|
||||
|
||||
|
@ -26,9 +26,7 @@ import supybot.conf as conf
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from imp import reload
|
||||
from . import packages
|
||||
reload(packages)
|
||||
|
||||
def get_user(msg):
|
||||
try:
|
||||
@ -155,6 +153,50 @@ class PackageInfo(callbacks.Plugin):
|
||||
|
||||
info = wrap(real_info, ['anything', optional('text')])
|
||||
|
||||
def real_depends(self, irc, msg, args, package, release):
|
||||
"""<package> [<release>]
|
||||
|
||||
Lookup dependencies for <package>, optionally in <release>
|
||||
"""
|
||||
channel = self.__getChannel(msg.args[0])
|
||||
reply_target = ircutils.replyTo(msg)
|
||||
(release, rest) = self.__getRelease(irc, release, channel)
|
||||
if not release:
|
||||
return
|
||||
reply = self.Apt.depends(package, release)
|
||||
if rest:
|
||||
if rest[0] == '|':
|
||||
try:
|
||||
target = rest
|
||||
while target[0] == '|':
|
||||
target = target[1:].strip()
|
||||
if target.lower() == "me":
|
||||
target = msg.nick
|
||||
queue(irc, reply_target, "%s: %s" % (target, reply))
|
||||
return
|
||||
except Exception as e:
|
||||
self.log.info("PackageInfo: (depends) Exception in pipe: %r" % e)
|
||||
pass
|
||||
elif rest[0] == '>':
|
||||
try:
|
||||
while rest[0] == '>':
|
||||
rest = rest[1:].strip()
|
||||
targets = [_ for _ in rest.split() if _] # Split and discard empty parts
|
||||
target = stripNick(targets[0]) # Take the first "nick" and strip off bad chars
|
||||
if target.lower() == "me":
|
||||
target = msg.nick # redirect
|
||||
if not target: # Throw error
|
||||
raise Exception('No target')
|
||||
queue(irc, target, "<%s> wants you to know: %s" % (msg.nick, reply))
|
||||
return
|
||||
except Exception as e:
|
||||
self.log.info("PackageInfo: (depends) Exception in redirect: %r" % e)
|
||||
pass
|
||||
|
||||
queue(irc, reply_target, reply)
|
||||
|
||||
depends = wrap(real_depends, ['anything', optional('text')])
|
||||
|
||||
def real_find(self, irc, msg, args, package, release):
|
||||
"""<package/filename> [<release>]
|
||||
|
||||
@ -216,6 +258,8 @@ class PackageInfo(callbacks.Plugin):
|
||||
(term, rest) = (rest.split(' ', 1) + [None])[:2]
|
||||
if cmd == "find":
|
||||
self.real_find(irc, msg, [], term, rest)
|
||||
elif cmd == "depends":
|
||||
self.real_depends(irc, msg, [], term, rest)
|
||||
else:
|
||||
self.real_info(irc, msg, [], term, rest)
|
||||
|
||||
@ -234,6 +278,8 @@ class PackageInfo(callbacks.Plugin):
|
||||
(term, rest) = (rest.split(' ', 1) + [None])[:2]
|
||||
if cmd == "find":
|
||||
self.real_find(irc, msg, [], term, rest)
|
||||
elif cmd == "depends":
|
||||
self.real_depends(irc, msg, [], term, rest)
|
||||
else:
|
||||
self.real_info(irc, msg, [], term, rest)
|
||||
|
||||
|
Reference in New Issue
Block a user