Add pypopper.py
This commit is contained in:
parent
24e916b560
commit
58525a66b4
|
@ -0,0 +1,178 @@
|
|||
"""pypopper: a file-based pop3 server
|
||||
|
||||
Useage:
|
||||
python pypopper.py <port> <path_to_message_file>
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
logging.basicConfig(format="%(name)s %(levelname)s - %(message)s")
|
||||
log = logging.getLogger("pypopper")
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
class ChatterboxConnection(object):
|
||||
END = "\r\n"
|
||||
def __init__(self, conn):
|
||||
self.conn = conn
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.conn, name)
|
||||
def sendall(self, data, END=END):
|
||||
if len(data) < 50:
|
||||
log.debug("send: %r", data)
|
||||
else:
|
||||
log.debug("send: %r...", data[:50])
|
||||
data += END
|
||||
print('Final: {}'.format(data))
|
||||
self.conn.sendall(data.encode('UTF-8'))
|
||||
def recvall(self, END=END):
|
||||
data = []
|
||||
while True:
|
||||
chunk = self.conn.recv(4096).decode('UTF-8')
|
||||
if END in chunk:
|
||||
data.append(chunk[:chunk.index(END)])
|
||||
break
|
||||
data.append(chunk)
|
||||
if len(data) > 1:
|
||||
pair = data[-2] + data[-1]
|
||||
if END in pair:
|
||||
data[-2] = pair[:pair.index(END)]
|
||||
data.pop()
|
||||
break
|
||||
log.debug("recv: %r", "".join(data))
|
||||
return "".join(data)
|
||||
|
||||
|
||||
class Message(object):
|
||||
def __init__(self, filename):
|
||||
msg = open(filename, "r")
|
||||
try:
|
||||
self.data = data = msg.read()
|
||||
self.size = len(data)
|
||||
split_lines = data.splitlines()
|
||||
self.top = '\r\n'.join(split_lines[0:5])
|
||||
self.bot = '\r\n'.join(split_lines[6:])
|
||||
print('Top:\n{}'.format(self.top))
|
||||
print('Bot:\n{}'.format(self.bot))
|
||||
|
||||
finally:
|
||||
msg.close()
|
||||
|
||||
|
||||
def handleUser(data, msg):
|
||||
return "+OK user accepted"
|
||||
|
||||
def handlePass(data, msg):
|
||||
return "+OK pass accepted"
|
||||
|
||||
def handleStat(data, msg):
|
||||
return "+OK 1 %i" % msg.size
|
||||
|
||||
def handleList(data, msg):
|
||||
return "+OK 1 messages (%i octets)\r\n1 %i\r\n." % (msg.size, msg.size)
|
||||
|
||||
def handleTop(data, msg):
|
||||
cmd, num, lines = data.split()
|
||||
assert num == "1", "unknown message number: %s" % num
|
||||
# lines = '5'
|
||||
# lines = int(lines)
|
||||
# text = msg.top + "\r\n\r\n" + "\r\n".join(msg.bot[:lines])
|
||||
|
||||
text = msg.top + "\r\n\r\n"
|
||||
print('Text:\n {}'.format(text))
|
||||
return "+OK top of message follows\r\n%s\r\n." % text
|
||||
|
||||
def handleRetr(data, msg):
|
||||
log.info("message sent")
|
||||
|
||||
text = msg.top + "\r\n\r\n" + msg.bot
|
||||
print('Text:\n {}'.format(text))
|
||||
|
||||
print('msg size: {}, msg data: \n{}'.format(len(text), text))
|
||||
return "+OK %i octets\r\n%s\r\n\r\n." % (len(text), text)
|
||||
|
||||
def handleDele(data, msg):
|
||||
return "+OK message 1 deleted"
|
||||
|
||||
def handleNoop(data, msg):
|
||||
return "+OK"
|
||||
|
||||
def handleQuit(data, msg):
|
||||
return "+OK pypopper POP3 server signing off"
|
||||
|
||||
dispatch = dict(
|
||||
USER=handleUser,
|
||||
PASS=handlePass,
|
||||
STAT=handleStat,
|
||||
LIST=handleList,
|
||||
TOP=handleTop,
|
||||
RETR=handleRetr,
|
||||
DELE=handleDele,
|
||||
NOOP=handleNoop,
|
||||
QUIT=handleQuit,
|
||||
)
|
||||
|
||||
def serve(host, port, filename):
|
||||
assert os.path.exists(filename)
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.bind((host, port))
|
||||
try:
|
||||
if host:
|
||||
hostname = host
|
||||
else:
|
||||
hostname = "localhost"
|
||||
log.info("pypopper POP3 serving '%s' on %s:%s", filename, hostname, port)
|
||||
while True:
|
||||
sock.listen(1)
|
||||
conn, addr = sock.accept()
|
||||
log.debug('Connected by %s', addr)
|
||||
try:
|
||||
print(filename)
|
||||
msg = Message(filename)
|
||||
conn = ChatterboxConnection(conn)
|
||||
conn.sendall("+OK pypopper file-based pop3 server ready")
|
||||
while True:
|
||||
data = conn.recvall()
|
||||
command = data.split(None, 1)[0]
|
||||
print('command: {}'.format(command))
|
||||
try:
|
||||
cmd = dispatch[command]
|
||||
except KeyError:
|
||||
conn.sendall("-ERR unknown command")
|
||||
else:
|
||||
conn.sendall(cmd(data, msg))
|
||||
if cmd is handleQuit:
|
||||
break
|
||||
finally:
|
||||
conn.close()
|
||||
msg = None
|
||||
except (SystemExit, KeyboardInterrupt):
|
||||
log.info("pypopper stopped")
|
||||
except Exception as ex:
|
||||
log.critical("fatal error", exc_info=ex)
|
||||
finally:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
sock.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
print("USAGE: [<host>:]<port> <path_to_message_file>")
|
||||
else:
|
||||
_, port, filename = sys.argv
|
||||
if ":" in port:
|
||||
host = port[:port.index(":")]
|
||||
port = port[port.index(":") + 1:]
|
||||
else:
|
||||
host = ""
|
||||
try:
|
||||
port = int(port)
|
||||
except Exception:
|
||||
print("Unknown port:", port)
|
||||
else:
|
||||
if os.path.exists(filename):
|
||||
print(host, port, filename)
|
||||
serve(host, port, filename)
|
||||
else:
|
||||
print("File not found:", filename)
|
Loading…
Reference in New Issue