PackageInfo: General overhaul.

* Add full support for Debian releases.
* Unify and simplify piping and redirection.
* Various general and minor improvements.
* Switch architecture for infos to amd64.
* Update default configuration.
* Improve APT update scripts.
This commit is contained in:
Krytarik Raido 2018-03-09 19:56:04 +01:00
parent 4956995f9a
commit 398247f0dd
7 changed files with 329 additions and 489 deletions

View File

@ -20,10 +20,10 @@ deb-src http://archive.ubuntu.com/ubuntu lucid main restricted universe multiver
supybot.plugins.PackageInfo.defaultRelease: supybot.plugins.PackageInfo.defaultRelease:
Set this to the default release to use when none is specified. (Channel) Set this to the default release to use when none is specified. (Channel)
Default: 'lucid' Default: 'zesty'
Whenever you create a new .list file, it is important to run the update_apt Whenever you create a new .list file, it is important to run the update_apt
and update_apt_file scripts that comes with this plugin. Before you run these, and update_apt_file scripts that come with this plugin. Before you run these,
you have to edit them to point to your apt dir. It's also useful to run them you have to edit them to point to your apt dir. It's also useful to run them
periodically from cron (say, once per week for update_apt and once per moth for periodically from cron (say, once per week for update_apt and once per moth for
update_apt_file). You also need to reload the plugin to make it pick up the new update_apt_file). You also need to reload the plugin to make it pick up the new
@ -39,11 +39,14 @@ Default: !
--Usage-- --Usage--
find <package/filename> [<release>] find <package/filename> [<release>]
Search for <package> or, of that fails, find <filename>'s package(s). Search for <package> or, if that fails, <filename> in packages,
Optionally in <release> optionally in <release>
info <package> [<release>] info <package> [<release>]
Lookup information for <package>, optionally in <release> Look up information for <package>, optionally in <release>
if supybot.plugins.PackageInfo.enabled is True the bot will also reply to depends <package> [<release>]
Look up dependencies for <package>, optionally in <release>
If supybot.plugins.PackageInfo.enabled is True the bot will also reply to
the commands if prefixed with supybot.plugins.PackageInfo.prefixchar. the commands if prefixed with supybot.plugins.PackageInfo.prefixchar.

View File

@ -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.1.0" __version__ = "1.2.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'],

View File

