Skip to content

Commit 0fb086e

Browse files
committed
.
1 parent 2ad66ed commit 0fb086e

3 files changed

Lines changed: 180 additions & 136 deletions

File tree

SMTP2Graph/Program.cs

Lines changed: 0 additions & 133 deletions
This file was deleted.

SMTP2Graph/SMTP2Graph.cs

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
using Microsoft.Graph;
2+
using Azure.Identity;
3+
using System.Net.Sockets;
4+
using System.Net;
5+
using System.Text.RegularExpressions;
6+
using System.Text;
7+
using Microsoft.Kiota.Abstractions;
8+
using Microsoft.Win32;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace SMTP2Graph;
12+
13+
class Service
14+
{
15+
private static ILogger<Service> Logger = LoggerFactory.Create((lf) => {
16+
lf.AddSimpleConsole((sc) => {
17+
sc.SingleLine = true;
18+
sc.TimestampFormat = "MM/dd/yyyy: hh:mm:ss: ";
19+
});
20+
if (OperatingSystem.IsWindows()) lf.AddEventLog();
21+
}).CreateLogger<Service>();
22+
23+
public static void Main()
24+
{
25+
Logger.LogInformation("Initializing SMTP2Graph...");
26+
var mail_listener = new TcpListener(IPAddress.Any, 25);
27+
var scopes = new[] { "https://graph.microsoft.com/.default" };
28+
29+
var options = new ClientSecretCredentialOptions
30+
{
31+
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
32+
};
33+
34+
Logger.LogInformation("Connecting to MS Graph...");
35+
36+
GraphServiceClient graphClient = null;
37+
38+
try
39+
{
40+
var clientSecretCredential = new ClientSecretCredential(
41+
GetConfigOption("TENANT_ID"),
42+
GetConfigOption("CLIENT_ID"),
43+
GetConfigOption("CLIENT_SECRET"),
44+
options
45+
);
46+
graphClient = new GraphServiceClient(clientSecretCredential, scopes);
47+
}
48+
catch (Exception e)
49+
{
50+
Logger.LogCritical(e, "Failed to connect to MS Graph.");
51+
Environment.Exit(0);
52+
}
53+
54+
Logger.LogInformation("Starting SMTP listener loop, ready for connections.");
55+
mail_listener.Start();
56+
while (true)
57+
{
58+
var client = mail_listener.AcceptTcpClient();
59+
Logger.LogInformation("Accepting connection, attempting to process...");
60+
_ = Task.Run(() => {
61+
try
62+
{
63+
client.NoDelay = true;
64+
var stream = client.GetStream();
65+
var sw = new StreamWriter(stream);
66+
var sr = new StreamReader(stream);
67+
sw.AutoFlush = true;
68+
sw.Write($"220 {Environment.MachineName} SMTP2Graph Service ready\r\n");
69+
HandleConnection(client, stream, sw, sr);
70+
}
71+
catch (Exception e)
72+
{
73+
if (client.Connected) client.Close();
74+
Logger.LogError(e, "An error occured in the SMTP listener loop.");
75+
}
76+
});
77+
}
78+
79+
string GetConfigOption(string Key)
80+
{
81+
string? env;
82+
env = Environment.GetEnvironmentVariable(Key);
83+
if (env != null) return env;
84+
if (OperatingSystem.IsWindows()) env = (string?)(Registry.LocalMachine.OpenSubKey(@"SOFTWARE\SMTP2Graph")?.GetValue(Key));
85+
if (env != null) return env;
86+
Logger.LogCritical($"Config option not set: {Key}.");
87+
Environment.Exit(0);
88+
return string.Empty;
89+
}
90+
91+
void HandleConnection(TcpClient client, Stream stream, StreamWriter sw, StreamReader sr)
92+
{
93+
string From = string.Empty;
94+
string MIME = string.Empty;
95+
while (true)
96+
{
97+
var msg = sr.ReadLine();
98+
if (msg == null) continue;
99+
if (msg.StartsWith("HELO") || msg.StartsWith("EHLO"))
100+
{
101+
Logger.LogInformation("HELO");
102+
sw.Write("250 OK\r\n");
103+
continue;
104+
}
105+
if (msg.StartsWith("MAIL FROM:"))
106+
{
107+
Logger.LogInformation("MAIL FROM");
108+
#pragma warning disable SYSLIB1045
109+
From = Regex.Match(msg, "<(.*)>").Groups[1].Value;
110+
#pragma warning restore SYSLIB1045
111+
sw.Write("250 OK\r\n");
112+
continue;
113+
}
114+
if (msg.StartsWith("RCPT TO:"))
115+
{
116+
Logger.LogInformation("RCPT TO");
117+
sw.Write("250 OK\r\n");
118+
continue;
119+
}
120+
if (msg.StartsWith("DATA"))
121+
{
122+
Logger.LogInformation("DATA");
123+
sw.Write("354 Start mail input; end with <CRLF>.<CRLF>\r\n");
124+
MIME = ReadMIME(sr);
125+
sw.Write("250 OK\r\n");
126+
Logger.LogInformation("Sending to MS Graph...");
127+
SendMessage(From, MIME);
128+
Logger.LogInformation("Sent.");
129+
continue;
130+
}
131+
if (msg.StartsWith("QUIT"))
132+
{
133+
Logger.LogInformation("QUIT");
134+
sw.Write($"221 {Environment.MachineName} Service closing transmission channel\r\n");
135+
break;
136+
}
137+
}
138+
Logger.LogInformation("Closing connection...");
139+
client.Close();
140+
Logger.LogInformation("Done.");
141+
}
142+
143+
string ReadMIME(StreamReader sr)
144+
{
145+
string MIME = string.Empty;
146+
while (true)
147+
{
148+
var msg = sr.ReadLine();
149+
if (msg == ".") break;
150+
if (msg == "..")
151+
{
152+
msg = ".";
153+
}
154+
MIME += msg + "\r\n";
155+
}
156+
return MIME;
157+
}
158+
159+
void SendMessage(string From, string MIME)
160+
{
161+
var mimeb64bytes = Encoding.UTF8.GetBytes(
162+
Convert.ToBase64String(
163+
Encoding.UTF8.GetBytes(MIME)
164+
)
165+
);
166+
var request = new RequestInformation
167+
{
168+
URI = new($"https://graph.microsoft.com/v1.0/users/{From}/sendMail"),
169+
HttpMethod = Method.POST
170+
};
171+
request.Headers.Add("Content-Type", "text/plain");
172+
request.Content = new MemoryStream(mimeb64bytes);
173+
_ = graphClient.RequestAdapter.SendNoContentAsync(request);
174+
}
175+
}
176+
}

SMTP2Graph/SMTP2Graph.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net9.0</TargetFramework>
5+
<TargetFramework>net10.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
<UserSecretsId>d63d6915-df69-48cf-8d6c-88fd2de116f1</UserSecretsId>
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Azure.Identity" Version="1.17.1" />
13-
<PackageReference Include="Microsoft.Graph" Version="5.98.0" />
12+
<PackageReference Include="Azure.Identity" Version="1.21.0" />
13+
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.6" />
14+
<PackageReference Include="Microsoft.Graph" Version="5.104.0" />
1415
</ItemGroup>
1516

1617
</Project>

0 commit comments

Comments
 (0)