2using System.Collections.Generic;
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 if (this.callbacks is
null)
96 this.callbacks =
new LinkedList<KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object>>();
98 this.callbacks.AddLast(
new KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object>(Callback,
State));
101 private void AddPeerHandlers()
103 if (!(this.peer is
null))
105 this.peer.OnSent += this.Peer_OnSent;
107 this.peer.OnClosed += this.Peer_OnClosed;
124 private void RemoveHandlers()
126 if (!(this.peer is
null))
128 this.peer.OnSent -= this.Peer_OnSent;
130 this.peer.OnClosed -= this.Peer_OnClosed;
142 public async Task<bool>
Peer_OnReceived(
object Sender,
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 this.CallCallbacks();
622 if (!(this.peer is
null))
629 private async Task ProcessStream(
string Xml)
633 int i = Xml.IndexOf(
"?>");
635 Xml = Xml.Substring(i + 2).TrimStart();
637 this.streamHeader = Xml;
639 i = Xml.IndexOf(
":stream");
641 this.streamFooter =
"</stream>";
643 this.streamFooter =
"</" + Xml.Substring(1, i - 1) +
":stream>";
645 XmlDocument Doc =
new XmlDocument()
647 PreserveWhitespace =
true
649 Doc.LoadXml(Xml + this.streamFooter);
651 if (Doc.DocumentElement.LocalName !=
"stream")
652 throw new XmppException(
"Invalid stream.", Doc.DocumentElement);
654 XmlElement Stream = Doc.DocumentElement;
660 if (this.version < 1.0)
661 throw new XmppException(
"Version not supported.", Stream);
664 throw new XmppException(
"Invalid destination JID.", Stream);
668 string Header =
"<?xml version='1.0'?><stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='" +
669 this.parentFullJid +
"' to='" + this.remoteFullJid +
"' version='1.0'>";
673 this.parent.AuthenticatePeer(this.peer, this.remoteFullJid);
679 Header +=
"<stream:error><invalid-from xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>" +
680 "<text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>" +
XML.
Encode(ex.Message) +
681 "</text></stream:error></stream:stream>";
683 this.headerSent =
true;
684 await this.
SendAsync(Header, (Sender, e) =>
686 return this.ToError();
692 if (!this.headerSent)
694 this.headerSent =
true;
699 this.xmppClient =
new XmppClient(
this, this.state, Header,
"</stream:stream>", this.parentFullJid,
700 typeof(XmppServerlessMessaging).GetTypeInfo().Assembly)
702 SendFromAddress =
true
705 await this.parent.PeerAuthenticated(
this);
706 await this.parent.NewXmppClient(this.xmppClient, this.parentFullJid, this.remoteFullJid);
708 this.xmppClient.OnStateChanged += this.XmppClient_OnStateChanged;
710 this.CallCallbacks();
715 await this.ToError();
719 private async Task XmppClient_OnStateChanged(
object _,
XmppState NewState)
721 this.state = NewState;
724 this.CallCallbacks();
727 this.parent?.PeerClosed(
this);
729 if (!(this.xmppClient is
null))
732 this.xmppClient =
null;
735 this.CallCallbacks();
746 if (!(this.xmppClient is
null))
747 return this.xmppClient.
State;
753 internal bool HeaderSent
755 get => this.headerSent;
756 set => this.headerSent = value;
767 this.RemoveHandlers();
769 this.AddPeerHandlers();
788 internal bool HasCallbacks
790 get {
return !(this.callbacks is
null); }
793 internal void ClearCallbacks()
795 this.callbacks =
null;
798 internal void CallCallbacks()
800 if (!(this.callbacks is
null))
802 foreach (KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>,
object> P
in this.callbacks)
806 P.Key(
this,
new PeerConnectionEventArgs(this.xmppClient, P.Value,
this.parentFullJid,
this.remoteFullJid));
814 this.callbacks =
null;
818 private async Task<bool> ProcessFragment(
string Xml)
829 Result = await h(
this, Xml);
844 private async Task Peer_OnClosed(
object Sender, EventArgs e)
846 await this.parent.PeerClosed(
this);
850 if (!(this.callbacks is
null))
851 this.CallCallbacks();
859 if (!(this.peer is
null))
873 if (!(this.xmppClient is
null))
884 this.xmppClient =
null;
888 private async Task Peer_OnSent(
object Sender,
byte[] Buffer,
int Offset,
int Count)
895 string s = this.encoding.GetString(Buffer, Offset, Count);
911 return this.
SendAsync(Packet,
null,
null);
920 public Task<bool>
SendAsync(
string Packet, EventHandlerAsync<DeliveryEventArgs> Callback,
object State)
922 byte[] Data = this.encoding.GetBytes(Packet);
924 if (!(this.peer is
null))
927 this.lastActivity = DateTime.Now;
930 return Task.FromResult(
true);
936 [Obsolete(
"Use DisposeAsync()")]
964 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 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.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
Task ReceiveText(string Text)
Called when text has been received.
Maintains a peer connection
void Continue()
Continues a paused connection.
Task DisposeAsync()
IDisposable.Dispose
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< bool > Peer_OnReceived(object Sender, byte[] Buffer, int Offset, int Count)
Data received from a peer.
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.
async void Dispose()
IDisposable.Dispose
bool Paused
If reading has been paused.
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.
XmppState
State of XMPP connection.
delegate Task< bool > TextEventHandler(object Sender, string Text)
Event handler for text packet events.