5using System.Security.Cryptography.X509Certificates;
7using System.Text.RegularExpressions;
8using System.Threading.Tasks;
146 return Version
switch
172 private readonly Dictionary<string, KeyValuePair<byte[], X509Certificate2>> certificates =
new Dictionary<string, KeyValuePair<byte[], X509Certificate2>>();
174 private int seqNr = 0;
189 #region Neuro-Foundation V1 handlers
191 this.
RegisterIqGetHandler(
"getToken", NamespaceProvisioningTokenNeuroFoundationV1, this.GetTokenHandler,
true);
192 this.
RegisterIqGetHandler(
"getTokenChallengeResponse", NamespaceProvisioningTokenNeuroFoundationV1, this.GetTokenChallengeResponseHandler,
false);
193 this.
RegisterIqGetHandler(
"getCertificate", NamespaceProvisioningTokenNeuroFoundationV1, this.GetCertificateHandler,
false);
195 this.
RegisterIqGetHandler(
"isFriend", NamespaceProvisioningDeviceNeuroFoundationV1, this.IsFriendHandler,
true);
196 this.
RegisterIqGetHandler(
"canRead", NamespaceProvisioningDeviceNeuroFoundationV1, this.CanReadHandler,
false);
197 this.
RegisterIqGetHandler(
"canControl", NamespaceProvisioningDeviceNeuroFoundationV1, this.CanControlHandler,
false);
199 this.
RegisterIqSetHandler(
"isFriendRule", NamespaceProvisioningOwnerNeuroFoundationV1, this.IsFriendRuleHandler,
true);
200 this.
RegisterIqSetHandler(
"canReadRule", NamespaceProvisioningOwnerNeuroFoundationV1, this.CanReadRuleHandler,
false);
201 this.
RegisterIqSetHandler(
"canControlRule", NamespaceProvisioningOwnerNeuroFoundationV1, this.CanControlRuleHandler,
false);
202 this.
RegisterIqSetHandler(
"clearCache", NamespaceProvisioningOwnerNeuroFoundationV1, this.ClearCacheHandler,
false);
203 this.
RegisterIqGetHandler(
"getDevices", NamespaceProvisioningOwnerNeuroFoundationV1, this.GetDevicesHandler,
false);
204 this.
RegisterIqSetHandler(
"deleteRules", NamespaceProvisioningOwnerNeuroFoundationV1, this.DeleteRulesHandler,
false);
206 this.
RegisterIqSetHandler(
"register", NamespaceIoTDiscoveryNeuroFoundationV1, this.RegisterHandler,
true);
207 this.
RegisterIqSetHandler(
"mine", NamespaceIoTDiscoveryNeuroFoundationV1, this.MineHandler,
false);
208 this.
RegisterIqSetHandler(
"update", NamespaceIoTDiscoveryNeuroFoundationV1, this.UpdateHandler,
false);
209 this.
RegisterIqSetHandler(
"remove", NamespaceIoTDiscoveryNeuroFoundationV1, this.RemoveHandler,
false);
210 this.
RegisterIqSetHandler(
"unregister", NamespaceIoTDiscoveryNeuroFoundationV1, this.UnregisterHandler,
false);
211 this.
RegisterIqSetHandler(
"disown", NamespaceIoTDiscoveryNeuroFoundationV1, this.DisownHandler,
false);
212 this.
RegisterIqGetHandler(
"search", NamespaceIoTDiscoveryNeuroFoundationV1, this.SearchHandler,
false);
214 this.
RegisterIqGetHandler(
"getPackageInfo", NamespaceSoftwareUpdatesNeuroFoundationV1, this.GetPackageInfoHandler,
true);
215 this.
RegisterIqGetHandler(
"getPackages", NamespaceSoftwareUpdatesNeuroFoundationV1, this.GetPackagesHandler,
false);
216 this.
RegisterIqSetHandler(
"subscribe", NamespaceSoftwareUpdatesNeuroFoundationV1, this.SubscribeHandler,
false);
217 this.
RegisterIqSetHandler(
"unsubscribe", NamespaceSoftwareUpdatesNeuroFoundationV1, this.UnsubscribeHandler,
false);
218 this.
RegisterIqGetHandler(
"getSubscriptions", NamespaceSoftwareUpdatesNeuroFoundationV1, this.GetSubscriptionsHandler,
false);
222 #region IEEE V1 handlers
224 this.
RegisterIqGetHandler(
"getToken", NamespaceProvisioningTokenIeeeV1, this.GetTokenHandler,
true);
225 this.
RegisterIqGetHandler(
"getTokenChallengeResponse", NamespaceProvisioningTokenIeeeV1, this.GetTokenChallengeResponseHandler,
false);
226 this.
RegisterIqGetHandler(
"getCertificate", NamespaceProvisioningTokenIeeeV1, this.GetCertificateHandler,
false);
228 this.
RegisterIqGetHandler(
"isFriend", NamespaceProvisioningDeviceIeeeV1, this.IsFriendHandler,
true);
229 this.
RegisterIqGetHandler(
"canRead", NamespaceProvisioningDeviceIeeeV1, this.CanReadHandler,
false);
230 this.
RegisterIqGetHandler(
"canControl", NamespaceProvisioningDeviceIeeeV1, this.CanControlHandler,
false);
232 this.
RegisterIqSetHandler(
"isFriendRule", NamespaceProvisioningOwnerIeeeV1, this.IsFriendRuleHandler,
true);
233 this.
RegisterIqSetHandler(
"canReadRule", NamespaceProvisioningOwnerIeeeV1, this.CanReadRuleHandler,
false);
234 this.
RegisterIqSetHandler(
"canControlRule", NamespaceProvisioningOwnerIeeeV1, this.CanControlRuleHandler,
false);
235 this.
RegisterIqSetHandler(
"clearCache", NamespaceProvisioningOwnerIeeeV1, this.ClearCacheHandler,
false);
236 this.
RegisterIqGetHandler(
"getDevices", NamespaceProvisioningOwnerIeeeV1, this.GetDevicesHandler,
false);
237 this.
RegisterIqSetHandler(
"deleteRules", NamespaceProvisioningOwnerIeeeV1, this.DeleteRulesHandler,
false);
243 this.
RegisterIqSetHandler(
"unregister", NamespaceIoTDiscoveryIeeeV1, this.UnregisterHandler,
false);
247 this.
RegisterIqGetHandler(
"getPackageInfo", NamespaceSoftwareUpdatesIeeeV1, this.GetPackageInfoHandler,
true);
248 this.
RegisterIqGetHandler(
"getPackages", NamespaceSoftwareUpdatesIeeeV1, this.GetPackagesHandler,
false);
249 this.
RegisterIqSetHandler(
"subscribe", NamespaceSoftwareUpdatesIeeeV1, this.SubscribeHandler,
false);
250 this.
RegisterIqSetHandler(
"unsubscribe", NamespaceSoftwareUpdatesIeeeV1, this.UnsubscribeHandler,
false);
251 this.
RegisterIqGetHandler(
"getSubscriptions", NamespaceSoftwareUpdatesIeeeV1, this.GetSubscriptionsHandler,
false);
261 this.
RegisterIqSetHandler(
"unregister", NamespaceIoTDiscoveryXsfV0, this.UnregisterHandler,
false);
273 #region Neuro-Foundation V1 handlers
275 this.
UnregisterIqGetHandler(
"getToken", NamespaceProvisioningTokenNeuroFoundationV1, this.GetTokenHandler,
true);
276 this.
UnregisterIqGetHandler(
"getTokenChallengeResponse", NamespaceProvisioningTokenNeuroFoundationV1, this.GetTokenChallengeResponseHandler,
false);
277 this.
UnregisterIqGetHandler(
"getCertificate", NamespaceProvisioningTokenNeuroFoundationV1, this.GetCertificateHandler,
false);
279 this.
UnregisterIqGetHandler(
"isFriend", NamespaceProvisioningDeviceNeuroFoundationV1, this.IsFriendHandler,
true);
280 this.
UnregisterIqGetHandler(
"canRead", NamespaceProvisioningDeviceNeuroFoundationV1, this.CanReadHandler,
false);
281 this.
UnregisterIqGetHandler(
"canControl", NamespaceProvisioningDeviceNeuroFoundationV1, this.CanControlHandler,
false);
283 this.
UnregisterIqSetHandler(
"isFriendRule", NamespaceProvisioningOwnerNeuroFoundationV1, this.IsFriendRuleHandler,
true);
284 this.
UnregisterIqSetHandler(
"canReadRule", NamespaceProvisioningOwnerNeuroFoundationV1, this.CanReadRuleHandler,
false);
285 this.
UnregisterIqSetHandler(
"canControlRule", NamespaceProvisioningOwnerNeuroFoundationV1, this.CanControlRuleHandler,
false);
286 this.
UnregisterIqSetHandler(
"clearCache", NamespaceProvisioningOwnerNeuroFoundationV1, this.ClearCacheHandler,
false);
287 this.
UnregisterIqGetHandler(
"getDevices", NamespaceProvisioningOwnerNeuroFoundationV1, this.GetDevicesHandler,
false);
288 this.
UnregisterIqSetHandler(
"deleteRules", NamespaceProvisioningOwnerNeuroFoundationV1, this.DeleteRulesHandler,
false);
290 this.
UnregisterIqSetHandler(
"register", NamespaceIoTDiscoveryNeuroFoundationV1, this.UnregisterHandler,
true);
294 this.
UnregisterIqSetHandler(
"unregister", NamespaceIoTDiscoveryNeuroFoundationV1, this.UnregisterHandler,
false);
298 this.
UnregisterIqGetHandler(
"getPackageInfo", NamespaceSoftwareUpdatesNeuroFoundationV1, this.GetPackageInfoHandler,
true);
299 this.
UnregisterIqGetHandler(
"getPackages", NamespaceSoftwareUpdatesNeuroFoundationV1, this.GetPackagesHandler,
false);
300 this.
UnregisterIqSetHandler(
"subscribe", NamespaceSoftwareUpdatesNeuroFoundationV1, this.SubscribeHandler,
false);
301 this.
UnregisterIqSetHandler(
"unsubscribe", NamespaceSoftwareUpdatesNeuroFoundationV1, this.UnsubscribeHandler,
false);
302 this.
UnregisterIqGetHandler(
"getSubscriptions", NamespaceSoftwareUpdatesNeuroFoundationV1, this.GetSubscriptionsHandler,
false);
306 #region IEEE V1 handlers
309 this.
UnregisterIqGetHandler(
"getTokenChallengeResponse", NamespaceProvisioningTokenIeeeV1, this.GetTokenChallengeResponseHandler,
false);
310 this.
UnregisterIqGetHandler(
"getCertificate", NamespaceProvisioningTokenIeeeV1, this.GetCertificateHandler,
false);
314 this.
UnregisterIqGetHandler(
"canControl", NamespaceProvisioningDeviceIeeeV1, this.CanControlHandler,
false);
316 this.
UnregisterIqSetHandler(
"isFriendRule", NamespaceProvisioningOwnerIeeeV1, this.IsFriendRuleHandler,
true);
317 this.
UnregisterIqSetHandler(
"canReadRule", NamespaceProvisioningOwnerIeeeV1, this.CanReadRuleHandler,
false);
318 this.
UnregisterIqSetHandler(
"canControlRule", NamespaceProvisioningOwnerIeeeV1, this.CanControlRuleHandler,
false);
319 this.
UnregisterIqSetHandler(
"clearCache", NamespaceProvisioningOwnerIeeeV1, this.ClearCacheHandler,
false);
320 this.
UnregisterIqGetHandler(
"getDevices", NamespaceProvisioningOwnerIeeeV1, this.GetDevicesHandler,
false);
321 this.
UnregisterIqSetHandler(
"deleteRules", NamespaceProvisioningOwnerIeeeV1, this.DeleteRulesHandler,
false);
331 this.
UnregisterIqGetHandler(
"getPackageInfo", NamespaceSoftwareUpdatesIeeeV1, this.GetPackageInfoHandler,
true);
332 this.
UnregisterIqGetHandler(
"getPackages", NamespaceSoftwareUpdatesIeeeV1, this.GetPackagesHandler,
false);
334 this.
UnregisterIqSetHandler(
"unsubscribe", NamespaceSoftwareUpdatesIeeeV1, this.UnsubscribeHandler,
false);
335 this.
UnregisterIqGetHandler(
"getSubscriptions", NamespaceSoftwareUpdatesIeeeV1, this.GetSubscriptionsHandler,
false);
352 this.tokenRequests =
null;
354 this.registryAccounts?.
Clear();
355 this.registryAccounts?.
Dispose();
356 this.registryAccounts =
null;
365 #region Provisioning, Tokens
367 private Task GetTokenHandler(
object Sender,
IqEventArgs e)
371 string Base64 = e.
Query.InnerText;
372 byte[] Bin = Convert.FromBase64String(Base64);
373 X509Certificate2 Certificate =
new X509Certificate2(Bin);
375 if (Certificate.Verify())
378 string Response = Convert.ToBase64String(Bin);
379 Bin = ((RSACryptoServiceProvider)Certificate.PublicKey.Key).Encrypt(Bin,
true);
380 string Challenge = Convert.ToBase64String(Bin);
388 SeqNr = this.seqNr++;
392 this.tokenRequests.
Add(SeqNr,
new KeyValuePair<
string, KeyValuePair<
byte[], X509Certificate2>>(Response,
393 new KeyValuePair<
byte[], X509Certificate2>(Bin, Certificate)));
396 e.
IqResult(
"<getTokenChallenge xmlns='" + e.
Query.NamespaceURI +
"' seqnr='" +
397 SeqNr.ToString() +
"'>" + Challenge +
"</getTokenChallenge>", e.
To);
407 return Task.CompletedTask;
410 private Task GetTokenChallengeResponseHandler(
object Sender,
IqEventArgs e)
412 XmlElement E = e.
Query;
415 if (!this.tokenRequests.
TryGetValue(SeqNr, out KeyValuePair<
string, KeyValuePair<
byte[], X509Certificate2>> Rec))
419 this.tokenRequests.
Remove(SeqNr);
421 string Response = E.InnerText;
422 if (Response != Rec.Key)
427 string Token = Convert.ToBase64String(Bin);
429 lock (this.certificates)
431 this.certificates[Token] = Rec.Value;
434 e.
IqResult(
"<getTokenResponse xmlns='" + e.
Query.NamespaceURI +
"' token='" + e.
To +
":" + Token +
"'/>",
439 return Task.CompletedTask;
442 private class TokenChallengesRecord
445 public Dictionary<string, bool> TokensToChallenge;
447 public List<ThingReference> Nodes =
null;
448 public List<string> Fields =
null;
450 public string[] ServiceTokens;
451 public string[] DeviceTokens;
452 public string[] UserTokens;
453 public bool Allowed =
false;
455 public int NrTokensToChallenge;
456 public bool AllOk =
true;
460 private class TokenChallengeRecord
462 public TokenChallengesRecord Challenges;
464 public string Response;
469 TokenChallengeRecord Rec2 = (TokenChallengeRecord)e.
State;
470 TokenChallengesRecord Rec = Rec2.Challenges;
474 if (e.
Ok && !(E is
null) && E.LocalName ==
"tokenChallengeResponse" && E.InnerText == Rec2.Response)
476 this.challengedTokens.
Add(e.
From +
" " + Rec2.Token,
true);
478 lock (Rec.TokensToChallenge)
480 Rec.TokensToChallenge[Rec2.Token] =
true;
481 Rec.NrTokensToChallenge--;
482 Done = Rec.NrTokensToChallenge == 0;
487 lock (Rec.TokensToChallenge)
490 Rec.NrTokensToChallenge--;
491 Done = Rec.NrTokensToChallenge == 0;
502 await this.TestAndSendCanReadResponse(Rec.Jid, Rec.e, Rec.Allowed, Rec.FieldTypes, Rec.Nodes, Rec.Fields,
503 Rec.ServiceTokens, Rec.DeviceTokens, Rec.UserTokens, Rec.QueryVersion);
510 await this.TestAndSendCanControlResponse(Rec.Jid, Rec.e, Rec.Allowed, Rec.Nodes, Rec.Fields,
511 Rec.ServiceTokens, Rec.DeviceTokens, Rec.UserTokens, Rec.QueryVersion);
516 private void AddValidTokens(Dictionary<string, bool> TokensToChallenge,
string[] Tokens,
string From)
518 foreach (
string Token
in Tokens)
520 if (!TokensToChallenge.ContainsKey(Token) && !
this.challengedTokens.ContainsKey(From +
" " + Token))
522 lock (this.certificates)
524 if (!this.certificates.ContainsKey(Token))
528 TokensToChallenge[Token] =
false;
533 private Task GetCertificateHandler(
object Sender,
IqEventArgs e)
535 KeyValuePair<byte[], X509Certificate2> Rec;
540 i = Token.IndexOf(
':');
543 string Address = Token[..i];
544 Token = Token[(i + 1)..];
549 return Task.CompletedTask;
553 lock (this.certificates)
555 Found = this.certificates.TryGetValue(Token, out Rec);
561 e.
IqResult(
"<certificate xmlns='" + e.
Query.NamespaceURI +
"'>" + Convert.ToBase64String(Rec.Key) +
"</certificate>", e.
To);
563 return Task.CompletedTask;
568 #region Provisioning, Device interface
570 private async Task IsFriendHandler(
object Sender,
IqEventArgs e)
577 Registration Registration = await this.GetRegistration(JID,
string.Empty,
string.Empty,
string.Empty, QueryVersion,
null);
586 AreFrieds =
Rule.CanSubscribeToPresence;
595 AreFrieds =
Rule.CanSubscribeToPresence;
602 AreFrieds =
Rule.CanSubscribeToPresence;
610 RemoteJID = RemoteJID,
611 CanSubscribeToPresence =
false
616 StringBuilder Xml =
new StringBuilder();
618 Xml.Append(
"<isFriend xmlns='");
620 Xml.Append(
"' jid='");
622 Xml.Append(
"' remoteJid='");
624 Xml.Append(
"' key='");
634 await e.
IqResult(
"<isFriendResponse xmlns='" + e.
Query.NamespaceURI +
"' jid='" +
XML.
Encode(RemoteJID) +
"' result='" +
638 private async Task CanReadHandler(
object Sender,
IqEventArgs e)
640 List<ThingReference> Nodes =
null;
641 List<string> Fields =
null;
642 XmlElement E = e.
Query;
644 string ServiceToken =
string.Empty;
645 string DeviceToken =
string.Empty;
646 string UserToken =
string.Empty;
651 foreach (XmlAttribute Attr
in E.Attributes)
656 ServiceToken = Attr.Value;
660 DeviceToken = Attr.Value;
664 UserToken = Attr.Value;
708 foreach (XmlNode N
in E.ChildNodes)
714 Nodes ??=
new List<ThingReference>();
715 Nodes.Add(this.ParseNodeInfo(E));
719 Fields ??=
new List<string>();
725 string[] ServiceTokens = ServiceToken.Split(space, StringSplitOptions.RemoveEmptyEntries);
726 string[] DeviceTokens = DeviceToken.Split(space, StringSplitOptions.RemoveEmptyEntries);
727 string[] UserTokens = UserToken.Split(space, StringSplitOptions.RemoveEmptyEntries);
728 Dictionary<string, bool> TokensToChallenge =
null;
729 int NrTokensToChallenge;
731 if (ServiceTokens.Length == 0 && DeviceTokens.Length == 0 && UserTokens.Length == 0)
732 NrTokensToChallenge = 0;
735 TokensToChallenge =
new Dictionary<string, bool>();
737 this.AddValidTokens(TokensToChallenge, ServiceTokens, e.
From.
Address);
738 this.AddValidTokens(TokensToChallenge, DeviceTokens, e.
From.
Address);
739 this.AddValidTokens(TokensToChallenge, UserTokens, e.
From.
Address);
741 NrTokensToChallenge = TokensToChallenge.Count;
742 if (NrTokensToChallenge > 0)
744 TokenChallengesRecord Rec =
new TokenChallengesRecord()
746 TokensToChallenge = TokensToChallenge,
749 FieldTypes = FieldTypes,
751 ServiceTokens = ServiceTokens,
752 DeviceTokens = DeviceTokens,
753 UserTokens = UserTokens,
755 NrTokensToChallenge = NrTokensToChallenge,
758 QueryVersion = QueryVersion
761 string[] Tokens =
new string[TokensToChallenge.Count];
762 TokensToChallenge.Keys.CopyTo(Tokens, 0);
764 foreach (
string Token
in Tokens)
766 KeyValuePair<byte[], X509Certificate2> Certificate;
768 lock (this.certificates)
770 Certificate = this.certificates[Token];
774 string Response = Convert.ToBase64String(Bin);
775 Bin = ((RSACryptoServiceProvider)Certificate.Value.PublicKey.Key).Encrypt(Bin,
true);
776 string Challenge = Convert.ToBase64String(Bin);
778 TokenChallengeRecord Rec2 =
new TokenChallengeRecord()
785 await this.Server.SendIqRequest(
"get", e.
To, e.
From,
string.
Empty,
786 "<tokenChallenge xmlns='" + NamespaceProvisioningToken(QueryVersion) +
"' token='" +
787 XML.
Encode(Token) +
"'>" + Challenge +
"</tokenChallenge>",
false,
this.TokenChallengeResponse, Rec2);
794 await this.TestAndSendCanReadResponse(Jid, e,
true, FieldTypes, Nodes, Fields, ServiceTokens, DeviceTokens, UserTokens, QueryVersion);
798 List<ThingReference> Nodes, List<string> Fields,
string[] ServiceTokens,
string[] DeviceTokens,
string[] UserTokens,
806 List<ThingReference> NodesAllowed =
null;
809 if (Nodes is
null || Nodes.Count == 0)
810 Nodes =
new List<ThingReference>() { ThingReference.Empty };
814 RemoteJid = RemoteJID,
815 RemoteDomain = RemoteDomain,
817 ServiceTokens = ServiceTokens,
818 DeviceTokens = DeviceTokens,
819 UserTokens = UserTokens
822 if (!(Fields is
null))
824 Context.Names =
new Dictionary<string, bool>();
826 foreach (
string Field in Fields)
849 Partition = Node.Partition
857 if (!(
Rule.Rules is
null))
871 NodesAllowed ??=
new List<ThingReference>();
872 NodesAllowed.Add(Node);
878 StringBuilder Xml =
new StringBuilder();
880 Xml.Append(
"<canRead xmlns='");
881 Xml.Append(NamespaceProvisioningOwner(
Registration.OwnerVersion));
882 Xml.Append(
"' jid='");
884 Xml.Append(
"' remoteJid='");
886 Xml.Append(
"' key='");
890 Xml.Append(
"' all='true");
893 if ((FieldTypes &
FieldType.Historical) != 0)
894 Xml.Append(
"' h='true");
896 if ((FieldTypes &
FieldType.Momentary) != 0)
897 Xml.Append(
"' m='true");
900 Xml.Append(
"' p='true");
902 if ((FieldTypes &
FieldType.Status) != 0)
903 Xml.Append(
"' s='true");
905 if ((FieldTypes &
FieldType.Computed) != 0)
906 Xml.Append(
"' c='true");
908 if ((FieldTypes &
FieldType.Identity) != 0)
909 Xml.Append(
"' i='true");
912 this.AppendTokens(Xml,
"st", ServiceTokens);
913 this.AppendTokens(Xml,
"dt", DeviceTokens);
914 this.AppendTokens(Xml,
"ut", UserTokens);
919 this.AppendNode(Xml, Node);
923 foreach (
string FieldName
in Context.Names.Keys)
925 Xml.Append(
"<f n='");
931 Xml.Append(
"</canRead>");
936 await this.SendCanReadResponse(Jid, e, !(NodesAllowed is
null),
Context.Types, NodesAllowed,
Context?.Names?.Keys, QueryVersion);
939 await this.SendCanReadResponse(Jid, e,
false, 0,
null,
null, QueryVersion);
942 private void AppendTokens(StringBuilder Xml,
string Attribute,
string[] Tokens)
944 if (!(Tokens is
null) && Tokens.Length > 0)
949 Xml.Append(Attribute);
952 foreach (
string Token
in Tokens)
965 List<ThingReference> Nodes, IEnumerable<string> Fields,
NamespaceSet Version)
967 StringBuilder Xml =
new StringBuilder();
969 Xml.Append(
"<canReadResponse xmlns='");
970 Xml.Append(NamespaceProvisioningOwner(Version));
971 Xml.Append(
"' result='");
973 Xml.Append(
"' jid='");
979 Xml.Append(
"' all='true");
982 if (FieldTypes.HasFlag(
FieldType.Momentary))
983 Xml.Append(
"' m='true");
986 Xml.Append(
"' p='true");
988 if (FieldTypes.HasFlag(
FieldType.Status))
989 Xml.Append(
"' s='true");
991 if (FieldTypes.HasFlag(
FieldType.Computed))
992 Xml.Append(
"' c='true");
994 if (FieldTypes.HasFlag(
FieldType.Identity))
995 Xml.Append(
"' i='true");
997 if (FieldTypes.HasFlag(
FieldType.Historical))
998 Xml.Append(
"' h='true");
1001 if (Nodes is
null && Fields is
null)
1007 if (!(Nodes is
null) && (Nodes.Count != 1 || !Nodes[0].IsEmpty))
1010 this.AppendNode(Xml, Node);
1013 if (!(Fields is
null))
1015 foreach (
string FieldName
in Fields)
1017 Xml.Append(
"<f n='");
1023 Xml.Append(
"</canReadResponse>");
1034 Xml.Append(
"<nd id='");
1037 if (!
string.IsNullOrEmpty(Node.
SourceId))
1039 Xml.Append(
"' src='");
1043 if (!
string.IsNullOrEmpty(Node.
Partition))
1045 Xml.Append(
"' pt='");
1052 private static readonly
char[] space =
new char[] {
' ' };
1062 lock (this.certificates)
1064 if (this.certificates.TryGetValue(Token, out KeyValuePair<
byte[], X509Certificate2> Rec))
1066 Certificate = Rec.Value;
1077 private async Task CanControlHandler(
object Sender,
IqEventArgs e)
1079 List<ThingReference> Nodes =
null;
1080 List<string> ParameterNames =
null;
1081 XmlElement E = e.
Query;
1082 string ServiceToken =
string.Empty;
1083 string DeviceToken =
string.Empty;
1084 string UserToken =
string.Empty;
1088 foreach (XmlAttribute Attr
in E.Attributes)
1093 ServiceToken = Attr.Value;
1097 DeviceToken = Attr.Value;
1101 UserToken = Attr.Value;
1110 foreach (XmlNode N
in E.ChildNodes)
1112 switch (N.LocalName)
1115 Nodes ??=
new List<ThingReference>();
1117 Nodes.Add(this.ParseNodeInfo(E));
1121 ParameterNames ??=
new List<string>();
1127 string[] ServiceTokens = ServiceToken.Split(space, StringSplitOptions.RemoveEmptyEntries);
1128 string[] DeviceTokens = DeviceToken.Split(space, StringSplitOptions.RemoveEmptyEntries);
1129 string[] UserTokens = UserToken.Split(space, StringSplitOptions.RemoveEmptyEntries);
1130 Dictionary<string, bool> TokensToChallenge =
null;
1131 int NrTokensToChallenge;
1133 if (ServiceTokens.Length == 0 && DeviceTokens.Length == 0 && UserTokens.Length == 0)
1134 NrTokensToChallenge = 0;
1137 TokensToChallenge =
new Dictionary<string, bool>();
1139 this.AddValidTokens(TokensToChallenge, ServiceTokens, e.
From.
Address);
1140 this.AddValidTokens(TokensToChallenge, DeviceTokens, e.
From.
Address);
1141 this.AddValidTokens(TokensToChallenge, UserTokens, e.
From.
Address);
1143 NrTokensToChallenge = TokensToChallenge.Count;
1144 if (NrTokensToChallenge > 0)
1146 TokenChallengesRecord Rec =
new TokenChallengesRecord()
1148 TokensToChallenge = TokensToChallenge,
1150 Fields = ParameterNames,
1152 ServiceTokens = ServiceTokens,
1153 DeviceTokens = DeviceTokens,
1154 UserTokens = UserTokens,
1156 NrTokensToChallenge = NrTokensToChallenge,
1159 QueryVersion = QueryVersion
1162 string[] Tokens =
new string[TokensToChallenge.Count];
1163 TokensToChallenge.Keys.CopyTo(Tokens, 0);
1165 foreach (
string Token
in Tokens)
1167 KeyValuePair<byte[], X509Certificate2> Certificate;
1169 lock (this.certificates)
1171 Certificate = this.certificates[Token];
1175 string Response =
System.Convert.ToBase64String(Bin);
1176 Bin = ((RSACryptoServiceProvider)Certificate.Value.PublicKey.Key).Encrypt(Bin,
true);
1177 string Challenge =
System.Convert.ToBase64String(Bin);
1179 TokenChallengeRecord Rec2 =
new TokenChallengeRecord()
1186 await this.Server.SendIqRequest(
"get", e.
To, e.
From,
string.
Empty,
1187 "<tokenChallenge xmlns='" + NamespaceProvisioningToken(QueryVersion) +
"' token='" +
1188 XML.
Encode(Token) +
"'>" + Challenge +
"</tokenChallenge>",
false,
this.TokenChallengeResponse, Rec2);
1195 await this.TestAndSendCanControlResponse(Jid, e,
true, Nodes, ParameterNames, ServiceTokens, DeviceTokens, UserTokens, QueryVersion);
1215 List<string> ParameterNames,
string[] ServiceTokens,
string[] DeviceTokens,
string[] UserTokens,
NamespaceSet QueryVersion)
1222 List<ThingReference> NodesAllowed =
null;
1225 if (Nodes is
null || Nodes.Count == 0)
1226 Nodes =
new List<ThingReference>() { ThingReference.Empty };
1230 RemoteJid = RemoteJID,
1231 RemoteDomain = RemoteDomain,
1232 ServiceTokens = ServiceTokens,
1233 DeviceTokens = DeviceTokens,
1234 UserTokens = UserTokens
1237 if (!(ParameterNames is
null))
1239 Context.Names =
new Dictionary<string, bool>();
1241 foreach (
string ParameterName
in ParameterNames)
1242 Context.Names[ParameterName] =
true;
1264 Partition = Node.Partition
1270 bool? Result =
null;
1272 if (!(
Rule.Rules is
null))
1277 if (Result.HasValue)
1282 if (Result.HasValue)
1286 NodesAllowed ??=
new List<ThingReference>();
1287 NodesAllowed.Add(Node);
1293 StringBuilder Xml =
new StringBuilder();
1295 Xml.Append(
"<canControl xmlns='");
1296 Xml.Append(NamespaceProvisioningOwner(QueryVersion));
1297 Xml.Append(
"' jid='");
1299 Xml.Append(
"' remoteJid='");
1301 Xml.Append(
"' key='");
1304 this.AppendTokens(Xml,
"st", ServiceTokens);
1305 this.AppendTokens(Xml,
"dt", DeviceTokens);
1306 this.AppendTokens(Xml,
"ut", UserTokens);
1311 this.AppendNode(Xml, Node);
1315 foreach (
string FieldName
in Context.Names.Keys)
1317 Xml.Append(
"<p n='");
1323 Xml.Append(
"</canControl>");
1328 await this.SendCanControlResponse(Jid, e, !(NodesAllowed is
null), NodesAllowed,
Context?.Names?.Keys);
1331 await this.SendCanControlResponse(Jid, e,
false,
null,
null);
1335 IEnumerable<string> ParameterNames)
1337 StringBuilder Xml =
new StringBuilder();
1339 Xml.Append(
"<canControlResponse xmlns='");
1340 Xml.Append(e.
Query.NamespaceURI);
1341 Xml.Append(
"' result='");
1343 Xml.Append(
"' jid='");
1348 if (Nodes is
null && ParameterNames is
null)
1354 if (!(Nodes is
null))
1357 this.AppendNode(Xml, Node);
1360 if (!(ParameterNames is
null))
1362 foreach (
string FieldName
in ParameterNames)
1364 Xml.Append(
"<p n='");
1370 Xml.Append(
"</canControlResponse>");
1381 #region Provisioning, Owner interface
1383 private async Task IsFriendRuleHandler(
object Sender,
IqEventArgs e)
1394 if (!Guid.TryParse(Key, out Guid ObjectId))
1407 if (
Rule.JID != JID ||
Rule.RemoteJID != RemoteJID)
1413 Registration Registration = await this.GetRegistration(JID,
string.Empty,
string.Empty,
string.Empty,
null, QueryVersion);
1421 Log.
Informational(
"Presence subscription rule changed.", JID, OwnerJid,
"SubscriptionRule",
1422 new KeyValuePair<string, object>(
"RemoteJID", RemoteJID),
1423 new KeyValuePair<string, object>(
"Allowed", AreFrieds));
1428 if (
Rule.CanSubscribeToPresence != AreFrieds)
1430 Rule.CanSubscribeToPresence = AreFrieds;
1443 if (!(
Rule is
null))
1445 if (
Rule.CanSubscribeToPresence != AreFrieds)
1447 Rule.CanSubscribeToPresence = AreFrieds;
1456 RemoteJID = RemoteDomain,
1457 CanSubscribeToPresence = AreFrieds
1470 if (!(
Rule is
null))
1472 if (
Rule.CanSubscribeToPresence != AreFrieds)
1474 Rule.CanSubscribeToPresence = AreFrieds;
1483 RemoteJID =
string.
Empty,
1484 CanSubscribeToPresence = AreFrieds
1498 await this.ClearCache(JID,
string.Empty,
string.Empty,
string.Empty,
Registration.Version);
1506 await this.RecommendBefriend(RemoteJID, JID, QueryVersion);
1508 catch (Exception ex)
1512 }, DateTime.Now.AddSeconds(5),
null);
1526 await this.Server.GetLastPresence(BareJid1, async (Sender, e) =>
1532 await this.Server.SendMessage(string.Empty, string.Empty, this.GetComponentAddress(e.To), e.From, string.Empty,
1533 "<unfriend xmlns='" + NamespaceProvisioningDevice(Version) +
"' jid='" + BareJid2 +
"'/>");
1536 catch (Exception ex)
1546 return this.MainDomain;
1560 if (ThingReferences is
null)
1561 await this.ClearCache(BareJid,
string.Empty,
string.Empty,
string.Empty, Version);
1566 await this.ClearCache(BareJid, Reference.
NodeId, Reference.
SourceId,
1581 string SourceId,
string Partition,
NamespaceSet Version)
1583 await this.Server.GetLastPresence(BareJid, async (Sender, e) =>
1587 StringBuilder Xml =
new StringBuilder();
1589 Xml.Append(
"<clearCache xmlns='");
1590 Xml.Append(NamespaceProvisioningDevice(Version));
1592 if (!
string.IsNullOrEmpty(NodeId))
1594 Xml.Append(
"' id='");
1598 if (!
string.IsNullOrEmpty(SourceId))
1600 Xml.Append(
"' src='");
1604 if (!
string.IsNullOrEmpty(Partition))
1606 Xml.Append(
"' pt='");
1613 string XmlString = Xml.ToString();
1617 await this.Server.SendIqRequest(
"set", From, e.
From,
string.
Empty, XmlString,
false, (sender2, e2) =>
1620 return this.Server.SendMessage(string.Empty, string.Empty, From, new XmppAddress(BareJid), string.Empty, XmlString);
1622 return Task.CompletedTask;
1626 await this.Server.SendMessage(
string.Empty,
string.Empty, From,
new XmppAddress(BareJid),
string.Empty, XmlString);
1628 catch (Exception ex)
1645 await this.Server.GetLastPresence(BareJid1, async (Sender, e) =>
1651 await this.Server.SendMessage(string.Empty, string.Empty, this.GetComponentAddress(e.To), e.From, string.Empty,
1652 "<friend xmlns='" + NamespaceProvisioningDevice(Version) +
"' jid='" + BareJid2 +
"'/>");
1655 catch (Exception ex)
1662 private async Task CanReadRuleHandler(
object Sender,
IqEventArgs e)
1672 if (!Guid.TryParse(Key, out Guid ObjectId))
1685 Rule Condition =
null;
1689 foreach (XmlNode N
in e.
Query.ChildNodes)
1691 if (N is XmlElement E)
1693 switch (E.LocalName)
1696 Node = this.ParseNodeInfo(E);
1699 ThingReferences.
Add(Node);
1703 List<string> Fields =
null;
1706 foreach (XmlAttribute Attr
in E.Attributes)
1747 foreach (XmlNode N2
in E.ChildNodes)
1749 if (N2 is XmlElement E2)
1751 switch (E2.LocalName)
1754 Fields ??=
new List<string>();
1763 Names = Fields?.ToArray(),
1764 Mask = (int)FieldTypes
1778 Value = GetDomain(RemoteJID)
1804 Condition =
new All();
1810 if (Condition is
null)
1844 new KeyValuePair<string, object>(
"RemoteJID", RemoteJID),
1845 new KeyValuePair<string, object>(
"Allowed", CanRead));
1851 await this.ClearCache(JID, ThingReferences?.ToArray(),
Registration.Version);
1854 private async Task CanControlRuleHandler(
object Sender,
IqEventArgs e)
1864 if (!Guid.TryParse(Key, out Guid ObjectId))
1877 Rule Condition =
null;
1881 foreach (XmlNode N
in e.
Query.ChildNodes)
1883 if (N is XmlElement E)
1885 switch (E.LocalName)
1888 Node = this.ParseNodeInfo(E);
1891 ThingReferences.
Add(Node);
1895 List<string> Parameters =
null;
1897 foreach (XmlNode N2
in E.ChildNodes)
1899 if (N2 is XmlElement E2)
1901 switch (E2.LocalName)
1904 Parameters ??=
new List<string>();
1913 Names = Parameters?.ToArray()
1927 Value = GetDomain(RemoteJID)
1953 Condition =
new All();
1959 if (Condition is
null)
1996 new KeyValuePair<string, object>(
"RemoteJID", RemoteJID),
1997 new KeyValuePair<string, object>(
"Allowed", CanControl));
2003 await this.ClearCache(JID, ThingReferences?.ToArray(),
Registration.Version);
2006 private async Task ClearCacheHandler(
object Sender,
IqEventArgs e)
2017 string.IsNullOrEmpty(NodeId) &&
2018 string.IsNullOrEmpty(SourceId) &&
2019 string.IsNullOrEmpty(Partition))
2027 else if (
string.IsNullOrEmpty(NodeId) &&
2028 string.IsNullOrEmpty(SourceId) &&
2029 string.IsNullOrEmpty(Partition))
2048 await this.ClearCache(Jid, NodeId, SourceId, Partition, R.Version);
2053 catch (Exception ex)
2059 private async Task GetDevicesHandler(
object Sender,
IqEventArgs e)
2063 XmlElement E = e.
Query;
2074 else if (MaxCount <= 0)
2080 List<KeyValuePair<Registration, IEnumerable<MetaDataTag>>> Found =
new List<KeyValuePair<Registration, IEnumerable<MetaDataTag>>>();
2083 IEnumerable<Registration> Registrations;
2086 "JID",
"NodeId",
"SourceId",
"Partition");
2090 if (Found.Count >= MaxCount)
2096 Found.Add(
new KeyValuePair<
Registration, IEnumerable<MetaDataTag>>(R, await LoadTags(R.
ObjectId)));
2101 catch (Exception ex)
2109 #region Thing Registry XEP-0347
2118 Result.Removed += UpdateRegistryAccount;
2129 catch (Exception ex)
2142 if (Account is
null)
2144 DateTime Now = DateTime.Now;
2155 Account.LastAction = DateTime.Now;
2159 Account2.LastAction = DateTime.Now;
2169 private async Task RegisterHandler(
object _,
IqEventArgs e)
2173 XmlElement E = e.
Query;
2178 Node = this.ParseNodeInfo(E);
2179 if (!this.ParseTags(e, E,
2180 out Dictionary<string, MetaDataTag> Tags,
2207 NrRegistrations = 1,
2209 FirstRegistration = DateTime.UtcNow,
2210 Version = QueryVersion,
2211 Location = Location,
2219 Registration.IsPublic =
true;
2224 Registration.IsPublic =
false;
2225 Registration.Owner =
string.
Empty;
2229 await SaveNewRegistrationTags(
Registration, this.GetArray(Tags));
2241 Registration.Key = Key;
2243 Registration.LastUpdate = DateTime.UtcNow;
2244 Registration.Location = Location;
2245 Registration.GeoId = GeoId;
2249 Registration.IsPublic =
true;
2254 Registration.IsPublic =
false;
2255 Registration.Owner =
string.
Empty;
2260 await SaveNewRegistrationTags(
Registration, this.GetArray(Tags));
2264 if (PrevGeoLocation)
2266 if (PrevGeoId != GeoId)
2267 await this.geo.Delete(PrevGeoId);
2274 else if (PrevGeoLocation)
2275 await this.geo.Delete(PrevGeoId);
2283 catch (Exception ex)
2289 private async Task<Registration> GetRegistration(
CaseInsensitiveString JID,
string NodeId,
string SourceId,
string Partition,
2301 bool Updated =
false;
2303 if (ThingVersion.HasValue && Result.Version != ThingVersion.Value)
2305 Result.Version = ThingVersion.Value;
2309 if (OwnerVersion.HasValue && Result.OwnerVersion != OwnerVersion.Value)
2311 Result.OwnerVersion = OwnerVersion.Value;
2321 private MetaDataTag[] GetArray(Dictionary<string, MetaDataTag> Tags)
2324 Tags.Values.CopyTo(Result, 0);
2328 private static KeyValuePair<string, object>[] GetLogParameters(
ThingReference Node, IEnumerable<MetaDataTag> Tags)
2330 List<KeyValuePair<string, object>> Result =
new List<KeyValuePair<string, object>>();
2332 if (!
string.IsNullOrEmpty(Node.
NodeId))
2333 Result.Add(
new KeyValuePair<string, object>(
"NodeId", Node.
NodeId));
2335 if (!
string.IsNullOrEmpty(Node.
SourceId))
2336 Result.Add(
new KeyValuePair<string, object>(
"SourceId", Node.
SourceId));
2338 if (!
string.IsNullOrEmpty(Node.
Partition))
2339 Result.Add(
new KeyValuePair<string, object>(
"Partition", Node.
Partition));
2341 if (!(Tags is
null))
2344 Result.Add(
new KeyValuePair<string, object>(Tag.
Name, Tag.
Value));
2347 return Result.ToArray();
2350 private static KeyValuePair<string, object>[] GetLogParameters(
Registration Node, IEnumerable<MetaDataTag> Tags)
2352 List<KeyValuePair<string, object>> Result =
new List<KeyValuePair<string, object>>();
2354 if (!
string.IsNullOrEmpty(Node.
NodeId))
2355 Result.Add(
new KeyValuePair<string, object>(
"NodeId", Node.
NodeId));
2357 if (!
string.IsNullOrEmpty(Node.
SourceId))
2358 Result.Add(
new KeyValuePair<string, object>(
"SourceId", Node.
SourceId));
2360 if (!
string.IsNullOrEmpty(Node.
Partition))
2361 Result.Add(
new KeyValuePair<string, object>(
"Partition", Node.
Partition));
2364 Result.Add(
new KeyValuePair<string, object>(Tag.
Name, Tag.
Value));
2366 return Result.ToArray();
2369 private async Task UpdateHandler(
object _,
IqEventArgs e)
2373 XmlElement E = e.
Query;
2378 Node = this.ParseNodeInfo(E);
2379 if (!this.ParseTags(e, E,
2380 out Dictionary<string, MetaDataTag> Tags,
2399 await e.
IqResult(
"<disowned xmlns='" + e.
Query.NamespaceURI +
"'/>", e.
To);
2417 Registration.LastUpdate = DateTime.UtcNow;
2418 Registration.Location = Location;
2419 Registration.GeoId = GeoId;
2422 await UpdateRegistrationTags(
Registration, this.GetArray(Tags));
2428 if (PrevGeoLocation)
2430 if (PrevGeoId != GeoId)
2431 await this.geo.Delete(PrevGeoId);
2438 else if (PrevGeoLocation)
2439 await this.geo.Delete(PrevGeoId);
2442 "Update", GetLogParameters(Node, Tags.Values));
2445 await IncAccountSuccessfulUpdates(e.
From.
BareJid);
2448 catch (Exception ex)
2466 private async Task UnregisterHandler(
object _,
IqEventArgs e)
2470 XmlElement E = e.
Query;
2473 Node = this.ParseNodeInfo(E);
2484 await this.DeleteRules(R);
2491 catch (Exception ex)
2509 private async Task MineHandler(
object _,
IqEventArgs e)
2511 XmlElement E = e.
Query;
2514 if (!this.ParseTags(e, E,
2515 out Dictionary<string, MetaDataTag> Tags,
2524 if (
string.IsNullOrEmpty(Key))
2532 IEnumerable<MetaDataTag> MatchTags;
2563 if (!Match || c != Tags.Count)
2570 string PrevGeoId = R.
GeoId;
2572 R.Key =
string.Empty;
2574 R.IsPublic = Public;
2575 R.Location = Location;
2584 Tag.IsPublic = Public;
2591 if (PrevGeoLocation)
2593 if (PrevGeoId != GeoId)
2594 await this.geo.Delete(PrevGeoId);
2596 await this.geo.Publish(R);
2599 await this.geo.Publish(R);
2601 else if (PrevGeoLocation)
2602 await this.geo.Delete(PrevGeoId);
2609 StringBuilder Response =
new StringBuilder();
2611 Response.Append(
"<claimed xmlns='");
2612 Response.Append(e.
Query.NamespaceURI);
2613 Response.Append(
"' jid='");
2616 if (!
string.IsNullOrEmpty(R.
NodeId))
2618 Response.Append(
"' id='");
2622 if (!
string.IsNullOrEmpty(R.
SourceId))
2624 Response.Append(
"' src='");
2630 Response.Append(
"' pt='");
2634 Response.Append(
"'/>");
2642 Response.Append(
"<claimed xmlns='");
2643 Response.Append(NamespaceIoTDiscovery(R.Version));
2644 Response.Append(
"' jid='");
2646 Response.Append(
"' public='");
2649 if (!
string.IsNullOrEmpty(R.
NodeId))
2651 Response.Append(
"' id='");
2655 if (!
string.IsNullOrEmpty(R.
SourceId))
2657 Response.Append(
"' src='");
2663 Response.Append(
"' pt='");
2667 Response.Append(
"'/>");
2669 await this.Server.GetLastPresence(R.
JID, async (sender, e2) =>
2672 await
this.Server.SendIqRequest(
"set", e.
To, e2.From,
string.
Empty, Response.
ToString(),
false,
null,
null);
2686 List<MetaDataTag> Tags =
new List<MetaDataTag>();
2691 return Tags.ToArray();
2708 private static Task<IEnumerable<MetaDataTag>> LoadTags(Guid RegistrationReference)
2715 Dictionary<string, MetaDataTag> Tags2 =
new Dictionary<string, MetaDataTag>(StringComparer.CurrentCultureIgnoreCase);
2718 Tags2[Tag.
Name] = Tag;
2720 if (!(Tags is
null))
2729 Tags2.Remove(Tag.
Name);
2733 Tag.ObjectId = Tag2.ObjectId;
2759 return Tags2.Values;
2774 private async Task DisownHandler(
object _,
IqEventArgs e)
2778 XmlElement E = e.
Query;
2782 Node = this.ParseNodeInfo(E);
2794 StringBuilder Request =
new StringBuilder();
2796 Request.Append(
"<disowned xmlns='");
2797 Request.Append(NamespaceIoTDiscovery(R.Version));
2798 AppendNodeReference(Request, Node);
2799 Request.Append(
"'/>");
2801 await this.Server.GetLastPresence(Jid, async (sender, e3) =>
2807 R.Key = Convert.ToBase64String(Gateway.NextBytes(16));
2809 R.Owner = string.Empty;
2811 await this.Server.SendIqRequest(
"set", e.To, e3.From, string.Empty,
2812 Request.ToString(), false, async (sender2, e4) =>
2818 await e.IqResult(string.Empty, e.To);
2820 IEnumerable<MetaDataTag> Tags = await LoadTags(R.ObjectId);
2822 await Database.StartBulk();
2825 await Database.Update(R);
2826 await Database.Delete(Tags);
2827 await this.DeleteRules(R);
2831 await Database.EndBulk();
2834 Log.Informational(
"Thing disowned.", R.JID, Jid,
"Disown", GetLogParameters(Node, Tags));
2836 await IncAccountSuccessfulDisownments(e.From.BareJid);
2839 await e.IqErrorNotAllowed(e.To,
"Thing needs to accept disownment.",
"en");
2841 catch (Exception ex)
2850 catch (Exception ex)
2859 await IncAccountFailedDisownments(e.
From.
BareJid);
2866 await IncAccountFailedDisownments(e.
From.
BareJid);
2868 catch (Exception ex)
2886 private async Task RemoveHandler(
object _,
IqEventArgs e)
2890 XmlElement E = e.
Query;
2894 Node = this.ParseNodeInfo(E);
2910 IEnumerable<MetaDataTag> Tags = await LoadTags(R.
ObjectId);
2912 Log.
Informational(
"Thing removed from public registry.", R.
JID, Jid,
"Remove", GetLogParameters(Node, Tags));
2917 StringBuilder Request =
new StringBuilder();
2919 Request.Append(
"<removed xmlns='");
2920 Request.Append(NamespaceIoTDiscovery(R.Version));
2921 AppendNodeReference(Request, Node);
2922 Request.Append(
"'/>");
2924 await this.Server.GetLastPresence(Jid, async (sender, e3) =>
2929 await
this.Server.SendIqRequest(
"set", e.
To, e3.From,
string.
Empty, Request.
ToString(),
false,
null,
null);
2931 catch (Exception ex)
2937 await IncAccountSuccessfulRemovals(e.
From.
BareJid);
2951 catch (Exception ex)
2969 private async Task SearchHandler(
object _,
IqEventArgs e)
2973 XmlElement E = e.
Query;
2986 else if (MaxCount <= 0)
2992 List<SearchOperator> SearchOperators =
new List<SearchOperator>();
2994 Dictionary<Guid, bool> RegistrationMatches =
new Dictionary<Guid, bool>();
2995 Dictionary<Guid, bool> BestRegistrationMatches =
null;
2996 const int MaxMaxTagCount = 10000;
2997 int MaxTagCount = MaxMaxTagCount;
2998 int MaxRegistrationCount = MaxMaxTagCount;
3001 string NameWildcard;
3003 foreach (XmlNode N
in E.ChildNodes)
3005 E2 = N as XmlElement;
3010 if (
string.IsNullOrEmpty(Name))
3018 await e.
IqResult(
"<found xmlns='" + e.
Query.NamespaceURI +
"' more='false'/>", e.
To);
3023 if (E2.HasAttribute(
"nameWildcard"))
3026 if (
string.IsNullOrEmpty(NameWildcard) || !Name.Contains(NameWildcard))
3027 NameWildcard =
null;
3030 NameWildcard =
null;
3032 switch (E2.LocalName)
3086 SearchOperators.Add(Op =
new StringTagMask(Name, NameWildcard,
3138 await e.
IqResult(
"<found xmlns='" + e.
Query.NamespaceURI +
"' more='false'/>", e.
To);
3153 RegistrationMatches.Clear();
3167 await e.
IqResult(
"<found xmlns='" + e.
Query.NamespaceURI +
"' more='false'/>", e.
To);
3168 await IncAccountSuccessfulSearches(e.
From.
BareJid, 0);
3173 if (c < MaxTagCount)
3176 c = RegistrationMatches.Count;
3177 if (c < MaxRegistrationCount)
3179 MaxRegistrationCount = c;
3180 BestRegistrationMatches = RegistrationMatches;
3185 if (MaxTagCount == MaxMaxTagCount || BestRegistrationMatches is
null)
3187 await e.
IqErrorBadRequest(e.
To,
"Too wide a search. Restrict the scope of the search by including restrictive tags.",
"en");
3192 List<KeyValuePair<Registration, IEnumerable<MetaDataTag>>> Result =
new List<KeyValuePair<Registration, IEnumerable<MetaDataTag>>>();
3193 Dictionary<string, MetaDataTag> TagsSorted =
new Dictionary<string, MetaDataTag>(StringComparer.CurrentCultureIgnoreCase);
3194 IEnumerable<MetaDataTag> Tags;
3196 foreach (Guid
Registration in BestRegistrationMatches.Keys)
3198 bool Applies =
true;
3205 if (Tag2.
Name ==
"KEY")
3211 TagsSorted[Tag2.
Name] = Tag2;
3221 Regex NameExpression =
new Regex(
3223 RegexOptions.Singleline);
3229 Match M = NameExpression.Match(Tag3.
Name);
3230 if (!M.Success || M.Index > 0 || M.Length != Tag3.
Name.Length)
3259 else if (Result.Count == MaxCount)
3270 Result.Add(
new KeyValuePair<
Registration, IEnumerable<MetaDataTag>>(R, Tags));
3274 await e.
IqResult(ResultSet(Result, More, e.
Query.NamespaceURI), e.
To);
3276 await IncAccountSuccessfulSearches(e.
From.
BareJid, Result.Count);
3278 catch (Exception ex)
3285 public static string ResultSet(List<KeyValuePair<
Registration, IEnumerable<MetaDataTag>>> Result,
bool More,
string Namespace)
3287 StringBuilder Response =
new StringBuilder();
3289 Response.Append(
"<found xmlns='");
3290 Response.Append(Namespace);
3291 Response.Append(
"' more='");
3293 Response.Append(
"'>");
3295 foreach (KeyValuePair<
Registration, IEnumerable<MetaDataTag>> P
in Result)
3297 Response.Append(
"<thing jid='");
3300 if (!
string.IsNullOrEmpty(P.Key.NodeId))
3302 Response.Append(
"' id='");
3303 Response.Append(
XML.
Encode(P.Key.NodeId));
3306 if (!
string.IsNullOrEmpty(P.Key.SourceId))
3308 Response.Append(
"' src='");
3309 Response.Append(
XML.
Encode(P.Key.SourceId));
3312 if (!
string.IsNullOrEmpty(P.Key.Partition))
3314 Response.Append(
"' pt='");
3315 Response.Append(
XML.
Encode(P.Key.Partition));
3318 Response.Append(
"'>");
3320 if (!(P.Value is
null))
3326 Response.Append(
"<str name='");
3328 Response.Append(
"' value='");
3329 Response.Append(
XML.
Encode(StrTag.TagValue));
3330 Response.Append(
"'/>");
3334 Response.Append(
"<num name='");
3336 Response.Append(
"' value='");
3338 Response.Append(
"'/>");
3343 Response.Append(
"</thing>");
3346 Response.Append(
"</found>");
3348 return Response.ToString();
3355 Account.NrSearchResultItems += NrItems;
3370 if (
string.IsNullOrEmpty(NodeId) &&
string.IsNullOrEmpty(SourceId) &&
string.IsNullOrEmpty(Partition))
3376 private bool ParseTags(
IqEventArgs e, XmlElement E,
3377 out Dictionary<string, MetaDataTag> Tags,
3383 double? Latitude =
null;
3384 double? Longitude =
null;
3385 double? Altitude =
null;
3387 Tags =
new Dictionary<string, MetaDataTag>();
3392 foreach (XmlNode N
in E.ChildNodes)
3394 E2 = N as XmlElement;
3399 if (Name.Length > 64)
3411 foreach (
char ch
in Name)
3413 if (ch ==
':' || ch ==
'#' ||
char.IsWhiteSpace(ch))
3420 switch (N.LocalName)
3424 if (Value.Length > 128)
3438 Tags[Name] = NumTag;
3443 Latitude = NumTag.TagValue;
3447 Longitude = NumTag.TagValue;
3451 Altitude = NumTag.TagValue;
3458 if (Tags.Count > 100)
3464 if (Latitude.HasValue && Longitude.HasValue && Key is
null)
3466 Location =
new GeoPosition(Latitude.Value, Longitude.Value, Altitude);
3468 StringBuilder sb =
new StringBuilder(
"iotdisco:");
3489 sb.Append(Uri.EscapeDataString(Tag.
Name));
3498 sb.Append(Uri.EscapeDataString(
this.MainDomain.Address));
3500 GeoId = sb.ToString();
3506 internal static void AppendNodeReference(StringBuilder Request,
ThingReference Node)
3508 if (!
string.IsNullOrEmpty(Node.
NodeId))
3510 Request.Append(
"' id='");
3514 if (!
string.IsNullOrEmpty(Node.
SourceId))
3516 Request.Append(
"' src='");
3520 if (!
string.IsNullOrEmpty(Node.
Partition))
3522 Request.Append(
"' pt='");
3527 private async Task DeleteRulesHandler(
object Sender,
IqEventArgs e)
3538 string.IsNullOrEmpty(NodeId) &&
3539 string.IsNullOrEmpty(SourceId) &&
3540 string.IsNullOrEmpty(Partition))
3544 await this.DeleteRules(R);
3548 else if (
string.IsNullOrEmpty(NodeId) &&
3549 string.IsNullOrEmpty(SourceId) &&
3550 string.IsNullOrEmpty(Partition))
3556 await this.DeleteRules(R);
3571 await this.DeleteRules(R);
3572 await this.ClearCache(Jid, NodeId, SourceId, Partition, R.Version);
3578 catch (Exception ex)
3586 #region Software Updates
3588 private async Task GetPackageInfoHandler(
object Sender,
IqEventArgs e)
3596 StringBuilder Xml =
new StringBuilder();
3597 Serialize(
Package, Xml, QueryVersion);
3603 if (!this.Server.IsServerDomain(e.
From.
Domain,
true))
3614 if (!this.CheckSameDomain(e))
3630 string FullFileName = Path.Combine(
XmppServerModule.PackagesFolder, FileName);
3631 if (!File.Exists(FullFileName))
3641 private static readonly Dictionary<CaseInsensitiveString, Package> packages =
new Dictionary<CaseInsensitiveString, Package>();
3642 private static bool packagesLoaded =
false;
3650 if (!packages.TryGetValue(FileName, out
Package))
3671 Serialize(
Package,
"packageInfo", Xml, Version);
3677 Xml.Append(LocalName);
3678 Xml.Append(
" fileName='");
3680 Xml.Append(
"' signature='");
3682 Xml.Append(
"' published='");
3687 Xml.Append(
"' supersedes='");
3691 Xml.Append(
"' created='");
3693 Xml.Append(
"' url='");
3695 Xml.Append(
"' bytes='");
3698 if (Version.HasValue)
3700 Xml.Append(
"' xmlns='");
3701 Xml.Append(NamespaceSoftwareUpdates(Version.Value));
3707 private async Task GetPackagesHandler(
object Sender,
IqEventArgs e)
3709 StringBuilder Xml =
new StringBuilder();
3711 Xml.Append(
"<packages xmlns='");
3712 Xml.Append(e.
Query.NamespaceURI);
3718 if (!File.Exists(FullFileName))
3721 Serialize(
Package, Xml,
null);
3724 Xml.Append(
"</packages>");
3729 public static async Task<Package[]> GetPackages()
3731 if (!packagesLoaded)
3741 packagesLoaded =
true;
3747 packages.Values.CopyTo(Result, 0);
3761 StringBuilder Xml =
new StringBuilder();
3762 Serialize(
Package, Xml, Version);
3763 return Xml.ToString();
3767 await this.Notify(
Package, Serializer);
3768 await this.Notify(
null, Serializer);
3780 StringBuilder Xml =
new StringBuilder();
3782 Xml.Append(
"<packageDeleted fileName='");
3784 Xml.Append(
"' xmlns='");
3785 Xml.Append(NamespaceSoftwareUpdates(Version));
3788 return Xml.ToString();
3792 await this.Notify(
Package, Serializer);
3793 await this.Notify(
null, Serializer);
3796 private delegate
string SerializePackageDelegate(
NamespaceSet Version);
3798 private async Task Notify(
Package Package, SerializePackageDelegate Serializer)
3800 Dictionary<CaseInsensitiveString, bool> Sent =
new Dictionary<CaseInsensitiveString, bool>();
3805 if (
string.Compare(Notification.BareJid,
Gateway.
XmppClient.BareJID,
true) == 0)
3811 if (!Sent.ContainsKey(To.
Address))
3813 string Xml = Serializer(Notification.Version);
3820 private async Task SubscribeHandler(
object Sender,
IqEventArgs e)
3822 if (!this.CheckSameDomain(e))
3828 if (FileName ==
"*")
3830 LinkedList<object> ToDelete =
null;
3835 if (Notification.FileName == FileName)
3839 ToDelete ??=
new LinkedList<object>();
3840 ToDelete.AddLast(Notification);
3848 FileName = FileName,
3851 Version = QueryVersion
3857 if (!(ToDelete is
null))
3873 if (Notification is
null)
3882 await e.
IqErrorNotAllowed(e.
To,
"Maximum number of subscriptions reached. Either unsubscribe, or use a wildcard subscription.",
"en");
3888 FileName = FileName,
3891 Version = QueryVersion
3901 private async Task UnsubscribeHandler(
object Sender,
IqEventArgs e)
3903 if (!this.CheckSameDomain(e))
3908 if (FileName ==
"*")
3919 if (!(Notification is
null))
3927 private async Task GetSubscriptionsHandler(
object Sender,
IqEventArgs e)
3929 if (!this.CheckSameDomain(e))
3932 StringBuilder Xml =
new StringBuilder();
3934 Xml.Append(
"<subscriptions xmlns='");
3935 Xml.Append(e.
Query.NamespaceURI);
3941 Xml.Append(
"<subscription>");
3942 Xml.Append(
XML.
Encode(Notification.FileName));
3943 Xml.Append(
"</subscription>");
3946 Xml.Append(
"</subscriptions>");
Helps with parsing of commong data types.
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
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.
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Static class managing the runtime environment of the IoT Gateway.
static DateTime ScheduleEvent(Action< object > Callback, DateTime When, object State)
Schedules a one-time event.
static XmppClient XmppClient
XMPP Client connection of gateway.
Base class for components.
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Set handler.
CaseInsensitiveString Subdomain
Subdomain name.
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
string Name
Component name.
readonly object synchObject
Synchronization object for thread-safe access to internal structures.
bool IsComponentDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the component domain, or optionally, an alternative component domain.
XmppServer Server
XMPP Server.
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Set handler.
Event arguments for IQ queries.
XmppAddress From
From address attribute
Task IqErrorNotAcceptable(XmppAddress From, string ErrorText, string Language)
Returns a not-acceptable error.
Task IqResult(string Xml, string From)
Returns a response to the current request.
Task IqErrorResourceConstraint(XmppAddress From, string ErrorText, string Language)
Returns a resource-constraint error.
Task IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
XmlElement Query
Query element, if found, null otherwise.
Task IqErrorNotAllowed(XmppAddress From, string ErrorText, string Language)
Returns a not-allowed error.
XmppAddress To
To address attribute
async Task IqError(string ErrorType, string Xml, XmppAddress From, string ErrorText, string Language)
Returns an error response to the current request.
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Task IqErrorForbidden(XmppAddress From, string ErrorText, string Language)
Returns a forbidden error.
Event arguments for responses to IQ queries.
object State
State object passed to the original request.
XmppAddress From
From address attribute
XmlElement FirstElement
First child element of the Response element.
bool Ok
If the response is an OK result response (true), or an error response (false).
Contains information about one XMPP address.
override string ToString()
object.ToString()
bool IsEmpty
If the address is empty.
CaseInsensitiveString Domain
Domain
CaseInsensitiveString Address
XMPP Address
CaseInsensitiveString BareJid
Bare JID
static readonly XmppAddress Empty
Empty address.
Task< bool > SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
static byte[] GetRandomNumbers(int NrBytes)
Generates a set of random numbers.
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
int IndexOf(CaseInsensitiveString value, StringComparison comparisonType)
Reports the zero-based index of the first occurrence of the specified string in the current System....
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
CaseInsensitiveString Substring(int startIndex, int length)
Retrieves a substring from this instance. The substring starts at a specified character position and ...
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
static Task EndBulk()
Ends bulk-processing of data. Must be called once for every call to StartBulk.
static string WildcardToRegex(string s, string Wildcard)
Converts a wildcard string to a regular expression string.
static Task StartBulk()
Starts bulk-proccessing of data. Must be followed by a call to EndBulk.
static async Task Update(object Object)
Updates an object in the database.
static async Task Delete(object Object)
Deletes an object in the database.
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
static Task< object > TryLoadObject(string CollectionName, object ObjectId)
Tries to load an object given its Object ID ObjectId and its collection name CollectionName .
This filter selects objects that conform to all child-filters provided.
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field matching a given regular expression.
Implements an in-memory cache.
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
void Dispose()
IDisposable.Dispose
bool Remove(KeyType Key)
Removes an item from the cache.
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
void Add(KeyType Key, ValueType Value)
Adds an item to the cache.
void Clear()
Clears the cache.
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
A chunked list is a linked list of chunks of objects of type T .
void Clear()
Clears the collection.
void Add(T Item)
Adds an item to the collection.
T[] ToArray()
Returns an array containing all elements of the collection.
Contains information about a position in a geo-spatial coordinate system.
Provisioning and registry service component.
const string NamespaceProvisioningOwnerIeeeV1
urn:ieee:iot:prov:o:1.0
const string NamespaceSoftwareUpdatesNeuroFoundationV1
urn:nf:iot:swu:1.0
const string NamespaceProvisioningTokenNeuroFoundationV1
urn:nf:iot:prov:t:1.0
static string NamespaceProvisioningOwner(NamespaceSet Version)
Returns the namespace for owner provisioning.
static string NamespaceSoftwareUpdates(NamespaceSet Version)
Returns the namespace for Software Updates.
static string NamespaceProvisioningToken(NamespaceSet Version)
Returns the namespace for token provisioning.
bool TryGetCertificate(string Token, out X509Certificate2 Certificate)
Tries to get a certificate for a given token.
const string NamespaceIoTDiscoveryNeuroFoundationV1
urn:nf:iot:disco:1.0
const string NamespaceProvisioningTokenIeeeV1
urn:ieee:iot:prov:t:1.0
const string NamespaceProvisioningDeviceIeeeV1
urn:ieee:iot:prov:d:1.0
override bool SupportsAccounts
If the component supports accounts (true), or if the subdomain name is the only valid address.
const string NamespaceIoTDiscoveryIeeeV1
urn:ieee:iot:disco:1.0
static string NamespaceIoTDiscovery(NamespaceSet Version)
Returns the namespace for IoT Discovery.
static string NamespaceProvisioningDevice(NamespaceSet Version)
Returns the namespace for device provisioning.
async Task ClearCache(CaseInsensitiveString BareJid, string NodeId, string SourceId, string Partition, NamespaceSet Version)
Notifies the entity its rule cache should be cleared.
async Task RecommendBefriend(CaseInsensitiveString BareJid1, CaseInsensitiveString BareJid2, NamespaceSet Version)
Notifies the entity identified by BareJid1 , that it should befriend (subscribe to presence from) the...
override void Dispose()
IDisposable.Dispose
async Task RecommendUnfriend(CaseInsensitiveString BareJid1, CaseInsensitiveString BareJid2, NamespaceSet Version)
Notifies the entity identified by BareJid1 , that it should remove friendship (presence subscription ...
const string NamespaceIoTDiscoveryXsfV0
urn:xmpp:iot:discovery
const string NamespaceProvisioningOwnerNeuroFoundationV1
urn:nf:iot:prov:o:1.0
async Task ClearCache(CaseInsensitiveString BareJid, IThingReference[] ThingReferences, NamespaceSet Version)
Notifies the entity its rule cache should be cleared.
const string NamespaceSoftwareUpdatesIeeeV1
urn:ieee:iot:swu:1.0
ProvisioningComponent(XmppServer Server, CaseInsensitiveString Subdomain, string Name, GeoSpatialComponent Geo)
Provisioning and registry service component.
const string NamespaceProvisioningDeviceNeuroFoundationV1
urn:nf:iot:prov:d:1.0
Guid ObjectId
Persisted object ID. Is null if object not persisted.
CaseInsensitiveString JID
JID.
int NrRegistrations
Number of registrations made.
string GeoId
The ID of the geo-spatial object.
string Partition
Optional partition in which the Node ID is unique.
string SourceId
Optional ID of source containing node.
DateTime FirstRegistration
Timestamp of first registration.
bool HasGeoLocation
If the object has a geo-spatial location.
int NrUpdates
Number of updates made.
CaseInsensitiveString Owner
Secret key
long NrSuccessfulDisownments
Number of successful disownments.
long NrSuccessfulClaims
Number of successful claims.
long NrSuccessfulUpdates
Number of successful updates.
long NrFailedSearches
Number of failed searches.
long NrFailedRemovals
Number of failed removals.
long NrFailedUpdates
Number of failed updates.
long NrSuccessfulRemovals
Number of successful removals.
long NrFailedClaims
Number of failed claims.
long NrSuccessfulSearches
Number of successful searches.
long NrFailedDisownments
Number of failed disownments.
Controls if a device with a remote JID is allowed to control a device with JID.
Controls if a device with a remote JID is allowed to subscribe to the presence of a device with JID.
Controls if a device with a remote JID is allowed to read data from a device with JID.
Abstract base class for rules.
void AddChildRule(Rule Rule)
Adds a child rule.
virtual ? bool Evaluate(Context Context)
Tries to evaluate the rule.
Filters things with a named numeric-valued tag equal to a given value.
Filters things with a named numeric-valued tag greater than a given value.
Filters things with a named numeric-valued tag greater than or equal to a given value.
Filters things with a named numeric-valued tag within a given range.
Filters things with a named numeric-valued tag lesser than a given value.
Filters things with a named numeric-valued tag lesser than or equal to a given value.
Filters things with a named numeric-valued tag not equal to a given value.
Filters things with a named numeric-valued tag outside a given range.
Abstract base class for all search operators.
bool HasNameWildcard
If a name wildcard is used.
abstract void AddValueFilters(ChunkedList< Filter > Filters)
Adds search filters for comparing the value of the tag using the operator.
abstract bool AppliesTo(MetaDataTag Tag)
Checks if the operator applies to a tag.
string NameWildcard
Wildcard, if any, used in the name.
Filters things with a named string-valued tag equal to a given value.
Filters things with a named string-valued tag greater than a given value.
Filters things with a named string-valued tag greater than or equal to a given value.
Filters things with a named string-valued tag within a given range.
Filters things with a named string-valued tag lesser than a given value.
Filters things with a named string-valued tag lesser than or equal to a given value.
Filters things with a named string-valued tag like a given value.
Filters things with a named string-valued tag not equal to a given value.
Filters things with a named string-valued tag outside a given range.
Filters things with a named string-valued tag matching a regular expression.
Contains information about a software package.
byte[] Signature
Cryptographic signature of package, as calculated by the issuer of the package.
CaseInsensitiveString FileName
Filename of package.
DateTime Published
When package was published.
string Url
URL of package.
long Bytes
Number of bytes of package.
DateTime Supersedes
Timestamp of superceded package.
DateTime Created
When package record was created
Service Module hosting the XMPP broker and its components.
static NamespaceSet GetVersion(string Namespace)
Gets the namespace set version corresponding to a given a namespace.
Base class for all sensor data fields.
Contains a reference to a thing
static ThingReference Empty
Empty thing reference. Can be used by sensors that are not part of a concentrator during readout.
string Partition
Optional partition in which the Node ID is unique.
bool IsEmpty
If the reference is an empty reference.
string SourceId
Optional ID of source containing node.
Interface for thing references.
string Partition
Optional partition in which the Node ID is unique.
string SourceId
Optional ID of source containing node.
class Names(Vector NamesVector)
Contains a collection of distinguished names.
RuleRange
Range of a rule change
NamespaceSet
Namespace versions
FieldType
Field Type flags