2016-11-27 17:38:47 +00:00
|
|
|
|
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
|
2016-11-28 16:26:04 +00:00
|
|
|
|
FreeLine = true;
|
2016-11-27 17:38:47 +00:00
|
|
|
|
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)
|
|
|
|
|
{
|
2016-11-30 20:09:45 +00:00
|
|
|
|
accountId = 0;
|
2016-11-27 17:38:47 +00:00
|
|
|
|
}
|
|
|
|
|
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,
|
2016-11-28 16:26:04 +00:00
|
|
|
|
SendedByServerIp = IPEndPoint.Address.ToString(),
|
2016-11-27 17:38:47 +00:00
|
|
|
|
From = MailFrom,
|
|
|
|
|
To = r,
|
|
|
|
|
Folder = folderid
|
|
|
|
|
};
|
|
|
|
|
context.Mails.Add(nm);
|
|
|
|
|
context.SaveChangesAsync();
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-28 16:26:04 +00:00
|
|
|
|
MailFromSet = false;
|
|
|
|
|
Recipients.Clear();
|
|
|
|
|
WaitForData = false;
|
2016-11-27 17:38:47 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SendResponse(ResponseCodes.C250, "OK");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Data += command.Command;
|
|
|
|
|
|
|
|
|
|
foreach (var s in command.Parameters)
|
|
|
|
|
{
|
|
|
|
|
Data += " " + s;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-28 16:26:04 +00:00
|
|
|
|
Data += "\r\n";
|
2016-11-27 17:38:47 +00:00
|
|
|
|
FreeLine = false;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (command.Command.ToUpper())
|
|
|
|
|
{
|
2016-11-28 16:26:04 +00:00
|
|
|
|
case "EHLO":
|
2016-11-30 20:09:45 +00:00
|
|
|
|
if (command.Parameters.Length < 1)
|
|
|
|
|
{
|
|
|
|
|
SendResponse(ResponseCodes.C501, "Hostname Required");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SenderHostname = command.Parameters[0];
|
|
|
|
|
Initialized = true;
|
|
|
|
|
SendExtensionResponse(Extensions.PIPELINING);
|
|
|
|
|
|
|
|
|
|
if (Configuration.STARTTLS_Active)
|
|
|
|
|
{
|
2016-12-09 14:41:35 +00:00
|
|
|
|
#pragma warning disable CS0162 // Unerreichbarer Code wurde entdeckt.
|
2016-11-30 20:09:45 +00:00
|
|
|
|
SendExtensionResponse(Extensions.STARTTLS);
|
2016-12-09 14:41:35 +00:00
|
|
|
|
#pragma warning restore CS0162 // Unerreichbarer Code wurde entdeckt.
|
2016-11-30 20:09:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SendResponse(ResponseCodes.C250, "SIZE " + (Configuration.MaxMessageSizeInKb * 1000).ToString());
|
|
|
|
|
return;
|
2016-11-27 17:38:47 +00:00
|
|
|
|
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":
|
2016-11-28 16:26:04 +00:00
|
|
|
|
SendResponse(ResponseCodes.C354, "Start mail input, end with <CRLF>.<CRLF>");
|
2016-11-27 17:38:47 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|