"""This module contains the MainCLI class.""" from sys import argv, stdin, stdout, stderr, exit import Const from cipher.KeyedCipher import InvalidKey from AutomaticClass import AutomaticClass class MainCLI: """This class is responsible for command-line parsing, etc.""" def __init__(self): """Run as a GNOME application or as a command-line application.""" if len(argv) > 1: self._runCommandLineApplication() else: self._runGnomeApplication() def _runGnomeApplication(self): """Run as a GNOME application. This method does not return until the application is finished. """ import pygtk pygtk.require("2.0") import gtk import gnome from MainGUI import MainGUI gnome.program_init(Const.NAME, Const.VERSION) MainGUI() gtk.mainloop() def _runCommandLineApplication(self): """Run as a command-line application. See the man page for a description of all of these arguments. This method does not return until the application is finished. """ from getopt import getopt, GetoptError operations = [] proxyMode = False try: opts, args = getopt(argv[1:], "c:C:k:p") for o, v in opts: if o.lower() == "-c": operations.append(_Operation( cipher = v, encrypt = (o == "-c"), key = None )) elif o == "-k": if not len(operations): usage("key specified before any ciphers", 1) operations[-1].key = v elif o == "-p": proxyMode = True else: raise AssertionError("unhandled option %s" % o) if proxyMode: self._runProxyMode(operations, args) else: self._runFilterMode(operations, args) except GetoptError, e: usage(e.msg, 1) def _runProxyMode(self, operations, args): """Handle the rest of the arguments and run in proxy mode. This method does not return. """ from Proxy import Proxy import asyncore import socket if not len(args) == 3: usage("proxy mode requires exactly 3 arguments", 1) proxiedHost, proxiedPort, listeningPort = args try: Proxy(proxiedHost, self._castToValidPort(proxiedPort), self._castToValidPort(listeningPort), self._composeOperations(operations), self._composeOperations(operations, True)) asyncore.loop() except socket.error, e: stderr.write("%s: socket error: %s\n" % (Const.NAME, e.args[1])) exit(1) def _castToValidPort(self, port): """Cast and return port as a valid port or complain and exit.""" MIN_PORT = 1 MAX_PORT = 65536 try: port = int(port) assert MIN_PORT <= port and port <= MAX_PORT return port except: stderr.write("%s: '%s' is not a valid port\n" % (Const.NAME, port)) exit(1) def _runFilterMode(self, operations, args): """Handle the rest of the arguments and run in filter mode. This method does not return until the application is finished. """ lenargs = len(args) input = stdin output = stdout try: if lenargs >= 1 and args[0] != "-": input = open(args[0], "r") if lenargs == 2 and args[1] != "-": output = open(args[1], "w") if lenargs > 2: usage("too many arguments", 1) operationsFunction = self._composeOperations(operations) while 1: line = input.readline() if not line: break output.write(operationsFunction(line)) if input != stdin: input.close() if output != stdin: output.close() except IOError, e: stderr.write("%s: %s: %s\n" % (Const.NAME, e.filename, e.strerror)) exit(1) def _composeOperations(self, operations, invert=False): """Call getFunction for each operation and compose a closure. invert - Should the meaning of the encrypt attribute for each operation be reversed? """ def closure(s): """Apply all of the operations on s, and return it.""" for f in functions: s = f(s) return s functions = [i.getFunction(invert) for i in operations] return closure class _Operation(AutomaticClass): """This is a simple class representing an encryption operation. The following attributes are used: cipher - This string is the name of a cipher. encrypt - If true, do encrypt. Otherwise, do decrypt. key - This string is the key to use with the cipher. """ def getAttributes(self): return AutomaticClass.getAttributes(self) + [ "cipher", "encrypt", "key"] def getFunction(self, invert = False): """This is a factory for a function representing this _Operation. Instantiate the cipher, set the key, and return either the encrypt or decrypt function. This function may call usage and exit. invert - Should the meaning of the encrypt attribute be reversed? """ try: CipherModule = __import__("cipher." + self.cipher, globals(), locals(), self.cipher) CipherClass = getattr(CipherModule, self.cipher) cipherInstance = CipherClass() if hasattr(cipherInstance, "key"): if self.key != None: cipherInstance.key = self.key else: if self.key != None: usage("no key required for cipher '%s'" % self.cipher, 1) if self.encrypt or invert: return cipherInstance.encrypt else: return cipherInstance.decrypt except ImportError: usage("failed to import cipher '%s'" % self.cipher, 1) except InvalidKey, e: usage("invalid key '%s' for cipher '%s'; it's not '%s'." % (e.key, operation.cipher, e.keyDescription), 1) def usage(warning=None, exitCode=None): """Print usage information. warning - If this is not None, it will be printed as well. exitCode - If this is not None, the application will exit with this exit code. """ if warning: stderr.write("%s: %s\n" % (Const.NAME, warning)) stderr.write("Try `man %s' for more information.\n" % Const.NAME) if exitCode != None: exit(exitCode)