3using System.Reflection;
6using System.Threading.Tasks;
18 private const int MaxFragmentSize = 40000000;
20 private readonly UTF8Encoding encoding =
new UTF8Encoding(
false,
false);
21 private readonly StringBuilder fragment =
new StringBuilder();
22 private int fragmentLength = 0;
27 private LinkedList<KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object>> callbacks =
null;
28 private DateTime lastActivity = DateTime.Now;
29 private int inputState = 0;
30 private int inputDepth = 0;
31 private readonly
string parentFullJid;
32 private string streamHeader;
33 private string streamFooter;
34 private string streamId;
35 private double version;
36 private string remoteFullJid;
37 private bool headerSent =
false;
60 this.AddPeerHandlers();
76 string StreamId,
double Version, EventHandlerAsync<PeerConnectionEventArgs> Callback,
object State)
80 this.remoteFullJid = RemoteFullJID;
81 this.streamHeader = StreamHeader;
82 this.streamFooter = StreamFooter;
83 this.streamId = StreamId;
84 this.version = Version;
87 this.callbacks =
new LinkedList<KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object>>();
88 this.callbacks.AddLast(
new KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object>(Callback,
State));
90 this.AddPeerHandlers();
93 internal void AddCallback(EventHandlerAsync<PeerConnectionEventArgs> Callback,
object State)
95 this.callbacks ??=
new LinkedList<KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object>>();
96 this.callbacks.AddLast(
new KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object>(Callback,
State));
99 private void AddPeerHandlers()
101 if (!(this.peer is
null))
103 this.peer.OnSent += this.Peer_OnSent;
105 this.peer.OnClosed += this.Peer_OnClosed;
122 private void RemoveHandlers()
124 if (!(this.peer is
null))
126 this.peer.OnSent -= this.Peer_OnSent;
128 this.peer.OnClosed -= this.Peer_OnClosed;
142 public async Task<bool>
Peer_OnReceived(
object Sender,
bool ConstantBuffer,
byte[] Buffer,
int Offset,
int Count)
144 string s = this.encoding.GetString(Buffer, Offset, Count);
146 this.lastActivity = DateTime.Now;
147 if (this.xmppClient is
null)
150 return await this.ParseIncoming(s);
153 private async Task<bool> ParseIncoming(
string s)
157 foreach (
char ch
in s)
159 switch (this.inputState)
164 this.fragment.Append(ch);
165 if (++this.fragmentLength > MaxFragmentSize)
167 await this.ToError();
175 await this.ToError();
181 this.fragment.Append(ch);
182 if (++this.fragmentLength > MaxFragmentSize)
184 await this.ToError();
193 await this.ProcessStream(this.fragment.ToString());
194 this.fragment.Clear();
195 this.fragmentLength = 0;
200 this.fragment.Append(ch);
201 if (++this.fragmentLength > MaxFragmentSize)
203 await this.ToError();
211 this.fragment.Append(ch);
212 if (++this.fragmentLength > MaxFragmentSize)
214 await this.ToError();
221 await this.ToError();
227 this.fragment.Append(ch);
228 if (++this.fragmentLength > MaxFragmentSize)
230 await this.ToError();
237 await this.ProcessStream(this.fragment.ToString());
238 this.fragment.Clear();
239 this.fragmentLength = 0;
246 this.fragment.Append(ch);
247 if (++this.fragmentLength > MaxFragmentSize)
249 await this.ToError();
255 else if (this.inputDepth > 1)
257 this.fragment.Append(ch);
258 if (++this.fragmentLength > MaxFragmentSize)
260 await this.ToError();
266 await this.ToError();
272 this.fragment.Append(ch);
273 if (++this.fragmentLength > MaxFragmentSize)
275 await this.ToError();
281 this.inputState = 13;
283 this.inputState += 2;
287 this.fragment.Append(ch);
288 if (++this.fragmentLength > MaxFragmentSize)
290 await this.ToError();
296 if (this.inputDepth < 1)
298 await this.ToError();
303 if (this.inputDepth == 1)
305 if (!await this.ProcessFragment(this.fragment.ToString()))
308 this.fragment.Clear();
309 this.fragmentLength = 0;
312 if (this.inputState > 0)
319 this.fragment.Append(ch);
320 if (++this.fragmentLength > MaxFragmentSize)
322 await this.ToError();
333 this.inputState += 2;
337 this.fragment.Append(ch);
338 if (++this.fragmentLength > MaxFragmentSize)
340 await this.ToError();
345 if (this.inputDepth == 1)
347 if (!await this.ProcessFragment(this.fragment.ToString()))
350 this.fragment.Clear();
351 this.fragmentLength = 0;
354 if (this.inputState != 0)
362 this.fragment.Append(ch);
363 if (++this.fragmentLength > MaxFragmentSize)
365 await this.ToError();
378 this.inputState += 2;
382 this.fragment.Append(ch);
383 if (++this.fragmentLength > MaxFragmentSize)
385 await this.ToError();
393 this.fragment.Append(ch);
394 if (++this.fragmentLength > MaxFragmentSize)
396 await this.ToError();
400 this.inputState -= 2;
404 this.fragment.Append(ch);
405 if (++this.fragmentLength > MaxFragmentSize)
407 await this.ToError();
413 this.inputState = 18;
416 await this.ToError();
422 this.fragment.Append(ch);
423 if (++this.fragmentLength > MaxFragmentSize)
425 await this.ToError();
432 await this.ToError();
438 this.fragment.Append(ch);
439 if (++this.fragmentLength > MaxFragmentSize)
441 await this.ToError();
449 this.fragment.Append(ch);
450 if (++this.fragmentLength > MaxFragmentSize)
452 await this.ToError();
462 this.fragment.Append(ch);
463 if (++this.fragmentLength > MaxFragmentSize)
465 await this.ToError();
471 this.inputState -= 2;
475 this.fragment.Append(ch);
476 if (++this.fragmentLength > MaxFragmentSize)
478 await this.ToError();
485 await this.ToError();
491 this.fragment.Append(ch);
492 if (++this.fragmentLength > MaxFragmentSize)
494 await this.ToError();
501 await this.ToError();
507 this.fragment.Append(ch);
508 if (++this.fragmentLength > MaxFragmentSize)
510 await this.ToError();
517 await this.ToError();
523 this.fragment.Append(ch);
524 if (++this.fragmentLength > MaxFragmentSize)
526 await this.ToError();
533 await this.ToError();
539 this.fragment.Append(ch);
540 if (++this.fragmentLength > MaxFragmentSize)
542 await this.ToError();
549 await this.ToError();
555 this.fragment.Append(ch);
556 if (++this.fragmentLength > MaxFragmentSize)
558 await this.ToError();
565 await this.ToError();
571 this.fragment.Append(ch);
572 if (++this.fragmentLength > MaxFragmentSize)
574 await this.ToError();
582 this.fragment.Append(ch);
583 if (++this.fragmentLength > MaxFragmentSize)
585 await this.ToError();
595 this.fragment.Append(ch);
596 if (++this.fragmentLength > MaxFragmentSize)
598 await this.ToError();
604 this.inputState -= 2;
615 private async Task ToError()
617 this.inputState = -1;
620 await this.CallCallbacks();
622 if (!(this.peer is
null))
629 private async Task ProcessStream(
string Xml)
633 int i = Xml.IndexOf(
"?>");
635 Xml = Xml[(i + 2)..].TrimStart();
637 this.streamHeader = Xml;
639 i = Xml.IndexOf(
":stream");
641 this.streamFooter =
"</stream>";
643 this.streamFooter =
"</" + Xml[1..i] +
":stream>";
645 XmlDocument Doc =
XML.
ParseXml(Xml + this.streamFooter,
true);
647 if (Doc.DocumentElement.LocalName !=
"stream")
648 throw new XmppException(
"Invalid stream.", Doc.DocumentElement);
650 XmlElement Stream = Doc.DocumentElement;
656 if (this.version < 1.0)
657 throw new XmppException(
"Version not supported.", Stream);
660 throw new XmppException(
"Invalid destination JID.", Stream);
664 string Header =
"<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='" +
665 this.parentFullJid +
"' to='" + this.remoteFullJid +
"' version='1.0'>";
671 Error = this.parent.AuthenticatePeer(this.peer, this.remoteFullJid);
679 if (!
string.IsNullOrEmpty(Error))
681 Header +=
"<stream:error><invalid-from xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>" +
682 "<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>" +
XML.
Encode(Error) +
683 "</text></stream:error></stream:stream>";
685 this.headerSent =
true;
686 await this.
SendAsync(Header, (Sender, e) =>
688 return this.ToError();
694 if (!this.headerSent)
696 this.headerSent =
true;
701 this.xmppClient =
new XmppClient(
this, this.state, Header,
"</stream:stream>", this.parentFullJid,
702 typeof(XmppServerlessMessaging).Assembly)
704 SendFromAddress =
true,
708 this.parent.PeerAuthenticated(
this);
709 await this.parent.NewXmppClient(this.xmppClient, this.parentFullJid, this.remoteFullJid);
711 this.xmppClient.OnStateChanged += this.XmppClient_OnStateChanged;
713 await this.CallCallbacks();
718 await this.ToError();
722 private async Task XmppClient_OnStateChanged(
object _,
XmppState NewState)
724 this.state = NewState;
727 await this.CallCallbacks();
730 this.parent?.PeerClosed(
this);
732 if (!(this.xmppClient is
null))
735 this.xmppClient =
null;
738 await this.CallCallbacks();
749 if (!(this.xmppClient is
null))
750 return this.xmppClient.
State;
756 internal bool HeaderSent
758 get => this.headerSent;
759 set => this.headerSent = value;
770 this.RemoveHandlers();
772 this.AddPeerHandlers();
791 internal bool HasCallbacks
793 get {
return !(this.callbacks is
null); }
796 internal void ClearCallbacks()
798 this.callbacks =
null;
801 internal async Task CallCallbacks()
803 if (!(this.callbacks is
null))
805 foreach (KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object> P
in this.callbacks)
806 await P.Key.Raise(
this,
new PeerConnectionEventArgs(this.xmppClient, P.Value,
this.parentFullJid,
this.remoteFullJid));
808 this.callbacks =
null;
812 private async Task<bool> ProcessFragment(
string Xml)
823 Result = await h(
this, Xml);
838 private Task Peer_OnClosed(
object Sender, EventArgs e)
840 this.parent.PeerClosed(
this);
844 if (!(this.callbacks is
null))
845 return this.CallCallbacks();
847 return Task.CompletedTask;
855 if (!(this.peer is
null))
869 if (!(this.xmppClient is
null))
880 this.xmppClient =
null;
884 private async Task Peer_OnSent(
object Sender,
bool ConstantBuffer,
byte[] Buffer,
int Offset,
int Count)
891 string s = this.encoding.GetString(Buffer, Offset, Count);
907 return this.
SendAsync(Packet,
null,
null);
916 public Task<bool>
SendAsync(
string Packet, EventHandlerAsync<DeliveryEventArgs> Callback,
object State)
918 byte[] Data = this.encoding.GetBytes(Packet);
920 if (!(this.peer is
null))
923 this.lastActivity = DateTime.Now;
926 return Task.FromResult(
true);
932 [Obsolete(
"Use DisposeAsync()")]
953 return (DateTime.Now -
this.lastActivity).TotalSeconds;
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
static string Encode(string s)
Encodes a string for use in XML.
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
void ReceiveText(string Text)
Called when text has been received.
Maintains a peer connection
void Continue()
Continues a paused connection.
bool Paused
If reading has been paused.
Task SendTcp(byte[] Packet)
Sends a packet to the peer at the other side of the TCP connection. Transmission is done asynchronous...
async Task DisposeAsync()
IDisposable.Dispose
PeerState(PeerConnection Peer, XmppServerlessMessaging Parent, string RemoteFullJID, string StreamHeader, string StreamFooter, string StreamId, double Version, EventHandlerAsync< PeerConnectionEventArgs > Callback, object State)
Peer connection state.
Task DisposeAsync()
IDisposable.Dispose
string RemoteFullJid
Remote Full JID
XmppClient XmppClient
XMPP client.
PeerConnection Peer
Peer-to-peer connection object.
Task< bool > SendAsync(string Packet, EventHandlerAsync< DeliveryEventArgs > Callback, object State)
Sends a packet.
bool Paused
If reading has been paused.
async Task< bool > Peer_OnReceived(object Sender, bool ConstantBuffer, byte[] Buffer, int Offset, int Count)
Data received from a peer.
void Dispose()
IDisposable.Dispose
Task< bool > SendAsync(string Packet)
Sends a packet.
TextEventHandler OnReceived
Event raised when a text packet (XML fragment) has been received.
async Task Close()
CLoses the connection.
TextEventHandler OnSent
Event raised when a text packet has been sent.
XmppState State
Current connection state.
PeerState(PeerConnection Peer, XmppServerlessMessaging Parent)
Peer connection state.
void Continue()
Continues a paused connection.
double AgeSeconds
Seconds since object was active.
XmppServerlessMessaging Parent
Parent object.
Class managing peer-to-peer serveless XMPP communication.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
XmppState State
Current state of connection.
Task OfflineAndDisposeAsync()
Sends an offline presence, and then disposes the object by calling DisposeAsync.
async Task DisposeAsync()
Closes the connection and disposes of all resources.
Interface for text transport layers.
class Header(ISimulationNode Parent, Model Model)
Represents an identity property.
XmppState
State of XMPP connection.
delegate Task< bool > TextEventHandler(object Sender, string Text)
Event handler for text packet events.