-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathqmqp.py
137 lines (115 loc) · 3.76 KB
/
qmqp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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()