Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
PeerState.cs
1using System;
2using System.Collections.Generic;
3using System.Reflection;
4using System.Text;
5using System.Xml;
6using System.Threading.Tasks;
8using Waher.Events;
10
12{
17 {
18 private const int MaxFragmentSize = 40000000;
19
20 private readonly UTF8Encoding encoding = new UTF8Encoding(false, false);
21 private readonly StringBuilder fragment = new StringBuilder();
22 private int fragmentLength = 0;
23 private XmppState state = XmppState.StreamNegotiation;
24 private PeerConnection peer;
25 private XmppServerlessMessaging parent;
26 private XmppClient xmppClient;
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;
38
42 public event TextEventHandler OnSent = null;
43
47 public event TextEventHandler OnReceived = null;
48
55 {
56 this.parent = Parent;
57 this.peer = Peer;
58 this.parentFullJid = Parent.FullJid;
59
60 this.AddPeerHandlers();
61 }
62
75 public PeerState(PeerConnection Peer, XmppServerlessMessaging Parent, string RemoteFullJID, string StreamHeader, string StreamFooter,
76 string StreamId, double Version, EventHandlerAsync<PeerConnectionEventArgs> Callback, object State)
77 {
78 this.parent = Parent;
79 this.peer = Peer;
80 this.remoteFullJid = RemoteFullJID;
81 this.streamHeader = StreamHeader;
82 this.streamFooter = StreamFooter;
83 this.streamId = StreamId;
84 this.version = Version;
85 this.parentFullJid = Parent.FullJid;
86
87 this.callbacks = new LinkedList<KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>, object>>();
88 this.callbacks.AddLast(new KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>, object>(Callback, State));
89
90 this.AddPeerHandlers();
91 }
92
93 internal void AddCallback(EventHandlerAsync<PeerConnectionEventArgs> Callback, object State)
94 {
95 if (this.callbacks is null)
96 this.callbacks = new LinkedList<KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>, object>>();
97
98 this.callbacks.AddLast(new KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>, object>(Callback, State));
99 }
100
101 private void AddPeerHandlers()
102 {
103 if (!(this.peer is null))
104 {
105 this.peer.OnSent += this.Peer_OnSent;
106 this.peer.OnReceived += this.Peer_OnReceived;
107 this.peer.OnClosed += this.Peer_OnClosed;
108 }
109 }
110
114 public bool Paused => this.peer.Paused;
115
119 public void Continue()
120 {
121 this.peer.Continue();
122 }
123
124 private void RemoveHandlers()
125 {
126 if (!(this.peer is null))
127 {
128 this.peer.OnSent -= this.Peer_OnSent;
129 this.peer.OnReceived -= this.Peer_OnReceived;
130 this.peer.OnClosed -= this.Peer_OnClosed;
131 }
132 }
133
142 public async Task<bool> Peer_OnReceived(object Sender, byte[] Buffer, int Offset, int Count)
143 {
144 string s = this.encoding.GetString(Buffer, Offset, Count);
145
146 this.lastActivity = DateTime.Now;
147 if (this.xmppClient is null)
148 await this.parent.ReceiveText(s);
149
150 return await this.ParseIncoming(s);
151 }
152
153 private async Task<bool> ParseIncoming(string s)
154 {
155 bool Result = true;
156
157 foreach (char ch in s)
158 {
159 switch (this.inputState)
160 {
161 case 0: // Waiting for first <
162 if (ch == '<')
163 {
164 this.fragment.Append(ch);
165 if (++this.fragmentLength > MaxFragmentSize)
166 {
167 await this.ToError();
168 return false;
169 }
170 else
171 this.inputState++;
172 }
173 else if (ch > ' ')
174 {
175 await this.ToError();
176 return false;
177 }
178 break;
179
180 case 1: // Waiting for ? or >
181 this.fragment.Append(ch);
182 if (++this.fragmentLength > MaxFragmentSize)
183 {
184 await this.ToError();
185 return false;
186 }
187 else if (ch == '?')
188 this.inputState++;
189 else if (ch == '>')
190 {
191 this.inputState = 5;
192 this.inputDepth = 1;
193 await this.ProcessStream(this.fragment.ToString());
194 this.fragment.Clear();
195 this.fragmentLength = 0;
196 }
197 break;
198
199 case 2: // In processing instruction. Waiting for ?>
200 this.fragment.Append(ch);
201 if (++this.fragmentLength > MaxFragmentSize)
202 {
203 await this.ToError();
204 return false;
205 }
206 else if (ch == '>')
207 this.inputState++;
208 break;
209
210 case 3: // Waiting for <stream
211 this.fragment.Append(ch);
212 if (++this.fragmentLength > MaxFragmentSize)
213 {
214 await this.ToError();
215 return false;
216 }
217 else if (ch == '<')
218 this.inputState++;
219 else if (ch > ' ')
220 {
221 await this.ToError();
222 return false;
223 }
224 break;
225
226 case 4: // Waiting for >
227 this.fragment.Append(ch);
228 if (++this.fragmentLength > MaxFragmentSize)
229 {
230 await this.ToError();
231 return false;
232 }
233 else if (ch == '>')
234 {
235 this.inputState++;
236 this.inputDepth = 1;
237 await this.ProcessStream(this.fragment.ToString());
238 this.fragment.Clear();
239 this.fragmentLength = 0;
240 }
241 break;
242
243 case 5: // Waiting for start element.
244 if (ch == '<')
245 {
246 this.fragment.Append(ch);
247 if (++this.fragmentLength > MaxFragmentSize)
248 {
249 await this.ToError();
250 return false;
251 }
252 else
253 this.inputState++;
254 }
255 else if (this.inputDepth > 1)
256 {
257 this.fragment.Append(ch);
258 if (++this.fragmentLength > MaxFragmentSize)
259 {
260 await this.ToError();
261 return false;
262 }
263 }
264 else if (ch > ' ')
265 {
266 await this.ToError();
267 return false;
268 }
269 break;
270
271 case 6: // Second character in tag
272 this.fragment.Append(ch);
273 if (++this.fragmentLength > MaxFragmentSize)
274 {
275 await this.ToError();
276 return false;
277 }
278 else if (ch == '/')
279 this.inputState++;
280 else if (ch == '!')
281 this.inputState = 13;
282 else
283 this.inputState += 2;
284 break;
285
286 case 7: // Waiting for end of closing tag
287 this.fragment.Append(ch);
288 if (++this.fragmentLength > MaxFragmentSize)
289 {
290 await this.ToError();
291 return false;
292 }
293 else if (ch == '>')
294 {
295 this.inputDepth--;
296 if (this.inputDepth < 1)
297 {
298 await this.ToError();
299 return false;
300 }
301 else
302 {
303 if (this.inputDepth == 1)
304 {
305 if (!await this.ProcessFragment(this.fragment.ToString()))
306 Result = false;
307
308 this.fragment.Clear();
309 this.fragmentLength = 0;
310 }
311
312 if (this.inputState > 0)
313 this.inputState = 5;
314 }
315 }
316 break;
317
318 case 8: // Wait for end of start tag
319 this.fragment.Append(ch);
320 if (++this.fragmentLength > MaxFragmentSize)
321 {
322 await this.ToError();
323 return false;
324 }
325 else if (ch == '>')
326 {
327 this.inputDepth++;
328 this.inputState = 5;
329 }
330 else if (ch == '/')
331 this.inputState++;
332 else if (ch <= ' ')
333 this.inputState += 2;
334 break;
335
336 case 9: // Check for end of childless tag.
337 this.fragment.Append(ch);
338 if (++this.fragmentLength > MaxFragmentSize)
339 {
340 await this.ToError();
341 return false;
342 }
343 else if (ch == '>')
344 {
345 if (this.inputDepth == 1)
346 {
347 if (!await this.ProcessFragment(this.fragment.ToString()))
348 Result = false;
349
350 this.fragment.Clear();
351 this.fragmentLength = 0;
352 }
353
354 if (this.inputState != 0)
355 this.inputState = 5;
356 }
357 else
358 this.inputState--;
359 break;
360
361 case 10: // Check for attributes.
362 this.fragment.Append(ch);
363 if (++this.fragmentLength > MaxFragmentSize)
364 {
365 await this.ToError();
366 return false;
367 }
368 else if (ch == '>')
369 {
370 this.inputDepth++;
371 this.inputState = 5;
372 }
373 else if (ch == '/')
374 this.inputState--;
375 else if (ch == '"')
376 this.inputState++;
377 else if (ch == '\'')
378 this.inputState += 2;
379 break;
380
381 case 11: // Double quote attribute.
382 this.fragment.Append(ch);
383 if (++this.fragmentLength > MaxFragmentSize)
384 {
385 await this.ToError();
386 return false;
387 }
388 else if (ch == '"')
389 this.inputState--;
390 break;
391
392 case 12: // Single quote attribute.
393 this.fragment.Append(ch);
394 if (++this.fragmentLength > MaxFragmentSize)
395 {
396 await this.ToError();
397 return false;
398 }
399 else if (ch == '\'')
400 this.inputState -= 2;
401 break;
402
403 case 13: // Third character in start of comment
404 this.fragment.Append(ch);
405 if (++this.fragmentLength > MaxFragmentSize)
406 {
407 await this.ToError();
408 return false;
409 }
410 else if (ch == '-')
411 this.inputState++;
412 else if (ch == '[')
413 this.inputState = 18;
414 else
415 {
416 await this.ToError();
417 return false;
418 }
419 break;
420
421 case 14: // Fourth character in start of comment
422 this.fragment.Append(ch);
423 if (++this.fragmentLength > MaxFragmentSize)
424 {
425 await this.ToError();
426 return false;
427 }
428 else if (ch == '-')
429 this.inputState++;
430 else
431 {
432 await this.ToError();
433 return false;
434 }
435 break;
436
437 case 15: // In comment
438 this.fragment.Append(ch);
439 if (++this.fragmentLength > MaxFragmentSize)
440 {
441 await this.ToError();
442 return false;
443 }
444 else if (ch == '-')
445 this.inputState++;
446 break;
447
448 case 16: // Second character in end of comment
449 this.fragment.Append(ch);
450 if (++this.fragmentLength > MaxFragmentSize)
451 {
452 await this.ToError();
453 return false;
454 }
455 else if (ch == '-')
456 this.inputState++;
457 else
458 this.inputState--;
459 break;
460
461 case 17: // Third character in end of comment
462 this.fragment.Append(ch);
463 if (++this.fragmentLength > MaxFragmentSize)
464 {
465 await this.ToError();
466 return false;
467 }
468 else if (ch == '>')
469 this.inputState = 5;
470 else
471 this.inputState -= 2;
472 break;
473
474 case 18: // Fourth character in start of CDATA
475 this.fragment.Append(ch);
476 if (++this.fragmentLength > MaxFragmentSize)
477 {
478 await this.ToError();
479 return false;
480 }
481 else if (ch == 'C')
482 this.inputState++;
483 else
484 {
485 await this.ToError();
486 return false;
487 }
488 break;
489
490 case 19: // Fifth character in start of CDATA
491 this.fragment.Append(ch);
492 if (++this.fragmentLength > MaxFragmentSize)
493 {
494 await this.ToError();
495 return false;
496 }
497 else if (ch == 'D')
498 this.inputState++;
499 else
500 {
501 await this.ToError();
502 return false;
503 }
504 break;
505
506 case 20: // Sixth character in start of CDATA
507 this.fragment.Append(ch);
508 if (++this.fragmentLength > MaxFragmentSize)
509 {
510 await this.ToError();
511 return false;
512 }
513 else if (ch == 'A')
514 this.inputState++;
515 else
516 {
517 await this.ToError();
518 return false;
519 }
520 break;
521
522 case 21: // Seventh character in start of CDATA
523 this.fragment.Append(ch);
524 if (++this.fragmentLength > MaxFragmentSize)
525 {
526 await this.ToError();
527 return false;
528 }
529 else if (ch == 'T')
530 this.inputState++;
531 else
532 {
533 await this.ToError();
534 return false;
535 }
536 break;
537
538 case 22: // Eighth character in start of CDATA
539 this.fragment.Append(ch);
540 if (++this.fragmentLength > MaxFragmentSize)
541 {
542 await this.ToError();
543 return false;
544 }
545 else if (ch == 'A')
546 this.inputState++;
547 else
548 {
549 await this.ToError();
550 return false;
551 }
552 break;
553
554 case 23: // Ninth character in start of CDATA
555 this.fragment.Append(ch);
556 if (++this.fragmentLength > MaxFragmentSize)
557 {
558 await this.ToError();
559 return false;
560 }
561 else if (ch == '[')
562 this.inputState++;
563 else
564 {
565 await this.ToError();
566 return false;
567 }
568 break;
569
570 case 24: // In CDATA
571 this.fragment.Append(ch);
572 if (++this.fragmentLength > MaxFragmentSize)
573 {
574 await this.ToError();
575 return false;
576 }
577 else if (ch == ']')
578 this.inputState++;
579 break;
580
581 case 25: // Second character in end of CDATA
582 this.fragment.Append(ch);
583 if (++this.fragmentLength > MaxFragmentSize)
584 {
585 await this.ToError();
586 return false;
587 }
588 else if (ch == ']')
589 this.inputState++;
590 else
591 this.inputState--;
592 break;
593
594 case 26: // Third character in end of CDATA
595 this.fragment.Append(ch);
596 if (++this.fragmentLength > MaxFragmentSize)
597 {
598 await this.ToError();
599 return false;
600 }
601 else if (ch == '>')
602 this.inputState = 5;
603 else if (ch != ']')
604 this.inputState -= 2;
605 break;
606
607 default:
608 break;
609 }
610 }
611
612 return Result;
613 }
614
615 private async Task ToError()
616 {
617 this.inputState = -1;
618 this.state = XmppState.Error;
619
620 this.CallCallbacks();
621
622 if (!(this.peer is null))
623 {
624 await this.peer.DisposeAsync();
625 this.peer = null;
626 }
627 }
628
629 private async Task ProcessStream(string Xml)
630 {
631 try
632 {
633 int i = Xml.IndexOf("?>");
634 if (i >= 0)
635 Xml = Xml.Substring(i + 2).TrimStart();
636
637 this.streamHeader = Xml;
638
639 i = Xml.IndexOf(":stream");
640 if (i < 0)
641 this.streamFooter = "</stream>";
642 else
643 this.streamFooter = "</" + Xml.Substring(1, i - 1) + ":stream>";
644
645 XmlDocument Doc = new XmlDocument()
646 {
647 PreserveWhitespace = true
648 };
649 Doc.LoadXml(Xml + this.streamFooter);
650
651 if (Doc.DocumentElement.LocalName != "stream")
652 throw new XmppException("Invalid stream.", Doc.DocumentElement);
653
654 XmlElement Stream = Doc.DocumentElement;
655
656 this.version = XML.Attribute(Stream, "version", 0.0);
657 this.streamId = XML.Attribute(Stream, "id");
658 this.remoteFullJid = XML.Attribute(Stream, "from");
659
660 if (this.version < 1.0)
661 throw new XmppException("Version not supported.", Stream);
662
663 if (this.parentFullJid != XML.Attribute(Stream, "to"))
664 throw new XmppException("Invalid destination JID.", Stream);
665
666 this.state = XmppState.Authenticating;
667
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'>";
670
671 try
672 {
673 this.parent.AuthenticatePeer(this.peer, this.remoteFullJid);
674 }
675 catch (Exception ex)
676 {
677 await this.parent.Exception(ex);
678
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>";
682
683 this.headerSent = true;
684 await this.SendAsync(Header, (Sender, e) =>
685 {
686 return this.ToError();
687 }, null);
688
689 return;
690 }
691
692 if (!this.headerSent)
693 {
694 this.headerSent = true;
695 await this.SendAsync(Header);
696 }
697
698 this.state = XmppState.Connected;
699 this.xmppClient = new XmppClient(this, this.state, Header, "</stream:stream>", this.parentFullJid,
700 typeof(XmppServerlessMessaging).GetTypeInfo().Assembly)
701 {
702 SendFromAddress = true
703 };
704
705 await this.parent.PeerAuthenticated(this);
706 await this.parent.NewXmppClient(this.xmppClient, this.parentFullJid, this.remoteFullJid);
707
708 this.xmppClient.OnStateChanged += this.XmppClient_OnStateChanged;
709
710 this.CallCallbacks();
711 }
712 catch (Exception ex)
713 {
714 await this.parent.Exception(ex);
715 await this.ToError();
716 }
717 }
718
719 private async Task XmppClient_OnStateChanged(object _, XmppState NewState)
720 {
721 this.state = NewState;
722
723 if (NewState == XmppState.Connected)
724 this.CallCallbacks();
725 else if (NewState == XmppState.Error || NewState == XmppState.Offline)
726 {
727 this.parent?.PeerClosed(this);
728
729 if (!(this.xmppClient is null))
730 {
731 await this.xmppClient.DisposeAsync();
732 this.xmppClient = null;
733 }
734
735 this.CallCallbacks();
736 }
737 }
738
743 {
744 get
745 {
746 if (!(this.xmppClient is null))
747 return this.xmppClient.State;
748 else
749 return this.state;
750 }
751 }
752
753 internal bool HeaderSent
754 {
755 get => this.headerSent;
756 set => this.headerSent = value;
757 }
758
763 {
764 get => this.peer;
765 internal set
766 {
767 this.RemoveHandlers();
768 this.peer = value;
769 this.AddPeerHandlers();
770 }
771 }
772
776 public XmppClient XmppClient => this.xmppClient;
777
781 public XmppServerlessMessaging Parent => this.parent;
782
786 public string RemoteFullJid => this.remoteFullJid;
787
788 internal bool HasCallbacks
789 {
790 get { return !(this.callbacks is null); }
791 }
792
793 internal void ClearCallbacks()
794 {
795 this.callbacks = null;
796 }
797
798 internal void CallCallbacks()
799 {
800 if (!(this.callbacks is null))
801 {
802 foreach (KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>, object> P in this.callbacks)
803 {
804 try
805 {
806 P.Key(this, new PeerConnectionEventArgs(this.xmppClient, P.Value, this.parentFullJid, this.remoteFullJid));
807 }
808 catch (Exception ex)
809 {
810 Log.Exception(ex);
811 }
812 }
813
814 this.callbacks = null;
815 }
816 }
817
818 private async Task<bool> ProcessFragment(string Xml)
819 {
820 bool Result;
822
823 if (h is null)
824 Result = false;
825 else
826 {
827 try
828 {
829 Result = await h(this, Xml);
830 }
831 catch (Exception ex)
832 {
833 Log.Exception(ex);
834 Result = false;
835 }
836
837 //if (Result && !(this.callbacks is null))
838 // this.CallCallbacks();
839 }
840
841 return Result;
842 }
843
844 private async Task Peer_OnClosed(object Sender, EventArgs e)
845 {
846 await this.parent.PeerClosed(this);
847 this.parent = null;
848 this.peer = null;
849
850 if (!(this.callbacks is null))
851 this.CallCallbacks();
852 }
853
857 public async Task Close()
858 {
859 if (!(this.peer is null))
860 {
861 try
862 {
863 await this.peer.DisposeAsync();
864 }
865 catch (Exception)
866 {
867 // Ignore.
868 }
869
870 this.peer = null;
871 }
872
873 if (!(this.xmppClient is null))
874 {
875 try
876 {
877 await this.xmppClient.OfflineAndDisposeAsync(true);
878 }
879 catch (Exception)
880 {
881 // Ignore.
882 }
883
884 this.xmppClient = null;
885 }
886 }
887
888 private async Task Peer_OnSent(object Sender, byte[] Buffer, int Offset, int Count)
889 {
890 TextEventHandler h = this.OnSent;
891 if (!(h is null))
892 {
893 try
894 {
895 string s = this.encoding.GetString(Buffer, Offset, Count);
896 await h(this, s);
897 }
898 catch (Exception ex)
899 {
900 Log.Exception(ex);
901 }
902 }
903 }
904
909 public Task<bool> SendAsync(string Packet)
910 {
911 return this.SendAsync(Packet, null, null);
912 }
913
920 public Task<bool> SendAsync(string Packet, EventHandlerAsync<DeliveryEventArgs> Callback, object State)
921 {
922 byte[] Data = this.encoding.GetBytes(Packet);
923
924 if (!(this.peer is null))
925 {
926 this.peer.SendTcp(Data, Callback, State);
927 this.lastActivity = DateTime.Now;
928 }
929
930 return Task.FromResult(true);
931 }
932
936 [Obsolete("Use DisposeAsync()")]
937 public async void Dispose()
938 {
939 try
940 {
941 await this.DisposeAsync();
942 }
943 catch (Exception ex)
944 {
945 Log.Exception(ex);
946 }
947 }
948
952 public Task DisposeAsync()
953 {
954 return this.Close();
955 }
956
960 public double AgeSeconds
961 {
962 get
963 {
964 return (DateTime.Now - this.lastActivity).TotalSeconds;
965 }
966 }
967 }
968}
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
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.
Definition: Log.cs:1647
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
Task ReceiveText(string Text)
Called when text has been received.
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...
Peer connection state.
Definition: PeerState.cs:17
async Task< bool > Peer_OnReceived(object Sender, byte[] Buffer, int Offset, int Count)
Data received from a peer.
Definition: PeerState.cs:142
PeerState(PeerConnection Peer, XmppServerlessMessaging Parent, string RemoteFullJID, string StreamHeader, string StreamFooter, string StreamId, double Version, EventHandlerAsync< PeerConnectionEventArgs > Callback, object State)
Peer connection state.
Definition: PeerState.cs:75
Task DisposeAsync()
IDisposable.Dispose
Definition: PeerState.cs:952
string RemoteFullJid
Remote Full JID
Definition: PeerState.cs:786
XmppClient XmppClient
XMPP client.
Definition: PeerState.cs:776
PeerConnection Peer
Peer-to-peer connection object.
Definition: PeerState.cs:763
Task< bool > SendAsync(string Packet, EventHandlerAsync< DeliveryEventArgs > Callback, object State)
Sends a packet.
Definition: PeerState.cs:920
async void Dispose()
IDisposable.Dispose
Definition: PeerState.cs:937
bool Paused
If reading has been paused.
Definition: PeerState.cs:114
Task< bool > SendAsync(string Packet)
Sends a packet.
Definition: PeerState.cs:909
TextEventHandler OnReceived
Event raised when a text packet (XML fragment) has been received.
Definition: PeerState.cs:47
async Task Close()
CLoses the connection.
Definition: PeerState.cs:857
TextEventHandler OnSent
Event raised when a text packet has been sent.
Definition: PeerState.cs:42
XmppState State
Current connection state.
Definition: PeerState.cs:743
PeerState(PeerConnection Peer, XmppServerlessMessaging Parent)
Peer connection state.
Definition: PeerState.cs:54
void Continue()
Continues a paused connection.
Definition: PeerState.cs:119
double AgeSeconds
Seconds since object was active.
Definition: PeerState.cs:961
XmppServerlessMessaging Parent
Parent object.
Definition: PeerState.cs:781
Class managing peer-to-peer serveless XMPP communication.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:59
XmppState State
Current state of connection.
Definition: XmppClient.cs:985
Task OfflineAndDisposeAsync()
Sends an offline presence, and then disposes the object by calling DisposeAsync.
Definition: XmppClient.cs:1119
async Task DisposeAsync()
Closes the connection and disposes of all resources.
Definition: XmppClient.cs:1164
Interface for text transport layers.
XmppState
State of XMPP connection.
Definition: XmppState.cs:7
delegate Task< bool > TextEventHandler(object Sender, string Text)
Event handler for text packet events.