### # Copyright (c) 2002-2005, Jeremiah Fincher # Copyright (c) 2008, James Vega # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions, and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions, and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of the author of this software nor the name of # contributors to this software may be used to endorse or promote products # derived from this software without specific prior written consent. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. ### import os import re import pwd import sys import crypt import errno import popen2 import random import select import struct import supybot.utils as utils from supybot.commands import * import supybot.plugins as plugins import supybot.ircutils as ircutils import supybot.callbacks as callbacks def progstats(): pw = pwd.getpwuid(os.getuid()) response = format('Process ID %i running as user %q and as group %q ' 'from directory %q with the command line %q. ' 'Running on Python %s.', os.getpid(), pw[0], pw[3], os.getcwd(), ' '.join(sys.argv), sys.version.translate(utils.str.chars, '\r\n')) return response class TimeoutError(IOError): pass def pipeReadline(fd, timeout=2): (r, _, _) = select.select([fd], [], [], timeout) if r: return r[0].readline() else: raise TimeoutError class Unix(callbacks.Plugin): def errno(self, irc, msg, args, s): """<error number or code> Returns the number of an errno code, or the errno code of a number. """ try: i = int(s) name = errno.errorcode[i] except ValueError: name = s.upper() try: i = getattr(errno, name) except AttributeError: irc.reply('I can\'t find the errno number for that code.') return except KeyError: name = '(unknown)' irc.reply(format('%s (#%i): %s', name, i, os.strerror(i))) errno = wrap(errno, ['something']) def progstats(self, irc, msg, args): """takes no arguments Returns various unix-y information on the running supybot process. """ irc.reply(progstats()) def pid(self, irc, msg, args): """takes no arguments Returns the current pid of the process for this Supybot. """ irc.reply(format('%i', os.getpid()), private=True) pid = wrap(pid, [('checkCapability', 'owner')]) _cryptre = re.compile(r'[./0-9A-Za-z]') def crypt(self, irc, msg, args, password, salt): """<password> [<salt>] Returns the resulting of doing a crypt() on <password> If <salt> is not given, uses a random salt. If running on a glibc2 system, prepending '$1$' to your salt will cause crypt to return an MD5sum based crypt rather than the standard DES based crypt. """ def makeSalt(): s = '\x00' while self._cryptre.sub('', s) != '': s = struct.pack('<h', random.randrange(-(2**15), 2**15)) return s if not salt: salt = makeSalt() irc.reply(crypt.crypt(password, salt)) crypt = wrap(crypt, ['something', additional('something')]) def spell(self, irc, msg, args, word): """<word> Returns the result of passing <word> to aspell/ispell. The results shown are sorted from best to worst in terms of being a likely match for the spelling of <word>. """ # We are only checking the first word spellCmd = self.registryValue('spell.command') if not spellCmd: irc.error('A spell checking command doesn\'t seem to be ' 'installed on this computer. If one is installed, ' 'reconfigure supybot.plugins.Unix.spell.command ' 'appropriately.', Raise=True) if word and not word[0].isalpha(): irc.error('<word> must begin with an alphabet character.') return if ' ' in word: irc.error('Spaces aren\'t allowed in the word.') return inst = popen2.Popen4([spellCmd, '-a']) (r, w) = (inst.fromchild, inst.tochild) try: s = r.readline() # Banner, hopefully. if 'sorry' in s.lower(): irc.error(s) return w.write(word) w.write('\n') w.flush() try: line = pipeReadline(r) # aspell puts extra whitespace, ignore it while not line.strip('\r\n'): line = pipeReadline(r) # cache an extra line in case aspell's first line says the word # is spelled correctly, but subsequent lines offer spelling # suggestions line2 = pipeReadline(r) except TimeoutError: irc.error('The spell command timed out.') return finally: r.close() w.close() inst.wait() # parse the output # aspell will sometimes list spelling suggestions after a '*' or '+' # line for complex words. if line[0] in '*+' and line2.strip('\r\n'): line = line2 if line[0] in '*+': resp = format('%q may be spelled correctly.', word) elif line[0] == '#': resp = format('I could not find an alternate spelling for %q',word) elif line[0] == '&': matches = line.split(':')[1].strip() resp = format('Possible spellings for %q: %L.', word, matches.split(', ')) else: resp = 'Something unexpected was seen in the [ai]spell output.' irc.reply(resp) spell = wrap(spell, ['something']) def fortune(self, irc, msg, args): """takes no arguments Returns a fortune from the *nix fortune program. """ fortuneCmd = self.registryValue('fortune.command') if fortuneCmd: args = [fortuneCmd] if self.registryValue('fortune.short'): args.append('-s') if self.registryValue('fortune.equal'): args.append('-e') if self.registryValue('fortune.offensive'): args.append('-a') args.extend(self.registryValue('fortune.files')) inst = popen2.Popen4(args) (r, w) = (inst.fromchild, inst.tochild) try: lines = r.readlines() lines = map(str.rstrip, lines) lines = filter(None, lines) if lines: irc.replies(lines, joiner=' ') else: irc.error('It seems the configured fortune command was ' 'not available.') finally: w.close() r.close() inst.wait() else: irc.error('I couldn\'t find the fortune command on this system. ' 'If it is installed on this system, reconfigure the ' 'supybot.plugins.Unix.fortune.command configuration ' 'variable appropriately.') def wtf(self, irc, msg, args, _, something): """[is] <something> Returns wtf <something> is. 'wtf' is a *nix command that first appeared in NetBSD 1.5. In most *nices, it's available in some sort of 'bsdgames' package. """ wtfCmd = self.registryValue('wtf.command') if wtfCmd: def commandError(): irc.error('It seems the configured wtf command ' 'was not available.') something = something.rstrip('?') inst = popen2.Popen4([wtfCmd, something]) (r, w) = (inst.fromchild, inst.tochild) try: response = utils.str.normalizeWhitespace(r.readline().strip()) if response: irc.reply(response) else: commandError() finally: r.close() w.close() inst.wait() else: irc.error('I couldn\'t find the wtf command on this system. ' 'If it is installed on this system, reconfigure the ' 'supybot.plugins.Unix.wtf.command configuration ' 'variable appropriately.') wtf = wrap(wtf, [optional(('literal', ['is'])), 'something']) Class = Unix # vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: