forked from andyedinborough/aenetmail
-
Notifications
You must be signed in to change notification settings - Fork 1
/
TextClient.cs
145 lines (120 loc) · 3.98 KB
/
TextClient.cs
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
138
139
140
141
142
143
144
145
using System;
using System.IO;
using System.Net.Sockets;
using System.Collections.Concurrent;
using System.Threading;
namespace AE.Net.Mail {
public abstract class TextClient : IDisposable {
protected TcpClient _Connection;
protected Stream _Stream;
protected BlockingCollection<String> _Responses = new BlockingCollection<String>();
private Thread _ReadThread;
public string Host { get; private set; }
public int Port { get; set; }
public bool Ssl { get; set; }
public bool IsConnected { get; private set; }
public bool IsAuthenticated { get; private set; }
public bool IsDisposed { get; private set; }
internal abstract void OnLogin(string username, string password);
internal abstract void OnLogout();
internal abstract void CheckResultOK(string result);
protected virtual void OnConnected(string result) {
CheckResultOK(result);
}
protected virtual void OnDispose() { }
public void Login(string username, string password) {
if (!IsConnected) {
throw new Exception("You must connect first!");
}
IsAuthenticated = false;
OnLogin(username, password);
IsAuthenticated = true;
}
public void Logout() {
IsAuthenticated = false;
OnLogout();
}
public void Connect(string hostname, int port, bool ssl) {
try {
Host = hostname;
Port = port;
Ssl = ssl;
_Connection = new TcpClient(hostname, port);
_Stream = _Connection.GetStream();
if (ssl) {
var sslSream = new System.Net.Security.SslStream(_Stream);
_Stream = sslSream;
sslSream.AuthenticateAsClient(hostname);
}
//Create a new thread to retrieve data (needed for Imap Idle).
_ReadThread = new Thread(ReceiveData);
_ReadThread.Name = "_ReadThread";
_ReadThread.Start();
string info = _Responses.Take();
OnConnected(info);
IsConnected = true;
Host = hostname;
} catch (Exception) {
IsConnected = false;
throw;
}
}
private void ReceiveData()
{
StreamReader _Reader = new StreamReader(_Stream, System.Text.Encoding.Default);
try {
while (!_Responses.IsAddingCompleted) { //this is nice, but in reality on shutdown we are still blocking on ReadLine
_Responses.Add(_Reader.ReadLine());
}
}
catch (Exception) {
_Reader.Dispose();
throw;
}
}
protected void CheckConnectionStatus() {
if (IsDisposed) throw new ObjectDisposedException(this.GetType().Name);
if (!IsConnected) throw new Exception("You must connect first!");
if (!IsAuthenticated) throw new Exception("You must authenticate first!");
}
protected virtual void SendCommand(string command) {
byte[] data = System.Text.Encoding.Default.GetBytes(command + "\r\n");
_Stream.Write(data, 0, data.Length);
}
protected string SendCommandGetResponse(string command) {
SendCommand(command);
return GetResponse();
}
protected virtual string GetResponse() {
return _Responses.Take();
}
protected virtual bool TryGetResponse(out string result, int milliseconds = 200)
{
return _Responses.TryTake(out result, milliseconds);
}
protected void SendCommandCheckOK(string command)
{
CheckResultOK(SendCommandGetResponse(command));
}
public void Disconnect() {
Logout();
_Responses.CompleteAdding();
if (!_ReadThread.Join(2000)){
_ReadThread.Abort();
}
if (_Stream != null) {
_Stream.Dispose();
}
}
public void Dispose() {
try {
OnDispose();
} catch (Exception) { }
Disconnect();
IsDisposed = true;
_Stream = null;
_ReadThread = null;
_Connection = null;
}
}
}