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:
parent
075b45ce9f
commit
4956995f9a
|
@ -22,7 +22,7 @@ import supybot
|
||||||
import supybot.world as world
|
import supybot.world as world
|
||||||
from imp import reload
|
from imp import reload
|
||||||
|
|
||||||
__version__ = "1.0.0"
|
__version__ = "1.1.0"
|
||||||
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
|
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
|
||||||
__contributors__ = {
|
__contributors__ = {
|
||||||
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Concept'],
|
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Concept'],
|
||||||
|
|
|
@ -35,6 +35,23 @@ def description(pkg):
|
||||||
return pkg['Description'].split('\n')[0]
|
return pkg['Description'].split('\n')[0]
|
||||||
return None
|
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:
|
class Apt:
|
||||||
def __init__(self, plugin):
|
def __init__(self, plugin):
|
||||||
self.aptdir = plugin.registryValue('aptdir')
|
self.aptdir = plugin.registryValue('aptdir')
|
||||||
|
@ -44,15 +61,6 @@ class Apt:
|
||||||
os.environ["LANG"] = "C"
|
os.environ["LANG"] = "C"
|
||||||
if self.aptdir:
|
if self.aptdir:
|
||||||
self.distros = sorted([x[:-5] for x in os.listdir(self.aptdir) if x.endswith('.list')])
|
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):
|
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 '.-_+/'])
|
_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))
|
return "%s is not a valid distribution: %s" % (distro, ", ".join(self.distros))
|
||||||
pkg = _pkg
|
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 not data:
|
||||||
if filelookup:
|
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:
|
||||||
if data[0] == 'sh:': # apt-file isn't installed
|
if data[0] == 'sh:': # apt-file isn't installed
|
||||||
self.log.error("PackageInfo/packages: apt-file is not 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 "File %s found in %s" % (pkg, ', '.join(data))
|
||||||
return 'Package/file %s does not exist in %s' % (pkg, distro)
|
return 'Package/file %s does not exist in %s' % (pkg, distro)
|
||||||
return "No packages matching '%s' could be found" % pkg
|
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:
|
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)
|
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:
|
else:
|
||||||
return "Found: %s" % ', '.join(pkgs[:5])
|
return "Found: %s" % ', '.join(pkgs[:5])
|
||||||
|
|
||||||
def info(self, pkg, chkdistro):
|
def raw_info(self, pkg, chkdistro):
|
||||||
if not pkg.strip():
|
if not pkg.strip():
|
||||||
return ''
|
return ''
|
||||||
_pkg = ''.join([x for x in pkg.strip().split(None,1)[0] if x.isalnum() or x in '.-_+'])
|
_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
|
pkg = _pkg
|
||||||
|
|
||||||
data = subprocess.getoutput(self.aptcommand % (distro, distro, distro, distro, 'show', pkg))
|
try:
|
||||||
data2 = subprocess.getoutput(self.aptcommand % (distro, distro, distro, distro, 'showsrc', pkg))
|
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:
|
if not data or 'E: No packages found' in data:
|
||||||
return 'Package %s does not exist in %s' % (pkg, distro)
|
return 'Package %s does not exist in %s' % (pkg, distro)
|
||||||
maxp = {'Version': '0~'}
|
maxp = {'Version': '0~'}
|
||||||
|
@ -151,10 +171,24 @@ class Apt:
|
||||||
if archs:
|
if archs:
|
||||||
archs = ' (Only available for %s)' % '; '.join(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" %
|
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['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
|
# Simple test
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -189,6 +223,8 @@ if __name__ == "__main__":
|
||||||
aptlookup = Apt(plugin)
|
aptlookup = Apt(plugin)
|
||||||
if command == "find":
|
if command == "find":
|
||||||
print(aptlookup.find(lookup, dists))
|
print(aptlookup.find(lookup, dists))
|
||||||
|
elif command == "depends":
|
||||||
|
print(aptlookup.depends(lookup, dists))
|
||||||
else:
|
else:
|
||||||
print(aptlookup.info(lookup, dists))
|
print(aptlookup.info(lookup, dists))
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,7 @@ import supybot.conf as conf
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
from imp import reload
|
|
||||||
from . import packages
|
from . import packages
|
||||||
reload(packages)
|
|
||||||
|
|
||||||
def get_user(msg):
|
def get_user(msg):
|
||||||
try:
|
try:
|
||||||
|
@ -155,6 +153,50 @@ class PackageInfo(callbacks.Plugin):
|
||||||
|
|
||||||
info = wrap(real_info, ['anything', optional('text')])
|
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):
|
def real_find(self, irc, msg, args, package, release):
|
||||||
"""<package/filename> [<release>]
|
"""<package/filename> [<release>]
|
||||||
|
|
||||||
|
@ -216,6 +258,8 @@ class PackageInfo(callbacks.Plugin):
|
||||||
(term, rest) = (rest.split(' ', 1) + [None])[:2]
|
(term, rest) = (rest.split(' ', 1) + [None])[:2]
|
||||||
if cmd == "find":
|
if cmd == "find":
|
||||||
self.real_find(irc, msg, [], term, rest)
|
self.real_find(irc, msg, [], term, rest)
|
||||||
|
elif cmd == "depends":
|
||||||
|
self.real_depends(irc, msg, [], term, rest)
|
||||||
else:
|
else:
|
||||||
self.real_info(irc, msg, [], term, rest)
|
self.real_info(irc, msg, [], term, rest)
|
||||||
|
|
||||||
|
@ -234,6 +278,8 @@ class PackageInfo(callbacks.Plugin):
|
||||||
(term, rest) = (rest.split(' ', 1) + [None])[:2]
|
(term, rest) = (rest.split(' ', 1) + [None])[:2]
|
||||||
if cmd == "find":
|
if cmd == "find":
|
||||||
self.real_find(irc, msg, [], term, rest)
|
self.real_find(irc, msg, [], term, rest)
|
||||||
|
elif cmd == "depends":
|
||||||
|
self.real_depends(irc, msg, [], term, rest)
|
||||||
else:
|
else:
|
||||||
self.real_info(irc, msg, [], term, rest)
|
self.real_info(irc, msg, [], term, rest)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue