# -*- Mode: Python; tab-width: 4 -*- # Copyright 1999, 2000 by eGroups, Inc. # # All Rights Reserved # # Permission to use, copy, modify, and distribute this software and # its documentation for any purpose and without fee is hereby # granted, provided that the above copyright notice appear in all # copies and that both that copyright notice and this permission # notice appear in supporting documentation, and that the name of # eGroups not be used in advertising or publicity pertaining to # distribution of the software without specific, written prior # permission. # # EGROUPS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN # NO EVENT SHALL EGROUPS BE LIABLE FOR ANY SPECIAL, INDIRECT OR # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. VERSION_STRING = '$Id: //depot/main/findmail/src/coroutine/backdoor.py#12 $' import coro import socket import string import StringIO import sys import traceback # Originally, this object implemented the file-output api, and set # sys.stdout and sys.stderr to 'self'. However, if any other # coroutine ran, it would see the captured definition of sys.stdout, # and would send its output here, instead of the expected place. Now # the code captures all output using StringIO. A little less # flexible, a little less efficient, but much less surprising! # [Note: this is exactly the same problem addressed by Scheme's # dynamic-wind facility] class backdoor: def __init__ (self, socket, line_separator='\r\n'): self.socket = socket self.buffer = '' self.lines = [] self.multilines = [] self.line_separator = line_separator # allow the user to change the prompts: if not sys.__dict__.has_key('ps1'): sys.ps1 = '>>> ' if not sys.__dict__.has_key('ps2'): sys.ps2 = '... ' def send (self, data): olb = lb = len(data) while lb: ns = self.socket.send (data) lb = lb - ns return olb def prompt (self): if self.multilines: self.send (sys.ps2) else: self.send (sys.ps1) def read_line (self): if self.lines: l = self.lines[0] self.lines = self.lines[1:] return l else: while not self.lines: block = self.socket.recv (8192) if not block: return None elif block == '\004': self.socket.close() return None else: self.buffer = self.buffer + block lines = string.split (self.buffer, self.line_separator) for l in lines[:-1]: self.lines.append (l) self.buffer = lines[-1] return self.read_line() def read_eval_print_loop (self): self.send ('Python ' + sys.version + self.line_separator) self.send (sys.copyright + self.line_separator) # this does the equivalent of 'from __main__ import *' env = sys.modules['__main__'].__dict__.copy() while 1: self.prompt() line = self.read_line() if line is None: break elif self.multilines: self.multilines.append(line) if line == '': code = string.join(self.multilines, '\n') self.parse(code, env) # we do this after the parsing so parse() knows not to do # a second round of multiline input if it really is an # unexpected EOF self.multilines = [] else: self.parse(line, env) def parse(self, line, env): save = sys.stdout, sys.stderr output = StringIO.StringIO() try: try: sys.stdout = sys.stderr = output co = compile (line, repr(self), 'eval') result = eval (co, env) if result is not None: print repr(result) env['_'] = result except SyntaxError: try: co = compile (line, repr(self), 'exec') exec co in env except SyntaxError, msg: # this is a hack, but it is a righteous hack: if not self.multilines and str(msg) == 'unexpected EOF while parsing': self.multilines.append(line) else: traceback.print_exc() except: traceback.print_exc() except: traceback.print_exc() finally: sys.stdout, sys.stderr = save self.send (output.getvalue()) del output def client (conn, addr): b = backdoor (conn) b.read_eval_print_loop() def serve (port=8023): s = coro.coroutine_socket() s.create_socket (socket.AF_INET, socket.SOCK_STREAM) s.set_reuse_addr() s.bind (('', port)) s.listen (1024) while 1: conn, addr = s.accept() print 'incoming connection from', addr coro.spawn (client, conn, addr) if __name__ == '__main__': coro.spawn (serve) coro.event_loop (30.0)