summaryrefslogtreecommitdiff
path: root/qmqp.py
diff options
context:
space:
mode:
authorLars Wirzenius <liw@esme>2005-12-03 20:41:56 +0200
committerLars Wirzenius <liw@esme>2005-12-03 20:41:56 +0200
commit0f7cd8b5551e2ec4333cb10561375872dd7e03d3 (patch)
tree217926700638acfa7fe1b523dae7f490308606e9 /qmqp.py
downloadeoc-0f7cd8b5551e2ec4333cb10561375872dd7e03d3.tar.gz
Initial import.
Diffstat (limited to 'qmqp.py')
-rw-r--r--qmqp.py137
1 files changed, 137 insertions, 0 deletions
diff --git a/qmqp.py b/qmqp.py
new file mode 100644
index 0000000..bc5d194
--- /dev/null
+++ b/qmqp.py
@@ -0,0 +1,137 @@
+#!/usr/bin/python
+
+# This module that implements sending mail via QMQP. See
+#
+# http://cr.yp.to/proto/qmqp.html
+#
+# for a description of the protocol.
+#
+# This module was written by Jaakko Niemi <liiwi@lonesom.pp.fi> for
+# Enemies of Carlotta. It is licensed the same way as Enemies of Carlotta:
+# GPL version 2.
+
+import socket, string
+
+class QMQPException(Exception):
+ '''Base class for all exceptions raised by this module.'''
+
+class QMQPTemporaryError(QMQPException):
+ '''Class for temporary errors'''
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return "QMQP-Server said: %s" % self.msg
+
+class QMQPPermanentError(QMQPException):
+ '''Class for permanent errors'''
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return "QMQP-Server said: %s" % self.msg
+
+class QMQPConnectionError(QMQPException):
+ '''Class for connection errors'''
+ def __init__(self, msg):
+ self.msg = msg
+
+ def __str__(self):
+ return "Error was: %s" % self.msg
+
+class QMQP:
+ '''I handle qmqp connection to a server'''
+
+ file = None
+
+ def __init__(self, host = 'localhost'):
+ '''Start'''
+ if host:
+ resp = self.connect(host)
+
+ def encode(self, stringi):
+ ret = '%d:%s,' % (len(stringi), stringi)
+ return ret
+
+ def decode(self, stringi):
+ stringi = string.split(stringi, ':', 1)
+ stringi[1] = string.rstrip(stringi[1], ',')
+ if len(stringi[1]) is not int(stringi[0]):
+ print 'malformed netstring encounterd'
+ return stringi[1]
+
+ def connect(self, host = 'localhost'):
+ for sres in socket.getaddrinfo(host, 628, 0, socket.SOCK_STREAM):
+ af, socktype, proto, canonname, sa = sres
+ try:
+ self.sock = socket.socket(af, socktype, proto)
+ self.sock.connect(sa)
+ except socket.error:
+ print 'connect failed'
+ if self.sock:
+ self.sock.close()
+ self.sock = None
+ continue
+ break
+ if not self.sock:
+ raise socket.error
+ return 0
+
+ def send(self, stringi):
+ if self.sock:
+ try:
+ self.sock.sendall(stringi)
+ except socket.error, err:
+ self.sock.close()
+ raise QMQPConnectionError, err
+ else:
+ print 'not connected'
+
+ def getreply(self):
+ if self.file is None:
+ self.file = self.sock.makefile('rb')
+ while 1:
+ line = self.file.readline()
+ if line == '':
+ self.sock.close()
+ print 'ERORORR'
+ break
+ line = self.decode(line)
+ return line
+
+ def quit(self):
+ self.sock.close()
+
+ def sendmail(self, from_addr, to_addrs, msg):
+ recipients = ''
+ msg = self.encode(msg)
+ from_addr = self.encode(from_addr)
+
+# I don't understand why len(to_addrs) <= 1 needs to be handled differently.
+# Anyway, it doesn't seem to work with Postfix. --liw
+# if len(to_addrs) > 1:
+# for t in to_addrs:
+# recipients = recipients + self.encode(t)
+# else:
+# recipients = self.encode(to_addrs[0])
+
+ for t in to_addrs:
+ recipients = recipients + self.encode(t)
+ output = self.encode(msg + from_addr + recipients)
+ self.send(output)
+ ret = self.getreply()
+ if ret[0] == 'K':
+ return ret[1:]
+ if ret[0] == 'Z':
+ raise QMQPTemporaryError, ret[1:]
+ if ret[0] == 'D':
+ raise QMQPPermanentError, ret[1:]
+
+if __name__ == '__main__':
+ a = QMQP()
+ maili = 'asfasdfsfdasfasd'
+ envelope_sender = 'liw@liw.iki.fi'
+ recips = [ 'liw@liw.iki.fi' ]
+ retcode = a.sendmail(envelope_sender, recips, maili)
+ print retcode
+ a.quit()