@ -18,15 +18,24 @@ import supybot.conf as conf
import supybot.registry as registry import supybot.registry as registry
def configure(advanced): def configure(advanced):
# This will be called by supybot to configure this module. advanced is # This will be called by Supybot to configure this module. advanced is
# a bool that specifies whether the user identified himself as an advanced # a bool that specifies whether the user identified himself as an advanced
# user or not. You should effect your configuration by manipulating the # user or not. You should affect your configuration by manipulating the
# registry as appropriate. # registry as appropriate.
def makeSource(release): def makeSourceUbuntu(release):
return """deb http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse return """deb http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
""" % (release, release) """ % (release, release)
#"""
def makeSourceDebian(release):
return """deb http://deb.debian.org/debian/ %s main contrib non-free
deb-src deb http://deb.debian.org/debian/ %s main contrib non-free
""" % (release, release)
def makeSourceDebianSec(release):
return """deb http://security.debian.org/ %s/updates main contrib non-free
deb-src deb http://security.debian.org/ %s/updates main contrib non-free
""" % (release, release)
from supybot.questions import output, expect, something, yn from supybot.questions import output, expect, something, yn
import subprocess import subprocess
@ -42,13 +51,13 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
enabled = yn("Enable this plugin in all channels?", default=True) enabled = yn("Enable this plugin in all channels?", default=True)
if enabled and advanced: if enabled and advanced:
prefixchar = something("Which prefix character should be bot respond to?", default=PackageInfo.prefixchar._default) prefixchar = something("Which prefix character should the bot respond to?", default=PackageInfo.prefixchar._default)
defaultRelease = something("What should be the default distrobution when not specified?", default=PackageInfo.defaultRelease._default) defaultRelease = something("What should be the default release when none is specified?", default=PackageInfo.defaultRelease._default)
aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=PackageInfo.aptdir._default) aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=PackageInfo.aptdir._default)
# People tend to thing this should be /var/cache/apt # People tend to think this should be /var/cache/apt
while aptdir.startswith('/var'): #NOTE: This is not a good hack. Maybe just blacklist /var/cache/apt (or use apt to report back the cache dir) while aptdir.startswith('/var'): #NOTE: This is not a good hack. Maybe just blacklist /var/cache/apt (or use apt to report back the cache dir)
output("NO! Do not use your systems apt directory") output("NO! Do not use your system's apt directory")
aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=PackageInfo.aptdir._default) aptdir = something("Which directory should be used for the apt cache when looking up packages?", default=PackageInfo.aptdir._default)
else: else:
@ -62,7 +71,8 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
PackageInfo.prefixchar.setValue(prefixchar) PackageInfo.prefixchar.setValue(prefixchar)
PackageInfo.defaultRelease.setValue(defaultRelease) PackageInfo.defaultRelease.setValue(defaultRelease)
default_dists = set(['dapper', 'hardy', 'lucid', 'maveric', 'natty', 'oneiric']) default_dists = set(['precise', 'trusty', 'xenial', 'yakkety', 'zesty', 'artful',
'oldstable', 'stable', 'unstable', 'testing', 'experimental'])
pluginDir = os.path.abspath(os.path.dirname(__file__)) pluginDir = os.path.abspath(os.path.dirname(__file__))
update_apt = os.path.join(pluginDir, 'update_apt') update_apt = os.path.join(pluginDir, 'update_apt')
update_apt_file = os.path.join(pluginDir, 'update_apt_file') update_apt_file = os.path.join(pluginDir, 'update_apt_file')
@ -72,7 +82,7 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
## Create the aptdir ## Create the aptdir
try: try:
os.makedirs(aptdir) os.makedirs(aptdir)
except OSError: # The error number would be OS dependant (17 on Linux 2.6, ?? on others). So just pass on this except OSError: # The error number would be OS dependent (17 on Linux 2.6, ?? on others). So just pass on this
pass pass
for release in default_dists: for release in default_dists:
@ -80,19 +90,31 @@ deb-src http://archive.ubuntu.com/ubuntu/ %s main restricted universe multiverse
try: try:
output("Creating %s" % filename) output("Creating %s" % filename)
fd = open(filename, 'wb') fd = open(filename, 'wb')
fd.write("# Apt sources list for Ubuntu %s\n" % release) fd.write("# Apt sources list for %s\n" % release)
fd.write(makeSource(release)) if release in ('oldstable', 'stable', 'unstable', 'testing', 'experimental'):
fd.write(makeSource(release + '-security')) fd.write(makeSourceDebian(release))
fd.write(makeSource(release + '-updates')) if release in ('oldstable', 'stable', 'testing'):
fd.write(makeSourceDebian(release + '-updates'))
fd.write(makeSourceDebianSec(release))
else:
fd.write(makeSourceUbuntu(release))
fd.write(makeSourceUbuntu(release + '-updates'))
fd.write(makeSourceUbuntu(release + '-security'))
fd.close() fd.close()
if release in ('unstable', 'experimental'):
continue
for sub in ('backports', 'proposed'): for sub in ('backports', 'proposed'):
sub_release = "%s-%s" % (release, sub) sub_release = "%s-%s" % (release, sub)
filename = os.path.join(aptdir, "%s.list" % sub_release) filename = os.path.join(aptdir, "%s.list" % sub_release)
output("Creating %s" % filename) output("Creating %s" % filename)
fd = open(filename, 'wb') fd = open(filename, 'wb')
fd.write("# Apt sources list for Ubuntu %s\n" % release) fd.write("# Apt sources list for %s\n" % sub_release)
fd.write(makeSource(sub_release)) if release in ('oldstable', 'stable', 'testing'):
fd.write(makeSourceDebian(sub_release.replace('proposed', 'proposed-updates')))
else:
fd.write(makeSourceUbuntu(sub_release))
fd.close() fd.close()
except Exception as e: except Exception as e:
output("Error writing to %r: %r (%s)" % (filename, str(e), type(e))) output("Error writing to %r: %r (%s)" % (filename, str(e), type(e)))
@ -116,7 +138,7 @@ conf.registerChannelValue(PackageInfo, 'enabled',
conf.registerChannelValue(PackageInfo, 'prefixchar', conf.registerChannelValue(PackageInfo, 'prefixchar',
conf.ValidPrefixChars('!', "Character the bot will respond to")) conf.ValidPrefixChars('!', "Character the bot will respond to"))
conf.registerChannelValue(PackageInfo, 'defaultRelease', conf.registerChannelValue(PackageInfo, 'defaultRelease',
registry.String('natty', "Default release to use when none is specified")) registry.String('zesty', "Default release to use when none is specified"))
conf.registerGlobalValue(PackageInfo, 'aptdir', conf.registerGlobalValue(PackageInfo, 'aptdir',
conf.Directory(conf.supybot.directories.data.dirize('aptdir'), "Path to the apt directory", private=True)) conf.Directory(conf.supybot.directories.data.dirize('aptdir'), "Path to the apt directory", private=True))

View File

@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- Encoding: utf-8 -*- # -*- Encoding: utf-8 -*-
### ###
# Copyright (c) 2006-2007 Dennis Kaarsemaker # Copyright (c) 2006-2007 Dennis Kaarsemaker
@ -18,38 +17,39 @@
import warnings import warnings
warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning) warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
import subprocess, os, apt import subprocess, os, apt, re
import supybot.utils as utils import supybot.utils as utils
from email.parser import FeedParser from email.parser import FeedParser
def component(arg): def component(arg):
if '/' in arg: return arg[:arg.find('/')] if '/' in arg:
return arg[:arg.find('/')]
return 'main' return 'main'
def description(pkg): def description(pkg):
if not pkg:
return None
if 'Description-en' in pkg: if 'Description-en' in pkg:
return pkg['Description-en'].split('\n')[0] return pkg['Description-en'].split('\n')[0]
elif 'Description' in pkg: elif 'Description' in pkg:
return pkg['Description'].split('\n')[0] return pkg['Description'].split('\n')[0]
return None return "Description not available"
def apt_cache(aptdir, distro, extra): def apt_cache(aptdir, distro, cmd, pkg):
return subprocess.check_output(['apt-cache', return subprocess.check_output(['apt-cache',
'-oAPT::Architecture=amd64',
'-oAPT::Architectures::=i386',
'-oAPT::Architectures::=amd64',
'-oDir::State::Lists=%s/%s' % (aptdir, distro), '-oDir::State::Lists=%s/%s' % (aptdir, distro),
'-oDir::State::Status=%s/%s.status' % (aptdir, distro), '-oDir::State::Status=%s/%s.status' % (aptdir, distro),
'-oDir::Etc::SourceList=%s/%s.list' % (aptdir, distro), '-oDir::Etc::SourceList=%s/%s.list' % (aptdir, distro),
'-oDir::Etc::SourceParts=""', '-oDir::Etc::SourceParts=""',
'-oDir::Cache=%s/cache' % aptdir, '-oDir::Cache=%s/cache' % aptdir] +
'-oAPT::Architecture=i386'] + cmd + [pkg.lower()]).decode('utf8')
extra).decode('utf8')
def apt_file(aptdir, distro, pkg): def apt_file(aptdir, distro, pkg):
return subprocess.check_output(['apt-file', return subprocess.check_output(['apt-file',
'-s', '%s/%s.list' % (aptdir, distro), '-s', '%s/%s.list' % (aptdir, distro),
'-c', '%s/apt-file/%s' % (aptdir, distro), '-c', '%s/apt-file/%s' % (aptdir, distro),
'-l', '-a', 'i386', '-l', '-i', '-a', 'amd64',
'search', pkg]).decode('utf8') 'search', pkg]).decode('utf8')
class Apt: class Apt:
@ -62,19 +62,22 @@ class Apt:
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')])
def find(self, pkg, chkdistro, filelookup=True): def _parse(self, pkg):
_pkg = ''.join([x for x in pkg.strip().split(None,1)[0] if x.isalnum() or x in '.-_+/']) parser = FeedParser()
distro = '' parser.feed(pkg)
if len(pkg.strip().split()) > 1: return parser.close()
distro = ''.join([x for x in pkg.strip().split(None,2)[1] if x.isalnum() or x in '.-_+'])
if not distro: def find(self, pkg, distro, filelookup=True):
distro = chkdistro
if distro not in self.distros: if distro not in self.distros:
return "%s is not a valid distribution: %s" % (distro, ", ".join(self.distros)) return "%r is not a valid release: %s" % (distro, ", ".join(self.distros))
pkg = _pkg
if distro.split('-')[0] in ('oldstable', 'stable', 'unstable', 'testing', 'experimental'):
pkgTracURL = "https://packages.debian.org"
else:
pkgTracURL = "https://packages.ubuntu.com"
try: try:
data = apt_cache(self.aptdir, distro, ['search', '-n', pkg]) data = apt_cache(self.aptdir, distro, ['search', '-n'], pkg)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
data = e.output data = e.output
if not data: if not data:
@ -85,111 +88,81 @@ class Apt:
data = e.output 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")
return "Please use http://packages.ubuntu.com/ to search for files" return "Please use %s/ to search for files" % pkgTracURL
if data[0] == 'E:': # No files in the cache dir if data[0] == 'E:': # No files in the cache dir
self.log.error("PackageInfo/packages: Please run the 'update_apt_file' script") self.log.error("PackageInfo/packages: Please run the 'update_apt_file' script")
return "Cache out of date, please contact the administrator" return "Cache out of date, please contact the administrator"
if data[0] == "Use" and data[1] == "of": if data[0] == "Use" and data[1] == "of":
url = "http://packages.ubuntu.com/search?searchon=contents&keywords=%s&mode=&suite=%s&arch=any" % (utils.web.urlquote(pkg), distro) return "%s/search?searchon=contents&keywords=%s&mode=exactfilename&suite=%s&arch=any" % (pkgTracURL, utils.web.urlquote(pkg), distro)
return url
if len(data) > 10: 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, utils.web.urlquote(pkg), distro) return "File %s found in %s and %d others <%s/search?searchon=contents&keywords=%s&mode=exactfilename&suite=%s&arch=any>" % (pkg, ', '.join(data[:10]), len(data)-10, pkgTracURL, utils.web.urlquote(pkg), distro)
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') if x] 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&section=all" % (', '.join(pkgs[:10]), len(pkgs)-10, utils.web.urlquote(pkg), distro) return "Found: %s and %d others <%s/search?keywords=%s&searchon=names&suite=%s&section=all>" % (', '.join(pkgs[:10]), len(pkgs)-10, pkgTracURL, utils.web.urlquote(pkg), distro)
else: else:
return "Found: %s" % ', '.join(pkgs[:5]) return "Found: %s" % ', '.join(pkgs[:5])
def raw_info(self, pkg, chkdistro): def raw_info(self, pkg, distro, archlookup=True):
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: if distro not in self.distros:
return "%r is not a valid distribution: %s" % (distro, ", ".join(self.distros)) return "%r is not a valid release: %s" % (distro, ", ".join(self.distros))
pkg = _pkg
try: try:
data = apt_cache(self.aptdir, distro, ['show', pkg]) data = apt_cache(self.aptdir, distro, ['show'], pkg)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
data = e.output 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~'}
packages = [x.strip() for x in data.split('\n\n')] packages = list(map(self._parse, [x for x in data.split('\n\n') if x]))
for p in packages: 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: if apt.apt_pkg.version_compare(maxp['Version'], p['Version']) <= 0:
maxp = p maxp = p
del parser
if not archlookup:
return maxp
try:
data2 = apt_cache(self.aptdir, distro, ['showsrc'], pkg)
except subprocess.CalledProcessError:
return maxp
maxp2 = {'Version': '0~'} maxp2 = {'Version': '0~'}
packages2 = [x.strip() for x in data2.split('\n\n')] packages2 = list(map(self._parse, [x for x in data2.split('\n\n') if x]))
for p in packages2: 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: if apt.apt_pkg.version_compare(maxp2['Version'], p['Version']) <= 0:
maxp2 = p 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 = re.match(r'.*^ %s \S+ \S+ \S+ arch=(?P<arch>\S+)$' % re.escape(pkg), maxp2['Package-List'],
archs = ' (Only available for %s)' % '; '.join(archs) re.I | re.M | re.DOTALL)
if archs:
archs = archs.group('arch').split(',')
if not ('any' in archs or 'all' in archs):
maxp['Architectures'] = ', '.join(archs)
maxp["Distribution"] = distro
maxp["Architectures"] = archs
return maxp return maxp
def info(self, pkg, chkdistro): def info(self, pkg, distro):
maxp = self.raw_info(pkg, chkdistro) maxp = self.raw_info(pkg, distro)
if isinstance(maxp, str): if isinstance(maxp, str):
return maxp 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.get('Source', None) or maxp['Package'], description(maxp), component(maxp['Section']),
maxp['Priority'], maxp['Version'], maxp["Distribution"], int(maxp['Size'])/1024, maxp['Installed-Size'], maxp["Architectures"])) maxp['Priority'], maxp['Version'], distro, int(maxp['Size'])/1024, maxp['Installed-Size'],
". (Only available for %s)" % maxp['Architectures'] if maxp.get('Architectures', None) else ""))
def depends(self, pkg, chkdistro): def depends(self, pkg, distro):
maxp = self.raw_info(pkg, chkdistro) maxp = self.raw_info(pkg, distro, archlookup=False)
if isinstance(maxp, str): if isinstance(maxp, str):
return maxp return maxp
return("%s (version %s in %s) depends on: %s" % return("%s (version: %s, %s): Depends on %s%s" %
(maxp['Package'], maxp["Version"], maxp["Distribution"], maxp["Depends"])) (maxp['Package'], maxp['Version'], distro, maxp.get('Depends', None) or "nothing",
". Recommends %s" % maxp['Recommends'] if maxp.get('Recommends', None) else ""))
# Simple test # Simple test
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
@ -210,21 +183,14 @@ if __name__ == "__main__":
def registryValue(self, *args, **kwargs): def registryValue(self, *args, **kwargs):
return "/home/bot/aptdir" return "/home/bot/aptdir"
command = argv[1].split(None, 1)[0]
try: try:
lookup = argv[1].split(None, 1)[1] (command, lookup) = argv[1].split(None, 1)
except: except:
print("Need something to lookup") print("Need something to look up")
sys.exit(1) sys.exit(1)
dists = "hardy" dist = "zesty"
if argc == 3: if argc == 3:
dists = argv[2] dist = argv[2]
plugin = FakePlugin() plugin = FakePlugin()
aptlookup = Apt(plugin) aptlookup = Apt(plugin)
if command == "find": print(getattr(aptlookup, command)(lookup, dist))
print(aptlookup.find(lookup, dists))
elif command == "depends":
print(aptlookup.depends(lookup, dists))
else:
print(aptlookup.info(lookup, dists))

