6using System.Threading.Tasks;
266 private readonly Dictionary<CaseInsensitiveString, Nodes> nodesByServiceAndName =
new Dictionary<CaseInsensitiveString, Nodes>();
267 private readonly Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>> subscriptionsByServiceAndNodeAndJid =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>>();
268 private readonly Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>> subscriptionsByJidAndServiceAndNode =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>>();
269 private readonly Dictionary<CaseInsensitiveString, NodeVerInfo> nodeVerByFullJidByBareJid =
new Dictionary<CaseInsensitiveString, NodeVerInfo>();
273 private bool disposed;
277 public Dictionary<string, PubSubNode> NodesByName;
300 this.
RegisterMessageHandler(
"x", Networking.XMPP.XmppClient.NamespaceData,
this.MessageFormHandler,
true);
304 Server.Accounts.OnDiscoveryQueryNodeGet += this.PepDiscoveryQueryGet;
305 Server.Accounts.OnDiscoveryQueryNodeItemsGet += this.PepDiscoveryQueryItemsGet;
306 Server.Accounts.DiscoverIdentities += this.PepDiscoverIdentities;
310 this.AddFeatures(
this, pubSubFeatures);
315 Server.OnPresenceLocalRecipient += this.Server_OnPresenceLocalRecipient;
331 Result.itemSerializer =
null;
339 AutoSubscribe =
false
342 private static readonly DefaultFeatures pepFeatures =
new DefaultFeatures()
388 if (Features.AutoCreate)
391 if (Features.AutoSubscribe)
402 this.disposed =
true;
421 this.Server.
UnregisterMessageHandler(
"x", Networking.XMPP.XmppClient.NamespaceData,
this.PepMessageFormHandler,
false);
424 this.Server.Accounts.OnDiscoveryQueryNodeGet -= this.PepDiscoveryQueryGet;
425 this.Server.Accounts.OnDiscoveryQueryNodeItemsGet -= this.PepDiscoveryQueryItemsGet;
427 this.RemoveFeatures(
this);
428 this.RemoveFeatures(this.Server.
Accounts);
430 this.itemSerializer =
null;
432 this.Server.OnPresenceLocalRecipient -= this.Server_OnPresenceLocalRecipient;
434 this.featuresByNodeVer?.
Dispose();
435 this.featuresByNodeVer =
null;
504 Nodes NodesOnService;
506 bool FirstRequestOnService =
false;
508 lock (this.nodesByServiceAndName)
510 if (!this.nodesByServiceAndName.TryGetValue(Service, out NodesOnService))
512 FirstRequestOnService =
true;
513 NodesOnService =
new Nodes()
515 NodesByName =
new Dictionary<string, PubSubNode>(),
519 this.nodesByServiceAndName[Service] = NodesOnService;
522 if (NodesOnService.NodesByName.TryGetValue(NodeName, out Node))
526 if (FirstRequestOnService && !
string.IsNullOrEmpty(Service))
531 lock (this.nodesByServiceAndName)
533 NodesOnService.NodesByName[NodeName] = Node2;
536 if (Node2.
Name == NodeName)
540 lock (this.nodesByServiceAndName)
542 NodesOnService.NodesArray =
new PubSubNode[NodesOnService.NodesByName.Count];
543 NodesOnService.NodesByName.Values.CopyTo(NodesOnService.NodesArray, 0);
555 if (!AutoCreateAccess.HasValue)
566 Created = DateTime.UtcNow,
569 AccessModel = AutoCreateAccess.
Value,
574 AutoSubscribe =
true,
582 lock (this.nodesByServiceAndName)
584 NodesOnService.NodesByName[NodeName] = Node;
586 NodesOnService.NodesArray =
new PubSubNode[NodesOnService.NodesByName.Count];
587 NodesOnService.NodesByName.Values.CopyTo(NodesOnService.NodesArray, 0);
602 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> SubscriptionsByServiceAndNode;
603 Dictionary<CaseInsensitiveString, Subscription> SubscriptionsByNode;
606 lock (this.subscriptionsByJidAndServiceAndNode)
608 if (!this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out SubscriptionsByServiceAndNode))
610 SubscriptionsByServiceAndNode =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>();
611 this.subscriptionsByJidAndServiceAndNode[Jid] = SubscriptionsByServiceAndNode;
614 if (SubscriptionsByServiceAndNode.TryGetValue(Service, out SubscriptionsByNode))
616 if (SubscriptionsByNode.TryGetValue(Node, out Result))
623 SubscriptionsByNode =
new Dictionary<CaseInsensitiveString, Subscription>();
631 lock (this.subscriptionsByJidAndServiceAndNode)
633 SubscriptionsByServiceAndNode[Service] = SubscriptionsByNode;
635 if (SubscriptionsByNode.TryGetValue(Node, out Result))
650 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> SubscriptionsByServiceAndNode;
651 Dictionary<CaseInsensitiveString, Subscription> SubscriptionsByNode;
654 lock (this.subscriptionsByJidAndServiceAndNode)
656 if (!this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out SubscriptionsByServiceAndNode))
658 SubscriptionsByServiceAndNode =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>();
659 this.subscriptionsByJidAndServiceAndNode[Jid] = SubscriptionsByServiceAndNode;
662 if (SubscriptionsByServiceAndNode.TryGetValue(Service, out SubscriptionsByNode))
665 SubscriptionsByNode.Values.CopyTo(Result, 0);
670 SubscriptionsByNode =
new Dictionary<CaseInsensitiveString, Subscription>();
679 SubscriptionsByNode.Values.CopyTo(Result, 0);
681 lock (this.subscriptionsByJidAndServiceAndNode)
683 SubscriptionsByServiceAndNode[Service] = SubscriptionsByNode;
697 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> SubscriptionsByNodeAndJid;
698 Dictionary<CaseInsensitiveString, Subscription> SubscriptionsByJid;
701 lock (this.subscriptionsByServiceAndNodeAndJid)
703 if (!this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out SubscriptionsByNodeAndJid))
705 SubscriptionsByNodeAndJid =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>();
706 this.subscriptionsByServiceAndNodeAndJid[Service] = SubscriptionsByNodeAndJid;
709 if (SubscriptionsByNodeAndJid.TryGetValue(Node, out SubscriptionsByJid))
712 SubscriptionsByJid.Values.CopyTo(Result, 0);
717 SubscriptionsByJid =
new Dictionary<CaseInsensitiveString, Subscription>();
726 SubscriptionsByJid.Values.CopyTo(Result, 0);
728 lock (this.subscriptionsByServiceAndNodeAndJid)
730 SubscriptionsByNodeAndJid[Node] = SubscriptionsByJid;
736 private Task PubSubGetHandler(
object Sender,
IqEventArgs e)
738 return this.PubSubGetHandler(
string.Empty, e, pubSubFeatures);
741 private Task PepGetHandler(
object Sender,
IqEventArgs e)
743 return this.PubSubGetHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
748 foreach (XmlNode N
in e.
Query.ChildNodes)
750 if (N is XmlElement E)
756 if (
string.IsNullOrEmpty(NodeName))
758 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
759 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
766 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
767 "<jid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"JID not specified.",
"en");
775 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
776 "<invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid JID",
"en");
788 if (Subscription is
null)
790 await e.
IqError(
"modify",
"<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
791 "<not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"No such subscriber.",
"en");
803 if (!
string.IsNullOrEmpty(SubID) && SubID != Subscription.ObjectId)
805 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
806 "<invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid subscription ID.",
"en");
810 DataForm Form = this.GetForm(Node, Subscription, e);
811 StringBuilder Xml =
new StringBuilder();
813 Xml.Append(
"<pubsub xmlns='");
815 Xml.Append(
"'><options node='");
817 Xml.Append(
"' jid='");
821 Xml.Append(
"</options></pubsub>");
828 if (!
string.IsNullOrEmpty(NodeName))
840 Subscription =
new Subscription();
841 Form = this.GetForm(Node, Subscription, e);
842 Xml =
new StringBuilder();
844 Xml.Append(
"<pubsub xmlns='");
846 Xml.Append(
"'><default");
848 if (!
string.IsNullOrEmpty(NodeName))
850 Xml.Append(
" node='");
857 Xml.Append(
"</default></pubsub>");
864 if (
string.IsNullOrEmpty(NodeName))
866 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
867 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
878 if (!(XmppServerModule.PersistenceLayer is
null) &&
879 await XmppServerModule.PersistenceLayer.IsBlocked(e.
From.
BareJid, Node.Creator))
892 switch (Node.AccessModel)
909 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
910 "<presence-subscription-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
911 "Presence subscription required to get item.",
"en");
925 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
926 "<not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/>",
927 e.
To,
"Not subscribed.",
"en");
932 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
933 "<pending-subscription xmlns='http://jabber.org/protocol/pubsub#errors'/>",
934 e.
To,
"Pending subscription.",
"en");
939 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
940 "<configuration-required xmlns='http://jabber.org/protocol/pubsub#errors'/>",
941 e.
To,
"Configuration required.",
"en");
948 List<PubSubItem> RequestedItems =
null;
949 IEnumerable<PubSubItem> Items;
956 bool ReverseOrder =
false;
958 if (Node.DeliverPayloads)
963 if (E.HasAttribute(
"max_items"))
975 if (Node.PersistItems)
977 foreach (XmlNode N2
in E.ChildNodes)
979 if (N2 is XmlElement E2 && E2.LocalName ==
"item")
981 RequestedItems ??=
new List<PubSubItem>();
985 PubSubItem Item = await this.LoadItem(Service, NodeName,
XML.
Attribute(E2,
"id"));
986 if (!(Item is
null) && Item.Node == NodeName)
987 RequestedItems.Add(Item);
996 if (!(RequestedItems is
null))
997 Items = RequestedItems.ToArray();
1000 List<Filter> Filters =
new List<Filter>()
1016 NewestFirst =
false;
1017 ReverseOrder =
true;
1028 if (Item.Node != NodeName)
1030 await e.
IqErrorBadRequest(e.
To,
"Before item identity points to item belonging to another node.",
"en");
1049 if (Item.Node != NodeName)
1051 await e.
IqErrorBadRequest(e.
To,
"After item identity points to item belonging to another node.",
"en");
1069 Items = await
Database.
Find<PubSubItem>(Offset, OneExtra ? Max + 1 : Max,
Filter,
"-Service",
"-Node",
"-Created");
1071 Items = await
Database.
Find<PubSubItem>(Offset, OneExtra ? Max + 1 : Max,
Filter,
"Service",
"Node",
"Created");
1075 if (Items is PubSubItem[] ItemArray)
1076 Array.Reverse(ItemArray);
1079 List<PubSubItem> ItemList =
new List<PubSubItem>();
1080 ItemList.AddRange(Items);
1089 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1090 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/>",
1091 e.
To,
"Node does not support persisted items and pagination.",
"en");
1094 else if (Node.NodeType ==
NodeType.collection || Node.TransientNode)
1096 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1097 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-items'/>",
1098 e.
To,
"Collection and transient nodes do not support item retrieval.",
"en");
1101 else if (!(Node.LastPublished is
null))
1102 Items =
new PubSubItem[] { Node.LastPublished };
1104 Items = Array.Empty<PubSubItem>();
1106 Xml =
new StringBuilder();
1108 Xml.Append(
"<pubsub xmlns='");
1110 Xml.Append(
"'><items node='");
1114 string First =
null;
1118 foreach (PubSubItem Item
in Items)
1126 First ??= Item.ItemIdOrObjectId;
1127 Last = Item.ItemIdOrObjectId;
1129 Xml.Append(
"<item id='");
1130 Xml.Append(
XML.
Encode(Item.ItemIdOrObjectId));
1132 if (Node.DeliverPayloads || !(RequestedItems is
null))
1134 Xml.Append(
"' publisher='");
1138 if (
XML.
IsValidXml(Item.Payload,
true,
true,
true,
true,
false,
false))
1139 Xml.Append(Item.Payload);
1141 Xml.Append(
"</item>");
1147 Xml.Append(
"</items>");
1152 Xml.Append(
"</pubsub>");
1157 case "subscriptions":
1158 Xml =
new StringBuilder();
1160 Xml.Append(
"<pubsub xmlns='");
1163 if (E.HasAttribute(
"node"))
1165 NodeName = E.GetAttribute(
"node");
1174 Xml.Append(
"'><subscriptions node='");
1184 Xml.Append(
"'><subscriptions>");
1191 Xml.Append(
"</subscriptions></pubsub>");
1196 case "affiliations":
1197 Xml =
new StringBuilder();
1199 Xml.Append(
"<pubsub xmlns='");
1202 if (E.HasAttribute(
"node"))
1204 NodeName = E.GetAttribute(
"node");
1213 Xml.Append(
"'><affiliations node='");
1219 Xml.Append(
"<affiliation node='");
1221 Xml.Append(
"' affiliation='");
1222 Xml.Append(ToString(Affiliation));
1223 Xml.Append(
"'/></affiliations>");
1230 Xml.Append(
"'><affiliations>");
1238 Xml.Append(
"<affiliation node='");
1240 Xml.Append(
"' affiliation='");
1241 Xml.Append(ToString(Affiliation));
1246 Xml.Append(
"</affiliations>");
1249 Xml.Append(
"</pubsub>");
1264 private static IEnumerable<Subscription> Subset(Subscription[] Subscriptions,
string NodeName)
1266 LinkedList<Subscription> Result =
new LinkedList<Subscription>();
1268 foreach (Subscription Subscription
in Subscriptions)
1270 if (Subscription.Node == NodeName)
1271 Result.AddLast(Subscription);
1277 private static void Serialize(IEnumerable<Subscription> Subscriptions, StringBuilder Xml)
1279 foreach (Subscription Subscription
in Subscriptions)
1281 Xml.Append(
"<subscription node='");
1282 Xml.Append(
XML.
Encode(Subscription.Node));
1283 Xml.Append(
"' jid='");
1284 Xml.Append(
XML.
Encode(Subscription.Jid));
1285 Xml.Append(
"' subscription='");
1286 Xml.Append(Subscription.Status.ToString());
1287 Xml.Append(
"' subid='");
1288 Xml.Append(Subscription.ObjectId);
1295 List<Field> Fields =
new List<Field>()
1298 new BooleanField(
null,
"pubsub#deliver",
"Enable notifications.",
false,
new string[] { Subscription.Deliver ?
"1" :
"0" },
null,
1300 new BooleanField(
null,
"pubsub#include_body",
"Include message body.",
false,
new string[] { Subscription.IncludeBody ?
"1" :
"0" },
null,
1302 new BooleanField(
null,
"pubsub#digest",
"Digest.",
false,
new string[] { Subscription.Digest ?
"1" :
"0" },
null,
1303 "Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually.",
BooleanDataType.
Instance,
new BasicValidation(),
string.Empty,
false,
false,
false),
1304 new TextSingleField(
null,
"pubsub#digest_frequency",
"Digest interval (ms):",
false,
new string[] { Subscription.DigestFrequencyMilliseconds.ToString() },
null,
1305 "The minimum number of milliseconds between sending any two notification digests.",
IntegerDataType.
Instance,
new RangeValidation(
"0",
int.MaxValue.ToString()),
string.Empty,
false,
false,
false),
1306 new TextSingleField(
null,
"pubsub#expire",
"Expiry time:",
false,
new string[] { Subscription.Expires == DateTime.MaxValue ? string.Empty :
XML.
Encode(Subscription.Expires) },
null,
1310 if (!(Node is
null) && Node.NodeType ==
NodeType.collection)
1312 Fields.Add(
new ListSingleField(
null,
"pubsub#subscription_type",
"Subcription Type:",
false,
new string[] { Subscription.Type.ToString() },
1313 new KeyValuePair<string, string>[]
1315 new KeyValuePair<string, string>(
"Receive notification of new items only",
"items"),
1316 new KeyValuePair<string, string>(
"Receive notification of new nodes only",
"nodes")
1317 },
"Type of subscription.",
null,
null,
string.Empty,
false,
false,
false));
1319 Fields.Add(
new ListSingleField(
null,
"pubsub#subscription_depth",
"Subcription Depth:",
false,
new string[] { Subscription.Depth == SubscriptonDepth.one ?
"1" : Subscription.Depth.ToString() },
1320 new KeyValuePair<string, string>[]
1322 new KeyValuePair<string, string>(
"Receive notification from direct child nodes only",
"1"),
1323 new KeyValuePair<string, string>(
"Receive notification from all descendent nodes",
"all")
1324 },
"Type of subscription.",
null,
null,
string.Empty,
false,
false,
false));
1330 private Task PubSubSetHandler(
object Sender,
IqEventArgs e)
1332 return this.PubSubSetHandler(
string.Empty, e, pubSubFeatures);
1335 private Task PepSetHandler(
object Sender,
IqEventArgs e)
1337 return this.PubSubSetHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
1343 Dictionary<CaseInsensitiveString, Subscription> List;
1344 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> List2;
1345 Subscription Subscription =
null;
1346 PubSubNode Node =
null;
1348 string NodeName =
null;
1350 bool InsertNode =
false;
1351 bool NodeUpdated =
false;
1352 bool InsertSubscription =
false;
1353 bool SubscriptionUpdated =
false;
1354 bool SubscriptionDeleted =
false;
1355 bool IncludeOptions =
false;
1356 bool NotificationToOwner =
false;
1358 foreach (XmlNode N
in e.
Query.ChildNodes)
1360 if (N is XmlElement E)
1362 switch (E.LocalName)
1367 await e.
IqErrorForbidden(e.
To,
"Only accounts registered on the broker are allowed to create nodes.",
"en");
1372 if (Account is
null)
1374 await e.
IqError(
"auth",
"<registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>", e.
To,
1375 "Only accounts registered on the broker are allowed to create nodes.",
"en");
1386 if (
string.IsNullOrEmpty(NodeName))
1388 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1389 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1394 if (!(Node is
null))
1400 if (
string.IsNullOrEmpty(Service))
1402 if (NodeName != e.
From.
Account && !(await XmppServerModule.GetAccountAsync(NodeName) is
null))
1404 await e.
IqErrorConflict(e.
To,
"Node name conflict: Conflicts with existing account.",
"en");
1408 if (!(IoTGateway.Gateway.RootFolder is
null))
1412 string s = Path.Combine(IoTGateway.Gateway.RootFolder, NodeName);
1416 await e.
IqErrorConflict(e.
To,
"Node name conflict: Conflicts with existing web file.",
"en");
1419 else if (Directory.Exists(s))
1421 await e.
IqErrorConflict(e.
To,
"Node name conflict: Conflicts with existing web folder.",
"en");
1432 Node =
new PubSubNode()
1438 Created = DateTime.UtcNow,
1447 if (await this.ConfigureNode(E, Node, e))
1455 if (
string.IsNullOrEmpty(NodeName))
1457 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1458 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1467 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1468 "<invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid JID",
"en");
1478 if (!Node.AllowSubscriptions)
1480 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1481 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='subscribe'/>", e.
To,
"Node does not support subscriptions.",
"en");
1485 if (!(XmppServerModule.PersistenceLayer is
null) &&
1486 await XmppServerModule.PersistenceLayer.IsBlocked(e.
From.
BareJid, Node.Creator))
1499 switch (Node.AccessModel)
1507 await e.
IqError(
"cancel",
"<not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1508 "<closed-node xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Not on whitelist.",
"en");
1525 foreach (
string s
in Node.RosterGroupsAllowed)
1529 if (
string.Compare(s, s2,
true) == 0)
1543 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1544 "<not-in-roster-group xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1545 "Not in corresponding roster group.",
"en");
1561 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1562 "<presence-subscription-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1563 "Presence subscription required to subscribe to node.",
"en");
1571 if (Subscription is
null)
1573 Subscription =
new Subscription()
1579 Status = NodeSubscriptionStatus.pending
1582 InsertSubscription =
true;
1589 SubscriptionUpdated =
true;
1593 NotificationToOwner =
true;
1597 if (Subscription is
null)
1601 if (Subscription is
null)
1603 Subscription =
new Subscription()
1609 Status = NodeSubscriptionStatus.subscribed
1612 InsertSubscription =
true;
1619 SubscriptionUpdated =
true;
1627 if (
string.IsNullOrEmpty(NodeName))
1629 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1630 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1639 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1640 "<invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid JID",
"en");
1653 if (Subscription is
null)
1655 await e.
IqError(
"cancel",
"<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1656 "<not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Not subscribed.",
"en");
1660 if (!
string.IsNullOrEmpty(SubId) && SubId != Subscription.ObjectId)
1662 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1663 "<invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid subscription ID.",
"en");
1667 lock (this.subscriptionsByJidAndServiceAndNode)
1669 if (this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out List2) &&
1670 List2.TryGetValue(Service, out List))
1672 List.Remove(NodeName);
1676 lock (this.subscriptionsByServiceAndNodeAndJid)
1678 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out List2) &&
1679 List2.TryGetValue(NodeName, out List))
1686 SubscriptionDeleted =
true;
1690 if (Subscription is
null)
1693 if (
string.IsNullOrEmpty(NodeName))
1695 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1696 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1705 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1706 "<invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid JID",
"en");
1719 if (Subscription is
null)
1721 await e.
IqError(
"cancel",
"<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1722 "<not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Not subscribed.",
"en");
1726 if (!
string.IsNullOrEmpty(SubId) && SubId != Subscription.ObjectId)
1728 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1729 "<invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid subscription ID.",
"en");
1734 if (!await this.ConfigureSubscription(E, Subscription, e))
1737 SubscriptionUpdated =
true;
1739 IncludeOptions =
true;
1744 if (
string.IsNullOrEmpty(NodeName))
1746 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1747 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1763 await e.
IqErrorForbidden(e.
To,
"Access denied. Not authorized to publish item on node.",
"en");
1767 if (Node.NodeType ==
NodeType.collection)
1769 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1770 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='publish'/>", e.
To,
1771 "Publishing items on collection nodes is not supported.",
"en");
1775 foreach (XmlNode N2
in E.ChildNodes)
1777 if (N2 is XmlElement E2 && E2.LocalName ==
"item" && E2.NamespaceURI == E.NamespaceURI)
1779 if (Node.TransientNode)
1781 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1782 "<item-forbidden xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Transient nodes do not accept items.",
"en");
1787 PubSubItem Item =
null;
1791 Item = await this.LoadItem(Service, NodeName, ItemId);
1793 if (!(Item is
null) && Item.Node != NodeName)
1795 await e.
IqErrorBadRequest(e.
To,
"Item identity points to item belonging to another node.",
"en");
1802 if (Node.PublishOnWeb && !
string.IsNullOrEmpty(ItemId))
1804 foreach (XmlNode N3
in E2.ChildNodes)
1806 if (N3 is XmlElement E3 &&
1807 E3.LocalName ==
"markdownContent" &&
1808 E3.NamespaceURI == WebServices.PubSubContent.Content.LilsisNamespace)
1810 StringBuilder Link =
new StringBuilder();
1814 if (Ports.Length > 0)
1816 Link.Append(
"https://");
1817 Link.Append(IoTGateway.Gateway.Domain);
1819 if (Array.IndexOf(Ports, Networking.HTTP.HttpServer.DefaultHttpsPort) < 0)
1822 Link.Append(Ports[0].ToString());
1829 Link.Append(
"http://");
1830 Link.Append(IoTGateway.Gateway.Domain);
1832 if (Ports.Length > 0 && Array.IndexOf(Ports, Networking.HTTP.HttpServer.DefaultHttpPort) < 0)
1835 Link.Append(Ports[0].ToString());
1840 Link.Append(
System.Web.HttpUtility.UrlEncode(Node.Name));
1845 Link.Append(ItemId);
1847 E3.SetAttribute(
"link", Link.ToString());
1852 Content = E2.InnerXml.Trim();
1854 byte[] ContentBin = Encoding.UTF8.GetBytes(Content);
1855 DateTime Now = DateTime.UtcNow;
1857 if (Node.DeliverPayloads &&
string.IsNullOrEmpty(Content))
1859 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1860 "<payload-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1861 "Missing payload.",
"en");
1865 if (ContentBin.Length > Node.MaxPayloadSize)
1867 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1868 "<payload-too-big xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1869 "Payload too big.",
"en");
1873 if (!
string.IsNullOrEmpty(Node.PayloadType))
1879 ContentResponse Temp = await Decoder.DecodeAsync(Node.PayloadType, ContentBin, Encoding.UTF8,
1880 Array.Empty<KeyValuePair<string, string>>(),
null,
null);
1884 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1885 "<invalid-payload xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1886 "Payload does not conform to Internet Content Type " + Node.PayloadType,
"en");
1892 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1893 "<invalid-payload xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1894 "Payload does not conform to Internet Content Type " + Node.PayloadType,
"en");
1900 foreach (XmlNode N3
in E2.ChildNodes)
1902 if (N3 is XmlElement E3 && E3.NamespaceURI != Node.PayloadType)
1904 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1905 "<invalid-payload xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1906 "Payload does not conform to namespace " + Node.PayloadType,
"en");
1913 if (!(Item is
null))
1915 Node.BytesPublished -= await this.GetItemSize(Item);
1918 Item.Payload = Content;
1925 if (Node.NrItems >= Node.MaxItems)
1927 if (Node.AutoDelete)
1929 bool Deleted =
false;
1935 Node.BytesDeleted += await this.GetItemSize(Item2);
1938 if (Node.NrItems < 0)
1949 if (Node.NrItems >= Node.MaxItems)
1956 Item =
new PubSubItem()
1966 if (Node.ItemExpireSeconds !=
int.MaxValue)
1967 Item.Expires = Now.AddSeconds(Node.ItemExpireSeconds);
1969 if (Node.PersistItems)
1973 this.items[Item.Key] = Item;
1976 Node.LastPublished = Item;
1979 if (Node.PersistItems)
1981 Node.BytesPublished += await this.GetItemSize(Item);
1985 Xml =
new StringBuilder();
1987 Xml.Append(
"<pubsub xmlns='");
1989 Xml.Append(
"'><publish node='");
1991 Xml.Append(
"'><item id='");
1992 Xml.Append(
XML.
Encode(Item.ItemIdOrObjectId));
1993 Xml.Append(
"'/></publish></pubsub>");
1997 if (Node.DeliverNotifications)
1998 await this.SendItemEventNotification(Node, Item, e.
From, e.
Language, Now);
2004 if (Node.PersistItems)
2006 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2007 "<item-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Item required.",
"en");
2018 if (
string.IsNullOrEmpty(NodeName))
2020 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2021 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
2032 if (Node.NodeType ==
NodeType.collection)
2034 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2035 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='publish'/>", e.
To,
2036 "Publishing items on collection nodes is not supported.",
"en");
2040 if (!Node.PersistItems)
2042 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2043 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/>",
2044 e.
To,
"Persisted items not supported.",
"en");
2048 foreach (XmlNode N2
in E.ChildNodes)
2050 if (N2 is XmlElement E2 && E2.LocalName ==
"item" && E2.NamespaceURI == E.NamespaceURI)
2053 PubSubItem Item =
null;
2055 Item = await this.LoadItem(Service, NodeName, ItemId);
2059 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2060 "<item-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Item required.",
"en");
2064 if (Item.Node != NodeName)
2066 await e.
IqErrorBadRequest(e.
To,
"Item identity points to item belonging to another node.",
"en");
2072 await e.
IqErrorForbidden(e.
To,
"Access denied. Not authorized to delete item on node.",
"en");
2076 Node.BytesDeleted += await this.GetItemSize(Item);
2079 if (Node.NrItems < 0)
2087 if (Node.DeliverNotifications)
2089 DateTime Now = DateTime.UtcNow;
2094 if (Rec.Expires < Now)
2096 await this.DeleteSubscription(Service, Rec);
2097 await this.NotifySubscriptionChanged(Rec, FromBareJid);
2099 else if (Rec.Deliver &&
2104 Xml =
new StringBuilder();
2108 Xml.Append(
"<event xmlns='");
2110 Xml.Append(
"'><items node='");
2112 Xml.Append(
"'><retract id='");
2113 Xml.Append(
XML.
Encode(Item.ItemIdOrObjectId));
2114 Xml.Append(
"'/></items></event><headers xmlns='");
2116 Xml.Append(
"'><header name='SubID'>");
2117 Xml.Append(Rec.ObjectId);
2118 Xml.Append(
"</header></headers>");
2121 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2132 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2133 "<item-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Item required.",
"en");
2148 lock (this.nodesByServiceAndName)
2150 if (this.nodesByServiceAndName.TryGetValue(Service, out Nodes Nodes))
2152 Nodes.NodesByName[Node.Name] = Node;
2154 Nodes.NodesArray =
new PubSubNode[Nodes.NodesByName.Count];
2155 Nodes.NodesByName.Values.CopyTo(Nodes.NodesArray, 0);
2160 else if (NodeUpdated)
2166 if (!(Subscription is
null))
2168 if (InsertSubscription)
2170 lock (this.subscriptionsByJidAndServiceAndNode)
2172 if (this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out List2) &&
2173 List2.TryGetValue(Service, out List))
2175 List[NodeName] = Subscription;
2179 lock (this.subscriptionsByServiceAndNodeAndJid)
2181 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out List2) &&
2182 List2.TryGetValue(NodeName, out List))
2184 List[Jid] = Subscription;
2190 else if (SubscriptionUpdated)
2192 else if (SubscriptionDeleted)
2196 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
2198 Xml =
new StringBuilder();
2200 Xml.Append(
"<pubsub xmlns='");
2203 Subscription.Serialize(Node, Xml, IncludeOptions);
2204 Xml.Append(
"</pubsub>");
2211 await this.SendLastItem(Service, Node, Subscription.Jid, FromBareJid, e.
Language);
2214 if (NotificationToOwner)
2218 new HiddenField(
"pubsub#subid", Subscription.ObjectId),
2220 new string[] { NodeName },
null,
"Name of node.",
2221 null,
null,
string.Empty,
false,
false,
false),
2222 new JidSingleField(
null,
"pubsub#subscriber_jid",
"Subscriber:",
false,
2223 new string[] { Jid },
null,
"JID of subscriber.",
2224 null,
null,
string.
Empty,
false,
false,
false),
2225 new BooleanField(
null,
"pubsub#allow",
"Authorize subscription request.",
true,
2226 new string[] {
"0" },
null,
"Check this box to authorize the subscription.",
2229 Title =
"Subscription request",
2230 Instructions =
new string[]
2232 Jid +
" wants to subscribe to " + NodeName +
".",
2233 "Approve this subscription request by checking the box below, and click OK.",
2234 "Deny this request by leaving the box uncheced, and click OK."
2238 Xml =
new StringBuilder();
2242 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2267 public Task<string>
PublishItem(
string Service,
string NodeName,
string AutoCreateAccess,
string From,
2268 string Domain,
string ItemId,
string XmlContent,
string Language)
2272 return this.
PublishItem(Service, NodeName, AutoCreateAccess, From, Domain, ItemId, Doc, Language);
2287 public Task<string>
PublishItem(
string Service,
string NodeName,
string AutoCreateAccess,
string From,
2288 string Domain,
string ItemId, XmlDocument XmlContent,
string Language)
2292 if (
string.IsNullOrEmpty(AutoCreateAccess))
2293 AutoCreateAccess2 =
null;
2295 AutoCreateAccess2 = Parsed;
2297 throw new ArgumentException(
"Invalid enum value", nameof(AutoCreateAccess));
2299 return this.
PublishItem(Service, NodeName, AutoCreateAccess2, From, Domain, ItemId, XmlContent, Language);
2315 string Domain,
string ItemId, XmlDocument XmlContent,
string Language)
2336 if (
string.IsNullOrEmpty(NodeName))
2337 throw new ArgumentException(
"Node name required.", nameof(NodeName));
2340 ??
throw new ArgumentException(
"Node not found.", nameof(NodeName));
2343 throw new ArgumentException(
"Publishing items on collection nodes is not supported.", nameof(NodeName));
2346 throw new ArgumentException(
"Transient nodes do not accept items.", nameof(NodeName));
2352 Item = await this.LoadItem(Service, NodeName, ItemId);
2354 if (!(Item is
null) && Item.
Node != NodeName)
2355 throw new ArgumentException(
"Item identity points to item belonging to another node.", nameof(ItemId));
2360 if (Node.
PublishOnWeb && !
string.IsNullOrEmpty(ItemId))
2362 if (!(XmlContent.DocumentElement is
null) &&
2363 XmlContent.DocumentElement.LocalName ==
"markdownContent" &&
2364 XmlContent.DocumentElement.NamespaceURI == WebServices.PubSubContent.Content.LilsisNamespace)
2366 StringBuilder Link =
new StringBuilder();
2370 if (Ports.Length > 0)
2372 Link.Append(
"https://");
2373 Link.Append(IoTGateway.Gateway.Domain);
2375 if (Array.IndexOf(Ports, Networking.HTTP.HttpServer.DefaultHttpsPort) < 0)
2378 Link.Append(Ports[0].ToString());
2385 Link.Append(
"http://");
2386 Link.Append(IoTGateway.Gateway.Domain);
2388 if (Ports.Length > 0 && Array.IndexOf(Ports, Networking.HTTP.HttpServer.DefaultHttpPort) < 0)
2391 Link.Append(Ports[0].ToString());
2396 Link.Append(
System.Web.HttpUtility.UrlEncode(Node.
Name));
2401 Link.Append(ItemId);
2403 XmlContent.DocumentElement.SetAttribute(
"link", Link.ToString());
2407 Content = XmlContent.InnerXml.Trim();
2409 byte[] ContentBin = Encoding.UTF8.GetBytes(Content);
2410 DateTime Now = DateTime.UtcNow;
2413 throw new ArgumentException(
"Missing payload.", nameof(XmlContent));
2416 throw new ArgumentException(
"Payload too big.", nameof(XmlContent));
2425 Array.Empty<KeyValuePair<string, string>>(),
null,
null);
2428 throw new ArgumentException(
"Payload does not conform to Internet Content Type " + Node.
PayloadType, nameof(XmlContent));
2432 throw new ArgumentException(
"Payload does not conform to Internet Content Type " + Node.
PayloadType, nameof(XmlContent));
2435 else if (XmlContent.DocumentElement is
null || XmlContent.DocumentElement.NamespaceURI != Node.
PayloadType)
2436 throw new ArgumentException(
"Payload does not conform to namespace " + Node.
PayloadType, nameof(XmlContent));
2439 if (!(Item is
null))
2441 Node.BytesPublished -= await this.GetItemSize(Item);
2443 Item.Publisher = From.
BareJid;
2444 Item.Payload = Content;
2455 bool Deleted =
false;
2461 Node.BytesDeleted += await this.GetItemSize(Item2);
2476 throw new InvalidOperationException(
"Too many items registered on node.");
2496 this.items[Item.Key] = Item;
2499 Node.LastPublished = Item;
2504 Node.BytesPublished += await this.GetItemSize(Item);
2509 await this.SendItemEventNotification(Node, Item, From, Language, Now);
2516 StringBuilder Xml =
new StringBuilder();
2518 Xml.Append(
"<event xmlns='");
2520 Xml.Append(
"'><items node='");
2522 Xml.Append(
"'><item id='");
2524 Xml.Append(
"' publisher='");
2534 Xml.Append(
"</item>");
2539 Xml.Append(
"</items></event>");
2549 Xml.Append(
"<addresses xmlns='");
2551 Xml.Append(
"'><address type='replyto' jid='");
2553 Xml.Append(
"'/></addresses>");
2556 EventXml = Xml.ToString();
2558 IEnumerable<IRosterItem> Roster = await XmppServerModule.GetRosterAsync(From.
Account);
2559 if (!(Roster is
null))
2567 if (!(Jids is
null))
2572 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2573 From.
BareJid, Jid2, Language, EventXml);
2580 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2591 if (Rec.Expires < Now)
2593 await this.DeleteSubscription(Node.
Service, Rec);
2594 await this.NotifySubscriptionChanged(Rec, FromBareJid);
2596 else if (Rec.Deliver &&
2602 Xml.Append(EventXml);
2603 Xml.Append(
"<headers xmlns='");
2605 Xml.Append(
"'><header name='SubID'>");
2606 Xml.Append(Rec.ObjectId);
2607 Xml.Append(
"</header></headers>");
2610 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2620 if (!Node.LastPublishedKnown)
2626 Node.LastPublished = Item;
2629 Node.LastPublishedKnown =
true;
2632 PubSubItem LastItem = Node.LastPublished;
2633 if (!(LastItem is
null))
2635 StringBuilder Xml =
new StringBuilder();
2637 Xml.Append(
"<event xmlns='");
2639 Xml.Append(
"'><items node='");
2641 Xml.Append(
"'><item id='");
2642 Xml.Append(
XML.
Encode(LastItem.ItemIdOrObjectId));
2643 Xml.Append(
"' publisher='");
2644 Xml.Append(
XML.
Encode(LastItem.Publisher));
2646 if (Node.DeliverPayloads)
2650 if (
XML.
IsValidXml(LastItem.Payload,
true,
true,
true,
true,
false,
false))
2651 Xml.Append(LastItem.Payload);
2653 Xml.Append(
"</item>");
2658 Xml.Append(
"</items></event>");
2659 Xml.Append(
"<delay xmlns='");
2661 Xml.Append(
"' stamp='");
2662 Xml.Append(
XML.
Encode(LastItem.Created));
2666 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2675 List<Filter> Filters =
new List<Filter>()
2681 if (!
string.IsNullOrEmpty(From))
2685 PubSubItem Item = await this.LoadItem(Service, NodeName, From);
2686 if (Item is
null || Item.Node != NodeName)
2697 List<PubSubItem> Result =
new List<PubSubItem>();
2699 string Last =
string.Empty;
2701 foreach (PubSubItem Item
in await
Database.
Find<PubSubItem>(0, MaxCount + 1,
new FilterAnd(Filters.ToArray()),
"-Service",
"-Node",
"-Created"))
2703 if (MaxCount-- <= 0)
2709 Last = Item.ItemIdOrObjectId;
2713 return new Tuple<PubSubItem[], bool, string>(Result.ToArray(), More, Last);
2718 StringBuilder Key =
new StringBuilder();
2720 Key.Append(Service);
2722 Key.Append(NodeName);
2727 if (this.items.
TryGetValue(Key.ToString(), out PubSubItem Item))
2741 if (Guid.TryParse(ItemId, out Guid Guid2))
2749 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> List2;
2750 Dictionary<CaseInsensitiveString, Subscription> List;
2752 lock (this.subscriptionsByJidAndServiceAndNode)
2754 if (this.subscriptionsByJidAndServiceAndNode.TryGetValue(Subscription.Jid, out List2) &&
2755 List2.TryGetValue(Service, out List))
2757 List.Remove(Subscription.Node);
2761 lock (this.subscriptionsByServiceAndNodeAndJid)
2763 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out List2) &&
2764 List2.TryGetValue(Subscription.Node, out List))
2766 List.Remove(Subscription.Jid);
2773 private async Task<int> GetItemSize(PubSubItem Item)
2775 if (!(this.itemSerializer is
null))
2778 await this.itemSerializer.
Serialize(Serializer,
false,
false, Item,
null);
2781 return Binary.Length;
2786 Encoding.UTF8.GetBytes(Item.ObjectId).Length +
2787 Encoding.UTF8.GetBytes(Item.ItemId).Length +
2788 Encoding.UTF8.GetBytes(Item.Node).Length +
2789 Encoding.UTF8.GetBytes(Item.Publisher).Length +
2790 Encoding.UTF8.GetBytes(Item.Payload).Length +
2795 private Task PubSubOwnerGetHandler(
object Sender,
IqEventArgs e)
2797 return this.PubSubOwnerGetHandler(
string.Empty, e, pubSubFeatures);
2800 private Task PepOwnerGetHandler(
object Sender,
IqEventArgs e)
2802 return this.PubSubOwnerGetHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
2809 foreach (XmlNode N
in e.
Query.ChildNodes)
2811 if (N is XmlElement E)
2813 switch (E.LocalName)
2817 if (
string.IsNullOrEmpty(NodeName))
2819 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2820 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
2837 DataForm Form = this.GetForm(Node, e);
2838 StringBuilder Xml =
new StringBuilder();
2840 Xml.Append(
"<pubsub xmlns='");
2842 Xml.Append(
"'><configure node='");
2846 Xml.Append(
"</configure></pubsub>");
2852 Node =
new PubSubNode()
2854 Domain = e.To.Address
2856 Form = this.GetForm(Node, e);
2857 Xml =
new StringBuilder();
2859 Xml.Append(
"<pubsub xmlns='");
2861 Xml.Append(
"'><default>");
2863 Xml.Append(
"</default></pubsub>");
2868 case "subscriptions":
2870 if (
string.IsNullOrEmpty(NodeName))
2872 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2873 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
2890 Xml =
new StringBuilder();
2892 Xml.Append(
"<pubsub xmlns='");
2894 Xml.Append(
"'><subscriptions node='");
2900 foreach (Subscription Subscription
in Subscriptions)
2902 Xml.Append(
"<subscription jid='");
2903 Xml.Append(
XML.
Encode(Subscription.Jid));
2904 Xml.Append(
"' subscription='");
2905 Xml.Append(Subscription.Status.ToString());
2909 Xml.Append(
"</subscriptions></pubsub>");
2914 case "affiliations":
2916 if (
string.IsNullOrEmpty(NodeName))
2918 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2919 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
2936 Xml =
new StringBuilder();
2938 Xml.Append(
"<pubsub xmlns='");
2940 Xml.Append(
"'><affiliations node='");
2944 foreach (KeyValuePair<CaseInsensitiveString, AffiliationStatus> Affiliation
in Node.GetAffiliations())
2946 Xml.Append(
"<affiliation jid='");
2948 Xml.Append(
"' affiliation='");
2953 Xml.Append(
"</affiliations></pubsub>");
2971 return "publish-only";
2980 new TextSingleField(
null,
"pubsub#title",
"Title:",
false,
new string[] { Node.Title },
null,
2982 new BooleanField(
null,
"pubsub#deliver_notifications",
"Event notifications.",
false,
2983 new string[] { Node.DeliverNotifications ?
"1" :
"0" },
null,
2984 "Whether to deliver event notifications.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2985 new BooleanField(
null,
"pubsub#deliver_payloads",
"Deliver payloads.",
false,
2986 new string[] { Node.DeliverPayloads ?
"1" :
"0" },
null,
2987 "Whether to deliver payloads with event notifications.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2988 new BooleanField(
null,
"pubsub#notify_config",
"Notify configurations.",
false,
2989 new string[] { Node.NotifyConfig ?
"1" :
"0" },
null,
2990 "Notify subscribers when the node configuration changes.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2991 new BooleanField(
null,
"pubsub#notify_delete",
"Notify delete.",
false,
2992 new string[] { Node.NotifyDelete ?
"1" :
"0" },
null,
2993 "Notify subscribers when the node is deleted.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2994 new BooleanField(
null,
"pubsub#notify_retract",
"Notify retract.",
false,
2995 new string[] { Node.NotifyRetract ?
"1" :
"0" },
null,
2996 "Notify subscribers when items are removed from the node.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2997 new BooleanField(
null,
"pubsub#notify_sub",
"Notify subscriptions.",
false,
2998 new string[] { Node.NotifySubscriptions ?
"1" :
"0" },
null,
2999 "Notify owners about new subscribers and unsubscribes.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
3000 new BooleanField(
null,
"pubsub#persist_items",
"Persist items.",
false,
3001 new string[] { Node.PersistItems ?
"1" :
"0" },
null,
3003 new TextSingleField(
null,
"pubsub#max_items",
"Items to persist:",
false,
3004 new string[] { Node.MaxItems.ToString() },
null,
3006 string.Empty,
false,
false,
false),
3007 new TextSingleField(
null,
"pubsub#item_expire",
"Seconds before items expire:",
false,
3008 new string[] { Node.ItemExpireSeconds.ToString() },
null,
3010 string.Empty,
false,
false,
false),
3011 new BooleanField(
null,
"pubsub#purge_offline",
"Purge when publisher offline.",
false,
3012 new string[] { Node.PurgeOffline ?
"1" :
"0" },
null,
3014 string.Empty,
false,
false,
false),
3015 new TextSingleField(
null,
"pubsub#node_expire",
"Date when node expires:",
false,
3016 new string[] {
XML.
Encode(Node.Expires,
true) },
null,
3018 string.Empty,
false,
false,
false),
3019 new BooleanField(
null,
"pubsub#subscribe",
"Allow subscriptions.",
false,
3020 new string[] { Node.AllowSubscriptions ?
"1" :
"0" },
null,
3022 string.Empty,
false,
false,
false),
3023 new ListSingleField(
null,
"pubsub#access_model",
"Access model:",
false,
3024 new string[] { Node.AccessModel.ToString() },
new KeyValuePair<string, string>[]
3026 new KeyValuePair<string, string>(
"Everyone has access",
"open"),
3027 new KeyValuePair<string, string>(
"Contacts with approved presence subscriptions",
"presence"),
3028 new KeyValuePair<string, string>(
"Contacts in specified groups",
"roster"),
3029 new KeyValuePair<string, string>(
"Only authorized entities",
"authorize"),
3030 new KeyValuePair<string, string>(
"Only if on whitelist",
"whitelist")
3033 string.Empty,
false,
false,
false),
3034 new TextMultiField(
null,
"pubsub#roster_groups_allowed",
"Roster groups allowed:",
false,
3035 Node.RosterGroupsAllowed,
null,
3037 string.Empty,
false,
false,
false),
3038 new ListSingleField(
null,
"pubsub#publish_model",
"Publish model:",
false,
3039 new string[] { Node.PublisherModel.ToString() },
new KeyValuePair<string, string>[]
3041 new KeyValuePair<string, string>(
"Only publishers may publish",
"publishers"),
3042 new KeyValuePair<string, string>(
"Subscribers may publish",
"subscribers"),
3043 new KeyValuePair<string, string>(
"Anyone may publish",
"open")
3046 string.Empty,
false,
false,
false),
3047 new TextSingleField(
null,
"pubsub#max_payload_size",
"Maximum payload size:",
false,
3048 new string[] { Node.MaxPayloadSize.ToString() },
null,
3050 string.Empty,
false,
false,
false),
3051 new ListSingleField(
null,
"pubsub#send_last_published_item",
"Send last item:",
false,
3052 new string[] { Node.SendLastPublishedItem.ToString() },
new KeyValuePair<string, string>[]
3054 new KeyValuePair<string, string>(
"Never",
"never"),
3055 new KeyValuePair<string, string>(
"When a new subscription is processed",
"on_sub"),
3056 new KeyValuePair<string, string>(
"When a new subscription is processed and whenever a subscriber comes online",
"on_sub_and_presence")
3059 string.Empty,
false,
false,
false),
3060 new BooleanField(
null,
"pubsub#presence_based_delivery",
"Notifications only to available users.",
false,
3061 new string[] { Node.PresenceBasedDelivery ?
"1" :
"0" },
null,
3063 string.Empty,
false,
false,
false),
3064 new ListSingleField(
null,
"pubsub#notification_type",
"Notification type:",
false,
3065 new string[] { Node.NotificationType.ToString() },
new KeyValuePair<string, string>[]
3067 new KeyValuePair<string, string>(
"Messages of type normal",
"normal"),
3068 new KeyValuePair<string, string>(
"Messages of type headline",
"headline")
3070 "Specify the delivery style for event notifications.",
null,
new BasicValidation(),
3071 string.Empty,
false,
false,
false),
3073 new string[] { Node.PayloadType },
null,
3075 string.Empty,
false,
false,
false),
3076 new TextSingleField(
null,
"pubsub#dataform_xslt",
"Data Forms XSLT:",
false,
3077 new string[] { Node.DataFormXsltUrl },
null,
3078 "The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine.",
StringDataType.
Instance,
new BasicValidation(),
3079 string.Empty,
false,
false,
false),
3080 new TextSingleField(
null,
"pubsub#body_xslt",
"Message Body XSLT:",
false,
3081 new string[] { Node.BodyXsltUrl },
null,
3083 string.Empty,
false,
false,
false),
3084 new TextMultiField(
null,
"pubsub#collection",
"Collections:",
false, Node.Collections.ToStringArray(),
null,
3086 string.Empty,
false,
false,
false),
3087 new JidMultiField(
null,
"pubsub#contact",
"Contacts:",
false, Node.Contact.ToStringArray(),
null,
3089 string.Empty,
false,
false,
false),
3090 new ListSingleField(
null,
"pubsub#itemreply",
"Send replies to:",
false,
3091 new string[] { Node.ItemReply.ToString() },
new KeyValuePair<string, string>[]
3093 new KeyValuePair<string, string>(
"Statically specify a replyto of the node owner(s)",
"owner"),
3094 new KeyValuePair<string, string>(
"Dynamically specify a replyto of the item publisher",
"publisher")
3096 "Whether owners or publisher should receive replies to items.",
null,
new BasicValidation(),
3097 string.Empty,
false,
false,
false),
3098 new ListSingleField(
null,
"pubsub#children_association_policy",
"Who can associate child nodes:",
false,
3099 new string[] { Node.ChildAssociationPolicy.ToString() },
new KeyValuePair<string, string>[]
3101 new KeyValuePair<string, string>(
"Anyone may associate leaf nodes with the collection",
"all"),
3102 new KeyValuePair<string, string>(
"Only collection node owners may associate leaf nodes with the collection",
"owners"),
3103 new KeyValuePair<string, string>(
"Only those on a whitelist may associate leaf nodes with the collection",
"whitelist")
3105 "Who may associate leaf nodes with a collection.",
null,
new BasicValidation(),
3106 string.Empty,
false,
false,
false),
3107 new TextMultiField(
null,
"pubsub#children_association_whitelist",
"Child association whitelist:",
false, Node.ChildAssociationWhitelist.ToStringArray(),
null,
3109 string.Empty,
false,
false,
false),
3110 new TextMultiField(
null,
"pubsub#children",
"Child nodes:",
false, Node.Children.ToStringArray(),
null,
3112 string.Empty,
false,
false,
false),
3113 new TextSingleField(
null,
"pubsub#children_max",
"Maximum amount of child nodes:",
false, Node.Children.ToStringArray(),
null,
3115 new RangeValidation(
"1",
int.MaxValue.ToString()),
string.Empty,
false,
false,
false),
3117 new string[] { Node.NodeType.ToString() },
new KeyValuePair<string, string>[]
3119 new KeyValuePair<string, string>(
"The node is a leaf node (default)",
"leaf"),
3120 new KeyValuePair<string, string>(
"The node is a collection node",
"collection")
3122 "Whether the node is a leaf (default) or a collection.",
null,
new BasicValidation(),
3123 string.Empty,
false,
false,
false),
3124 new JidMultiField(
null,
"pubsub#replyroom",
"Reply rooms:",
false, Node.ReplyRooms.ToStringArray(),
null,
3126 string.Empty,
false,
false,
false),
3127 new JidMultiField(
null,
"pubsub#replyto",
"Reply to:",
false, Node.ReplyTo.ToStringArray(),
null,
3129 string.Empty,
false,
false,
false),
3130 new TextMultiField(
null,
"pubsub#description",
"Description:",
false, Node.Description,
null,
3132 new TextSingleField(
null,
"pubsub#language",
"Default language:",
false,
new string[] { Node.Language },
null,
3136 private Task PubSubOwnerSetHandler(
object Sender,
IqEventArgs e)
3138 return this.PubSubOwnerSetHandler(
string.Empty, e, pubSubFeatures);
3141 private Task PepOwnerSetHandler(
object Sender,
IqEventArgs e)
3143 return this.PubSubOwnerSetHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
3149 PubSubNode Node =
null;
3150 bool NodeUpdated =
false;
3152 foreach (XmlNode N
in e.
Query.ChildNodes)
3154 if (N is XmlElement E)
3156 switch (E.LocalName)
3160 if (
string.IsNullOrEmpty(NodeName))
3162 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3163 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
3174 if (await this.ConfigureNode(E, Node, e))
3198 string RedirectUri =
null;
3200 foreach (XmlNode N2
in E.ChildNodes)
3202 if (N2 is XmlElement E2 && E2.LocalName ==
"redirect" && E2.NamespaceURI == E.NamespaceURI)
3209 await this.DeleteNode(Service, Node, FromBareJid);
3223 (
bool CanPurge,
bool OnlyItemsBySameEntity) = await Node.CanPurgeNode(e.
From.
BareJid);
3230 Node.LastPublished =
null;
3232 if (!Node.PersistItems)
3234 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3235 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/>",
3236 e.
To,
"Node does not persist items.",
"en");
3240 if (OnlyItemsBySameEntity)
3255 if (Node.DeliverNotifications)
3257 DateTime Now = DateTime.UtcNow;
3258 StringBuilder Xml =
null;
3262 if (Rec.Expires < Now)
3264 await this.DeleteSubscription(Service, Rec);
3265 await this.NotifySubscriptionChanged(Rec, FromBareJid);
3267 else if (Rec.Deliver &&
3272 Xml =
new StringBuilder();
3276 Xml.Append(
"<event xmlns='");
3278 Xml.Append(
"'><purge node='");
3280 Xml.Append(
"'/></event><headers xmlns='");
3282 Xml.Append(
"'><header name='SubID'>");
3283 Xml.Append(Rec.ObjectId);
3284 Xml.Append(
"</header></headers>");
3287 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
3295 case "subscriptions":
3297 if (
string.IsNullOrEmpty(NodeName))
3299 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3300 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
3317 foreach (XmlNode N2
in E.ChildNodes)
3319 if (N2 is XmlElement E2 && E2.LocalName ==
"subscription" &&
3320 E2.NamespaceURI == E.NamespaceURI)
3326 if (Subscription is
null)
3332 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> List2;
3333 Dictionary<CaseInsensitiveString, Subscription> List =
null;
3335 Subscription =
new Subscription()
3344 lock (this.subscriptionsByJidAndServiceAndNode)
3346 if (this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out List2) &&
3347 List2.TryGetValue(Service, out List))
3349 List[NodeName] = Subscription;
3353 lock (this.subscriptionsByServiceAndNodeAndJid)
3355 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out List2) &&
3356 List2.TryGetValue(NodeName, out List))
3358 List[Jid] = Subscription;
3364 else if (Subscription.Status == Status)
3368 Subscription.Status = Status;
3371 await this.DeleteSubscription(Service, Subscription);
3376 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
3383 case "affiliations":
3385 if (
string.IsNullOrEmpty(NodeName))
3387 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3388 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
3405 foreach (XmlNode N2
in E.ChildNodes)
3407 if (N2 is XmlElement E2 && E2.LocalName ==
"affiliation" &&
3408 E2.NamespaceURI == E.NamespaceURI)
3414 if (s ==
"publish-only")
3419 Node.SetAffiliation(Jid, Affiliation);
3420 await this.NotifyAffiliationChanged(NodeName, Jid, Affiliation, FromBareJid);
3450 Dictionary<CaseInsensitiveString, Subscription> List =
null;
3451 string NodeName = Node.Name;
3453 lock (this.subscriptionsByServiceAndNodeAndJid)
3455 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out Dictionary<
CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> List2) &&
3456 List2.TryGetValue(NodeName, out List))
3458 List2.Remove(NodeName);
3464 if (!(List is
null))
3466 foreach (Subscription Subscription
in List.Values)
3469 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
3477 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
3483 lock (this.nodesByServiceAndName)
3485 if (this.nodesByServiceAndName.TryGetValue(Service, out Nodes Nodes))
3487 Nodes.NodesByName.Remove(NodeName);
3489 Nodes.NodesArray =
new PubSubNode[Nodes.NodesByName.Count];
3490 Nodes.NodesByName.Values.CopyTo(Nodes.NodesArray, 0);
3498 private async Task NotifySubscriptionChanged(Subscription Subscription,
XmppAddress FromBareJid)
3500 StringBuilder Xml =
new StringBuilder();
3502 Xml.Append(
"<event xmlns='");
3504 Xml.Append(
"'><subscription node='");
3505 Xml.Append(
XML.
Encode(Subscription.Node));
3506 Xml.Append(
"' jid='");
3507 Xml.Append(
XML.
Encode(Subscription.Jid));
3508 Xml.Append(
"' subscription='");
3509 Xml.Append(Subscription.Status.ToString());
3510 Xml.Append(
"'/></event>");
3513 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
3520 StringBuilder Xml =
new StringBuilder();
3522 Xml.Append(
"<event xmlns='");
3524 Xml.Append(
"'><affiliations node='");
3526 Xml.Append(
"'><affiliation jid='");
3528 Xml.Append(
"' affiliation='");
3529 Xml.Append(ToString(Affiliation));
3530 Xml.Append(
"'/></affiliations></event>");
3533 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
3538 private async Task<bool> ConfigureNode(XmlElement E, PubSubNode Node,
IqEventArgs e)
3540 foreach (XmlNode N2
in E.ChildNodes)
3542 if (N2 is XmlElement E2 && E2.LocalName ==
"x" && E2.NamespaceURI == Networking.XMPP.XmppClient.NamespaceData)
3548 switch (Form[
"FORM_TYPE"]?.ValueString)
3570 case "pubsub#access_model":
3571 if (!
string.IsNullOrEmpty(F.ValueString))
3574 Node.AccessModel = AccessModel;
3577 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3578 "<unsupported-access-model xmlns='http://jabber.org/protocol/pubsub#errors'/>",
3579 e.
To,
"Access model not supported.",
"en");
3585 case "pubsub#body_xslt":
3586 Node.BodyXsltUrl = F.ValueString;
3589 case "pubsub#collection":
3590 Node.Collections = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3591 Node.IsRoot = (Node.Collections is
null || Node.Collections.Length == 0);
3594 case "pubsub#contact":
3595 Node.Contact = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3598 case "pubsub#dataform_xslt":
3599 Node.DataFormXsltUrl = F.ValueString;
3602 case "pubsub#deliver_notifications":
3603 if (!
string.IsNullOrEmpty(F.ValueString))
3606 Node.DeliverNotifications = b;
3615 case "pubsub#deliver_payloads":
3616 if (!
string.IsNullOrEmpty(F.ValueString))
3619 Node.DeliverPayloads = b;
3628 case "pubsub#itemreply":
3629 if (!
string.IsNullOrEmpty(F.ValueString))
3631 if (Enum.TryParse(F.ValueString, out
NodeItemReply ItemReply))
3632 Node.ItemReply = ItemReply;
3641 case "pubsub#children_association_policy":
3642 if (!
string.IsNullOrEmpty(F.ValueString))
3645 Node.ChildAssociationPolicy = ChildAssociationPolicy;
3654 case "pubsub#children_association_whitelist":
3655 Node.ChildAssociationWhitelist = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3658 case "pubsub#children":
3659 Node.Children = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3662 case "pubsub#children_max":
3663 if (!
string.IsNullOrEmpty(F.ValueString))
3665 if (
int.TryParse(F.ValueString, out
int i) && i > 0)
3666 Node.MaxChildren = i;
3675 case "pubsub#max_items":
3676 if (!
string.IsNullOrEmpty(F.ValueString))
3678 if (
int.TryParse(F.ValueString, out
int i) && i > 0)
3688 case "pubsub#item_expire":
3689 if (!
string.IsNullOrEmpty(F.ValueString))
3691 if (
int.TryParse(F.ValueString, out
int i) && i > 0)
3692 Node.ItemExpireSeconds = i;
3701 case "pubsub#node_expire":
3702 if (!
string.IsNullOrEmpty(F.ValueString))
3704 if (
XML.
TryParse(F.ValueString, out DateTime TP) && TP >= DateTime.Today)
3714 case "pubsub#max_payload_size":
3715 if (!
string.IsNullOrEmpty(F.ValueString))
3717 if (
int.TryParse(F.ValueString, out
int i) && i > 0)
3718 Node.MaxPayloadSize = i;
3727 case "pubsub#node_type":
3728 if (!
string.IsNullOrEmpty(F.ValueString))
3740 case "pubsub#notify_config":
3741 if (!
string.IsNullOrEmpty(F.ValueString))
3744 Node.NotifyConfig = b;
3753 case "pubsub#notify_delete":
3754 if (!
string.IsNullOrEmpty(F.ValueString))
3757 Node.NotifyDelete = b;
3766 case "pubsub#notify_retract":
3767 if (!
string.IsNullOrEmpty(F.ValueString))
3770 Node.NotifyRetract = b;
3779 case "pubsub#notify_sub":
3780 if (!
string.IsNullOrEmpty(F.ValueString))
3783 Node.NotifySubscriptions = b;
3792 case "pubsub#persist_items":
3793 if (!
string.IsNullOrEmpty(F.ValueString))
3796 Node.PersistItems = b;
3805 case "pubsub#presence_based_delivery":
3806 if (!
string.IsNullOrEmpty(F.ValueString))
3809 Node.PresenceBasedDelivery = b;
3818 case "pubsub#publish_model":
3819 if (!
string.IsNullOrEmpty(F.ValueString))
3831 case "pubsub#replyroom":
3832 Node.ReplyRooms = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3835 case "pubsub#replyto":
3836 Node.ReplyTo = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3839 case "pubsub#roster_groups_allowed":
3840 Node.RosterGroupsAllowed = CheckEmpty(F.ValueStrings);
3843 case "pubsub#send_last_published_item":
3844 if (!
string.IsNullOrEmpty(F.ValueString))
3856 case "pubsub#subscribe":
3857 if (!
string.IsNullOrEmpty(F.ValueString))
3860 Node.AllowSubscriptions = b;
3869 case "pubsub#title":
3870 Node.Title = F.ValueString;
3873 case "pubsub#description":
3874 Node.Description = CheckEmpty(F.ValueStrings);
3877 case "pubsub#language":
3878 Node.Language = F.ValueString;
3882 Node.PayloadType = F.ValueString;
3885 case "pubsub#notification_type":
3886 if (!
string.IsNullOrEmpty(F.ValueString))
3898 case "pubsub#purge_offline":
3899 if (!
string.IsNullOrEmpty(F.ValueString))
3902 Node.PurgeOffline = b;
3954 private static string[] CheckEmpty(
string[] Array)
3956 if (!(Array is
null) && Array.Length == 1 &&
string.IsNullOrEmpty(Array[0]))
3957 return System.Array.Empty<
string>();
3962 private async Task<bool> ConfigureSubscription(XmlElement E, Subscription Subscription,
IqEventArgs e)
3964 foreach (XmlNode N2
in E.ChildNodes)
3966 if (N2 is XmlElement E2 && E2.LocalName ==
"x" && E2.NamespaceURI == Networking.XMPP.XmppClient.NamespaceData)
3972 switch (Form[
"FORM_TYPE"]?.ValueString)
3982 case "pubsub#deliver":
3984 Subscription.Deliver = b;
3992 case "pubsub#digest":
3994 Subscription.Digest = b;
4002 case "pubsub#include_body":
4004 Subscription.IncludeBody = b;
4012 case "pubsub#digest_frequency":
4013 if (
int.TryParse(F.ValueString, out
int i))
4014 Subscription.DigestFrequencyMilliseconds = i;
4022 case "pubsub#expire":
4024 Subscription.Expires = TP;
4025 else if (DateTime.TryParse(F.ValueString, out TP))
4026 Subscription.Expires = TP;
4034 case "pubsub#subscription_type":
4044 case "pubsub#subscription_depth":
4045 if (F.ValueString ==
"1")
4098 return !(provisioningClient is
null) && provisioningClient.
OwnerJid == BareJid;
4103 return this.MessageFormHandler(
string.Empty, e, pubSubFeatures);
4108 return this.MessageFormHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
4115 Field FormTypeField = Form[
"FORM_TYPE"];
4116 if (FormTypeField is
null)
4125 string SubscriptionId = Form[
"pubsub#subid"]?.ValueString ??
string.Empty;
4129 if (!
CommonTypes.
TryParse(Form[
"pubsub#allow"]?.ValueString ??
string.Empty, out
bool Allow))
4133 if (Subscription is
null ||
4134 Subscription.ObjectId != SubscriptionId ||
4135 Subscription.Node != NodeName ||
4136 Subscription.Jid != Jid)
4154 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
4159 await this.SendLastItem(Service, Node, Subscription.Jid, FromBareJid, e.
Language);
4173 private Task PepDiscoveryQueryGet(
object Sender,
IqEventArgs e)
4178 private Task PepDiscoverIdentities(
object Sender, StringBuilder sb)
4180 sb.Append(
"<identity category='pubsub' type='pep'/>");
4181 return Task.CompletedTask;
4186 XmlElement E = e.
Query;
4188 if (
string.IsNullOrEmpty(NodeName))
4189 await base.DiscoveryQueryGet(Sender, e);
4197 StringBuilder Xml =
new StringBuilder();
4199 Xml.Append(
"<query xmlns='");
4201 Xml.Append(
"' node='");
4203 Xml.Append(
"'><identity category='pubsub' type='");
4204 Xml.Append(Node.NodeType.ToString());
4206 if (!
string.IsNullOrEmpty(Node.Title))
4208 Xml.Append(
"' name='");
4213 int NrSubscribers = 0;
4224 new JidSingleField(
"pubsub#creator",
"Node creator", Node.Creator),
4226 new TextSingleField(
"pubsub#title",
"A short name for the node", Node.Title),
4227 new TextMultiField(
"pubsub#description",
"A description of the node", Node.Description),
4228 new ListSingleField(
"pubsub#language",
"Default language", Node.Language),
4229 new JidMultiField(
"pubsub#contact",
"People to contact with questions", Node.Contact.ToStringArray()),
4230 new TextSingleField(
"pubsub#max_items",
"Max # of items to persist", Node.MaxItems.ToString()),
4231 new JidMultiField(
"pubsub#owner",
"Node owners", Node.Owners.ToStringArray()),
4232 new JidMultiField(
"pubsub#publisher",
"Publishers to this node", Node.Publishers.ToStringArray()),
4233 new TextSingleField(
"pubsub#num_subscribers",
"Number of subscribers to this node", NrSubscribers.ToString()));
4236 Xml.Append(
"</query>");
4248 Xml.Append(
"<identity category='pubsub' type='service' name='Publish/Subscribe service' />");
4249 return Task.CompletedTask;
4257 private Task PepDiscoveryQueryItemsGet(
object Sender,
IqEventArgs e)
4264 XmlElement E = e.
Query;
4265 StringBuilder Xml =
new StringBuilder();
4267 Xml.Append(
"<query xmlns='");
4270 if (E.HasAttribute(
"node"))
4281 Xml.Append(
"' node='");
4285 switch (Node.NodeType)
4288 if (!(Node.Children is
null))
4290 foreach (
string Child
in Node.Children)
4296 Xml.Append(
"<item jid='");
4298 Xml.Append(
"' node='");
4301 if (!
string.IsNullOrEmpty(Node2.Title))
4303 Xml.Append(
"' name='");
4315 foreach (PubSubItem Item
in Items)
4317 Xml.Append(
"<item jid='");
4319 Xml.Append(
"' name='");
4320 Xml.Append(
XML.
Encode(Item.ItemIdOrObjectId));
4333 Xml.Append(
"<item jid='");
4335 Xml.Append(
"' node='");
4338 if (!
string.IsNullOrEmpty(Node.Title))
4340 Xml.Append(
"' name='");
4348 Xml.Append(
"</query>");
4353 private async Task Server_OnPresenceLocalRecipient(
object Sender,
PresenceEventArgs e)
4363 string NodeVer =
null;
4367 if (N is XmlElement E &&
4368 E.LocalName ==
"c" &&
4369 E.NamespaceURI ==
"http://jabber.org/protocol/caps")
4374 NodeVer = Node +
"#" + Version;
4379 if (NodeVer is
null)
4383 lock (this.nodeVerByFullJidByBareJid)
4385 if (!this.nodeVerByFullJidByBareJid.TryGetValue(e.
From.
BareJid,
4386 out NodeVerInfo NodeVerInfo))
4388 NodeVerInfo =
new NodeVerInfo()
4390 NodeVerPerFullJid =
new Dictionary<CaseInsensitiveString, string>()
4393 this.nodeVerByFullJidByBareJid[e.
From.
BareJid] = NodeVerInfo;
4396 NodeVerInfo.NodeVerPerFullJid[e.
From.
Address] = NodeVer;
4397 NodeVerInfo.UpdateStaticLocked();
4406 NodeVer +
"'/>",
false, async (sender2, e2) =>
4413 !((E2 = e2.FirstElement) is
null) &&
4414 E2.LocalName ==
"query" &&
4418 Dictionary<string, bool> Features = new Dictionary<string, bool>();
4420 foreach (XmlNode N in E2.ChildNodes)
4422 if (N is XmlElement E3 && E3.LocalName ==
"feature")
4423 Features[XML.Attribute(E3,
"var")] = true;
4426 this.featuresByNodeVer[NodeVer] = Features;
4433 catch (Exception ex)
4447 lock (this.nodeVerByFullJidByBareJid)
4449 if (this.nodeVerByFullJidByBareJid.TryGetValue(e.
From.
BareJid,
4450 out NodeVerInfo NodeVerInfo) &&
4451 NodeVerInfo.NodeVerPerFullJid.Remove(e.
From.
Address))
4453 if (NodeVerInfo.NodeVerPerFullJid.Count == 0)
4454 this.nodeVerByFullJidByBareJid.Remove(e.
From.
BareJid);
4456 NodeVerInfo.UpdateStaticLocked();
4462 catch (Exception ex)
4470 string SubscriberBareJid = Subscriber.
BareJid;
4475 PubSubNode[] NodeArray =
null;
4480 lock (this.nodesByServiceAndName)
4482 if (this.nodesByServiceAndName.TryGetValue(Service, out Nodes))
4483 NodeArray = Nodes.NodesArray;
4490 await this.GetNodeAsync(Service,
string.Empty,
null, Publisher.
ToBareJID(),
null);
4492 lock (this.nodesByServiceAndName)
4494 if (!this.nodesByServiceAndName.TryGetValue(Service, out Nodes))
4497 NodeArray = Nodes.NodesArray;
4501 foreach (PubSubNode Node
in NodeArray)
4503 if (Node.AutoSubscribe && Node.AccessModel ==
NodeAccessModel.presence)
4510 await this.SendLastItem(Service, Node, Jid, FromBareJid, Language);
4518 KeyValuePair<CaseInsensitiveString, string>[] NodeVerPerFullJid;
4520 lock (this.nodeVerByFullJidByBareJid)
4522 if (!this.nodeVerByFullJidByBareJid.TryGetValue(BareJid, out NodeVerInfo NodeVerInfo))
4525 NodeVerPerFullJid = NodeVerInfo.NodeVerPerFullJidStatic;
4528 string Feature = Node.Name +
"+notify";
4529 List<CaseInsensitiveString> FullJids =
null;
4531 foreach (KeyValuePair<CaseInsensitiveString, string> P
in NodeVerPerFullJid)
4533 if (!(this.featuresByNodeVer?.TryGetValue(P.Value, out Dictionary<string, bool> Features) ??
false))
4536 if (!Features.ContainsKey(Feature))
4539 FullJids ??=
new List<CaseInsensitiveString>();
4540 FullJids.Add(P.Key);
4543 return FullJids?.ToArray();
4546 private class NodeVerInfo
4548 public Dictionary<CaseInsensitiveString, string> NodeVerPerFullJid;
4549 public KeyValuePair<CaseInsensitiveString, string>[] NodeVerPerFullJidStatic;
4551 public void UpdateStaticLocked()
4555 this.NodeVerPerFullJidStatic =
new KeyValuePair<CaseInsensitiveString, string>[this.NodeVerPerFullJid.Count];
4557 foreach (KeyValuePair<CaseInsensitiveString, string> P
in this.NodeVerPerFullJid)
4558 this.NodeVerPerFullJidStatic[i++] = P;
4580 return (NrNodes, NrItems);
Helps with parsing of commong data types.
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Contains information about a response to a content request.
bool HasError
If an error occurred.
Static class managing encoding and decoding of internet content.
static bool Decodes(string ContentType, out Grade Grade, out IContentDecoder Decoder)
If an object with a given content type can be decoded.
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 bool TryParse(string s, out DateTime Value)
Tries to decode a string encoded DateTime.
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
static bool IsValidXml(string Xml)
Checks if a string is valid 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.
Implements an HTTP server.
int[] OpenHttpPorts
HTTP Ports successfully opened.
HttpServer(params ISniffer[] Sniffers)
Implements an HTTPS server.
int[] OpenHttpsPorts
HTTPS Ports successfully opened.
Implements an XMPP provisioning client interface.
string OwnerJid
JID of owner, if known or available.
Maintains information about an item in the roster.
string[] Groups
Any groups the roster item belongs to.
string BareJid
Bare JID of the roster item.
Base class for components.
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters a message handler.
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.
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.
void UnregisterFeature(string Namespace)
Unregisters a feature.
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers a message handler.
void RegisterFeature(string Namespace)
Registers a feature.
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 IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
XmlElement Query
Query element, if found, null otherwise.
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 IqErrorConflict(XmppAddress From, string ErrorText, string Language)
Returns a conflict error.
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 Messages.
XmppAddress From
From address attribute
XmppAddress To
To address attribute
XmlElement Content
Content element, if found, null otherwise.
Presence information event arguments.
string Type
Type attribute.
string Language
Language attribute.
XmppAddress To
To attribute.
XmppAddress From
From attribute.
Contains information about a restricted query, as deinfed in XEP-0059: Result Set Management
const string NamespaceResultSetManagement
http://jabber.org/protocol/rsm
int? Index
Request results from this index.
string After
Return items after this key.
static RestrictedQuery IsRestricted(XmlNode FirstSibling)
Searches for a restricted query, by traversing siblings.
int? Max
If result set should be limited in size.
string Before
Return items before this key.
Contains information about a result page, as deinfed in XEP-0059: Result Set Management
void Append(StringBuilder Xml)
Appends pagination information to an XML request.
XmlElement StanzaElement
Stanza element.
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
XmppAddress ToBareJID()
Returns the Bare JID as an XmppAddress object.
CaseInsensitiveString BareJid
Bare JID
static readonly XmppAddress Empty
Empty address.
CaseInsensitiveString Account
Account
bool IsFullJID
If the Address is a Full JID.
const string DiscoveryItemsNamespace
http://jabber.org/protocol/disco#items (XEP-0030)
const string ExtendedAddressingNamespace
http://jabber.org/protocol/address (XEP-0033)
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters a message handler.
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Set handler.
const string DiscoveryNamespace
http://jabber.org/protocol/disco#info (XEP-0030)
Task< bool > SendMessage(string Type, string Id, string From, string To, string Language, string ContentXml)
Sends a Message stanza to a recipient.
Task< bool > SendIqRequest(string Type, string From, string To, string Language, string ContentXml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza to a recipient.
bool IsServerDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the server domain, or optionally, an alternative domain.
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Get handler.
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers an IQ-Get handler.
const string DelayedDeliveryNamespace
urn:xmpp:delay (XEP-0203)
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsFeature)
Unregisters an IQ-Set handler.
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsFeature)
Registers a message handler.
Accounts Accounts
Accounts
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 Length
Gets the number of characters in the current CaseInsensitiveString object.
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
bool StartsWith(CaseInsensitiveString value)
Determines whether the beginning of this string instance matches the specified 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 IDatabaseProvider Provider
Registered database provider.
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 .
Persists objects into binary files.
Task< ObjectSerializer > GetObjectSerializerEx(object Object)
Gets the object serializer corresponding to a specific object.
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 greater than a given value.
This filter selects objects that have a named field lesser than a given value.
Base class for all filter classes.
Manages binary serialization of data.
void FlushBits()
Flushes any bit field values.
byte[] GetSerialization()
Gets the binary serialization.
Serializes a class, taking into account attributes defined in Attributes.
virtual async Task Serialize(ISerializer Writer, bool WriteTypeCode, bool Embedded, object Value, object State)
Serializes a value.
Implements an in-memory cache.
bool ContainsKey(KeyType Key)
Checks if a key is available in the cache.
void Dispose()
IDisposable.Dispose
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Static class that dynamically manages types and interfaces available in the runtime environment.
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Static class managing persistent settings.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
Default features for a service.
PubSub component, as defined in XEP-0060. https://xmpp.org/extensions/xep-0060.html
const string NamespacePubSubMultiCollecton
http://jabber.org/protocol/pubsub#multi-collection
const string NamespacePubSubAccessPresence
http://jabber.org/protocol/pubsub#access-presence
const string FormTypeNodeMetaData
http://jabber.org/protocol/pubsub#meta-data
Task< string > PublishItem(string Service, string NodeName, string AutoCreateAccess, string From, string Domain, string ItemId, string XmlContent, string Language)
Call this method for automated publishing of pubsub items from hosted services.
const string NamespacePubSubLeasedSubscription
http://jabber.org/protocol/pubsub#leased-subscription
const string NamespacePubSub
http://jabber.org/protocol/pubsub
const string NamespacePubSubCreateAndConfigure
http://jabber.org/protocol/pubsub#create-and-configure
const string NamespacePubSubDeleteNodes
http://jabber.org/protocol/pubsub#delete-nodes
const string NamespacePubSubAccessAuthorize
http://jabber.org/protocol/pubsub#access-authorize
const string NamespacePubSubOwner
http://jabber.org/protocol/pubsub#owner
const string NamespacePubSubRetrieveDefaultSub
http://jabber.org/protocol/pubsub#retrieve-default-sub
const string NamespacePubSubPersistentItems
http://jabber.org/protocol/pubsub#persistent-items
const string NamespacePubSubManageSubscriptions
http://jabber.org/protocol/pubsub#manage-subscriptions
const string NamespacePubSubemberAffiliation
http://jabber.org/protocol/pubsub#member-affiliation
const string NamespacePubSubDeleteItems
http://jabber.org/protocol/pubsub#delete-items
override Task AppendServiceDiscoveryIdentities(StringBuilder Xml, IqEventArgs e, string Node)
Component.AppendServiceDiscoveryIdentities(StringBuilder, IqEventArgs, string)
Task< string > PublishItem(string Service, string NodeName, NodeAccessModel? AutoCreateAccess, string From, string Domain, string ItemId, XmlDocument XmlContent, string Language)
Call this method for automated publishing of pubsub items from hosted services.
async Task<(int, int)> DeleteExpiredNodes()
Deletes expired nodes
const string NamespacePubSubAutoSubscribe
http://jabber.org/protocol/pubsub#auto-subscribe
const string NamespacePubSubItemIds
http://jabber.org/protocol/pubsub#item-ids
const string NamespacePubSubAutoCreate
http://jabber.org/protocol/pubsub#auto-create
async Task< PubSubNode > GetNodeAsync(CaseInsensitiveString Service, CaseInsensitiveString NodeName, NodeAccessModel? AutoCreateAccess, XmppAddress From, CaseInsensitiveString Domain)
Gets a pubsub node.
async Task< string > PublishItem(string Service, string NodeName, NodeAccessModel? AutoCreateAccess, XmppAddress From, CaseInsensitiveString Domain, CaseInsensitiveString ItemId, XmlDocument XmlContent, string Language)
Call this method for automated publishing of pubsub items from hosted services.
const string NamespacePubSubSubscribe
http://jabber.org/protocol/pubsub#subscribe
Task< string > PublishItem(string Service, string NodeName, string AutoCreateAccess, string From, string Domain, string ItemId, XmlDocument XmlContent, string Language)
Call this method for automated publishing of pubsub items from hosted services.
async Task< Subscription[]> GetSubscriptionsOnNode(CaseInsensitiveString Service, CaseInsensitiveString Node)
Gets available subscriptions on a node.
const string FormTypeSubscriptionAuthorization
http://jabber.org/protocol/pubsub#subscribe_authorization
const string FormTypeSubscribeOptions
http://jabber.org/protocol/pubsub#subscribe_options
const string NamespacePubSubRetrieveDefault
http://jabber.org/protocol/pubsub#retrieve-default
const string NamespacePubSubMetaData
http://jabber.org/protocol/pubsub#meta-data
override void Dispose()
IDisposable.Dispose
const string NamespaceStanzaHeaders
http://jabber.org/protocol/shim
const string NamespacePubSubNodeConfiguration
http://jabber.org/protocol/pubsub#config-node
const string NamespacePubSubSubscriptionOptions
http://jabber.org/protocol/pubsub#subscription-options
const string NamespacePubSubAccessOpen
http://jabber.org/protocol/pubsub#access-open
const string NamespacePubSubRetrieveSubscriptions
http://jabber.org/protocol/pubsub#retrieve-subscriptions
const string NamespacePubSubPublisheAffiliation
http://jabber.org/protocol/pubsub#publisher-affiliation
async Task< Subscription[]> GetSubscriptionsByJid(CaseInsensitiveString Service, CaseInsensitiveString Jid)
Gets available subscriptions for a JID.
const string NamespacePubSubRetractItems
http://jabber.org/protocol/pubsub#retract-items
const string NamespacePubSubCreateNodes
http://jabber.org/protocol/pubsub#create-nodes
const string NamespacePubSubLastPublished
http://jabber.org/protocol/pubsub#last-published
const string NamespacePubSubAccessRoster
http://jabber.org/protocol/pubsub#access-roster
const string NamespacePubSubPublish
http://jabber.org/protocol/pubsub#publish
const string NamespacePubSubPurgeNodes
http://jabber.org/protocol/pubsub#purge-nodes
async Task< Subscription > GetSubscriptionAsync(CaseInsensitiveString Service, CaseInsensitiveString Node, CaseInsensitiveString Jid)
Gets a subscription object.
static async Task< PubSubComponent > Create(XmppServer Server, CaseInsensitiveString Subdomain, string Name)
PubSub component, as defined in XEP-0060.
const string NamespacePubSubOutcastAffiliation
http://jabber.org/protocol/pubsub#outcast-affiliation
const string NamespacePubSubRetrieveAffiliations
http://jabber.org/protocol/pubsub#retrieve-affiliations
const string NamespacePubSubAccessWhitelist
http://jabber.org/protocol/pubsub#access-whitelist
const string NamespacePubSubEvents
http://jabber.org/protocol/pubsub#event
const string FormTypeNodeConfig
http://jabber.org/protocol/pubsub#node_config
const string NamespacePubSubRetrieveItems
http://jabber.org/protocol/pubsub#retrieve-items
override Task DiscoveryQueryGet(object Sender, IqEventArgs e)
Component.DiscoveryQueryGet(object, IqEventArgs)
override Task DiscoveryQueryItemsGet(object Sender, IqEventArgs e)
Get handler for Service Discovery items query (XEP-0030).
const string NamespacePubSubSubscriptionNotifications
http://jabber.org/protocol/pubsub#subscription-notifications
const string NamespacePubSubPublishOnlyAffiliation
http://jabber.org/protocol/pubsub#publish-only-affiliation
const string NamespacePubSubModifyAffiliations
http://jabber.org/protocol/pubsub#modify-affiliations
const string NamespacePubSubPresenceSubscribe
http://jabber.org/protocol/pubsub#presence-subscribe
const string NamespacePubSubCollections
http://jabber.org/protocol/pubsub#collections
const string NamespacePubSubMultiItems
http://jabber.org/protocol/pubsub#multi-items
override bool SupportsAccounts
If the component supports accounts (true), or if the subdomain name is the only valid address.
Defines a published item.
CaseInsensitiveString ItemIdOrObjectId
Item ID, if not empty, else the object ID.
CaseInsensitiveString Node
Name of node.
CaseInsensitiveString Publisher
Publisher of content.
string ObjectId
Object ID.
Defines a node on which items can be published.
bool DeliverNotifications
Whether to deliver event notifications
NodeAccessModel AccessModel
Who may subscribe and retrieve items
bool AutoDelete
If old items are automatically deleted.
CaseInsensitiveString Name
Name of node.
int MaxItems
The maximum number of items to persist
int MaxPayloadSize
The maximum number of items to persist
bool PublishOnWeb
If the items published to the node should be available on the web or not.
bool PersistItems
Whether to persist items to storage
string PayloadType
The type of node data, usually specified by the namespace of the payload (if any); MAY be list-single...
int NrItems
Number of items.
bool AutoSubscribe
If the node is a root node or not.
bool TransientNode
If the node is transient (i.e. does not persist items, and do not deliver payloads.
NotificationType NotificationType
Notification type
int ItemExpireSeconds
Number of seconds after which to automatically purge items.
CaseInsensitiveString Service
Name of PubSub Service.
bool DeliverPayloads
Whether to deliver payloads with event notifications; applies only to leaf nodes
NodeType NodeType
Whether the node is a leaf (default) or a collection
Defines a node subscription.
CaseInsensitiveString Jid
JID to whom event notifications will be sent.
CaseInsensitiveString Node
Node name
Basic interface for Internet Content decoders. A class implementing this interface and having a defau...
Interface for XMPP user accounts.
Interface for roster items.
Affiliation
Affiliation enumeration
SubscriptionStatus
Roster item subscription status enumeration.
NodeAccessModel
Node access model.
NodeType
Whether the node is a leaf (default) or a collection
AffiliationStatus
User affiliation
NotificationType
Delivery style for notifications
SubscriptionType
Subscription type
NodeItemReply
Whether owners or publisher should receive replies to items
NodeChildAssociationPolicy
Who may associate leaf nodes with a collection
PublisherModel
Publisher model
NodeSubscriptionStatus
Node subscription status
SubscriptonDepth
Subscription depth
SendLastPublishedItem
When to send the last published item