Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
XmppBroker.cs
1using System;
2using System.Text.RegularExpressions;
3using System.Threading.Tasks;
4using Waher.Events;
7
9{
13 public class XmppBroker : IDisposable
14 {
15 private readonly XmppBrokerNode node;
16 private XmppClient xmppClient;
17 private readonly string host;
18 private readonly int port;
19 private readonly bool tls;
20 private readonly string userName;
21 private readonly string password;
22 private readonly string passwordMechanism;
23 private readonly bool trustServer = false;
24 private readonly bool allowInsecureMechanisms = false;
25
38 private XmppBroker(XmppBrokerNode Node, string Host, int Port, bool Tls, string UserName, string Password,
39 string PasswordMechanism, bool TrustServer, bool AllowInsecureMechanisms)
40 {
41 this.node = Node;
42 this.host = Host;
43 this.port = Port;
44 this.tls = Tls;
45 this.userName = UserName;
46 this.password = Password;
47 this.passwordMechanism = PasswordMechanism;
48 this.trustServer = TrustServer;
49 this.allowInsecureMechanisms = AllowInsecureMechanisms;
50 }
51
65 public static async Task<XmppBroker> Create(XmppBrokerNode Node, string Host, int Port, bool Tls, string UserName, string Password,
66 string PasswordMechanism, bool TrustServer, bool AllowInsecureMechanisms)
67 {
68 XmppBroker Result = new XmppBroker(Node, Host, Port, Tls, UserName, Password, PasswordMechanism, TrustServer, AllowInsecureMechanisms);
69
70 await Result.Open();
71
72 return Result;
73 }
74
75 internal XmppClient Client => this.xmppClient;
76
77 private async Task Open()
78 {
79 if (string.IsNullOrEmpty(this.passwordMechanism))
80 {
81 this.xmppClient = new XmppClient(this.host, this.port, this.userName, this.password, "en",
82 typeof(XmppBroker).Assembly);
83 }
84 else
85 {
86 this.xmppClient = new XmppClient(this.host, this.port, this.userName, this.password, this.passwordMechanism, "en",
87 typeof(XmppBroker).Assembly);
88 }
89
90 this.xmppClient.TrustServer = this.trustServer;
91 this.xmppClient.AllowEncryption = this.tls;
92 this.xmppClient.AllowCramMD5 = this.allowInsecureMechanisms;
93 this.xmppClient.AllowDigestMD5 = this.allowInsecureMechanisms;
94 this.xmppClient.AllowPlain = this.allowInsecureMechanisms;
95 this.xmppClient.AllowScramSHA1 = this.allowInsecureMechanisms;
96
97 this.xmppClient.OnStateChanged += this.XmppClient_OnStateChanged;
98 this.xmppClient.OnPresence += this.XmppClient_OnPresence;
99 this.xmppClient.OnPresenceSubscribe += this.XmppClient_OnPresenceSubscribe;
100 this.xmppClient.OnPresenceSubscribed += this.XmppClient_OnPresenceSubscribed;
101 this.xmppClient.OnPresenceUnsubscribe += this.XmppClient_OnPresenceUnsubscribe;
102 this.xmppClient.OnPresenceUnsubscribed += this.XmppClient_OnPresenceUnsubscribed;
103 this.xmppClient.OnRosterItemAdded += this.XmppClient_OnRosterItemAdded;
104 this.xmppClient.OnRosterItemRemoved += this.XmppClient_OnRosterItemRemoved;
105 this.xmppClient.OnRosterItemUpdated += this.XmppClient_OnRosterItemUpdated;
106
107 foreach (INode Node in await this.node.ChildNodes)
108 {
109 if (Node is XmppExtensionNode Extension &&
110 !Extension.IsRegisteredExtension(this.xmppClient))
111 {
112 await Extension.RegisterExtension(this.xmppClient);
113 }
114 }
115
116 await this.xmppClient.Connect();
117 }
118
119 private async Task Close()
120 {
121 if (!(this.xmppClient is null))
122 {
123 foreach (INode Node in await this.node.ChildNodes)
124 {
125 if (Node is XmppExtensionNode Extension &&
126 Extension.IsRegisteredExtension(this.xmppClient))
127 {
128 await Extension.UnregisterExtension(this.xmppClient);
129 }
130 }
131
132 this.xmppClient.OnStateChanged -= this.XmppClient_OnStateChanged;
133 this.xmppClient.OnPresence -= this.XmppClient_OnPresence;
134 this.xmppClient.OnPresenceSubscribe -= this.XmppClient_OnPresenceSubscribe;
135 this.xmppClient.OnPresenceSubscribed -= this.XmppClient_OnPresenceSubscribed;
136 this.xmppClient.OnPresenceUnsubscribe -= this.XmppClient_OnPresenceUnsubscribe;
137 this.xmppClient.OnPresenceUnsubscribed -= this.XmppClient_OnPresenceUnsubscribed;
138 this.xmppClient.OnRosterItemAdded -= this.XmppClient_OnRosterItemAdded;
139 this.xmppClient.OnRosterItemRemoved -= this.XmppClient_OnRosterItemRemoved;
140 this.xmppClient.OnRosterItemUpdated -= this.XmppClient_OnRosterItemUpdated;
141
142 await this.xmppClient.OfflineAndDisposeAsync();
143 this.xmppClient = null;
144 }
145 }
146
150 public void Dispose()
151 {
152 Task.Run(() => this.Close());
153 }
154
155 private async Task XmppClient_OnStateChanged(object Sender, XmppState NewState)
156 {
157 try
158 {
159 switch (NewState)
160 {
161 case XmppState.Connected:
162 await this.node.RemoveErrorAsync("Offline");
163 await this.node.RemoveErrorAsync("Error");
164 break;
165
166 case XmppState.Error:
167 await this.node.LogErrorAsync("Error", "Connection to broker failed.");
168 break;
169
170 case XmppState.Offline:
171 await this.node.LogErrorAsync("Offline", "Connection is closed.");
172 break;
173 }
174 }
175 catch (Exception ex)
176 {
177 Log.Exception(ex);
178 }
179 }
180
181 private async Task XmppClient_OnPresenceSubscribe(object Sender, PresenceEventArgs e)
182 {
183 if (this.node is null)
184 {
185 await e.Decline();
186 return;
187 }
188
189 RosterItemNode RosterItem = await this.node.GetRosterItem(e.FromBareJID, false);
190
191 if (string.IsNullOrEmpty(this.node.AutoAcceptPattern))
192 {
193 await e.Decline();
194
195 if (!(RosterItem is null))
196 await RosterItem.LogInformationAsync("Presence subscription received and declined.");
197
198 return;
199 }
200
201 try
202 {
203 Regex Parsed = new Regex(this.node.AutoAcceptPattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
204 Match M = Parsed.Match(e.FromBareJID);
205
206 if (M.Success && M.Index == 0 && M.Length == e.FromBareJID.Length)
207 {
208 await e.Accept();
209
210 if (RosterItem is null)
211 RosterItem = await this.node.GetRosterItem(e.FromBareJID, true);
212
213 await RosterItem.LogInformationAsync("Presence subscription received and accepted.");
214 }
215 else
216 {
217 await e.Decline();
218
219 if (!(RosterItem is null))
220 await RosterItem.LogInformationAsync("Presence subscription received and declined.");
221 }
222 }
223 catch (Exception ex)
224 {
225 await e.Decline();
226
227 if (!(RosterItem is null))
228 await RosterItem.LogInformationAsync("Presence subscription received and declined.");
229
230 await this.node.LogErrorAsync("Unable to parse regular expression: " + ex.Message);
231 }
232 }
233
234 private async Task XmppClient_OnPresenceSubscribed(object Sender, PresenceEventArgs e)
235 {
236 if (this.node is null)
237 return;
238
239 RosterItemNode RosterItem = await this.node.GetRosterItem(e.FromBareJID, true);
240 await RosterItem.LogInformationAsync("Subscribed.");
241 }
242
243 private async Task XmppClient_OnPresenceUnsubscribe(object Sender, PresenceEventArgs e)
244 {
245 await e.Accept();
246
247 if (this.node is null)
248 return;
249
250 RosterItemNode RosterItem = await this.node.GetRosterItem(e.FromBareJID, false);
251
252 if (!(RosterItem is null))
253 {
254 await RosterItem.LogInformationAsync("Presence unsubscribed by " + e.FromBareJID);
255 await RosterItem.DestroyAsync();
256 }
257 }
258
259 private async Task XmppClient_OnPresenceUnsubscribed(object Sender, PresenceEventArgs e)
260 {
261 if (this.node is null)
262 return;
263
264 RosterItemNode RosterItem = await this.node.GetRosterItem(e.FromBareJID, false);
265
266 if (!(RosterItem is null))
267 {
268 await RosterItem.LogInformationAsync("Unsubscribed from " + e.FromBareJID);
269 await RosterItem.DestroyAsync();
270 }
271 }
272
273 private Task XmppClient_OnPresence(object Sender, PresenceEventArgs e)
274 {
275 return Task.CompletedTask;
276 }
277
278 private async Task XmppClient_OnRosterItemAdded(object Sender, RosterItem Item)
279 {
280 if (this.node is null)
281 return;
282
283 RosterItemNode Node = await this.node.GetRosterItem(Item.BareJid, true);
284 bool Changed =
285 Node.SubscriptionState != Item.State ||
286 Node.ContactName != Item.Name ||
287 Node.PendingSubscription != Item.PendingSubscription ||
288 !AreSame(Node.Groups, Item.Groups);
289
290 if (Changed)
291 {
292 Node.SubscriptionState = Item.State;
293 Node.ContactName = Item.Name;
294 Node.PendingSubscription = Item.PendingSubscription;
295 Node.Groups = Item.Groups ?? new string[0];
296
297 await Node.UpdateAsync();
298 }
299 }
300
301 private static bool AreSame(string[] A1, string[] A2)
302 {
303 if ((A1 is null) ^ (A2 is null))
304 return false;
305
306 if (A1 is null)
307 return true;
308
309 int c = A1.Length;
310 if (c != A2.Length)
311 return false;
312
313 int i;
314
315 for (i = 0; i < c; i++)
316 {
317 if (A1[i] != A2[i])
318 return false;
319 }
320
321 return true;
322 }
323
324 private Task XmppClient_OnRosterItemUpdated(object Sender, RosterItem Item)
325 {
326 return this.XmppClient_OnRosterItemAdded(Sender, Item);
327 }
328
329 private async Task XmppClient_OnRosterItemRemoved(object Sender, RosterItem Item)
330 {
331 if (this.node is null)
332 return;
333
334 RosterItemNode Node = await this.node.GetRosterItem(Item.BareJid, false);
335 if (Node is null)
336 return;
337
338 await Node.DestroyAsync();
339 }
340 }
341}
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
Event arguments for presence events.
string FromBareJID
Bare JID of resource sending the presence.
async Task Decline()
Declines a subscription or unsubscription request.
async Task Accept()
Accepts a subscription or unsubscription request.
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
SubscriptionState State
roup Current subscription state.
Definition: RosterItem.cs:268
string[] Groups
Any groups the roster item belongs to.
Definition: RosterItem.cs:186
string BareJid
Bare JID of the roster item.
Definition: RosterItem.cs:276
PendingSubscription PendingSubscription
If there's a pending unanswered presence subscription or unsubscription request made to the contact.
Definition: RosterItem.cs:291
string Name
Name of the roster item.
Definition: RosterItem.cs:282
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:59
Task OfflineAndDisposeAsync()
Sends an offline presence, and then disposes the object by calling DisposeAsync.
Definition: XmppClient.cs:1119
Task Connect()
Connects the client.
Definition: XmppClient.cs:641
Represents an XMPP broker.
Definition: XmppBroker.cs:14
static async Task< XmppBroker > Create(XmppBrokerNode Node, string Host, int Port, bool Tls, string UserName, string Password, string PasswordMechanism, bool TrustServer, bool AllowInsecureMechanisms)
Creates an XMPP broker
Definition: XmppBroker.cs:65
void Dispose()
IDisposable.Dispose
Definition: XmppBroker.cs:150
Node representing an XMPP broker.
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
Task< IEnumerable< INode > > ChildNodes
Child nodes. If no child nodes are available, null is returned.
Definition: INode.cs:140
XmppState
State of XMPP connection.
Definition: XmppState.cs:7