View File

@ -14,69 +14,79 @@
# #
### ###
import supybot.utils as utils
from supybot.commands import * from supybot.commands import *
import supybot.plugins as plugins import supybot.utils as utils
import supybot.ircutils as ircutils import supybot.ircutils as ircutils
import supybot.ircmsgs as ircmsgs import supybot.ircmsgs as ircmsgs
import supybot.callbacks as callbacks
import supybot.ircutils as ircutils
import supybot.ircdb as ircdb import supybot.ircdb as ircdb
import supybot.callbacks as callbacks
import supybot.conf as conf import supybot.conf as conf
import os import re, time
import re
import time
from . import packages from . import packages
def get_user(msg):
try:
user = ircdb.users.getUser(msg.prefix)
except:
return False
return user
_stripNickChars = """!"#$%&'()*+,./:;<=>?@~""" _stripNickChars = """!"#$%&'()*+,./:;<=>?@~"""
def stripNick(nick): def stripNick(nick):
while nick and nick[-1] in _stripNickChars: while nick and nick[-1] in _stripNickChars:
nick = nick[:-1] nick = nick[:-1]
return nick return nick
def defaultIgnored(hostmask, recipient):
if not conf.supybot.defaultIgnore():
return False
if conf.version <= '0.83.4.1' \
and ircutils.isChannel(recipient):
return False
try:
user = ircdb.users.getUser(hostmask)
except KeyError:
return True
return False
def checkIgnored(hostmask, recipient):
try:
user = ircdb.users.getUser(hostmask)
if user._checkCapability('owner'):
return False
elif user.ignore:
return True
except KeyError:
pass
if ircdb.ignores.checkIgnored(hostmask):
return True
if ircutils.isChannel(recipient):
c = ircdb.channels.getChannel(recipient)
if c.checkIgnored(hostmask):
return True
return False
## Taken from Encyclopedia ## ## Taken from Encyclopedia ##
# Repeat filtering message queue # Repeat filtering message queue
msgcache = {} msgcache = {}
def queue(irc, to, msg): def queue(irc, target, msg):
now = time.time() now = time.time()
for m in list(msgcache.keys()): for m in list(msgcache.keys()):
if msgcache[m] < now - 30: if msgcache[m] < now - 30:
msgcache.pop(m) msgcache.pop(m)
for m in msgcache: for m in msgcache:
if m[0] == irc and m[1] == to: if m[0] == irc and m[1] == target:
oldmsg = m[2] oldmsg = m[2]
if msg == oldmsg or oldmsg.endswith(msg): if oldmsg.endswith(msg):
break break
if msg.endswith(oldmsg): if msg.endswith(oldmsg) and msg[:-len(oldmsg)].endswith(': '):
msg = msg[:-len(oldmsg)] + 'please see above' msg = msg[:-len(oldmsg)] + 'Please see above'
else: else:
msgcache[(irc, to, msg)] = now msgcache[(irc, target, msg)] = now
irc.queueMsg(ircmsgs.privmsg(to, msg)) irc.reply(msg, to=target)
class PackageInfo(callbacks.Plugin): class PackageInfo(callbacks.Plugin):
"""Lookup package information via apt-cache/apt-file""" """Look up package information via apt-cache/apt-file"""
threaded = True threaded = True
space_re = re.compile(r' *')
def __init__(self, irc): def __init__(self, irc):
self.__parent = super(PackageInfo, self) self.__parent = super(PackageInfo, self)
self.__parent.__init__(irc) self.__parent.__init__(irc)
self.Apt = packages.Apt(self) self.Apt = packages.Apt(self)
def callPrecedence(self, irc):
before = []
for cb in irc.callbacks:
if cb.name() == 'IRCLogin':
before.append(cb)
return (before, [])
def __getRelease(self, irc, release, channel, doError=True): def __getRelease(self, irc, release, channel, doError=True):
if release: if release:
release = release.strip() release = release.strip()
@ -87,253 +97,124 @@ class PackageInfo(callbacks.Plugin):
return (None, None) return (None, None)
if not release: if not release:
return (defaultRelease, None) return (defaultRelease, None)
(release, rest) = (release.split(' ', 1) + [None])[:2] (release, rest) = (release.split(None, 1) + [None])[:2]
if release[0] in ('|', '>'): if release[0] in '|>':
return (defaultRelease, "%s %s" % (release, rest)) return (defaultRelease, "%s %s" % (release, rest))
return (release, rest) return (release, rest)
def __getChannel(self, channel): def __handleRest(self, msg, target, reply, rest):
return ircutils.isChannel(channel) and channel or None targeto = target
prefix = ''
if rest[0] == '|':
rest = rest.lstrip('|').strip()
if rest:
if rest.lower() == "me":
rest = msg.nick
prefix = "%s: " % rest
elif rest[0] == '>':
rest = rest.lstrip('>').strip()
if rest:
# Take the first "nick" and strip off bad chars
target = stripNick(rest.split()[0])
if target.lower() == "me":
target = msg.nick
prefix = "<%s> wants you to know: " % msg.nick
def __getReplyChars(self, channel): if target.lower() != targeto.lower() and ircutils.isChannel(target):
prefix_chars = list(self.registryValue("prefixchar", channel)) target = targeto
address_chars = list(str( conf.supybot.reply.whenAddressedBy.chars() )) prefix = "(Forwarding to channels is not permitted) "
if channel: elif msg.nick.lower() in (target.lower(), prefix[:-2].lower()) \
address_chars = list(str( conf.supybot.reply.whenAddressedBy.chars.get(channel) )) and msg.nick.lower() != targeto.lower():
return tuple(set(prefix_chars + address_chars)) target = msg.nick
prefix = "(In the future, please use a private message to investigate) "
def __getCommand(self, text, channel): return (target, prefix + reply)
reply_chars = self.__getReplyChars(channel)
my_commands = self.listCommands()
if text[0] in reply_chars:
text = text[1:]
return text.strip().lower().split(' ', 1)[0]
def real_info(self, irc, msg, args, package, release): def real_info(self, irc, msg, args, package, release=None):
"""<package> [<release>] """<package> [<release>]
Lookup information for <package>, optionally in <release> Look up information for <package>, optionally in <release>
""" """
channel = self.__getChannel(msg.args[0]) channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
reply_target = ircutils.replyTo(msg) if not self.registryValue("enabled", channel):
return
(release, rest) = self.__getRelease(irc, release, channel) (release, rest) = self.__getRelease(irc, release, channel)
if not release: if not release:
return return
target = ircutils.replyTo(msg)
reply = self.Apt.info(package, release) reply = self.Apt.info(package, release)
if rest: if rest:
if rest[0] == '|': (target, reply) = self.__handleRest(msg, target, reply, rest)
try: queue(irc, target, reply)
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: (info) 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: (info) Exception in redirect: %r" % e)
pass
queue(irc, reply_target, reply)
info = wrap(real_info, ['anything', optional('text')]) info = wrap(real_info, ['anything', optional('text')])
def real_depends(self, irc, msg, args, package, release): def real_depends(self, irc, msg, args, package, release=None):
"""<package> [<release>] """<package> [<release>]
Lookup dependencies for <package>, optionally in <release> Look up dependencies for <package>, optionally in <release>
""" """
channel = self.__getChannel(msg.args[0]) channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
reply_target = ircutils.replyTo(msg) if not self.registryValue("enabled", channel):
return
(release, rest) = self.__getRelease(irc, release, channel) (release, rest) = self.__getRelease(irc, release, channel)
if not release: if not release:
return return
target = ircutils.replyTo(msg)
reply = self.Apt.depends(package, release) reply = self.Apt.depends(package, release)
if rest: if rest:
if rest[0] == '|': (target, reply) = self.__handleRest(msg, target, reply, rest)
try: queue(irc, target, reply)
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')]) 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=None):
"""<package/filename> [<release>] """<package/filename> [<release>]
Search for <package> or, of that fails, find <filename>'s package(s). Search for <package> or, if that fails, <filename> in packages,
Optionally in <release> optionally in <release>
""" """
channel = self.__getChannel(msg.args[0]) channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
reply_target = ircutils.replyTo(msg) if not self.registryValue("enabled", channel):
return
(release, rest) = self.__getRelease(irc, release, channel) (release, rest) = self.__getRelease(irc, release, channel)
if not release: if not release:
return return
target = ircutils.replyTo(msg)
reply = self.Apt.find(package, release) reply = self.Apt.find(package, release)
if rest: if rest:
if rest[0] == '|': (target, reply) = self.__handleRest(msg, target, reply, rest)
try: queue(irc, target, reply)
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: (find) 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: (find) Exception in redirect: %r" % e)
pass
queue(irc, reply_target, reply)
find = wrap(real_find, ['anything', optional('text')]) find = wrap(real_find, ['anything', optional('text')])
def privmsg(self, irc, msg, user):
channel = self.__getChannel(msg.args[0])
text = self.space_re.subn(' ', msg.args[1].strip())[0]
my_commands = self.listCommands()
if text[0] == self.registryValue("prefixchar"):
text = text[1:].strip()
if user and text[0] in list(conf.supybot.reply.whenAddressedBy.chars()):
return
(cmd, rest) = (text.split(' ', 1) + [None])[:2]
if cmd not in my_commands:
return
if not rest:
return
(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)
def chanmsg(self, irc, msg, user):
channel = self.__getChannel(msg.args[0])
text = self.space_re.subn(' ', msg.args[1].strip())[0]
my_commands = self.listCommands()
if text[0] != self.registryValue("prefixchar", channel):
return
text = text[1:]
(cmd, rest) = (text.split(' ', 1) + [None])[:2]
if cmd not in my_commands:
return
if not rest:
return
(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)
def doPrivmsg(self, irc, msg): def doPrivmsg(self, irc, msg):
if chr(1) in msg.args[1]: # CTCP if chr(1) in msg.args[1]: # CTCP
return return
if not msg.args[1]: text = msg.args[1].strip()
if not text:
return return
channel = self.__getChannel(msg.args[0]) channel = msg.args[0] if ircutils.isChannel(msg.args[0]) else None
if not self.registryValue("enabled", channel): if text[0] == self.registryValue("prefixchar", channel):
text = text[1:].strip()
elif channel or text[0] in conf.supybot.reply.whenAddressedBy.chars():
return return
user = get_user(msg) if not text:
if channel: return
self.chanmsg(irc, msg, user) (cmd, rest) = (text.split(None, 1) + [None])[:2]
else: if not cmd:
if user: return
return cmd = cmd.lower()
self.privmsg(irc, msg, user) if not (cmd in ("info", "depends", "find") and rest):
return
(package, release) = (rest.split(None, 1) + [None])[:2]
irc = callbacks.NestedCommandsIrcProxy(irc, msg, [])
getattr(self, "real_%s" % cmd)(irc, msg, [], package, release)
def inFilter(self, irc, msg): def inFilter(self, irc, msg):
if msg.command != "PRIVMSG": if not defaultIgnored(msg.prefix, msg.args[0]):
return msg return msg
if not conf.supybot.defaultIgnore(): if checkIgnored(msg.prefix, msg.args[0]):
return msg return msg
text = msg.args[1].strip() if msg.command == "PRIVMSG":
if len(text) < 6:
return msg
user = get_user(msg)
channel = self.__getChannel(msg.args[0])
reply_chars = self.__getReplyChars(channel)
my_commands = self.listCommands()
cmd = self.__getCommand(text, channel)
if cmd not in my_commands:
return msg
if user:
if not channel and text[0] == self.registryValue("prefixchar"):
msg.args = (msg.args[0], text[1:])
return msg
if channel:
if text[0] not in reply_chars:
return msg
# if not hasattr(irc, 'reply'):
# irc = callbacks.ReplyIrcProxy(irc, msg)
# self.doPrivmsg(irc, msg)
else:
if text[1] in reply_chars:
msg.args = (msg.args[0], text[1:])
irc = callbacks.ReplyIrcProxy(irc, msg)
self.doPrivmsg(irc, msg) self.doPrivmsg(irc, msg)
return msg return msg
Class = PackageInfo Class = PackageInfo

