178 lines
5.1 KiB
Python
178 lines
5.1 KiB
Python
"""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) |