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 Microsoft.EntityFrameworkCore;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -18,6 +19,12 @@ namespace SMTPServer
|
|||||||
|
|
||||||
public DbSet<Accounts> Accounts { get; set; }
|
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)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
=> optionsBuilder.UseMySql(@"Server=" + Hostname + ";database=" + DatabaseName + ";uid=" + Uid + ";pwd=" + Pwd + ";");
|
=> optionsBuilder.UseMySql(@"Server=" + Hostname + ";database=" + DatabaseName + ";uid=" + Uid + ";pwd=" + Pwd + ";");
|
||||||
}
|
}
|
||||||
@ -32,9 +39,55 @@ namespace SMTPServer
|
|||||||
public class Accounts
|
public class Accounts
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
[MaxLength(255)]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public int Domain { get; set; }
|
public int Domain { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
public bool CatchAll { get; set; }
|
||||||
public bool Active { 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.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SMTPServer
|
namespace MailServer
|
||||||
{
|
{
|
||||||
public class Mail
|
public class Mail
|
||||||
{
|
{
|
||||||
public string To { get; private set; }
|
|
||||||
public string From { get; private set; }
|
public string From { get; private set; }
|
||||||
|
public string To { get; private set; }
|
||||||
|
|
||||||
public string Subject { get; private set; }
|
public string Data { get; set; }
|
||||||
public string MessageId { get; private set; }
|
|
||||||
public string Date { get; private set; }
|
|
||||||
|
|
||||||
public string MIME_Version { get; private set; }
|
public Mail(string from, string to, string data)
|
||||||
|
|
||||||
public string Others { get; private set; }
|
|
||||||
|
|
||||||
|
|
||||||
public Mail(string mail)
|
|
||||||
{
|
{
|
||||||
|
From = from;
|
||||||
|
To = to;
|
||||||
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetSenderDomain()
|
public string GetSenderDomain()
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
using DnsClient;
|
using DnsClient;
|
||||||
|
using MailServer;
|
||||||
|
using SMTPServer;
|
||||||
using SMTPServer.Exceptions;
|
using SMTPServer.Exceptions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace SMTPServer
|
namespace MailServer
|
||||||
{
|
{
|
||||||
public class MailQueue
|
public class MailQueue
|
||||||
{
|
{
|
||||||
@ -11,8 +13,9 @@ namespace SMTPServer
|
|||||||
|
|
||||||
public static async void AddToMesssageQueueAsync(Mail mail)
|
public static async void AddToMesssageQueueAsync(Mail mail)
|
||||||
{
|
{
|
||||||
|
#pragma warning disable IDE0017 // Die Objektinitialisierung kann vereinfacht werden.
|
||||||
var qmail = new QueueMail(mail);
|
var qmail = new QueueMail(mail);
|
||||||
|
#pragma warning restore IDE0017 // Die Objektinitialisierung kann vereinfacht werden.
|
||||||
qmail.DestinationDnsNames = await GetDNSEntries.GetMXRecordAsync(mail.GetSenderDomain());
|
qmail.DestinationDnsNames = await GetDNSEntries.GetMXRecordAsync(mail.GetSenderDomain());
|
||||||
qmail.DestinationIps = await GetDNSEntries.GetIPAddressFromDnsArrayAsync(qmail.DestinationDnsNames);
|
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" />
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
|
||||||
<PropertyGroup Label="Configuration">
|
<PropertyGroup Label="Configuration">
|
||||||
<NoWarn>1701;1702;1705;1006</NoWarn>
|
<NoWarn>1701;1702;1705;1006</NoWarn>
|
||||||
@ -18,13 +18,12 @@
|
|||||||
<EmbeddedResource Remove="DNSClient\**" />
|
<EmbeddedResource Remove="DNSClient\**" />
|
||||||
<EmbeddedResource Remove="DNS\**" />
|
<EmbeddedResource Remove="DNS\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DnsClient">
|
<PackageReference Include="DnsClient">
|
||||||
<Version>1.0.0-beta-1011</Version>
|
<Version>1.0.0-beta-1011</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.DotNet.Cli.Utils">
|
<PackageReference Include="Microsoft.DotNet.Cli.Utils">
|
||||||
<Version>1.0.0-preview2-1-003177</Version>
|
<Version>1.0.0-preview3-004056</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
|
||||||
<Version>1.1.0-preview4-final</Version>
|
<Version>1.1.0-preview4-final</Version>
|
||||||
@ -58,7 +57,7 @@
|
|||||||
<Version>1.1.0-rtm-10012</Version>
|
<Version>1.1.0-rtm-10012</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.IO.FileSystem">
|
<PackageReference Include="System.IO.FileSystem">
|
||||||
<Version>4.0.1</Version>
|
<Version>4.3.0</Version>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Net.NameResolution">
|
<PackageReference Include="System.Net.NameResolution">
|
||||||
<Version>4.3.0</Version>
|
<Version>4.3.0</Version>
|
||||||
|
@ -4,12 +4,13 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using SMTPServer.Exceptions;
|
using SMTPServer.Exceptions;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using MailServer.SMTPServer;
|
||||||
|
|
||||||
namespace SMTPServer
|
namespace SMTPServer
|
||||||
{
|
{
|
||||||
class MailTransferAgent
|
class MailTransferAgent
|
||||||
{
|
{
|
||||||
public const string Hostname = "mail.fstamm.net";
|
/* public const string Hostname = "mail.fstamm.net";
|
||||||
private static MTACommandsDict MTACommandsDict = new MTACommandsDict();
|
private static MTACommandsDict MTACommandsDict = new MTACommandsDict();
|
||||||
private static Thread Thread;
|
private static Thread Thread;
|
||||||
private static List<MTASession> Sessions { get; set; }
|
private static List<MTASession> Sessions { get; set; }
|
||||||
@ -62,6 +63,6 @@ namespace SMTPServer
|
|||||||
{
|
{
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using SMTPServer.Exceptions;
|
using MailServer.SMTPServer;
|
||||||
|
using SMTPServer.Exceptions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@ -12,9 +13,7 @@ namespace SMTPServer
|
|||||||
|
|
||||||
public class ConnectedEventArgs : EventArgs
|
public class ConnectedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public delegate void ReceivedLineEventHandler(string line);
|
|
||||||
|
|
||||||
public event ReceivedLineEventHandler NewLine;
|
|
||||||
|
|
||||||
private Encoding Encoding { get; set; }
|
private Encoding Encoding { get; set; }
|
||||||
private string Others { get; set; }
|
private string Others { get; set; }
|
||||||
@ -71,7 +70,7 @@ namespace SMTPServer
|
|||||||
if (c.Equals('\n'))
|
if (c.Equals('\n'))
|
||||||
{
|
{
|
||||||
Console.WriteLine(line);
|
Console.WriteLine(line);
|
||||||
NewLine(line);
|
//NewLine(line);
|
||||||
line = "";
|
line = "";
|
||||||
}
|
}
|
||||||
else line += c;
|
else line += c;
|
||||||
|
@ -35,8 +35,10 @@ namespace SMTPServer
|
|||||||
//t.Start();
|
//t.Start();
|
||||||
//TestAsync();
|
//TestAsync();
|
||||||
|
|
||||||
var listener = new PortListener(5122);
|
//var listener = new PortListener(5122);
|
||||||
listener.OnConnected += Listener_OnConnected;
|
//listener.OnConnected += Listener_OnConnected;
|
||||||
|
|
||||||
|
var l = new MailServer.SMTPServer.SmtpPortListener(5122);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@ -60,7 +62,7 @@ namespace SMTPServer
|
|||||||
|
|
||||||
private static void Listener_OnConnected(object source, ConnectedEventArgs e)
|
private static void Listener_OnConnected(object source, ConnectedEventArgs e)
|
||||||
{
|
{
|
||||||
var session = new MTASession(e);
|
//var session = new MTASession(e);
|
||||||
//throw new NotImplementedException();
|
//throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -4,7 +4,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace SMTPServer
|
namespace MailServer.SMTPServer
|
||||||
{
|
{
|
||||||
public class MTACommandsDict : Dictionary<MTACommands, String>
|
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