Folder structure reconstruction

This commit is contained in:
Fabian Stamm
2016-12-09 15:41:35 +01:00
parent df48102215
commit c4c32efb04
98 changed files with 5 additions and 2 deletions

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace MailServer.SMTPServer
{
public class MTACommandsDict : Dictionary<MTACommands, String>
{
public MTACommandsDict()
{
Add(MTACommands.HELO, "HELO");
Add(MTACommands.DATA, "DATA");
Add(MTACommands.EXPN, "EXPN");
Add(MTACommands.HELP, "HELP");
Add(MTACommands.MAIL_FROM, "MAIL FROM");
Add(MTACommands.NOOP, "NOOP");
Add(MTACommands.QUIT, "QUIT");
Add(MTACommands.RCPT_TO, "RCPT TO");
Add(MTACommands.RSET, "RSET");
Add(MTACommands.SAML_FROM, "SAML FROM");
Add(MTACommands.SEND_FROM, "SEND FROM");
Add(MTACommands.SOML_FROM, "SOML FROM");
Add(MTACommands.TURN, "TURN");
Add(MTACommands.VERB, "VERB");
Add(MTACommands.VRFY, "VRFY");
Add(MTACommands.ATRN, "ATRN");
Add(MTACommands.AUTH, "AUTH");
Add(MTACommands.BDAT, "BDAT");
Add(MTACommands.EHLO, "EHLO");
Add(MTACommands.ETRN, "ETRN");
Add(MTACommands.RCPT, "RCPT");
Add(MTACommands.SAML, "SAML");
Add(MTACommands.SEND, "SEND");
Add(MTACommands.SOML, "SOML");
Add(MTACommands.STARTTL, "STARTTLs");
Add(MTACommands.AUTH_LOGIN, "AUTH LOGIN");
}
}
public enum MTACommands
{
HELO,
MAIL_FROM,
RCPT_TO,
DATA,
RSET,
QUIT,
HELP,
VRFY,
EXPN,
VERB,
NOOP,
TURN,
SEND_FROM,
SOML_FROM,
SAML_FROM,
ATRN,
AUTH,
BDAT,
EHLO,
ETRN,
RCPT,
SAML,
SEND,
SOML,
STARTTL,
AUTH_LOGIN
}
public enum Extensions
{
PIPELINING,
SIZE,
STARTTLS,
}
public enum ResponseCodes
{
C211 = 211,
C214 = 214,
C220 = 220,
C221 = 221,
C250 = 250,
C251 = 251,
C252 = 252,
C253 = 253,
C334 = 334,
C354 = 354,
C355 = 355,
C421 = 421,
C432 = 432,
C450 = 450,
C451 = 451,
C452 = 452,
C453 = 453,
C454 = 454,
C458 = 458,
C459 = 459,
C500 = 500,
C501 = 501,
C502 = 502,
C503 = 503,
C504 = 504,
C521 = 521,
C530 = 530,
C534 = 534,
C538 = 538,
C550 = 550,
C551 = 551,
C552 = 552,
C553 = 553,
C554 = 554
}
}

View 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.Equals(""))
{
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];
}
}
}
}

View File

@ -0,0 +1,38 @@
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;
public SmtpPortListener(int port)
{
Port = port;
Logging.AddLogMessage(Logging.LoggingType.INFO, "Start TCP listener on port: " + Port);
Thread thread = new Thread(StartListeningAsync);
thread.Start();
}
public async void StartListeningAsync()
{
Listener = new TcpListener(IPAddress.Any, Port);
Listener.Start();
while (true)
{
var client = await Listener.AcceptTcpClientAsync();
var ip = (IPEndPoint)client.Client.RemoteEndPoint;
Logging.AddLogMessage(Logging.LoggingType.INFO, "New Client Logged in with ip: " + ip.Address.ToString() + "On port: " + Port + " to port: " + ip.Port);
new SmtpServerSession(client);
}
}
}
}

View File

@ -0,0 +1,319 @@
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
FreeLine = true;
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)
{
accountId = 0;
}
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 = IPEndPoint.Address.ToString(),
From = MailFrom,
To = r,
Folder = folderid
};
context.Mails.Add(nm);
context.SaveChangesAsync();
}
MailFromSet = false;
Recipients.Clear();
WaitForData = false;
return;
}
SendResponse(ResponseCodes.C250, "OK");
return;
}
else
{
Data += command.Command;
foreach (var s in command.Parameters)
{
Data += " " + s;
}
Data += "\r\n";
FreeLine = false;
}
return;
}
switch (command.Command.ToUpper())
{
case "EHLO":
if (command.Parameters.Length < 1)
{
SendResponse(ResponseCodes.C501, "Hostname Required");
return;
}
SenderHostname = command.Parameters[0];
Initialized = true;
SendExtensionResponse(Extensions.PIPELINING);
if (Configuration.STARTTLS_Active)
{
#pragma warning disable CS0162 // Unerreichbarer Code wurde entdeckt.
SendExtensionResponse(Extensions.STARTTLS);
#pragma warning restore CS0162 // Unerreichbarer Code wurde entdeckt.
}
SendResponse(ResponseCodes.C250, "SIZE " + (Configuration.MaxMessageSizeInKb * 1000).ToString());
return;
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>.<CRLF>");
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
}
}
}

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
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; }
public IPEndPoint IPEndPoint { get; private set; }
private NetworkStream Stream { get; set; }
private Encoding Encoding = Encoding.ASCII;
private string Others;
private Thread Worker;
private bool Active = true;
private int SessionID { get; set; }
public SmtpSession(TcpClient client)
{
Client = client;
IPEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
Stream = client.GetStream();
Others = "";
SessionID = new Random(123).Next(10000, 99999);
Logging.AddLogMessage(Logging.LoggingType.INFO, "New SMTPSession with id '" + SessionID + "' started");
Worker = new Thread(ReadClientInput);
Worker.Start();
}
public void SendExtensionResponse(Extensions extension) => SendResponse("250-" + extension.ToString());
public void SendResponse(ResponseCodes responseCode) => SendResponse(responseCode, "");
public void SendResponse(ResponseCodes responseCode, string args) => SendResponse(((int)responseCode).ToString() + " " + args);
public void SendResponse(string response)
{
Logging.AddLogMessage(Logging.LoggingType.INFO, response);
response += '\r' + '\n';
var bytes = Encoding.UTF8.GetBytes(response);
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);
Logging.AddLogMessage(Logging.LoggingType.DEBUG, "Seession " + SessionID + ": received line: " + line);
NewLine(line);
line = "";
}
else
{
line += c;
r = false;
}
}
Others = line;
}
}
public void CloseAll()
{
Stream.Dispose();
Client.Dispose();
Active = false;
}
}
}