View File

@ -7,76 +7,65 @@ DIR=""
# Be quiet by default. # Be quiet by default.
VERBOSE=0 VERBOSE=0
## Please don't change anything below this line, unless you really know what ## Please don't change anything below this line, unless you really know what
## you are doing and don't bother me with whatever errors it produces :) ## you are doing and don't bother me with whatever errors it produces :)
# Print usage information. # Print usage information.
usage() { usage() {
echo "Usage $0 [OPTION]..." cat <<EOF
echo "Updates the APT package cache for PackageInfo" Usage: $0 [OPTION]...
echo ""
echo "-h, --help Display this message and exit." Updates the APT package cache for PackageInfo.
echo "-v, --verbose Be more verbose than normal."
echo "-V, --very-verbose Be even more verbose than normal." -d, --dir <DIR> Specify directory for APT package cache.
echo "-d, --dir[=DIR] Sets the directory to use when updating the APT package cache." -v, --verbose Be more verbose than normal.
echo "" -V, --very-verbose Be even more verbose than normal.
echo "Note:" -h, --help Display this message and exit.
echo " Please separate each option with a space, eg:"
echo " $0 -v -d /home/bot/aptdir" Note:
echo " Rather than:" Please separate each option with a space, eg:
echo " $0 -vd /home/bot/aptdir" $0 -v -d /home/bot/aptdir
echo "" Rather than:
echo "This script is intended to be ran automatically (eg: cron), so it shows no output by default." $0 -vd /home/bot/aptdir
echo "You can make the script more verbose with either the -v/--verbose or -V/--very-verbose options."
echo "The -d/--dir option sets the directory where this script looks for *.list files for apt-get." This script is intended to be run automatically (eg: cron), so it shows no output by default.
You can make the script more verbose with the -v/--verbose or -V/--very-verbose options.
The -d/--dir option sets the directory where this script looks for *.list files for apt-get.
EOF
} }
# Prints an error message, usage (above), then exit with the specified exit value. # Prints an error message, usage (above), then exits with the specified exit value.
error() { error() {
local exit_val=$1 local exit_val=$1
shift shift
echo $@ >&2 echo "$@" >&2
usage >&2 usage >&2
exit $exit_val exit $exit_val
} }
# Runs apt-get update in the specified directory for the specified distribution. # Runs apt-get update in the specified directory for the specified distribution.
update_apt() { update_apt() {
local apt_dir="$1"
local dist="$2"
local apt_args=""
if [ $VERBOSE -eq 0 ]; then if [ $VERBOSE -eq 0 ]; then
apt_args="-qq" apt_args="-qq"
elif [ $VERBOSE -eq 1 ]; then elif [ $VERBOSE -eq 1 ]; then
apt_args="-q" apt_args="-q"
fi fi
apt-get $apt_args -o="APT::Architecture=i386" \ apt-get $apt_args \
-o="APT::Architecture=amd64" \
-o="APT::Architectures::=i386" \ -o="APT::Architectures::=i386" \
-o "APT::Architectures::=amd64" \ -o="APT::Architectures::=amd64" \
-o="Dir::State::Lists=$apt_dir/$dist" \ -o="Dir::State::Lists=$DIR/$DIST" \
-o="Dir::State::Status=$apt_dir/$dist.status" \ -o="Dir::State::Status=$DIR/$DIST.status" \
-o="Dir::Cache=$apt_dir/cache" \ -o="Dir::Etc::SourceList=$DIR/$DIST.list" \
-o="Dir::Etc::SourceList=$apt_dir/$dist.list" \ -o="Dir::Etc::SourceParts=''" \
-o="Dir::Etc::SourceParts=\"\"" \ -o="Dir::Cache=$DIR/cache" \
update update
return $?
} }
# main()
# Acepted arguments are:
# -h,--help
# -v,--verbose
# -V,--very-verbose
# -d,--dir[=DIR]
# Check command-line arguments # Check command-line arguments
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case "$1" in case $1 in
-h|--help) -h|--help)
usage usage
exit 0 exit 0
@ -88,13 +77,13 @@ while [ $# -gt 0 ]; do
VERBOSE=2 VERBOSE=2
;; ;;
-d|--dir) -d|--dir)
[ -z "$2" ] && error 1 "\"-d|--dir\" requires an argument." [ -z "$2" ] && error 1 '"-d/--dir" requires an argument.'
shift shift
DIR="$1" DIR="$1"
;; ;;
--dir=*) --dir=*)
DIR="${1:6}" DIR="${1:6}"
[ -z "$DIR" ] && error 1 "\"--dir\" requires an argument." [ -z "$DIR" ] && error 1 '"--dir" requires an argument.'
;; ;;
-*) -*)
error 1 "Unknown option \"$1\"." error 1 "Unknown option \"$1\"."
@ -106,38 +95,33 @@ while [ $# -gt 0 ]; do
shift shift
done done
apt_get=$(which apt-get 2>/dev/null) # Check if apt-get is installed and bail if not.
if ! which apt-get >/dev/null 2>&1; then
# Check that apt-get exists and bail if it doesn't. echo "ERROR: apt-get not found. Please install apt-get in your PATH." >&2
if [ $? -ne 0 ]; then
echo "ERROR: apt-get not found. Please install apt-get in your \$PATH." >&2
exit 1 exit 1
fi fi
#TODO: Remove this section and error out if DIR is not set,
# This could hide errors where DIR/-d was not set, an error message.
if [ -z "$DIR" ]; then if [ -z "$DIR" ]; then
DIR=/home/bot/aptdir error 1 "ERROR: No DIR set in the script and no -d/--dir option given."
echo "WARNING: No DIR set and no -d/--dir option given, defaulting to \"$DIR\"" >&2
echo "WARNING: Please set DIR on line 5 of $(readlink -f $0) or use the -d/--dir option" >&2
fi fi
#[ -z "$DIR" ] && error 1 "ERROT: Please set DIR on line 5 of $(readlink -f $0) or use the -d/--dir option" items=("$DIR"/*.list)
[ ! -e "${items[0]}" ] && error 1 "ERROR: Could not find any *.list files in \"$DIR\"."
DIR="$(echo $DIR | sed 's,/*$,,')" # Normalize $DIR for DIST in "${items[@]}"; do
items=$(ls "${DIR}"/*.list 2>/dev/null)
[ $? -ne 0 ] && error 1 "Could not find \"*.list\" files in \"$DIR\"."
for DIST in $items; do
[ -h "$DIST" ] && continue # Ignore symbolic links [ -h "$DIST" ] && continue # Ignore symbolic links
# Extract the distribution name from the .list file name. # Extract the distribution name from the .list file name.
DIST="${DIST:${#DIR}}" DIST=${DIST##*/}
DIST="${DIST/.list}" DIST=${DIST%.list}
DIST="${DIST:1}"
touch "${DIR}/${DIST}.status" # Create APT status file [ $VERBOSE -gt 0 ] && echo "INFO: Processing $DIST"
mkdir -p "${DIR}/${DIST}/partial" # APT needs this to exist touch "$DIR/$DIST.status" # Create APT status file
update_apt "$DIR" "$DIST" # Update the package list with apt-get mkdir -p "$DIR/$DIST/partial" # APT needs this to exist
update_apt # Update APT package cache
if [ $? -ne 0 ]; then
[ $VERBOSE -eq 0 ] && echo "Try passing -v or -V to get the error message." >&2
echo "ERROR: apt-get update failed for $DIST." >&2
exit_val=$?
fi
done done
exit $exit_val

View File

@ -4,7 +4,7 @@
# or use the --dir command-line option. # or use the --dir command-line option.
DIR="" DIR=""
# Be quiet by default # Be quiet by default.
VERBOSE=0 VERBOSE=0
## Please don't change anything below this line, unless you really know what ## Please don't change anything below this line, unless you really know what
@ -12,53 +12,48 @@ VERBOSE=0
# Print usage information. # Print usage information.
usage() { usage() {
echo "Usage $0 [OPTION]..." cat <<EOF
echo "Updates the apt-file cache for PackageInfo" Usage: $0 [OPTION]...
echo ""
echo "-h, --help Display this message and exit." Updates the apt-file cache for PackageInfo.
echo "-v, --verbose Be more verbose than normal."
echo "-d, --dir[=DIR] Sets the directory to use when updating the apt-file cache." -d, --dir <DIR> Specify directory for apt-file cache.
echo "" -v, --verbose Be more verbose than normal.
echo "Note:" -h, --help Display this message and exit.
echo " Please separate each option with a space, eg:"
echo " $0 -v -d /home/bot/aptdir" Note:
echo " Rather than:" Please separate each option with a space, eg:
echo " $0 -vd /home/bot/aptdir" $0 -v -d /home/bot/aptdir
echo "" Rather than:
echo "This script is intended to be ran automatically (eg: cron), so it shows no output by default." $0 -vd /home/bot/aptdir
echo "You can make the script more verbose with the -v/--verbose option."
echo "The -d/--dir option sets the directory where this script looks for *.list files for apt-file." This script is intended to be run automatically (eg: cron), so it shows no output by default.
You can make the script more verbose with the -v/--verbose option.
The -d/--dir option sets the directory where this script looks for *.list files for apt-file.
EOF
} }
# Prints an error message, usage (above), then exit with the specified exit value. # Prints an error message, usage (above), then exits with the specified exit value.
error() { error() {
local exit_val=$1 local exit_val=$1
shift shift
echo $@ >&2 echo "$@" >&2
usage >&2 usage >&2
exit $exit_val exit $exit_val
} }
# Runs apt-file in the specified directory against the specified distribution. # Runs apt-file update in the specified directory for the specified distribution.
update_apt() { update_apt_file() {
local DIST="$1"
if [ $VERBOSE -eq 0 ]; then if [ $VERBOSE -eq 0 ]; then
apt-file -N -l -c "$DIR/apt-file/$DIST" -s "$DIR/$DIST.list" -a i386 update >/dev/null 2>&1 apt-file -N -l -c "$DIR/apt-file/$DIST" -s "$DIR/$DIST.list" -a amd64 update >/dev/null 2>&1
else else
apt-file -N -l -c "$DIR/apt-file/$DIST" -s "$DIR/$DIST.list" -a i386 update apt-file -N -l -c "$DIR/apt-file/$DIST" -s "$DIR/$DIST.list" -a amd64 update
fi fi
} }
# main()
# Acepted arguments are:
# -h,--help
# -v,--verbose
# -d,--dir[=DIR]
# Check command-line arguments # Check command-line arguments
while [ $# -ne 0 ]; do while [ $# -gt 0 ]; do
case "$1" in case $1 in
-h|--help) -h|--help)
usage usage
exit 0 exit 0
@ -67,61 +62,50 @@ while [ $# -ne 0 ]; do
VERBOSE=1 VERBOSE=1
;; ;;
-d|--dir) -d|--dir)
[ -z "$2" ] && error 1 "\"-d|--dir\" requires an argument." [ -z "$2" ] && error 1 '"-d/--dir" requires an argument.'
shift shift
DIR="$1" DIR="$1"
;; ;;
--dir=*) --dir=*)
DIR="${1:6}" DIR="${1:6}"
[ -z "$DIR" ] && error 1 "\"--dir\" requires an argument." [ -z "$DIR" ] && error 1 '"--dir" requires an argument.'
;; ;;
-*) -*)
error 1 "Unknown option \"$1\"." error 1 "Unknown option \"$1\"."
;; ;;
*) *)
error 1 "This script takes no non-argument parameterss." error 1 "This script takes no non-argument parameters."
;; ;;
esac esac
shift shift
done done
apt_file=$(which apt-file 2>/dev/null) # Check if apt-file is installed and bail if not.
if ! which apt-file >/dev/null 2>&1; then
# Check if apt-file is installed and bail if it isn't. echo "ERROR: apt-file not found. Please install apt-file in your PATH." >&2
if [ $? -ne 0 ]; then
echo "ERROR: apt-file not found. Please install apt-file in your \$PATH." >&2
exit 1 exit 1
fi fi
#TODO: Remove this and error out if DIR is not set,
# This is legacy code and needs to disappear sometime.
if [ -z "$DIR" ]; then if [ -z "$DIR" ]; then
DIR=/home/bot/aptdir error 1 "ERROR: No DIR set in the script and no -d/--dir option given."
echo "WARNING: No DIR set and no -d/--dir option given, defaulting to \"$DIR\"" >&2
echo "WARNING: Please set DIR on line 5 of $(readlink -f $0) or use the -d/--dir option" >&2
fi fi
#[ -z "$DIR" ] && error 1 "Please set DIR on line 5 of $(readlink -f $0) or use the -d/--dir option" items=("$DIR"/*.list)
[ ! -e "${items[0]}" ] && error 1 "ERROR: Could not find any *.list files in \"$DIR\"."
DIR="$(echo $DIR | sed 's,/*$,,')" # Normalize $DIR for DIST in "${items[@]}"; do
[ -h "$DIST" ] && continue # Ignore symbolic links
# Extract the distribution name from the .list file name.
DIST=${DIST##*/}
DIST=${DIST%.list}
items=$(ls "${DIR}"/*.list 2>/dev/null) [ $VERBOSE -gt 0 ] && echo "INFO: Processing $DIST"
[ $? -ne 0 ] && error 1 "ERROR: Could not find \"*.list\" files in \"$DIR\"." mkdir -p "$DIR/apt-file/$DIST" # Create apt-file directory if it doesn't exist
update_apt_file # Update apt-file cache
for DIST in $items; do
[ -h $DIST ] && continue # Ignore symbolic links
# Extract the distribution from the .list file name
DIST="${DIST:${#DIR}}"
DIST="${DIST/.list}"
DIST="${DIST:1}"
mkdir -p "$DIR/apt-file/$DIST" # Create apt-file directory, if it doesn't exist
[ $VERBOSE -ne 0 ] && echo "INFO: Processing $DIST"
update_apt "$DIST" # Update apt-file database
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
[ $VERBOSE -eq 0 ] && echo "Try passing -v to get the error message." >&2 [ $VERBOSE -eq 0 ] && echo "Try passing -v to get the error message." >&2
error 1 "ERROR: apt-file failed for ${DIST}!." echo "ERROR: apt-file update failed for $DIST." >&2
exit_val=$?
fi fi
done done
exit $exit_val