-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample.py
More file actions
117 lines (86 loc) · 2.94 KB
/
example.py
File metadata and controls
117 lines (86 loc) · 2.94 KB
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
import socket
import base64
import suelta
# Make working with both Python 2.6+ and 3 easier
from suelta.util import bytes
# Walkthrough - and test - for Suelta.
# We're going to make a piss-poor IMAP client.
HOST = 'peirce.dave.cridland.net'
SERVICE = 'imap'
# Set this to None, typically, for auto-select, or a SASL mechanism name, like:
# MECHANISM = 'DIGEST-MD5'
MECHANISM = None
# Setup sasl object *now*...
# This gets called with questions.
def secquery(mech, question):
print("Answering yes to: %s" % question)
return True
def callback(mech, vals):
print("Need user information for %s login to %s on %s" % (mech.name, mech.sasl.service, mech.sasl.host))
import getpass
for x, v in list(vals.items()):
if x == 'password':
vals[x] = getpass.getpass( 'Password: ' )
else:
vals[x] = input( x+': ' )
print("Fulfilling: %s" % vals)
mech.fulfill(vals)
# We'll authenticate as "test". The password is (or was, when I wrote this) "test".
sasl = suelta.SASL(HOST, SERVICE,
username='test',
sec_query=secquery,
request_values=callback,
mech=MECHANISM)
fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
fd.connect((HOST, 143))
f = fd.makefile(mode='rw')
# Read through the banner.
# A real IMAP client would look for capabilities here.
print(f.readline())
# ... But we'll ask for them explicitly.
f.write('. CAPABILITY\r\n')
f.flush()
caps = f.readline() # Very trusting.
caps = caps.strip()
# Close to vomiting, now:
caps = [ mech.split('=')[1] for mech in caps.split(' ') if mech.startswith('AUTH=') ]
print(f.readline())
print("Available mechs: %s" % caps)
mech = sasl.choose_mechanism(caps)
print("Going to use %s - %s" % (mech, mech.name))
# If we do initial response, here, we could process an empty string to get it.
# This *is* the case on IMAP with SASL-IR.
f.write('. AUTHENTICATE %s\r\n' % mech.name)
f.flush()
# Loop until we're done.
# IMAP SASL profile does base64 everywhere, like many protocols.
# It's up to us to handle that, Suelta expects the real data.
while True:
stuff = f.readline()
print(stuff)
if stuff[0] == '.':
break
if stuff[0] == '+':
gunk = base64.b64decode(bytes(stuff[2:]))
print("Got: %s" % gunk)
my_gunk = mech.process(gunk)
print("Sending: %s" % my_gunk)
if my_gunk is None:
my_stuff = b'+'
else:
my_stuff =base64.b64encode(my_gunk).replace(b'\n', b'')
my_stuff += b'\r\n'
my_stuff = my_stuff.decode('utf-8')
# Note the slightly hairy way you need to encode to avoid linebreaks.
f.write(my_stuff)
f.flush()
ok = stuff.split(' ')[1]
if ok == 'OK':
print("Server says OK.")
if mech.okay():
print("Mechanism says OK.")
## Be happy.
else:
print("Mutual auth failed: Disaster!")
else:
print("Auth failed - wrong password?")