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;
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 this.callbacks ??= new LinkedList<KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>, object>>();
96 this.callbacks.AddLast(new KeyValuePair<EventHandlerAsync<PeerConnectionEventArgs>, object>(Callback, State));
97 }
98
99 private void AddPeerHandlers()
100 {
101 if (!(this.peer is null))
102 {
103 this.peer.OnSent += this.Peer_OnSent;
104 this.peer.OnReceived += this.Peer_OnReceived;
105 this.peer.OnClosed += this.Peer_OnClosed;
106 }
107 }
108
112 public bool Paused => this.peer.Paused;
113
117 public void Continue()
118 {
119 this.peer.Continue();
120 }
121
122 private void RemoveHandlers()
123 {
124 if (!(this.peer is null))
125 {
126 this.peer.OnSent -= this.Peer_OnSent;
127 this.peer.OnReceived -= this.Peer_OnReceived;
128 this.peer.OnClosed -= this.Peer_OnClosed;
129 }
130 }
131
142 public async Task<bool> Peer_OnReceived(object Sender, bool ConstantBuffer, 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 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 await 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[(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[1..i] + ":stream>";
644
645 XmlDocument Doc = XML.ParseXml(Xml + this.streamFooter, true);
646
647 if (Doc.DocumentElement.LocalName != "stream")
648 throw new XmppException("Invalid stream.", Doc.DocumentElement);
649
650 XmlElement Stream = Doc.DocumentElement;
651
652 this.version = XML.Attribute(Stream, "version", 0.0);
653 this.streamId = XML.Attribute(Stream, "id");
654 this.remoteFullJid = XML.Attribute(Stream, "from");
655
656 if (this.version < 1.0)
657 throw new XmppException("Version not supported.", Stream);
658
659 if (this.parentFullJid != XML.Attribute(Stream, "to"))
660 throw new XmppException("Invalid destination JID.", Stream);
661
662 this.state = XmppState.Authenticating;
663
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'>";
666
667 string Error;
668
669 try
670 {
671 Error = this.parent.AuthenticatePeer(this.peer, this.remoteFullJid);
672 }
673 catch (Exception ex)
674 {
675 this.parent.Exception(ex);
676 Error = ex.Message;
677 }
678
679 if (!string.IsNullOrEmpty(Error))
680 {
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>";
684
685 this.headerSent = true;
686 await this.SendAsync(Header, (Sender, e) =>
687 {
688 return this.ToError();
689 }, null);
690
691 return;
692 }
693
694 if (!this.headerSent)
695 {
696 this.headerSent = true;
697 await this.SendAsync(Header);
698 }
699
700 this.state = XmppState.Connected;
701 this.xmppClient = new XmppClient(this, this.state, Header, "</stream:stream>", this.parentFullJid,
702 typeof(XmppServerlessMessaging).Assembly)
703 {
704 SendFromAddress = true,
705 EnforceE2ee = true
706 };
707
708 this.parent.PeerAuthenticated(this);
709 await this.parent.NewXmppClient(this.xmppClient, this.parentFullJid, this.remoteFullJid);
710
711 this.xmppClient.OnStateChanged += this.XmppClient_OnStateChanged;
712
713 await this.CallCallbacks();
714 }
715 catch (Exception ex)
716 {
717 this.parent.Exception(ex);
718 await this.ToError();
719 }
720 }
721
722 private async Task XmppClient_OnStateChanged(object _, XmppState NewState)
723 {
724 this.state = NewState;
725
726 if (NewState == XmppState.Connected)
727 await this.CallCallbacks();
728 else if (NewState == XmppState.Error || NewState == XmppState.Offline)
729 {
730 this.parent?.PeerClosed(this);
731
732 if (!(this.xmppClient is null))
733 {
734 await this.xmppClient.DisposeAsync();
735 this.xmppClient = null;
736 }
737
738 await this.CallCallbacks();
739 }
740 }
741
746 {
747 get
748 {
749 if (!(this.xmppClient is null))
750 return this.xmppClient.State;
751 else
752 return this.state;
753 }
754 }
755
756 internal bool HeaderSent
757 {
758 get => this.headerSent;
759 set => this.headerSent = value;
760 }
761
766 {
767 get => this.peer;
768 internal set
769 {
770 this.RemoveHandlers();
771 this.peer = value;
772 this.AddPeerHandlers();
773 }
774 }
775
779 public XmppClient XmppClient => this.xmppClient;
780
784 public XmppServerlessMessaging Parent => this.parent;
785
789 public string RemoteFullJid => this.remoteFullJid;
790
791 internal bool HasCallbacks
792 {
793 get { return !(this.callbacks is null); }
794 }
795
796 internal void ClearCallbacks()
797 {
798 this.callbacks = null;
799 }
800
801 internal async Task CallCallbacks()
802 {
803 if (!(this.callbacks is null))
804 {
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));
807
808 this.callbacks = null;
809 }
810 }
811
812 private async Task<bool> ProcessFragment(string Xml)
813 {
814 bool Result;
816
817 if (h is null)
818 Result = false;
819 else
820 {
821 try
822 {
823 Result = await h(this, Xml);
824 }
825 catch (Exception ex)
826 {
827 Log.Exception(ex);
828 Result = false;
829 }
830
831 //if (Result && !(this.callbacks is null))
832 // this.CallCallbacks();
833 }
834
835 return Result;
836 }
837
838 private Task Peer_OnClosed(object Sender, EventArgs e)
839 {
840 this.parent.PeerClosed(this);
841 this.parent = null;
842 this.peer = null;
843
844 if (!(this.callbacks is null))
845 return this.CallCallbacks();
846 else
847 return Task.CompletedTask;
848 }
849
853 public async Task Close()
854 {
855 if (!(this.peer is null))
856 {
857 try
858 {
859 await this.peer.DisposeAsync();
860 }
861 catch (Exception)
862 {
863 // Ignore.
864 }
865
866 this.peer = null;
867 }
868
869 if (!(this.xmppClient is null))
870 {
871 try
872 {
873 await this.xmppClient.OfflineAndDisposeAsync(true);
874 }
875 catch (Exception)
876 {
877 // Ignore.
878 }
879
880 this.xmppClient = null;
881 }
882 }
883
884 private async Task Peer_OnSent(object Sender, bool ConstantBuffer, byte[] Buffer, int Offset, int Count)
885 {
886 TextEventHandler h = this.OnSent;
887 if (!(h is null))
888 {
889 try
890 {
891 string s = this.encoding.GetString(Buffer, Offset, Count);
892 await h(this, s);
893 }
894 catch (Exception ex)
895 {
896 Log.Exception(ex);
897 }
898 }
899 }
900
905 public Task<bool> SendAsync(string Packet)
906 {
907 return this.SendAsync(Packet, null, null);
908 }
909
916 public Task<bool> SendAsync(string Packet, EventHandlerAsync<DeliveryEventArgs> Callback, object State)
917 {
918 byte[] Data = this.encoding.GetBytes(Packet);
919
920 if (!(this.peer is null))
921 {
922 this.peer.SendTcp(true, Data, Callback, State);
923 this.lastActivity = DateTime.Now;
924 }
925
926 return Task.FromResult(true);
927 }
928
932 [Obsolete("Use DisposeAsync()")]
933 public void Dispose()
934 {
935 this.DisposeAsync().Wait();
936 }
937
941 public Task DisposeAsync()
942 {
943 return this.Close();
944 }
945
949 public double AgeSeconds
950 {
951 get
952 {
953 return (DateTime.Now - this.lastActivity).TotalSeconds;
954 }
955 }
956 }
957}
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:1062
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:29
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Definition: XML.cs:713
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
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:1657
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
void 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...
async Task DisposeAsync()
IDisposable.Dispose
Peer connection state.
Definition: PeerState.cs:17
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:941
string RemoteFullJid
Remote Full JID
Definition: PeerState.cs:789
XmppClient XmppClient
XMPP client.
Definition: PeerState.cs:779
PeerConnection Peer
Peer-to-peer connection object.
Definition: PeerState.cs:766
Task< bool > SendAsync(string Packet, EventHandlerAsync< DeliveryEventArgs > Callback, object State)
Sends a packet.
Definition: PeerState.cs:916
bool Paused
If reading has been paused.
Definition: PeerState.cs:112
async Task< bool > Peer_OnReceived(object Sender, bool ConstantBuffer, byte[] Buffer, int Offset, int Count)
Data received from a peer.
Definition: PeerState.cs:142
void Dispose()
IDisposable.Dispose
Definition: PeerState.cs:933
Task< bool > SendAsync(string Packet)
Sends a packet.
Definition: PeerState.cs:905
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:853
TextEventHandler OnSent
Event raised when a text packet has been sent.
Definition: PeerState.cs:42
XmppState State
Current connection state.
Definition: PeerState.cs:746
PeerState(PeerConnection Peer, XmppServerlessMessaging Parent)
Peer connection state.
Definition: PeerState.cs:54
void Continue()
Continues a paused connection.
Definition: PeerState.cs:117
double AgeSeconds
Seconds since object was active.
Definition: PeerState.cs:950
XmppServerlessMessaging Parent
Parent object.
Definition: PeerState.cs:784
Class managing peer-to-peer serveless XMPP communication.
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:58
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:1107
async Task DisposeAsync()
Closes the connection and disposes of all resources.
Definition: XmppClient.cs:1145
Interface for text transport layers.
Definition: ImplTypes.g.cs:58
class Header(ISimulationNode Parent, Model Model)
Represents an identity property.
Definition: Header.cs:18
XmppState
State of XMPP connection.
Definition: XmppState.cs:7
delegate Task< bool > TextEventHandler(object Sender, string Text)
Event handler for text packet events.