SMTP Server basic functionality

This commit is contained in:
Fabian Stamm 2016-11-27 18:38:47 +01:00
parent aab06c8ea8
commit 181f5ef908
17 changed files with 664 additions and 135 deletions

View 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;
}
}

View File

@ -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; }
}
}

View 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();
}
}
}

View 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
}
}

View File

@ -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")]

View File

@ -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);
}
}
}
}

View File

@ -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()

View File

@ -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);

View File

@ -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>

View File

@ -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;
}
}*/
}
}

View File

@ -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;

View File

@ -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();
}
/*

View File

@ -4,7 +4,7 @@ using System.IO;
using System.Linq;
using System.Text;
namespace SMTPServer
namespace MailServer.SMTPServer
{
public class MTACommandsDict : Dictionary<MTACommands, String>
{

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 = "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,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);
}
}
}
}

View 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
}
}
}

View 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;
}
}
}