Code
·
139 lines
·
4745 bytes
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
139using MailSharp.Common;
using MailSharp.Common.Services;
using MailSharp.IMAP.Metrics;
using MailSharp.IMAP.Session;
using System.Net;
using System.Net.Sockets;
namespace MailSharp.IMAP.Server;
public class ImapServer
{
private readonly IConfiguration configuration;
private readonly ILogger<ImapServer> logger;
private readonly ILogger<ImapSession> sessionLogger;
private readonly AuthenticationService authService;
private readonly MailboxService mailboxService;
private readonly List<ServerContext> listeners = [];
private readonly ImapMetrics metrics;
private CancellationTokenSource? cts;
public ImapServer(
IConfiguration configuration,
ILogger<ImapServer> logger,
ILogger<ImapSession> sessionLogger,
AuthenticationService authService,
MailboxService mailboxService,
ImapMetrics metrics)
{
this.configuration = configuration;
this.logger = logger;
this.sessionLogger = sessionLogger;
this.authService = authService;
this.mailboxService = mailboxService;
this.metrics = metrics;
var ports = configuration.GetSection("ImapSettings:Ports").Get<List<PortConfig>>()
?? throw new InvalidOperationException("Ports not configured");
foreach (var port in ports)
{
listeners.Add(new(new TcpListener(IPAddress.Parse(port.Host), port.Port), port.Security));
}
}
public async Task StartAsync()
{
cts = new CancellationTokenSource();
var active = new List<ServerContext>();
foreach (var context in listeners)
{
try
{
context.Listener.Start();
var eventIdConfig = configuration.GetSection("ImapEventIds:ServerStarted").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing ImapEventIds:ServerStarted");
logger.LogInformation(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
configuration["ImapLogMessages:ServerStarted"],
context.Listener.LocalEndpoint, context.Security);
active.Add(context);
}
catch (SocketException ex)
{
var eventIdConfig = configuration.GetSection("ImapEventIds:ServerStartFailed").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing ImapEventIds:ServerStartFailed");
logger.LogError(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
ex,
configuration["ImapLogMessages:ServerStartFailed"],
((IPEndPoint)context.Listener.LocalEndpoint).Port,
ex.Message);
}
}
if (active.Count > 0)
await Task.WhenAll(active.Select(context =>
Task.Run(() => AcceptClientsAsync(context, cts.Token), cts.Token)));
await StopAsync();
}
private async Task AcceptClientsAsync(ServerContext context, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
var client = await context.Listener.AcceptTcpClientAsync(cancellationToken);
var eventIdConfig = configuration.GetSection("ImapEventIds:ClientAccepted").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing ImapEventIds:ClientAccepted");
logger.LogInformation(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
configuration["ImapLogMessages:ClientAccepted"],
client.Client.RemoteEndPoint);
var session = new ImapSession(client, configuration, context.Security, authService, mailboxService, metrics, sessionLogger);
_ = session.ProcessAsync(cancellationToken);
}
catch (OperationCanceledException)
{
var eventIdConfig = configuration.GetSection("ImapEventIds:ListenerStopped").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing ImapEventIds:ListenerStopped");
logger.LogInformation(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
configuration["ImapLogMessages:ListenerStopped"],
context.Listener.LocalEndpoint);
break;
}
catch (Exception ex)
{
var eventIdConfig = configuration.GetSection("ImapEventIds:ClientAcceptError").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing ImapEventIds:ClientAcceptError");
logger.LogError(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
ex,
configuration["ImapLogMessages:ClientAcceptError"],
context.Listener.LocalEndpoint);
}
}
}
public async Task StopAsync()
{
if (cts != null)
{
await cts.CancelAsync();
}
foreach (var context in listeners)
{
context.Listener.Stop();
var eventIdConfig = configuration.GetSection("ImapEventIds:ListenerStopped").Get<EventIdConfig>()
?? throw new InvalidOperationException("Missing ImapEventIds:ListenerStopped");
logger.LogInformation(
new EventId(eventIdConfig.Id, eventIdConfig.Name),
configuration["ImapLogMessages:ListenerStopped"],
context.Listener.LocalEndpoint);
context.Listener.Dispose();
}
listeners.Clear();
}
}