SMTP Server basic functionality
This commit is contained in:
parent
aab06c8ea8
commit
181f5ef908
12
MailServer/MailServer/Configuration.cs
Normal file
12
MailServer/MailServer/Configuration.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MailServer
|
||||
{
|
||||
public class Configuration
|
||||
{
|
||||
public const string Hostname = "localhost";
|
||||
public bool STARTTLS_Active = false;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -18,6 +19,12 @@ namespace SMTPServer
|
||||
|
||||
public DbSet<Accounts> Accounts { get; set; }
|
||||
|
||||
public DbSet<Folders> Folders { get; set; }
|
||||
|
||||
public DbSet<Aliases> Aliases { get; set; }
|
||||
|
||||
public DbSet<Mails> Mails { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseMySql(@"Server=" + Hostname + ";database=" + DatabaseName + ";uid=" + Uid + ";pwd=" + Pwd + ";");
|
||||
}
|
||||
@ -32,9 +39,55 @@ namespace SMTPServer
|
||||
public class Accounts
|
||||
{
|
||||
public int Id { get; set; }
|
||||
[MaxLength(255)]
|
||||
public string Name { get; set; }
|
||||
public int Domain { get; set; }
|
||||
public string Password { get; set; }
|
||||
public bool CatchAll { get; set; }
|
||||
public bool Active { get; set; }
|
||||
}
|
||||
|
||||
public class Folders
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int AccountId { get; set; }
|
||||
[MaxLength(100)]
|
||||
public string Name { get; set; }
|
||||
public int StandardFolder { get; set; }
|
||||
}
|
||||
|
||||
public enum StandardFolders
|
||||
{
|
||||
INBOX = 1,
|
||||
TRASH = 2,
|
||||
SPAM = 3
|
||||
}
|
||||
|
||||
public class Aliases
|
||||
{
|
||||
public int Id { get; set; }
|
||||
[MaxLength(255)]
|
||||
public string SourceName { get; set; }
|
||||
public int SourceDomain { get; set; }
|
||||
public int DestinationAccount { get; set; }
|
||||
}
|
||||
|
||||
public class Mails
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int AccountId { get; set; }
|
||||
public DateTime ReceiveDate { get; set; }
|
||||
public string Data { get; set; }
|
||||
|
||||
[MaxLength(15)]
|
||||
public string SendedByServerIp { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string From { get; set; }
|
||||
|
||||
[MaxLength(255)]
|
||||
public string To { get; set; }
|
||||
public int Folder { get; set; }
|
||||
public bool Readed { get; set; }
|
||||
}
|
||||
}
|
52
MailServer/MailServer/DatabaseHelper.cs
Normal file
52
MailServer/MailServer/DatabaseHelper.cs
Normal file
@ -0,0 +1,52 @@
|
||||
using MailServer.Exceptions;
|
||||
using SMTPServer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MailServer
|
||||
{
|
||||
class DatabaseHelper
|
||||
{
|
||||
MysqlDB DBContext;
|
||||
public DatabaseHelper()
|
||||
{
|
||||
DBContext = new MysqlDB();
|
||||
DBContext.Database.EnsureCreatedAsync();
|
||||
}
|
||||
|
||||
public Folders GetInboxFromAccount(int accountid) => GetStandardFolderFromAccount(accountid, StandardFolders.INBOX);
|
||||
|
||||
public Folders GetStandardFolderFromAccount(int accountid, StandardFolders folder)
|
||||
{
|
||||
var res = DBContext.Folders.Where(x => x.AccountId.Equals(accountid) && x.StandardFolder.Equals(folder));
|
||||
if (res.Count() < 1) throw new NotFoundException("Folder for specified account not found", Exceptions.Type.FOLDER);
|
||||
return res.FirstOrDefault();
|
||||
}
|
||||
|
||||
public Accounts GetAccount(string name, string domain) => GetAccount(name, GetDomain(domain).Id);
|
||||
|
||||
public Accounts GetAccount(string name, int domain)
|
||||
{
|
||||
name = name.ToLower();
|
||||
var res = DBContext.Accounts.Where(x => x.Domain.Equals(domain) && x.Name.Equals(name));
|
||||
if (res.Count() < 1) throw new NotFoundException("no account found", Exceptions.Type.ACCOUNT);
|
||||
return res.FirstOrDefault();
|
||||
}
|
||||
|
||||
public Domains GetDomain(string domain)
|
||||
{
|
||||
domain = domain.ToLower();
|
||||
var res = DBContext.Domains.Where(x => x.Domain.Equals(domain));
|
||||
if (res.Count() < 1) throw new NotFoundException("no domain found", Exceptions.Type.DOMAIN);
|
||||
return res.FirstOrDefault();
|
||||
}
|
||||
|
||||
public void AddMail(Mails mail)
|
||||
{
|
||||
DBContext.Add(mail);
|
||||
DBContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
35
MailServer/MailServer/Exceptions/NotFoundException.cs
Normal file
35
MailServer/MailServer/Exceptions/NotFoundException.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MailServer.Exceptions
|
||||
{
|
||||
class NotFoundException : Exception
|
||||
{
|
||||
public Type Type { get; set; }
|
||||
|
||||
public NotFoundException(Type type) : base()
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public NotFoundException(string message, Type type) : base(message)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public NotFoundException(string message, Exception inner, Type type) : base(message, inner)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type
|
||||
{
|
||||
DOMAIN,
|
||||
ACCOUNT,
|
||||
ALIAS,
|
||||
MAIL,
|
||||
FOLDER
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._Count")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._Mail")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._QueueEntered")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._DestinationIps")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._DestinationDnsNames")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._From")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._To")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._Subject")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._Others")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._Date")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._MessageId")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.Mail._MIME_Version")]
|
||||
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Stil", "IDE1006:Benennungsstile", Justification = "<Ausstehend>", Scope = "member", Target = "~P:SMTPServer.MailQueue.QueueMail._DestinationIndex")]
|
||||
|
@ -1,86 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace SMTPServer
|
||||
{
|
||||
class MTASession
|
||||
{
|
||||
private static MTACommandsDict MTACommandsDict = new MTACommandsDict();
|
||||
private ConnectedEventArgs ConnectedEventArgs { get; set; }
|
||||
|
||||
private bool Initialized { get; set; }
|
||||
private string SenderHostname { get; set; }
|
||||
|
||||
private bool AuthActive { get; set; }
|
||||
private string Username { get; set; }
|
||||
private string Password { get; set; }
|
||||
private bool Authenticated { get; set; }
|
||||
|
||||
private bool DATA { get; set; }
|
||||
|
||||
private string Mail_From { get; set; }
|
||||
|
||||
public MTASession(ConnectedEventArgs e)
|
||||
{
|
||||
ConnectedEventArgs = e;
|
||||
Thread th = new Thread(new ThreadStart(StartCommunication));
|
||||
th.Start();
|
||||
}
|
||||
|
||||
private void StartCommunication()
|
||||
{
|
||||
ConnectedEventArgs.SendResponse(ResponseCodes.C220, MailTransferAgent.Hostname);
|
||||
ConnectedEventArgs.NewLine += ConnectedEventArgs_NewLine;
|
||||
}
|
||||
|
||||
private void ConnectedEventArgs_NewLine(string line)
|
||||
{
|
||||
if (line.StartsWith(MTACommandsDict[MTACommands.RSET]))
|
||||
{
|
||||
// Schauen was alles resettet wird
|
||||
}
|
||||
|
||||
if (!Initialized)
|
||||
{
|
||||
if (line.StartsWith(MTACommandsDict[MTACommands.HELO]))
|
||||
{
|
||||
ConnectedEventArgs.SendResponse(ResponseCodes.C250, MailTransferAgent.Hostname);
|
||||
//} else if (line.StartsWith(MTACommandsDict[MTACommands.EHLO])) {
|
||||
// ConnectedEventArgs.SendResponse(ResponseCodes.C250, MailTransferAgent.Hostname);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConnectedEventArgs.SendResponse(ResponseCodes.C503, MailTransferAgent.Hostname);
|
||||
return;
|
||||
}
|
||||
Initialized = true;
|
||||
}
|
||||
|
||||
if (line.StartsWith(MTACommandsDict[MTACommands.AUTH_LOGIN]))
|
||||
{
|
||||
AuthActive = true;
|
||||
ConnectedEventArgs.SendResponse(ResponseCodes.C334);
|
||||
}
|
||||
else if (line.StartsWith(MTACommandsDict[MTACommands.MAIL_FROM]))
|
||||
{
|
||||
var args = line.Split(':');
|
||||
if (args.Length < 2)
|
||||
{
|
||||
ConnectedEventArgs.SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
var mailFrom = args[1];
|
||||
mailFrom.Replace('<', ' ');
|
||||
}
|
||||
else if (line.StartsWith(MTACommandsDict[MTACommands.QUIT]))
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
} else
|
||||
{
|
||||
ConnectedEventArgs.SendResponse(ResponseCodes.C500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,25 +2,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SMTPServer
|
||||
namespace MailServer
|
||||
{
|
||||
public class Mail
|
||||
{
|
||||
public string To { get; private set; }
|
||||
public string From { get; private set; }
|
||||
public string To { get; private set; }
|
||||
|
||||
public string Subject { get; private set; }
|
||||
public string MessageId { get; private set; }
|
||||
public string Date { get; private set; }
|
||||
public string Data { get; set; }
|
||||
|
||||
public string MIME_Version { get; private set; }
|
||||
|
||||
public string Others { get; private set; }
|
||||
|
||||
|
||||
public Mail(string mail)
|
||||
public Mail(string from, string to, string data)
|
||||
{
|
||||
|
||||
From = from;
|
||||
To = to;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public string GetSenderDomain()
|
||||
|
@ -1,9 +1,11 @@
|
||||
using DnsClient;
|
||||
using MailServer;
|
||||
using SMTPServer;
|
||||
using SMTPServer.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SMTPServer
|
||||
namespace MailServer
|
||||
{
|
||||
public class MailQueue
|
||||
{
|
||||
@ -11,8 +13,9 @@ namespace SMTPServer
|
||||
|
||||
public static async void AddToMesssageQueueAsync(Mail mail)
|
||||
{
|
||||
#pragma warning disable IDE0017 // Die Objektinitialisierung kann vereinfacht werden.
|
||||
var qmail = new QueueMail(mail);
|
||||
|
||||
#pragma warning restore IDE0017 // Die Objektinitialisierung kann vereinfacht werden.
|
||||
qmail.DestinationDnsNames = await GetDNSEntries.GetMXRecordAsync(mail.GetSenderDomain());
|
||||
qmail.DestinationIps = await GetDNSEntries.GetIPAddressFromDnsArrayAsync(qmail.DestinationDnsNames);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<NoWarn>1701;1702;1705;1006</NoWarn>
|
||||
@ -18,13 +18,12 @@
|
||||
<EmbeddedResource Remove="DNSClient\**" />
|
||||
<EmbeddedResource Remove="DNS\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DnsClient">
|
||||
<Version>1.0.0-beta-1011</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.DotNet.Cli.Utils">
|
||||
<Version>1.0.0-preview2-1-003177</Version>
|
||||
<Version>1.0.0-preview3-004056</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
|
||||
<Version>1.1.0-preview4-final</Version>
|
||||
@ -58,7 +57,7 @@
|
||||
<Version>1.1.0-rtm-10012</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.IO.FileSystem">
|
||||
<Version>4.0.1</Version>
|
||||
<Version>4.3.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Net.NameResolution">
|
||||
<Version>4.3.0</Version>
|
||||
|
@ -4,12 +4,13 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using SMTPServer.Exceptions;
|
||||
using System.Net;
|
||||
using MailServer.SMTPServer;
|
||||
|
||||
namespace SMTPServer
|
||||
{
|
||||
class MailTransferAgent
|
||||
{
|
||||
public const string Hostname = "mail.fstamm.net";
|
||||
/* public const string Hostname = "mail.fstamm.net";
|
||||
private static MTACommandsDict MTACommandsDict = new MTACommandsDict();
|
||||
private static Thread Thread;
|
||||
private static List<MTASession> Sessions { get; set; }
|
||||
@ -62,6 +63,6 @@ namespace SMTPServer
|
||||
{
|
||||
|
||||
return null;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using SMTPServer.Exceptions;
|
||||
using MailServer.SMTPServer;
|
||||
using SMTPServer.Exceptions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
@ -12,9 +13,7 @@ namespace SMTPServer
|
||||
|
||||
public class ConnectedEventArgs : EventArgs
|
||||
{
|
||||
public delegate void ReceivedLineEventHandler(string line);
|
||||
|
||||
public event ReceivedLineEventHandler NewLine;
|
||||
|
||||
|
||||
private Encoding Encoding { get; set; }
|
||||
private string Others { get; set; }
|
||||
@ -71,7 +70,7 @@ namespace SMTPServer
|
||||
if (c.Equals('\n'))
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
NewLine(line);
|
||||
//NewLine(line);
|
||||
line = "";
|
||||
}
|
||||
else line += c;
|
||||
|
@ -35,8 +35,10 @@ namespace SMTPServer
|
||||
//t.Start();
|
||||
//TestAsync();
|
||||
|
||||
var listener = new PortListener(5122);
|
||||
listener.OnConnected += Listener_OnConnected;
|
||||
//var listener = new PortListener(5122);
|
||||
//listener.OnConnected += Listener_OnConnected;
|
||||
|
||||
var l = new MailServer.SMTPServer.SmtpPortListener(5122);
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -60,7 +62,7 @@ namespace SMTPServer
|
||||
|
||||
private static void Listener_OnConnected(object source, ConnectedEventArgs e)
|
||||
{
|
||||
var session = new MTASession(e);
|
||||
//var session = new MTASession(e);
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
/*
|
||||
|
@ -4,7 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SMTPServer
|
||||
namespace MailServer.SMTPServer
|
||||
{
|
||||
public class MTACommandsDict : Dictionary<MTACommands, String>
|
||||
{
|
45
MailServer/MailServer/SMTPServer/SmtpCommand.cs
Normal file
45
MailServer/MailServer/SMTPServer/SmtpCommand.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MailServer.SMTPServer
|
||||
{
|
||||
public class SmtpCommand
|
||||
{
|
||||
public string Command { get; set; }
|
||||
public string[] Parameters { get; set; }
|
||||
|
||||
public SmtpCommand(string command)
|
||||
{
|
||||
Parameters = new string[0];
|
||||
if (command.Length == 0)
|
||||
{
|
||||
Command = "FREELINE";
|
||||
return;
|
||||
}
|
||||
else if (command.Equals("."))
|
||||
{
|
||||
Command = "JUSTADOT";
|
||||
return;
|
||||
}
|
||||
# if DEBUG
|
||||
else if (command.Equals("<CRLF>.<CRLF>"))
|
||||
{
|
||||
Command = "DATAEND";
|
||||
return;
|
||||
}
|
||||
# endif
|
||||
|
||||
var sp = command.Split(' ');
|
||||
|
||||
Command = sp[0];
|
||||
|
||||
if (sp.Length < 2) return;
|
||||
Parameters = new string[sp.Length - 1];
|
||||
for (int i = 1; i < sp.Length; i++)
|
||||
{
|
||||
Parameters[i - 1] = sp[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
MailServer/MailServer/SMTPServer/SmtpPortListener.cs
Normal file
39
MailServer/MailServer/SMTPServer/SmtpPortListener.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace MailServer.SMTPServer
|
||||
{
|
||||
public class SmtpPortListener
|
||||
{
|
||||
private static List<int> Ports { get; set; }
|
||||
private int Port { get; set; }
|
||||
private TcpListener Listener;
|
||||
private List<SmtpServerSession> Sessions { get; set; }
|
||||
|
||||
public SmtpPortListener(int port)
|
||||
{
|
||||
Sessions = new List<SmtpServerSession>();
|
||||
Port = port;
|
||||
Thread thread = new Thread(StartListeningAsync);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
public async void StartListeningAsync()
|
||||
{
|
||||
Listener = new TcpListener(IPAddress.Any, Port);
|
||||
Listener.Start();
|
||||
|
||||
while (true)
|
||||
{
|
||||
//var socket = await Listener.AcceptSocketAsync();
|
||||
var client = await Listener.AcceptTcpClientAsync();
|
||||
var session = new SmtpServerSession(client);
|
||||
Sessions.Add(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
297
MailServer/MailServer/SMTPServer/SmtpServerSession.cs
Normal file
297
MailServer/MailServer/SMTPServer/SmtpServerSession.cs
Normal file
@ -0,0 +1,297 @@
|
||||
using SMTPServer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
|
||||
namespace MailServer.SMTPServer
|
||||
{
|
||||
class SmtpServerSession : SmtpSession
|
||||
{
|
||||
private bool Initialized { get; set; }
|
||||
private string SenderHostname { get; set; }
|
||||
|
||||
private bool MailFromSet { get; set; }
|
||||
private string MailFrom { get; set; }
|
||||
|
||||
private List<string> Recipients { get; set; }
|
||||
|
||||
private bool WaitForData { get; set; }
|
||||
private string Data { get; set; }
|
||||
private bool FreeLine { get; set; }
|
||||
|
||||
private bool AuthActive { get; set; }
|
||||
private string Username { get; set; }
|
||||
private string Password { get; set; }
|
||||
private bool Authenticated { get; set; }
|
||||
|
||||
public SmtpServerSession(TcpClient client) : base(client) {
|
||||
Recipients = new List<string>();
|
||||
SendResponse(ResponseCodes.C220, Configuration.Hostname + " Welcome to SimpleMail DotNetCore based AIO MailServer");
|
||||
NewLine += SmtpServerSession_NewLine;
|
||||
}
|
||||
|
||||
private void SmtpServerSession_NewLine(string line)
|
||||
{
|
||||
try
|
||||
{
|
||||
var command = new SmtpCommand(line);
|
||||
|
||||
if (WaitForData)
|
||||
{
|
||||
#if DEBUG
|
||||
bool dataend = false;
|
||||
if (command.Command.Equals("DATAEND"))
|
||||
{
|
||||
dataend = true;
|
||||
}
|
||||
#endif
|
||||
if (!FreeLine && command.Command.Equals("FREELINE"))
|
||||
{
|
||||
FreeLine = true;
|
||||
return;
|
||||
}
|
||||
else if ((FreeLine && command.Command.Equals("JUSTADOT")) || dataend)
|
||||
{
|
||||
SendResponse(ResponseCodes.C250, "OK");
|
||||
foreach (var r in Recipients)
|
||||
{
|
||||
var split = r.Split('@');
|
||||
var name = split[0];
|
||||
var domain = split[1];
|
||||
|
||||
using (var context = new MysqlDB())
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
int accountId = 0;
|
||||
|
||||
var dmn = context.Domains.Where(x => x.Domain.Equals(domain));
|
||||
var domainidx = dmn.FirstOrDefault().Id;
|
||||
|
||||
var res = context.Accounts.Where(x => x.Domain.Equals(domainidx) && x.Name.Equals(name));
|
||||
if (res.Count() < 1)
|
||||
{
|
||||
var res2 = context.Aliases.Where(x => x.SourceName.Equals(name) && x.SourceDomain.Equals(domain));
|
||||
if (res2.Count() < 1)
|
||||
{
|
||||
var res3 = context.Accounts.Where(x => x.Domain.Equals(domain) && x.CatchAll == true);
|
||||
if (res3.Count() < 1)
|
||||
{
|
||||
//ToDo unmöglich machen!!
|
||||
}
|
||||
else
|
||||
{
|
||||
accountId = res3.FirstOrDefault().Id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
accountId = res2.FirstOrDefault().DestinationAccount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
accountId = res.FirstOrDefault().Id;
|
||||
}
|
||||
int folderid = 0;
|
||||
|
||||
var res4 = context.Folders.Where(x => x.AccountId == accountId && x.StandardFolder == (int)StandardFolders.INBOX);
|
||||
if (res4.Count() < 1)
|
||||
{
|
||||
//ToDo ERROR
|
||||
folderid = 0;
|
||||
}
|
||||
else folderid = res4.FirstOrDefault().Id;
|
||||
|
||||
var nm = new Mails()
|
||||
{
|
||||
AccountId = accountId,
|
||||
ReceiveDate = DateTime.Now,
|
||||
Data = Data,
|
||||
SendedByServerIp = "0.0.0.0",
|
||||
From = MailFrom,
|
||||
To = r,
|
||||
Folder = folderid
|
||||
};
|
||||
|
||||
context.Mails.Add(nm);
|
||||
context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
SendResponse(ResponseCodes.C250, "OK");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Data += command.Command;
|
||||
|
||||
foreach (var s in command.Parameters)
|
||||
{
|
||||
Data += " " + s;
|
||||
}
|
||||
|
||||
Data += "\n";
|
||||
FreeLine = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (command.Command.ToUpper())
|
||||
{
|
||||
case "HELO":
|
||||
if (command.Parameters.Length < 1)
|
||||
{
|
||||
SendResponse(ResponseCodes.C501, "Hostname Required");
|
||||
return;
|
||||
}
|
||||
SenderHostname = command.Parameters[0];
|
||||
Initialized = true;
|
||||
SendResponse(ResponseCodes.C250, "OK");
|
||||
return;
|
||||
case "RSET":
|
||||
//ToDo
|
||||
return;
|
||||
case "QUIT":
|
||||
SendResponse(ResponseCodes.C221, Configuration.Hostname + " closing transmission channel");
|
||||
CloseAll();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Initialized)
|
||||
{
|
||||
switch (command.Command.ToUpper())
|
||||
{
|
||||
case "MAIL":
|
||||
if (command.Parameters.Length < 1)
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
if (!command.Parameters[0].ToUpper().StartsWith("FROM"))
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
if (command.Parameters.Length == 1)
|
||||
{
|
||||
var m = command.Parameters[0].Split(':');
|
||||
if (m.Length != 2)
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
MailFrom = m[1];
|
||||
}
|
||||
else if (command.Parameters.Length == 2)
|
||||
{
|
||||
MailFrom = command.Parameters[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
|
||||
MailFrom.Replace("<", String.Empty);
|
||||
MailFrom.Replace(">", String.Empty);
|
||||
MailFromSet = true;
|
||||
SendResponse(ResponseCodes.C250, "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MailFromSet)
|
||||
{
|
||||
switch (command.Command.ToUpper())
|
||||
{
|
||||
case "RCPT":
|
||||
if (command.Parameters.Length < 1)
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
if (!command.Parameters[0].ToUpper().StartsWith("TO"))
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
|
||||
var rcpt = "";
|
||||
if (command.Parameters.Length == 1)
|
||||
{
|
||||
var m = command.Parameters[0].Split(':');
|
||||
if (m.Length != 2)
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
rcpt = m[1];
|
||||
}
|
||||
else if (command.Parameters.Length == 2)
|
||||
{
|
||||
rcpt = command.Parameters[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
|
||||
rcpt = rcpt.Replace("<", String.Empty);
|
||||
rcpt = rcpt.Replace(">", String.Empty);
|
||||
|
||||
var split = rcpt.Split('@');
|
||||
if (split.Length != 2)
|
||||
{
|
||||
SendResponse(ResponseCodes.C501);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var context = new MysqlDB())
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
var res = context.Domains.Where(x => x.Domain.Equals(split[1]));
|
||||
if (res.Count() < 1)
|
||||
{
|
||||
SendResponse(ResponseCodes.C550, "No such user here");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Recipients.Add(rcpt);
|
||||
SendResponse(ResponseCodes.C250, "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Recipients.Count > 0)
|
||||
{
|
||||
switch (command.Command.ToUpper())
|
||||
{
|
||||
case "DATA":
|
||||
SendResponse(ResponseCodes.C354, "Start mail input, end with <CRLF>.<CRLS>");
|
||||
WaitForData = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
SendResponse(ResponseCodes.C500, command.Command);
|
||||
//throw new NotImplementedException();
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
Logging.AddException(e);
|
||||
SendResponse(ResponseCodes.C554, "internal error");
|
||||
SendResponse(ResponseCodes.C221, "Closing Transmission");
|
||||
CloseAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearFields()
|
||||
{
|
||||
//ToDo einbauen
|
||||
}
|
||||
|
||||
}
|
||||
}
|
103
MailServer/MailServer/SMTPServer/SmtpSession.cs
Normal file
103
MailServer/MailServer/SMTPServer/SmtpSession.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace MailServer.SMTPServer
|
||||
{
|
||||
public delegate void ReceivedLineEventHandler(string line);
|
||||
|
||||
class SmtpSession
|
||||
{
|
||||
public event ReceivedLineEventHandler NewLine;
|
||||
|
||||
private TcpClient Client { get; set; }
|
||||
private NetworkStream Stream { get; set; }
|
||||
private Encoding Encoding = Encoding.ASCII;
|
||||
private string Others;
|
||||
private Thread Worker;
|
||||
private bool Active = true;
|
||||
|
||||
public SmtpSession(TcpClient client)
|
||||
{
|
||||
Client = client;
|
||||
Stream = client.GetStream();
|
||||
Others = "";
|
||||
Worker = new Thread(ReadClientInput);
|
||||
Worker.Start();
|
||||
}
|
||||
|
||||
public void SendResponse(ResponseCodes responseCode) => SendResponse(responseCode, "");
|
||||
|
||||
public void SendResponse(ResponseCodes responseCode, string args)
|
||||
{
|
||||
var text = ((int)responseCode).ToString();
|
||||
text += " " + args +'\r' + '\n';
|
||||
var bytes = Encoding.UTF8.GetBytes(text);
|
||||
Stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
private void ReadClientInput()
|
||||
{
|
||||
while (Active)
|
||||
{
|
||||
if (Stream.DataAvailable)
|
||||
{
|
||||
byte[] buffer = new byte[Client.ReceiveBufferSize];
|
||||
var readed = Stream.Read(buffer, 0, buffer.Length);
|
||||
|
||||
var str = Encoding.GetString(buffer, 0, readed);
|
||||
lock (Others)
|
||||
{
|
||||
Others += str;
|
||||
}
|
||||
|
||||
CheckLines();
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool r = false;
|
||||
|
||||
private void CheckLines()
|
||||
{
|
||||
lock (Others)
|
||||
{
|
||||
var line = "";
|
||||
foreach (char c in Others)
|
||||
{
|
||||
if (c.Equals('\r'))
|
||||
{
|
||||
r = true;
|
||||
}
|
||||
else if (r && c.Equals('\n'))
|
||||
{
|
||||
//Console.WriteLine(line);
|
||||
NewLine(line);
|
||||
line = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
line += c;
|
||||
r = false;
|
||||
}
|
||||
}
|
||||
Others = line;
|
||||
}
|
||||
}
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
Stream.Dispose();
|
||||
Client.Dispose();
|
||||
Active = false;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user