2using System.Collections.Generic;
6using System.Threading.Tasks;
267 private readonly Dictionary<CaseInsensitiveString, Nodes> nodesByServiceAndName =
new Dictionary<CaseInsensitiveString, Nodes>();
268 private readonly Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>> subscriptionsByServiceAndNodeAndJid =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>>();
269 private readonly Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>> subscriptionsByJidAndServiceAndNode =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>>();
270 private readonly Dictionary<CaseInsensitiveString, NodeVerInfo> nodeVerByFullJidByBareJid =
new Dictionary<CaseInsensitiveString, NodeVerInfo>();
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);
313 if (provisioningClient is
null)
318 provisioningClient =
null;
321 Server.OnPresenceLocalRecipient += this.Server_OnPresenceLocalRecipient;
337 Result.itemSerializer =
null;
345 AutoSubscribe =
false
348 private static readonly DefaultFeatures pepFeatures =
new DefaultFeatures()
394 if (Features.AutoCreate)
397 if (Features.AutoSubscribe)
423 this.Server.
UnregisterMessageHandler(
"x", Networking.XMPP.XmppClient.NamespaceData,
this.PepMessageFormHandler,
false);
426 this.Server.Accounts.OnDiscoveryQueryNodeGet -= this.PepDiscoveryQueryGet;
427 this.Server.Accounts.OnDiscoveryQueryNodeItemsGet -= this.PepDiscoveryQueryItemsGet;
429 this.RemoveFeatures(
this);
430 this.RemoveFeatures(this.Server.
Accounts);
432 this.itemSerializer =
null;
434 this.Server.OnPresenceLocalRecipient -= this.Server_OnPresenceLocalRecipient;
436 this.featuresByNodeVer?.
Dispose();
437 this.featuresByNodeVer =
null;
505 Nodes NodesOnService;
507 bool FirstRequestOnService =
false;
509 lock (this.nodesByServiceAndName)
511 if (!this.nodesByServiceAndName.TryGetValue(Service, out NodesOnService))
513 FirstRequestOnService =
true;
514 NodesOnService =
new Nodes()
516 NodesByName =
new Dictionary<string, PubSubNode>(),
520 this.nodesByServiceAndName[Service] = NodesOnService;
523 if (NodesOnService.NodesByName.TryGetValue(NodeName, out Node))
527 if (FirstRequestOnService && !
string.IsNullOrEmpty(Service))
532 lock (this.nodesByServiceAndName)
534 NodesOnService.NodesByName[NodeName] = Node2;
537 if (Node2.
Name == NodeName)
541 lock (this.nodesByServiceAndName)
543 NodesOnService.NodesArray =
new PubSubNode[NodesOnService.NodesByName.Count];
544 NodesOnService.NodesByName.Values.CopyTo(NodesOnService.NodesArray, 0);
556 if (!AutoCreateAccess.HasValue)
567 Created = DateTime.Now,
570 AccessModel = AutoCreateAccess.
Value,
575 AutoSubscribe =
true,
583 lock (this.nodesByServiceAndName)
585 NodesOnService.NodesByName[NodeName] = Node;
587 NodesOnService.NodesArray =
new PubSubNode[NodesOnService.NodesByName.Count];
588 NodesOnService.NodesByName.Values.CopyTo(NodesOnService.NodesArray, 0);
603 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> SubscriptionsByServiceAndNode;
604 Dictionary<CaseInsensitiveString, Subscription> SubscriptionsByNode;
607 lock (this.subscriptionsByJidAndServiceAndNode)
609 if (!this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out SubscriptionsByServiceAndNode))
611 SubscriptionsByServiceAndNode =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>();
612 this.subscriptionsByJidAndServiceAndNode[Jid] = SubscriptionsByServiceAndNode;
615 if (SubscriptionsByServiceAndNode.TryGetValue(Service, out SubscriptionsByNode))
617 if (SubscriptionsByNode.TryGetValue(Node, out Result))
624 SubscriptionsByNode =
new Dictionary<CaseInsensitiveString, Subscription>();
632 lock (this.subscriptionsByJidAndServiceAndNode)
634 SubscriptionsByServiceAndNode[Service] = SubscriptionsByNode;
636 if (SubscriptionsByNode.TryGetValue(Node, out Result))
651 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> SubscriptionsByServiceAndNode;
652 Dictionary<CaseInsensitiveString, Subscription> SubscriptionsByNode;
655 lock (this.subscriptionsByJidAndServiceAndNode)
657 if (!this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out SubscriptionsByServiceAndNode))
659 SubscriptionsByServiceAndNode =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>();
660 this.subscriptionsByJidAndServiceAndNode[Jid] = SubscriptionsByServiceAndNode;
663 if (SubscriptionsByServiceAndNode.TryGetValue(Service, out SubscriptionsByNode))
666 SubscriptionsByNode.Values.CopyTo(Result, 0);
671 SubscriptionsByNode =
new Dictionary<CaseInsensitiveString, Subscription>();
680 SubscriptionsByNode.Values.CopyTo(Result, 0);
682 lock (this.subscriptionsByJidAndServiceAndNode)
684 SubscriptionsByServiceAndNode[Service] = SubscriptionsByNode;
698 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> SubscriptionsByNodeAndJid;
699 Dictionary<CaseInsensitiveString, Subscription> SubscriptionsByJid;
702 lock (this.subscriptionsByServiceAndNodeAndJid)
704 if (!this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out SubscriptionsByNodeAndJid))
706 SubscriptionsByNodeAndJid =
new Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>>();
707 this.subscriptionsByServiceAndNodeAndJid[Service] = SubscriptionsByNodeAndJid;
710 if (SubscriptionsByNodeAndJid.TryGetValue(Node, out SubscriptionsByJid))
713 SubscriptionsByJid.Values.CopyTo(Result, 0);
718 SubscriptionsByJid =
new Dictionary<CaseInsensitiveString, Subscription>();
727 SubscriptionsByJid.Values.CopyTo(Result, 0);
729 lock (this.subscriptionsByServiceAndNodeAndJid)
731 SubscriptionsByNodeAndJid[Node] = SubscriptionsByJid;
737 private Task PubSubGetHandler(
object Sender,
IqEventArgs e)
739 return this.PubSubGetHandler(
string.Empty, e, pubSubFeatures);
742 private Task PepGetHandler(
object Sender,
IqEventArgs e)
744 return this.PubSubGetHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
749 foreach (XmlNode N
in e.
Query.ChildNodes)
751 if (N is XmlElement E)
757 if (
string.IsNullOrEmpty(NodeName))
759 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
760 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
767 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
768 "<jid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"JID not specified.",
"en");
776 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
777 "<invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid JID",
"en");
789 if (Subscription is
null)
791 await e.
IqError(
"modify",
"<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
792 "<not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"No such subscriber.",
"en");
804 if (!
string.IsNullOrEmpty(SubID) && SubID != Subscription.ObjectId)
806 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
807 "<invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid subscription ID.",
"en");
811 DataForm Form = this.GetForm(Node, Subscription, e);
812 StringBuilder Xml =
new StringBuilder();
814 Xml.Append(
"<pubsub xmlns='");
816 Xml.Append(
"'><options node='");
818 Xml.Append(
"' jid='");
822 Xml.Append(
"</options></pubsub>");
829 if (!
string.IsNullOrEmpty(NodeName))
841 Subscription =
new Subscription();
842 Form = this.GetForm(Node, Subscription, e);
843 Xml =
new StringBuilder();
845 Xml.Append(
"<pubsub xmlns='");
847 Xml.Append(
"'><default");
849 if (!
string.IsNullOrEmpty(NodeName))
851 Xml.Append(
" node='");
858 Xml.Append(
"</default></pubsub>");
865 if (
string.IsNullOrEmpty(NodeName))
867 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
868 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
879 if (!(XmppServerModule.PersistenceLayer is
null) &&
880 await XmppServerModule.PersistenceLayer.IsBlocked(e.
From.
BareJid, Node.Creator))
893 switch (Node.AccessModel)
910 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
911 "<presence-subscription-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
912 "Presence subscription required to get item.",
"en");
922 if (Subscription is
null)
927 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
928 "<not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/>",
929 e.
To,
"Not subscribed.",
"en");
934 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
935 "<pending-subscription xmlns='http://jabber.org/protocol/pubsub#errors'/>",
936 e.
To,
"Pending subscription.",
"en");
941 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
942 "<configuration-required xmlns='http://jabber.org/protocol/pubsub#errors'/>",
943 e.
To,
"Configuration required.",
"en");
950 List<PubSubItem> RequestedItems =
null;
951 IEnumerable<PubSubItem> Items;
958 bool ReverseOrder =
false;
960 if (Node.DeliverPayloads)
965 if (E.HasAttribute(
"max_items"))
977 if (Node.PersistItems)
979 foreach (XmlNode N2
in E.ChildNodes)
981 if (N2 is XmlElement E2 && E2.LocalName ==
"item")
983 if (RequestedItems is
null)
984 RequestedItems =
new List<PubSubItem>();
988 PubSubItem Item = await this.LoadItem(Service, NodeName,
XML.
Attribute(E2,
"id"));
989 if (!(Item is
null) && Item.Node == NodeName)
990 RequestedItems.Add(Item);
999 if (!(RequestedItems is
null))
1000 Items = RequestedItems.ToArray();
1003 List<Filter> Filters =
new List<Filter>()
1019 NewestFirst =
false;
1020 ReverseOrder =
true;
1031 if (Item.Node != NodeName)
1033 await e.
IqErrorBadRequest(e.
To,
"Before item identity points to item belonging to another node.",
"en");
1052 if (Item.Node != NodeName)
1054 await e.
IqErrorBadRequest(e.
To,
"After item identity points to item belonging to another node.",
"en");
1072 Items = await
Database.
Find<PubSubItem>(Offset, OneExtra ? Max + 1 : Max,
Filter,
"-Service",
"-Node",
"-Created");
1074 Items = await
Database.
Find<PubSubItem>(Offset, OneExtra ? Max + 1 : Max,
Filter,
"Service",
"Node",
"Created");
1078 if (Items is PubSubItem[] ItemArray)
1079 Array.Reverse(ItemArray);
1082 List<PubSubItem> ItemList =
new List<PubSubItem>();
1083 ItemList.AddRange(Items);
1092 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1093 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/>",
1094 e.
To,
"Node does not support persisted items and pagination.",
"en");
1097 else if (Node.NodeType ==
NodeType.collection || Node.TransientNode)
1099 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1100 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='retrieve-items'/>",
1101 e.
To,
"Collection and transient nodes do not support item retrieval.",
"en");
1104 else if (!(Node.LastPublished is
null))
1105 Items =
new PubSubItem[] { Node.LastPublished };
1107 Items =
new PubSubItem[0];
1109 Xml =
new StringBuilder();
1111 Xml.Append(
"<pubsub xmlns='");
1113 Xml.Append(
"'><items node='");
1117 string First =
null;
1121 foreach (PubSubItem Item
in Items)
1130 First = Item.ItemIdOrObjectId;
1132 Last = Item.ItemIdOrObjectId;
1134 Xml.Append(
"<item id='");
1135 Xml.Append(
XML.
Encode(Item.ItemIdOrObjectId));
1137 if (Node.DeliverPayloads || !(RequestedItems is
null))
1139 Xml.Append(
"' publisher='");
1143 if (
XML.
IsValidXml(Item.Payload,
true,
true,
true,
true,
false,
false))
1144 Xml.Append(Item.Payload);
1146 Xml.Append(
"</item>");
1152 Xml.Append(
"</items>");
1157 Xml.Append(
"</pubsub>");
1162 case "subscriptions":
1163 Xml =
new StringBuilder();
1165 Xml.Append(
"<pubsub xmlns='");
1168 if (E.HasAttribute(
"node"))
1170 NodeName = E.GetAttribute(
"node");
1179 Xml.Append(
"'><subscriptions node='");
1189 Xml.Append(
"'><subscriptions>");
1196 Xml.Append(
"</subscriptions></pubsub>");
1201 case "affiliations":
1202 Xml =
new StringBuilder();
1204 Xml.Append(
"<pubsub xmlns='");
1207 if (E.HasAttribute(
"node"))
1209 NodeName = E.GetAttribute(
"node");
1218 Xml.Append(
"'><affiliations node='");
1224 Xml.Append(
"<affiliation node='");
1226 Xml.Append(
"' affiliation='");
1227 Xml.Append(ToString(Affiliation));
1228 Xml.Append(
"'/></affiliations>");
1235 Xml.Append(
"'><affiliations>");
1243 Xml.Append(
"<affiliation node='");
1245 Xml.Append(
"' affiliation='");
1246 Xml.Append(ToString(Affiliation));
1251 Xml.Append(
"</affiliations>");
1254 Xml.Append(
"</pubsub>");
1269 private static IEnumerable<Subscription> Subset(Subscription[] Subscriptions,
string NodeName)
1271 LinkedList<Subscription> Result =
new LinkedList<Subscription>();
1273 foreach (Subscription Subscription
in Subscriptions)
1275 if (Subscription.Node == NodeName)
1276 Result.AddLast(Subscription);
1282 private static void Serialize(IEnumerable<Subscription> Subscriptions, StringBuilder Xml)
1284 foreach (Subscription Subscription
in Subscriptions)
1286 Xml.Append(
"<subscription node='");
1287 Xml.Append(
XML.
Encode(Subscription.Node));
1288 Xml.Append(
"' jid ='");
1289 Xml.Append(
XML.
Encode(Subscription.Jid));
1290 Xml.Append(
"' subscription='");
1291 Xml.Append(Subscription.Status.ToString());
1292 Xml.Append(
"' subid='");
1293 Xml.Append(Subscription.ObjectId);
1300 List<Field> Fields =
new List<Field>()
1303 new BooleanField(
null,
"pubsub#deliver",
"Enable notifications.",
false,
new string[] { Subscription.Deliver ?
"1" :
"0" },
null,
1305 new BooleanField(
null,
"pubsub#include_body",
"Include message body.",
false,
new string[] { Subscription.IncludeBody ?
"1" :
"0" },
null,
1307 new BooleanField(
null,
"pubsub#digest",
"Digest.",
false,
new string[] { Subscription.Digest ?
"1" :
"0" },
null,
1308 "Whether an entity wants to receive digests (aggregations) of notifications or all notifications individually.",
BooleanDataType.
Instance,
new BasicValidation(),
string.Empty,
false,
false,
false),
1309 new TextSingleField(
null,
"pubsub#digest_frequency",
"Digest interval (ms):",
false,
new string[] { Subscription.DigestFrequencyMilliseconds.ToString() },
null,
1310 "The minimum number of milliseconds between sending any two notification digests.",
IntegerDataType.
Instance,
new RangeValidation(
"0",
int.MaxValue.ToString()),
string.Empty,
false,
false,
false),
1311 new TextSingleField(
null,
"pubsub#expire",
"Expiry time:",
false,
new string[] { Subscription.Expires == DateTime.MaxValue ? string.Empty :
XML.
Encode(Subscription.Expires) },
null,
1315 if (!(Node is
null) && Node.NodeType ==
NodeType.collection)
1317 Fields.Add(
new ListSingleField(
null,
"pubsub#subscription_type",
"Subcription Type:",
false,
new string[] { Subscription.Type.ToString() },
1318 new KeyValuePair<string, string>[]
1320 new KeyValuePair<string, string>(
"Receive notification of new items only",
"items"),
1321 new KeyValuePair<string, string>(
"Receive notification of new nodes only",
"nodes")
1322 },
"Type of subscription.",
null,
null,
string.Empty,
false,
false,
false));
1324 Fields.Add(
new ListSingleField(
null,
"pubsub#subscription_depth",
"Subcription Depth:",
false,
new string[] { Subscription.Depth == SubscriptonDepth.one ?
"1" : Subscription.Depth.ToString() },
1325 new KeyValuePair<string, string>[]
1327 new KeyValuePair<string, string>(
"Receive notification from direct child nodes only",
"1"),
1328 new KeyValuePair<string, string>(
"Receive notification from all descendent nodes",
"all")
1329 },
"Type of subscription.",
null,
null,
string.Empty,
false,
false,
false));
1335 private Task PubSubSetHandler(
object Sender,
IqEventArgs e)
1337 return this.PubSubSetHandler(
string.Empty, e, pubSubFeatures);
1340 private Task PepSetHandler(
object Sender,
IqEventArgs e)
1342 return this.PubSubSetHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
1348 Dictionary<CaseInsensitiveString, Subscription> List;
1349 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> List2;
1350 Subscription Subscription =
null;
1351 PubSubNode Node =
null;
1353 string NodeName =
null;
1355 bool InsertNode =
false;
1356 bool NodeUpdated =
false;
1357 bool InsertSubscription =
false;
1358 bool SubscriptionUpdated =
false;
1359 bool SubscriptionDeleted =
false;
1360 bool IncludeOptions =
false;
1361 bool NotificationToOwner =
false;
1363 foreach (XmlNode N
in e.
Query.ChildNodes)
1365 if (N is XmlElement E)
1367 switch (E.LocalName)
1372 await e.
IqErrorForbidden(e.
To,
"Only accounts registered on the broker are allowed to create nodes.",
"en");
1377 if (Account is
null)
1379 await e.
IqError(
"auth",
"<registration-required xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>", e.
To,
1380 "Only accounts registered on the broker are allowed to create nodes.",
"en");
1391 if (
string.IsNullOrEmpty(NodeName))
1393 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1394 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1399 if (!(Node is
null))
1405 if (
string.IsNullOrEmpty(Service))
1407 if (NodeName != e.
From.
Account && !(await XmppServerModule.GetAccountAsync(NodeName) is
null))
1409 await e.
IqErrorConflict(e.
To,
"Node name conflict: Conflicts with existing account.",
"en");
1413 if (!(IoTGateway.Gateway.RootFolder is
null))
1417 string s = Path.Combine(IoTGateway.Gateway.RootFolder, NodeName);
1421 await e.
IqErrorConflict(e.
To,
"Node name conflict: Conflicts with existing web file.",
"en");
1424 else if (Directory.Exists(s))
1426 await e.
IqErrorConflict(e.
To,
"Node name conflict: Conflicts with existing web folder.",
"en");
1437 Node =
new PubSubNode()
1443 Created = DateTime.Now,
1452 if (await this.ConfigureNode(E, Node, e))
1460 if (
string.IsNullOrEmpty(NodeName))
1462 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1463 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1472 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1473 "<invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid JID",
"en");
1483 if (!Node.AllowSubscriptions)
1485 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1486 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='subscribe'/>", e.
To,
"Node does not support subscriptions.",
"en");
1490 if (!(XmppServerModule.PersistenceLayer is
null) &&
1491 await XmppServerModule.PersistenceLayer.IsBlocked(e.
From.
BareJid, Node.Creator))
1504 switch (Node.AccessModel)
1512 await e.
IqError(
"cancel",
"<not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1513 "<closed-node xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Not on whitelist.",
"en");
1530 foreach (
string s
in Node.RosterGroupsAllowed)
1534 if (
string.Compare(s, s2,
true) == 0)
1548 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1549 "<not-in-roster-group xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1550 "Not in corresponding roster group.",
"en");
1566 await e.
IqError(
"auth",
"<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1567 "<presence-subscription-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1568 "Presence subscription required to subscribe to node.",
"en");
1576 if (Subscription is
null)
1578 Subscription =
new Subscription()
1584 Status = NodeSubscriptionStatus.pending
1587 InsertSubscription =
true;
1594 SubscriptionUpdated =
true;
1598 NotificationToOwner =
true;
1602 if (Subscription is
null)
1606 if (Subscription is
null)
1608 Subscription =
new Subscription()
1614 Status = NodeSubscriptionStatus.subscribed
1617 InsertSubscription =
true;
1624 SubscriptionUpdated =
true;
1632 if (
string.IsNullOrEmpty(NodeName))
1634 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1635 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1644 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1645 "<invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid JID",
"en");
1658 if (Subscription is
null)
1660 await e.
IqError(
"cancel",
"<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1661 "<not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Not subscribed.",
"en");
1665 if (!
string.IsNullOrEmpty(SubId) && SubId != Subscription.ObjectId)
1667 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1668 "<invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid subscription ID.",
"en");
1672 lock (this.subscriptionsByJidAndServiceAndNode)
1674 if (this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out List2) &&
1675 List2.TryGetValue(Service, out List))
1677 List.Remove(NodeName);
1681 lock (this.subscriptionsByServiceAndNodeAndJid)
1683 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out List2) &&
1684 List2.TryGetValue(NodeName, out List))
1691 SubscriptionDeleted =
true;
1695 if (Subscription is
null)
1698 if (
string.IsNullOrEmpty(NodeName))
1700 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1701 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1710 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1711 "<invalid-jid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid JID",
"en");
1724 if (Subscription is
null)
1726 await e.
IqError(
"cancel",
"<unexpected-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1727 "<not-subscribed xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Not subscribed.",
"en");
1731 if (!
string.IsNullOrEmpty(SubId) && SubId != Subscription.ObjectId)
1733 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1734 "<invalid-subid xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Invalid subscription ID.",
"en");
1739 if (!await this.ConfigureSubscription(E, Subscription, e))
1742 SubscriptionUpdated =
true;
1744 IncludeOptions =
true;
1749 if (
string.IsNullOrEmpty(NodeName))
1751 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1752 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
1768 await e.
IqErrorForbidden(e.
To,
"Access denied. Not authorized to publish item on node.",
"en");
1772 if (Node.NodeType ==
NodeType.collection)
1774 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1775 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='publish'/>", e.
To,
1776 "Publishing items on collection nodes is not supported.",
"en");
1780 foreach (XmlNode N2
in E.ChildNodes)
1782 if (N2 is XmlElement E2 && E2.LocalName ==
"item" && E2.NamespaceURI == E.NamespaceURI)
1784 if (Node.TransientNode)
1786 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1787 "<item-forbidden xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Transient nodes do not accept items.",
"en");
1792 PubSubItem Item =
null;
1796 Item = await this.LoadItem(Service, NodeName, ItemId);
1798 if (!(Item is
null) && Item.Node != NodeName)
1800 await e.
IqErrorBadRequest(e.
To,
"Item identity points to item belonging to another node.",
"en");
1807 if (Node.PublishOnWeb && !
string.IsNullOrEmpty(ItemId))
1809 foreach (XmlNode N3
in E2.ChildNodes)
1811 if (N3 is XmlElement E3 &&
1812 E3.LocalName ==
"markdownContent" &&
1813 E3.NamespaceURI == WebServices.PubSubContent.Content.LilsisNamespace)
1815 StringBuilder Link =
new StringBuilder();
1819 if (Ports.Length > 0)
1821 Link.Append(
"https://");
1822 Link.Append(IoTGateway.Gateway.Domain);
1827 Link.Append(Ports[0].ToString());
1834 Link.Append(
"http://");
1835 Link.Append(IoTGateway.Gateway.Domain);
1840 Link.Append(Ports[0].ToString());
1845 Link.Append(System.Web.HttpUtility.UrlEncode(Node.Name));
1850 Link.Append(ItemId);
1852 E3.SetAttribute(
"link", Link.ToString());
1857 Content = E2.InnerXml.Trim();
1859 byte[] ContentBin = Encoding.UTF8.GetBytes(Content);
1860 DateTime Now = DateTime.Now;
1862 if (Node.DeliverPayloads &&
string.IsNullOrEmpty(Content))
1864 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1865 "<payload-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1866 "Missing payload.",
"en");
1870 if (ContentBin.Length > Node.MaxPayloadSize)
1872 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1873 "<payload-too-big xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1874 "Payload too big.",
"en");
1878 if (!
string.IsNullOrEmpty(Node.PayloadType))
1884 await Decoder.DecodeAsync(Node.PayloadType, ContentBin, Encoding.UTF8,
1885 new KeyValuePair<string, string>[0],
null);
1889 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1890 "<invalid-payload xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1891 "Payload does not conform to Internet Content Type " + Node.PayloadType,
"en");
1897 foreach (XmlNode N3
in E2.ChildNodes)
1899 if (N3 is XmlElement E3 && E3.NamespaceURI != Node.PayloadType)
1901 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
1902 "<invalid-payload xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
1903 "Payload does not conform to namespace " + Node.PayloadType,
"en");
1910 if (!(Item is
null))
1912 Node.BytesPublished -= await this.GetItemSize(Item);
1915 Item.Payload = Content;
1922 if (Node.NrItems >= Node.MaxItems)
1924 if (Node.AutoDelete)
1926 bool Deleted =
false;
1932 Node.BytesDeleted += await this.GetItemSize(Item2);
1935 if (Node.NrItems < 0)
1946 if (Node.NrItems >= Node.MaxItems)
1953 Item =
new PubSubItem()
1963 if (Node.ItemExpireSeconds !=
int.MaxValue)
1964 Item.Expires = Now.AddSeconds(Node.ItemExpireSeconds);
1966 if (Node.PersistItems)
1970 this.items[Item.Key] = Item;
1973 Node.LastPublished = Item;
1976 if (Node.PersistItems)
1978 Node.BytesPublished += await this.GetItemSize(Item);
1982 Xml =
new StringBuilder();
1984 Xml.Append(
"<pubsub xmlns='");
1986 Xml.Append(
"'><publish node='");
1988 Xml.Append(
"'><item id='");
1989 Xml.Append(
XML.
Encode(Item.ItemIdOrObjectId));
1990 Xml.Append(
"'/></publish></pubsub>");
1994 if (Node.DeliverNotifications)
1995 await this.SendItemEventNotification(Node, Item, e.
From, e.
Language, Now);
2001 if (Node.PersistItems)
2003 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2004 "<item-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Item required.",
"en");
2015 if (
string.IsNullOrEmpty(NodeName))
2017 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2018 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
2029 if (Node.NodeType ==
NodeType.collection)
2031 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2032 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='publish'/>", e.
To,
2033 "Publishing items on collection nodes is not supported.",
"en");
2037 if (!Node.PersistItems)
2039 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2040 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/>",
2041 e.
To,
"Persisted items not supported.",
"en");
2045 foreach (XmlNode N2
in E.ChildNodes)
2047 if (N2 is XmlElement E2 && E2.LocalName ==
"item" && E2.NamespaceURI == E.NamespaceURI)
2050 PubSubItem Item =
null;
2052 Item = await this.LoadItem(Service, NodeName, ItemId);
2056 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2057 "<item-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Item required.",
"en");
2061 if (Item.Node != NodeName)
2063 await e.
IqErrorBadRequest(e.
To,
"Item identity points to item belonging to another node.",
"en");
2069 await e.
IqErrorForbidden(e.
To,
"Access denied. Not authorized to delete item on node.",
"en");
2073 Node.BytesDeleted += await this.GetItemSize(Item);
2076 if (Node.NrItems < 0)
2084 if (Node.DeliverNotifications)
2086 DateTime Now = DateTime.Now;
2091 if (Rec.Expires < Now)
2093 await this.DeleteSubscription(Service, Rec);
2094 await this.NotifySubscriptionChanged(Rec, FromBareJid);
2096 else if (Rec.Deliver &&
2101 Xml =
new StringBuilder();
2105 Xml.Append(
"<event xmlns='");
2107 Xml.Append(
"'><items node='");
2109 Xml.Append(
"'><retract id='");
2110 Xml.Append(
XML.
Encode(Item.ItemIdOrObjectId));
2111 Xml.Append(
"'/></items></event><headers xmlns='");
2113 Xml.Append(
"'><header name='SubID'>");
2114 Xml.Append(Rec.ObjectId);
2115 Xml.Append(
"</header></headers>");
2118 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2129 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2130 "<item-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Item required.",
"en");
2145 lock (this.nodesByServiceAndName)
2147 if (this.nodesByServiceAndName.TryGetValue(Service, out Nodes Nodes))
2149 Nodes.NodesByName[Node.Name] = Node;
2151 Nodes.NodesArray =
new PubSubNode[Nodes.NodesByName.Count];
2152 Nodes.NodesByName.Values.CopyTo(Nodes.NodesArray, 0);
2157 else if (NodeUpdated)
2163 if (!(Subscription is
null))
2165 if (InsertSubscription)
2167 lock (this.subscriptionsByJidAndServiceAndNode)
2169 if (this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out List2) &&
2170 List2.TryGetValue(Service, out List))
2172 List[NodeName] = Subscription;
2176 lock (this.subscriptionsByServiceAndNodeAndJid)
2178 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out List2) &&
2179 List2.TryGetValue(NodeName, out List))
2181 List[Jid] = Subscription;
2187 else if (SubscriptionUpdated)
2189 else if (SubscriptionDeleted)
2193 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
2195 Xml =
new StringBuilder();
2197 Xml.Append(
"<pubsub xmlns='");
2200 Subscription.Serialize(Node, Xml, IncludeOptions);
2201 Xml.Append(
"</pubsub>");
2208 await this.SendLastItem(Service, Node, Subscription.Jid, FromBareJid, e.
Language);
2211 if (NotificationToOwner)
2215 new HiddenField(
"pubsub#subid", Subscription.ObjectId),
2217 new string[] { NodeName },
null,
"Name of node.",
2218 null,
null,
string.Empty,
false,
false,
false),
2219 new JidSingleField(
null,
"pubsub#subscriber_jid",
"Subscriber:",
false,
2220 new string[] { Jid },
null,
"JID of subscriber.",
2221 null,
null,
string.
Empty,
false,
false,
false),
2222 new BooleanField(
null,
"pubsub#allow",
"Authorize subscription request.",
true,
2223 new string[] {
"0" },
null,
"Check this box to authorize the subscription.",
2226 Title =
"Subscription request",
2227 Instructions =
new string[]
2229 Jid +
" wants to subscribe to " + NodeName +
".",
2230 "Approve this subscription request by checking the box below, and click OK.",
2231 "Deny this request by leaving the box uncheced, and click OK."
2235 Xml =
new StringBuilder();
2239 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2264 public Task<string>
PublishItem(
string Service,
string NodeName,
string AutoCreateAccess,
string From,
2265 string Domain,
string ItemId,
string XmlContent,
string Language)
2267 XmlDocument Doc =
new XmlDocument()
2269 PreserveWhitespace =
true
2272 Doc.LoadXml(XmlContent);
2274 return this.
PublishItem(Service, NodeName, AutoCreateAccess, From, Domain, ItemId, Doc, Language);
2289 public Task<string>
PublishItem(
string Service,
string NodeName,
string AutoCreateAccess,
string From,
2290 string Domain,
string ItemId, XmlDocument XmlContent,
string Language)
2294 if (
string.IsNullOrEmpty(AutoCreateAccess))
2295 AutoCreateAccess2 =
null;
2297 AutoCreateAccess2 = Parsed;
2299 throw new ArgumentException(
"Invalid enum value", nameof(AutoCreateAccess));
2301 return this.
PublishItem(Service, NodeName, AutoCreateAccess2, From, Domain, ItemId, XmlContent, Language);
2317 string Domain,
string ItemId, XmlDocument XmlContent,
string Language)
2338 if (
string.IsNullOrEmpty(NodeName))
2339 throw new ArgumentException(
"Node name required.", nameof(NodeName));
2342 ??
throw new ArgumentException(
"Node not found.", nameof(NodeName));
2345 throw new ArgumentException(
"Publishing items on collection nodes is not supported.", nameof(NodeName));
2348 throw new ArgumentException(
"Transient nodes do not accept items.", nameof(NodeName));
2354 Item = await this.LoadItem(Service, NodeName, ItemId);
2356 if (!(Item is
null) && Item.
Node != NodeName)
2357 throw new ArgumentException(
"Item identity points to item belonging to another node.", nameof(ItemId));
2362 if (Node.
PublishOnWeb && !
string.IsNullOrEmpty(ItemId))
2364 if (!(XmlContent.DocumentElement is
null) &&
2365 XmlContent.DocumentElement.LocalName ==
"markdownContent" &&
2366 XmlContent.DocumentElement.NamespaceURI == WebServices.PubSubContent.Content.LilsisNamespace)
2368 StringBuilder Link =
new StringBuilder();
2372 if (Ports.Length > 0)
2374 Link.Append(
"https://");
2375 Link.Append(IoTGateway.Gateway.Domain);
2380 Link.Append(Ports[0].ToString());
2387 Link.Append(
"http://");
2388 Link.Append(IoTGateway.Gateway.Domain);
2393 Link.Append(Ports[0].ToString());
2398 Link.Append(System.Web.HttpUtility.UrlEncode(Node.
Name));
2403 Link.Append(ItemId);
2405 XmlContent.DocumentElement.SetAttribute(
"link", Link.ToString());
2409 Content = XmlContent.InnerXml.Trim();
2411 byte[] ContentBin = Encoding.UTF8.GetBytes(Content);
2412 DateTime Now = DateTime.Now;
2415 throw new ArgumentException(
"Missing payload.", nameof(XmlContent));
2418 throw new ArgumentException(
"Payload too big.", nameof(XmlContent));
2426 await Decoder.DecodeAsync(Node.
PayloadType, ContentBin, Encoding.UTF8,
2427 new KeyValuePair<string, string>[0],
null);
2431 throw new ArgumentException(
"Payload does not conform to Internet Content Type " + Node.
PayloadType, nameof(XmlContent));
2434 else if (XmlContent.DocumentElement is
null || XmlContent.DocumentElement.NamespaceURI != Node.
PayloadType)
2435 throw new ArgumentException(
"Payload does not conform to namespace " + Node.
PayloadType, nameof(XmlContent));
2438 if (!(Item is
null))
2440 Node.BytesPublished -= await this.GetItemSize(Item);
2442 Item.Publisher = From.
BareJid;
2443 Item.Payload = Content;
2454 bool Deleted =
false;
2460 Node.BytesDeleted += await this.GetItemSize(Item2);
2495 this.items[Item.Key] = Item;
2498 Node.LastPublished = Item;
2503 Node.BytesPublished += await this.GetItemSize(Item);
2508 await this.SendItemEventNotification(Node, Item, From, Language, Now);
2515 StringBuilder Xml =
new StringBuilder();
2517 Xml.Append(
"<event xmlns='");
2519 Xml.Append(
"'><items node='");
2521 Xml.Append(
"'><item id='");
2523 Xml.Append(
"' publisher='");
2533 Xml.Append(
"</item>");
2538 Xml.Append(
"</items></event>");
2548 Xml.Append(
"<addresses xmlns='");
2550 Xml.Append(
"'><address type='replyto' jid='");
2552 Xml.Append(
"'/></addresses>");
2555 EventXml = Xml.ToString();
2557 IEnumerable<IRosterItem> Roster = await XmppServerModule.GetRosterAsync(From.
Account);
2558 if (!(Roster is
null))
2566 if (!(Jids is
null))
2571 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2572 From.
BareJid, Jid2, Language, EventXml);
2579 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2590 if (Rec.Expires < Now)
2592 await this.DeleteSubscription(Node.
Service, Rec);
2593 await this.NotifySubscriptionChanged(Rec, FromBareJid);
2595 else if (Rec.Deliver &&
2601 Xml.Append(EventXml);
2602 Xml.Append(
"<headers xmlns='");
2604 Xml.Append(
"'><header name='SubID'>");
2605 Xml.Append(Rec.ObjectId);
2606 Xml.Append(
"</header></headers>");
2609 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2619 if (!Node.LastPublishedKnown)
2625 Node.LastPublished = Item;
2628 Node.LastPublishedKnown =
true;
2631 PubSubItem LastItem = Node.LastPublished;
2632 if (!(LastItem is
null))
2634 StringBuilder Xml =
new StringBuilder();
2636 Xml.Append(
"<event xmlns='");
2638 Xml.Append(
"'><items node='");
2640 Xml.Append(
"'><item id='");
2641 Xml.Append(
XML.
Encode(LastItem.ItemIdOrObjectId));
2642 Xml.Append(
"' publisher='");
2643 Xml.Append(
XML.
Encode(LastItem.Publisher));
2645 if (Node.DeliverPayloads)
2649 if (
XML.
IsValidXml(LastItem.Payload,
true,
true,
true,
true,
false,
false))
2650 Xml.Append(LastItem.Payload);
2652 Xml.Append(
"</item>");
2657 Xml.Append(
"</items></event>");
2658 Xml.Append(
"<delay xmlns='");
2660 Xml.Append(
"' stamp='");
2661 Xml.Append(
XML.
Encode(LastItem.Created));
2665 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
2674 List<Filter> Filters =
new List<Filter>()
2680 if (!
string.IsNullOrEmpty(From))
2684 PubSubItem Item = await this.LoadItem(Service, NodeName, From);
2685 if (Item is
null || Item.Node != NodeName)
2696 List<PubSubItem> Result =
new List<PubSubItem>();
2698 string Last =
string.Empty;
2700 foreach (PubSubItem Item
in await
Database.
Find<PubSubItem>(0, MaxCount + 1,
new FilterAnd(Filters.ToArray()),
"-Service",
"-Node",
"-Created"))
2702 if (MaxCount-- <= 0)
2708 Last = Item.ItemIdOrObjectId;
2712 return new Tuple<PubSubItem[], bool, string>(Result.ToArray(), More, Last);
2717 StringBuilder Key =
new StringBuilder();
2719 Key.Append(Service);
2721 Key.Append(NodeName);
2726 if (this.items.
TryGetValue(Key.ToString(), out PubSubItem Item))
2740 if (Guid.TryParse(ItemId, out Guid Guid2))
2748 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> List2;
2749 Dictionary<CaseInsensitiveString, Subscription> List;
2751 lock (this.subscriptionsByJidAndServiceAndNode)
2753 if (this.subscriptionsByJidAndServiceAndNode.TryGetValue(Subscription.Jid, out List2) &&
2754 List2.TryGetValue(Service, out List))
2756 List.Remove(Subscription.Node);
2760 lock (this.subscriptionsByServiceAndNodeAndJid)
2762 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out List2) &&
2763 List2.TryGetValue(Subscription.Node, out List))
2765 List.Remove(Subscription.Jid);
2772 private async Task<int> GetItemSize(PubSubItem Item)
2774 if (!(this.itemSerializer is
null))
2777 await this.itemSerializer.
Serialize(Serializer,
false,
false, Item,
null);
2780 return Binary.Length;
2785 Encoding.UTF8.GetBytes(Item.ObjectId).Length +
2786 Encoding.UTF8.GetBytes(Item.ItemId).Length +
2787 Encoding.UTF8.GetBytes(Item.Node).Length +
2788 Encoding.UTF8.GetBytes(Item.Publisher).Length +
2789 Encoding.UTF8.GetBytes(Item.Payload).Length +
2794 private Task PubSubOwnerGetHandler(
object Sender,
IqEventArgs e)
2796 return this.PubSubOwnerGetHandler(
string.Empty, e, pubSubFeatures);
2799 private Task PepOwnerGetHandler(
object Sender,
IqEventArgs e)
2801 return this.PubSubOwnerGetHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
2808 foreach (XmlNode N
in e.
Query.ChildNodes)
2810 if (N is XmlElement E)
2812 switch (E.LocalName)
2816 if (
string.IsNullOrEmpty(NodeName))
2818 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2819 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
2836 DataForm Form = this.GetForm(Node, e);
2837 StringBuilder Xml =
new StringBuilder();
2839 Xml.Append(
"<pubsub xmlns='");
2841 Xml.Append(
"'><configure node='");
2845 Xml.Append(
"</configure></pubsub>");
2851 Node =
new PubSubNode()
2853 Domain = e.To.Address
2855 Form = this.GetForm(Node, e);
2856 Xml =
new StringBuilder();
2858 Xml.Append(
"<pubsub xmlns='");
2860 Xml.Append(
"'><default>");
2862 Xml.Append(
"</default></pubsub>");
2867 case "subscriptions":
2869 if (
string.IsNullOrEmpty(NodeName))
2871 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2872 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
2889 Xml =
new StringBuilder();
2891 Xml.Append(
"<pubsub xmlns='");
2893 Xml.Append(
"'><subscriptions node='");
2899 foreach (Subscription Subscription
in Subscriptions)
2901 Xml.Append(
"<subscription jid='");
2902 Xml.Append(
XML.
Encode(Subscription.Jid));
2903 Xml.Append(
"' subscription='");
2904 Xml.Append(Subscription.Status.ToString());
2908 Xml.Append(
"</subscriptions></pubsub>");
2913 case "affiliations":
2915 if (
string.IsNullOrEmpty(NodeName))
2917 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
2918 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
2935 Xml =
new StringBuilder();
2937 Xml.Append(
"<pubsub xmlns='");
2939 Xml.Append(
"'><affiliations node='");
2943 foreach (KeyValuePair<CaseInsensitiveString, AffiliationStatus> Affiliation
in Node.GetAffiliations())
2945 Xml.Append(
"<affiliation jid='");
2947 Xml.Append(
"' affiliation='");
2952 Xml.Append(
"</affiliations></pubsub>");
2970 return "publish-only";
2979 new TextSingleField(
null,
"pubsub#title",
"Title:",
false,
new string[] { Node.Title },
null,
2981 new BooleanField(
null,
"pubsub#deliver_notifications",
"Event notifications.",
false,
2982 new string[] { Node.DeliverNotifications ?
"1" :
"0" },
null,
2983 "Whether to deliver event notifications.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2984 new BooleanField(
null,
"pubsub#deliver_payloads",
"Deliver payloads.",
false,
2985 new string[] { Node.DeliverPayloads ?
"1" :
"0" },
null,
2986 "Whether to deliver payloads with event notifications.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2987 new BooleanField(
null,
"pubsub#notify_config",
"Notify configurations.",
false,
2988 new string[] { Node.NotifyConfig ?
"1" :
"0" },
null,
2989 "Notify subscribers when the node configuration changes.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2990 new BooleanField(
null,
"pubsub#notify_delete",
"Notify delete.",
false,
2991 new string[] { Node.NotifyDelete ?
"1" :
"0" },
null,
2992 "Notify subscribers when the node is deleted.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2993 new BooleanField(
null,
"pubsub#notify_retract",
"Notify retract.",
false,
2994 new string[] { Node.NotifyRetract ?
"1" :
"0" },
null,
2995 "Notify subscribers when items are removed from the node.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2996 new BooleanField(
null,
"pubsub#notify_sub",
"Notify subscriptions.",
false,
2997 new string[] { Node.NotifySubscriptions ?
"1" :
"0" },
null,
2998 "Notify owners about new subscribers and unsubscribes.",
BooleanDataType.
Instance,
null,
string.Empty,
false,
false,
false),
2999 new BooleanField(
null,
"pubsub#persist_items",
"Persist items.",
false,
3000 new string[] { Node.PersistItems ?
"1" :
"0" },
null,
3002 new TextSingleField(
null,
"pubsub#max_items",
"Items to persist:",
false,
3003 new string[] { Node.MaxItems.ToString() },
null,
3005 string.Empty,
false,
false,
false),
3006 new TextSingleField(
null,
"pubsub#item_expire",
"Seconds before items expire:",
false,
3007 new string[] { Node.ItemExpireSeconds.ToString() },
null,
3009 string.Empty,
false,
false,
false),
3010 new BooleanField(
null,
"pubsub#purge_offline",
"Purge when publisher offline.",
false,
3011 new string[] { Node.PurgeOffline ?
"1" :
"0" },
null,
3013 string.Empty,
false,
false,
false),
3014 new TextSingleField(
null,
"pubsub#node_expire",
"Date when node expires:",
false,
3015 new string[] {
XML.
Encode(Node.Expires,
true) },
null,
3017 string.Empty,
false,
false,
false),
3018 new BooleanField(
null,
"pubsub#subscribe",
"Allow subscriptions.",
false,
3019 new string[] { Node.AllowSubscriptions ?
"1" :
"0" },
null,
3021 string.Empty,
false,
false,
false),
3022 new ListSingleField(
null,
"pubsub#access_model",
"Access model:",
false,
3023 new string[] { Node.AccessModel.ToString() },
new KeyValuePair<string, string>[]
3025 new KeyValuePair<string, string>(
"Everyone has access",
"open"),
3026 new KeyValuePair<string, string>(
"Contacts with approved presence subscriptions",
"presence"),
3027 new KeyValuePair<string, string>(
"Contacts in specified groups",
"roster"),
3028 new KeyValuePair<string, string>(
"Only authorized entities",
"authorize"),
3029 new KeyValuePair<string, string>(
"Only if on whitelist",
"whitelist")
3032 string.Empty,
false,
false,
false),
3033 new TextMultiField(
null,
"pubsub#roster_groups_allowed",
"Roster groups allowed:",
false,
3034 Node.RosterGroupsAllowed,
null,
3036 string.Empty,
false,
false,
false),
3037 new ListSingleField(
null,
"pubsub#publish_model",
"Publish model:",
false,
3038 new string[] { Node.PublisherModel.ToString() },
new KeyValuePair<string, string>[]
3040 new KeyValuePair<string, string>(
"Only publishers may publish",
"publishers"),
3041 new KeyValuePair<string, string>(
"Subscribers may publish",
"subscribers"),
3042 new KeyValuePair<string, string>(
"Anyone may publish",
"open")
3045 string.Empty,
false,
false,
false),
3046 new TextSingleField(
null,
"pubsub#max_payload_size",
"Maximum payload size:",
false,
3047 new string[] { Node.MaxPayloadSize.ToString() },
null,
3049 string.Empty,
false,
false,
false),
3050 new ListSingleField(
null,
"pubsub#send_last_published_item",
"Send last item:",
false,
3051 new string[] { Node.SendLastPublishedItem.ToString() },
new KeyValuePair<string, string>[]
3053 new KeyValuePair<string, string>(
"Never",
"never"),
3054 new KeyValuePair<string, string>(
"When a new subscription is processed",
"on_sub"),
3055 new KeyValuePair<string, string>(
"When a new subscription is processed and whenever a subscriber comes online",
"on_sub_and_presence")
3058 string.Empty,
false,
false,
false),
3059 new BooleanField(
null,
"pubsub#presence_based_delivery",
"Notifications only to available users.",
false,
3060 new string[] { Node.PresenceBasedDelivery ?
"1" :
"0" },
null,
3062 string.Empty,
false,
false,
false),
3063 new ListSingleField(
null,
"pubsub#notification_type",
"Notification type:",
false,
3064 new string[] { Node.NotificationType.ToString() },
new KeyValuePair<string, string>[]
3066 new KeyValuePair<string, string>(
"Messages of type normal",
"normal"),
3067 new KeyValuePair<string, string>(
"Messages of type headline",
"headline")
3069 "Specify the delivery style for event notifications.",
null,
new BasicValidation(),
3070 string.Empty,
false,
false,
false),
3072 new string[] { Node.PayloadType },
null,
3074 string.Empty,
false,
false,
false),
3075 new TextSingleField(
null,
"pubsub#dataform_xslt",
"Data Forms XSLT:",
false,
3076 new string[] { Node.DataFormXsltUrl },
null,
3077 "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(),
3078 string.Empty,
false,
false,
false),
3079 new TextSingleField(
null,
"pubsub#body_xslt",
"Message Body XSLT:",
false,
3080 new string[] { Node.BodyXsltUrl },
null,
3082 string.Empty,
false,
false,
false),
3083 new TextMultiField(
null,
"pubsub#collection",
"Collections:",
false, Node.Collections.ToStringArray(),
null,
3085 string.Empty,
false,
false,
false),
3086 new JidMultiField(
null,
"pubsub#contact",
"Contacts:",
false, Node.Contact.ToStringArray(),
null,
3088 string.Empty,
false,
false,
false),
3089 new ListSingleField(
null,
"pubsub#itemreply",
"Send replies to:",
false,
3090 new string[] { Node.ItemReply.ToString() },
new KeyValuePair<string, string>[]
3092 new KeyValuePair<string, string>(
"Statically specify a replyto of the node owner(s)",
"owner"),
3093 new KeyValuePair<string, string>(
"Dynamically specify a replyto of the item publisher",
"publisher")
3095 "Whether owners or publisher should receive replies to items.",
null,
new BasicValidation(),
3096 string.Empty,
false,
false,
false),
3097 new ListSingleField(
null,
"pubsub#children_association_policy",
"Who can associate child nodes:",
false,
3098 new string[] { Node.ChildAssociationPolicy.ToString() },
new KeyValuePair<string, string>[]
3100 new KeyValuePair<string, string>(
"Anyone may associate leaf nodes with the collection",
"all"),
3101 new KeyValuePair<string, string>(
"Only collection node owners may associate leaf nodes with the collection",
"owners"),
3102 new KeyValuePair<string, string>(
"Only those on a whitelist may associate leaf nodes with the collection",
"whitelist")
3104 "Who may associate leaf nodes with a collection.",
null,
new BasicValidation(),
3105 string.Empty,
false,
false,
false),
3106 new TextMultiField(
null,
"pubsub#children_association_whitelist",
"Child association whitelist:",
false, Node.ChildAssociationWhitelist.ToStringArray(),
null,
3108 string.Empty,
false,
false,
false),
3109 new TextMultiField(
null,
"pubsub#children",
"Child nodes:",
false, Node.Children.ToStringArray(),
null,
3111 string.Empty,
false,
false,
false),
3112 new TextSingleField(
null,
"pubsub#children_max",
"Maximum amount of child nodes:",
false, Node.Children.ToStringArray(),
null,
3114 new RangeValidation(
"1",
int.MaxValue.ToString()),
string.Empty,
false,
false,
false),
3116 new string[] { Node.NodeType.ToString() },
new KeyValuePair<string, string>[]
3118 new KeyValuePair<string, string>(
"The node is a leaf node (default)",
"leaf"),
3119 new KeyValuePair<string, string>(
"The node is a collection node",
"collection")
3121 "Whether the node is a leaf (default) or a collection.",
null,
new BasicValidation(),
3122 string.Empty,
false,
false,
false),
3123 new JidMultiField(
null,
"pubsub#replyroom",
"Reply rooms:",
false, Node.ReplyRooms.ToStringArray(),
null,
3125 string.Empty,
false,
false,
false),
3126 new JidMultiField(
null,
"pubsub#replyto",
"Reply to:",
false, Node.ReplyTo.ToStringArray(),
null,
3128 string.Empty,
false,
false,
false),
3129 new TextMultiField(
null,
"pubsub#description",
"Description:",
false, Node.Description,
null,
3131 new TextSingleField(
null,
"pubsub#language",
"Default language:",
false,
new string[] { Node.Language },
null,
3135 private Task PubSubOwnerSetHandler(
object Sender,
IqEventArgs e)
3137 return this.PubSubOwnerSetHandler(
string.Empty, e, pubSubFeatures);
3140 private Task PepOwnerSetHandler(
object Sender,
IqEventArgs e)
3142 return this.PubSubOwnerSetHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
3148 PubSubNode Node =
null;
3149 bool NodeUpdated =
false;
3151 foreach (XmlNode N
in e.
Query.ChildNodes)
3153 if (N is XmlElement E)
3155 switch (E.LocalName)
3159 if (
string.IsNullOrEmpty(NodeName))
3161 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3162 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
3173 if (await this.ConfigureNode(E, Node, e))
3197 string RedirectUri =
null;
3199 foreach (XmlNode N2
in E.ChildNodes)
3201 if (N2 is XmlElement E2 && E2.LocalName ==
"redirect" && E2.NamespaceURI == E.NamespaceURI)
3208 await this.DeleteNode(Service, Node, FromBareJid);
3222 (
bool CanPurge,
bool OnlyItemsBySameEntity) = await Node.CanPurgeNode(e.
From.
BareJid);
3229 Node.LastPublished =
null;
3231 if (!Node.PersistItems)
3233 await e.
IqError(
"cancel",
"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3234 "<unsupported xmlns='http://jabber.org/protocol/pubsub#errors' feature='persistent-items'/>",
3235 e.
To,
"Node does not persist items.",
"en");
3239 if (OnlyItemsBySameEntity)
3254 if (Node.DeliverNotifications)
3256 DateTime Now = DateTime.Now;
3257 StringBuilder Xml =
null;
3261 if (Rec.Expires < Now)
3263 await this.DeleteSubscription(Service, Rec);
3264 await this.NotifySubscriptionChanged(Rec, FromBareJid);
3266 else if (Rec.Deliver &&
3271 Xml =
new StringBuilder();
3275 Xml.Append(
"<event xmlns='");
3277 Xml.Append(
"'><purge node='");
3279 Xml.Append(
"'/></event><headers xmlns='");
3281 Xml.Append(
"'><header name='SubID'>");
3282 Xml.Append(Rec.ObjectId);
3283 Xml.Append(
"</header></headers>");
3286 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
3294 case "subscriptions":
3296 if (
string.IsNullOrEmpty(NodeName))
3298 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3299 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
3316 foreach (XmlNode N2
in E.ChildNodes)
3318 if (N2 is XmlElement E2 && E2.LocalName ==
"subscription" &&
3319 E2.NamespaceURI == E.NamespaceURI)
3325 if (Subscription is
null)
3331 Dictionary<CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> List2;
3332 Dictionary<CaseInsensitiveString, Subscription> List =
null;
3334 Subscription =
new Subscription()
3343 lock (this.subscriptionsByJidAndServiceAndNode)
3345 if (this.subscriptionsByJidAndServiceAndNode.TryGetValue(Jid, out List2) &&
3346 List2.TryGetValue(Service, out List))
3348 List[NodeName] = Subscription;
3352 lock (this.subscriptionsByServiceAndNodeAndJid)
3354 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out List2) &&
3355 List2.TryGetValue(NodeName, out List))
3357 List[Jid] = Subscription;
3363 else if (Subscription.Status == Status)
3367 Subscription.Status = Status;
3370 await this.DeleteSubscription(Service, Subscription);
3375 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
3382 case "affiliations":
3384 if (
string.IsNullOrEmpty(NodeName))
3386 await e.
IqError(
"modify",
"<bad-request xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3387 "<nodeid-required xmlns='http://jabber.org/protocol/pubsub#errors'/>", e.
To,
"Node name required.",
"en");
3404 foreach (XmlNode N2
in E.ChildNodes)
3406 if (N2 is XmlElement E2 && E2.LocalName ==
"affiliation" &&
3407 E2.NamespaceURI == E.NamespaceURI)
3413 if (s ==
"publish-only")
3418 Node.SetAffiliation(Jid, Affiliation);
3419 await this.NotifyAffiliationChanged(NodeName, Jid, Affiliation, FromBareJid);
3449 Dictionary<CaseInsensitiveString, Subscription> List =
null;
3450 string NodeName = Node.Name;
3452 lock (this.subscriptionsByServiceAndNodeAndJid)
3454 if (this.subscriptionsByServiceAndNodeAndJid.TryGetValue(Service, out Dictionary<
CaseInsensitiveString, Dictionary<CaseInsensitiveString, Subscription>> List2) &&
3455 List2.TryGetValue(NodeName, out List))
3457 List2.Remove(NodeName);
3463 if (!(List is
null))
3465 foreach (Subscription Subscription
in List.Values)
3468 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
3476 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
3482 lock (this.nodesByServiceAndName)
3484 if (this.nodesByServiceAndName.TryGetValue(Service, out Nodes Nodes))
3486 Nodes.NodesByName.Remove(NodeName);
3488 Nodes.NodesArray =
new PubSubNode[Nodes.NodesByName.Count];
3489 Nodes.NodesByName.Values.CopyTo(Nodes.NodesArray, 0);
3497 private async Task NotifySubscriptionChanged(Subscription Subscription,
XmppAddress FromBareJid)
3499 StringBuilder Xml =
new StringBuilder();
3501 Xml.Append(
"<event xmlns='");
3503 Xml.Append(
"'><subscription node='");
3504 Xml.Append(
XML.
Encode(Subscription.Node));
3505 Xml.Append(
"' jid='");
3506 Xml.Append(
XML.
Encode(Subscription.Jid));
3507 Xml.Append(
"' subscription='");
3508 Xml.Append(Subscription.Status.ToString());
3509 Xml.Append(
"'/></event>");
3512 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
3519 StringBuilder Xml =
new StringBuilder();
3521 Xml.Append(
"<event xmlns='");
3523 Xml.Append(
"'><affiliations node='");
3525 Xml.Append(
"'><affiliation jid='");
3527 Xml.Append(
"' affiliation='");
3528 Xml.Append(ToString(Affiliation));
3529 Xml.Append(
"'/></affiliations></event>");
3532 Guid.NewGuid().ToString().Replace(
"-",
string.Empty),
3537 private async Task<bool> ConfigureNode(XmlElement E, PubSubNode Node,
IqEventArgs e)
3539 foreach (XmlNode N2
in E.ChildNodes)
3541 if (N2 is XmlElement E2 && E2.LocalName ==
"x" && E2.NamespaceURI == Networking.XMPP.XmppClient.NamespaceData)
3547 switch (Form[
"FORM_TYPE"]?.ValueString)
3569 case "pubsub#access_model":
3570 if (!
string.IsNullOrEmpty(F.ValueString))
3573 Node.AccessModel = AccessModel;
3576 await e.
IqError(
"modify",
"<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>" +
3577 "<unsupported-access-model xmlns='http://jabber.org/protocol/pubsub#errors'/>",
3578 e.
To,
"Access model not supported.",
"en");
3584 case "pubsub#body_xslt":
3585 Node.BodyXsltUrl = F.ValueString;
3588 case "pubsub#collection":
3589 Node.Collections = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3590 Node.IsRoot = (Node.Collections is
null || Node.Collections.Length == 0);
3593 case "pubsub#contact":
3594 Node.Contact = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3597 case "pubsub#dataform_xslt":
3598 Node.DataFormXsltUrl = F.ValueString;
3601 case "pubsub#deliver_notifications":
3602 if (!
string.IsNullOrEmpty(F.ValueString))
3605 Node.DeliverNotifications = b;
3614 case "pubsub#deliver_payloads":
3615 if (!
string.IsNullOrEmpty(F.ValueString))
3618 Node.DeliverPayloads = b;
3627 case "pubsub#itemreply":
3628 if (!
string.IsNullOrEmpty(F.ValueString))
3630 if (Enum.TryParse(F.ValueString, out
NodeItemReply ItemReply))
3631 Node.ItemReply = ItemReply;
3640 case "pubsub#children_association_policy":
3641 if (!
string.IsNullOrEmpty(F.ValueString))
3644 Node.ChildAssociationPolicy = ChildAssociationPolicy;
3653 case "pubsub#children_association_whitelist":
3654 Node.ChildAssociationWhitelist = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3657 case "pubsub#children":
3658 Node.Children = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3661 case "pubsub#children_max":
3662 if (!
string.IsNullOrEmpty(F.ValueString))
3664 if (
int.TryParse(F.ValueString, out
int i) && i > 0)
3665 Node.MaxChildren = i;
3674 case "pubsub#max_items":
3675 if (!
string.IsNullOrEmpty(F.ValueString))
3677 if (
int.TryParse(F.ValueString, out
int i) && i > 0)
3687 case "pubsub#item_expire":
3688 if (!
string.IsNullOrEmpty(F.ValueString))
3690 if (
int.TryParse(F.ValueString, out
int i) && i > 0)
3691 Node.ItemExpireSeconds = i;
3700 case "pubsub#node_expire":
3701 if (!
string.IsNullOrEmpty(F.ValueString))
3703 if (
XML.
TryParse(F.ValueString, out DateTime TP) && TP >= DateTime.Today)
3713 case "pubsub#max_payload_size":
3714 if (!
string.IsNullOrEmpty(F.ValueString))
3716 if (
int.TryParse(F.ValueString, out
int i) && i > 0)
3717 Node.MaxPayloadSize = i;
3726 case "pubsub#node_type":
3727 if (!
string.IsNullOrEmpty(F.ValueString))
3739 case "pubsub#notify_config":
3740 if (!
string.IsNullOrEmpty(F.ValueString))
3743 Node.NotifyConfig = b;
3752 case "pubsub#notify_delete":
3753 if (!
string.IsNullOrEmpty(F.ValueString))
3756 Node.NotifyDelete = b;
3765 case "pubsub#notify_retract":
3766 if (!
string.IsNullOrEmpty(F.ValueString))
3769 Node.NotifyRetract = b;
3778 case "pubsub#notify_sub":
3779 if (!
string.IsNullOrEmpty(F.ValueString))
3782 Node.NotifySubscriptions = b;
3791 case "pubsub#persist_items":
3792 if (!
string.IsNullOrEmpty(F.ValueString))
3795 Node.PersistItems = b;
3804 case "pubsub#presence_based_delivery":
3805 if (!
string.IsNullOrEmpty(F.ValueString))
3808 Node.PresenceBasedDelivery = b;
3817 case "pubsub#publish_model":
3818 if (!
string.IsNullOrEmpty(F.ValueString))
3830 case "pubsub#replyroom":
3831 Node.ReplyRooms = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3834 case "pubsub#replyto":
3835 Node.ReplyTo = CheckEmpty(F.ValueStrings.ToCaseInsensitiveStringArray());
3838 case "pubsub#roster_groups_allowed":
3839 Node.RosterGroupsAllowed = CheckEmpty(F.ValueStrings);
3842 case "pubsub#send_last_published_item":
3843 if (!
string.IsNullOrEmpty(F.ValueString))
3855 case "pubsub#subscribe":
3856 if (!
string.IsNullOrEmpty(F.ValueString))
3859 Node.AllowSubscriptions = b;
3868 case "pubsub#title":
3869 Node.Title = F.ValueString;
3872 case "pubsub#description":
3873 Node.Description = CheckEmpty(F.ValueStrings);
3876 case "pubsub#language":
3877 Node.Language = F.ValueString;
3881 Node.PayloadType = F.ValueString;
3884 case "pubsub#notification_type":
3885 if (!
string.IsNullOrEmpty(F.ValueString))
3897 case "pubsub#purge_offline":
3898 if (!
string.IsNullOrEmpty(F.ValueString))
3901 Node.PurgeOffline = b;
3953 private static string[] CheckEmpty(
string[] Array)
3955 if (!(Array is
null) && Array.Length == 1 &&
string.IsNullOrEmpty(Array[0]))
3956 return new string[0];
3961 private async Task<bool> ConfigureSubscription(XmlElement E, Subscription Subscription,
IqEventArgs e)
3963 foreach (XmlNode N2
in E.ChildNodes)
3965 if (N2 is XmlElement E2 && E2.LocalName ==
"x" && E2.NamespaceURI == Networking.XMPP.XmppClient.NamespaceData)
3971 switch (Form[
"FORM_TYPE"]?.ValueString)
3981 case "pubsub#deliver":
3983 Subscription.Deliver = b;
3991 case "pubsub#digest":
3993 Subscription.Digest = b;
4001 case "pubsub#include_body":
4003 Subscription.IncludeBody = b;
4011 case "pubsub#digest_frequency":
4012 if (
int.TryParse(F.ValueString, out
int i))
4013 Subscription.DigestFrequencyMilliseconds = i;
4021 case "pubsub#expire":
4023 Subscription.Expires = TP;
4024 else if (DateTime.TryParse(F.ValueString, out TP))
4025 Subscription.Expires = TP;
4033 case "pubsub#subscription_type":
4043 case "pubsub#subscription_depth":
4044 if (F.ValueString ==
"1")
4097 return !(provisioningClient is
null) && provisioningClient.
OwnerJid == BareJid;
4102 return this.MessageFormHandler(
string.Empty, e, pubSubFeatures);
4107 return this.MessageFormHandler((e.
To.
IsEmpty ? e.
From : e.
To).BareJid, e, pepFeatures);
4114 Field FormTypeField = Form[
"FORM_TYPE"];
4115 if (FormTypeField is
null)
4124 string SubscriptionId = Form[
"pubsub#subid"]?.ValueString ??
string.Empty;
4128 if (!
CommonTypes.
TryParse(Form[
"pubsub#allow"]?.ValueString ??
string.Empty, out
bool Allow))
4132 if (Subscription is
null ||
4133 Subscription.ObjectId != SubscriptionId ||
4134 Subscription.Node != NodeName ||
4135 Subscription.Jid != Jid)
4153 await this.NotifySubscriptionChanged(Subscription, FromBareJid);
4158 await this.SendLastItem(Service, Node, Subscription.Jid, FromBareJid, e.
Language);
4172 private Task PepDiscoveryQueryGet(
object Sender,
IqEventArgs e)
4177 private Task PepDiscoverIdentities(
object Sender, StringBuilder sb)
4179 sb.Append(
"<identity category='pubsub' type='pep'/>");
4180 return Task.CompletedTask;
4185 XmlElement E = e.
Query;
4187 if (
string.IsNullOrEmpty(NodeName))
4188 await base.DiscoveryQueryGet(Sender, e);
4196 StringBuilder Xml =
new StringBuilder();
4198 Xml.Append(
"<query xmlns='");
4200 Xml.Append(
"' node='");
4202 Xml.Append(
"'><identity category='pubsub' type='");
4203 Xml.Append(Node.NodeType.ToString());
4205 if (!
string.IsNullOrEmpty(Node.Title))
4207 Xml.Append(
"' name='");
4212 int NrSubscribers = 0;
4223 new JidSingleField(
"pubsub#creator",
"Node creator", Node.Creator),
4225 new TextSingleField(
"pubsub#title",
"A short name for the node", Node.Title),
4226 new TextMultiField(
"pubsub#description",
"A description of the node", Node.Description),
4227 new ListSingleField(
"pubsub#language",
"Default language", Node.Language),
4228 new JidMultiField(
"pubsub#contact",
"People to contact with questions", Node.Contact.ToStringArray()),
4229 new TextSingleField(
"pubsub#max_items",
"Max # of items to persist", Node.MaxItems.ToString()),
4230 new JidMultiField(
"pubsub#owner",
"Node owners", Node.Owners.ToStringArray()),
4231 new JidMultiField(
"pubsub#publisher",
"Publishers to this node", Node.Publishers.ToStringArray()),
4232 new TextSingleField(
"pubsub#num_subscribers",
"Number of subscribers to this node", NrSubscribers.ToString()));
4235 Xml.Append(
"</query>");
4247 Xml.Append(
"<identity category='pubsub' type='service' name='Publish/Subscribe service' />");
4248 return Task.CompletedTask;
4256 private Task PepDiscoveryQueryItemsGet(
object Sender,
IqEventArgs e)
4263 XmlElement E = e.
Query;
4264 StringBuilder Xml =
new StringBuilder();
4266 Xml.Append(
"<query xmlns='");
4269 if (E.HasAttribute(
"node"))
4280 Xml.Append(
"' node='");
4284 switch (Node.NodeType)
4287 if (!(Node.Children is
null))
4289 foreach (
string Child
in Node.Children)
4295 Xml.Append(
"<item jid='");
4297 Xml.Append(
"' node='");
4300 if (!
string.IsNullOrEmpty(Node2.Title))
4302 Xml.Append(
"' name='");
4314 foreach (PubSubItem Item
in Items)
4316 Xml.Append(
"<item jid='");
4318 Xml.Append(
"' name='");
4319 Xml.Append(
XML.
Encode(Item.ItemIdOrObjectId));
4332 Xml.Append(
"<item jid='");
4334 Xml.Append(
"' node='");
4337 if (!
string.IsNullOrEmpty(Node.Title))
4339 Xml.Append(
"' name='");
4347 Xml.Append(
"</query>");
4352 private async Task Server_OnPresenceLocalRecipient(
object Sender,
PresenceEventArgs e)
4359 string NodeVer =
null;
4363 if (N is XmlElement E &&
4364 E.LocalName ==
"c" &&
4365 E.NamespaceURI ==
"http://jabber.org/protocol/caps")
4370 NodeVer = Node +
"#" + Version;
4375 if (NodeVer is
null)
4379 lock (this.nodeVerByFullJidByBareJid)
4381 if (!this.nodeVerByFullJidByBareJid.TryGetValue(e.
From.
BareJid,
4382 out NodeVerInfo NodeVerInfo))
4384 NodeVerInfo =
new NodeVerInfo()
4386 NodeVerPerFullJid =
new Dictionary<CaseInsensitiveString, string>()
4389 this.nodeVerByFullJidByBareJid[e.
From.
BareJid] = NodeVerInfo;
4392 NodeVerInfo.NodeVerPerFullJid[e.
From.
Address] = NodeVer;
4393 NodeVerInfo.UpdateStaticLocked();
4402 NodeVer +
"'/>", async (sender2, e2) =>
4409 !((E2 = e2.FirstElement) is
null) &&
4410 E2.LocalName ==
"query" &&
4414 Dictionary<string, bool> Features = new Dictionary<string, bool>();
4416 foreach (XmlNode N in E2.ChildNodes)
4418 if (N is XmlElement E3 && E3.LocalName ==
"feature")
4419 Features[XML.Attribute(E3,
"var")] = true;
4422 this.featuresByNodeVer[NodeVer] = Features;
4429 catch (Exception ex)
4443 lock (this.nodeVerByFullJidByBareJid)
4445 if (this.nodeVerByFullJidByBareJid.TryGetValue(e.
From.
BareJid,
4446 out NodeVerInfo NodeVerInfo) &&
4447 NodeVerInfo.NodeVerPerFullJid.Remove(e.
From.
Address))
4449 if (NodeVerInfo.NodeVerPerFullJid.Count == 0)
4450 this.nodeVerByFullJidByBareJid.Remove(e.
From.
BareJid);
4452 NodeVerInfo.UpdateStaticLocked();
4458 catch (Exception ex)
4466 string SubscriberBareJid = Subscriber.
BareJid;
4471 PubSubNode[] NodeArray =
null;
4476 lock (this.nodesByServiceAndName)
4478 if (this.nodesByServiceAndName.TryGetValue(Service, out Nodes))
4479 NodeArray = Nodes.NodesArray;
4486 await this.GetNodeAsync(Service,
string.Empty,
null, Publisher.
ToBareJID(),
null);
4488 lock (this.nodesByServiceAndName)
4490 if (!this.nodesByServiceAndName.TryGetValue(Service, out Nodes))
4493 NodeArray = Nodes.NodesArray;
4497 foreach (PubSubNode Node
in NodeArray)
4499 if (Node.AutoSubscribe && Node.AccessModel ==
NodeAccessModel.presence)
4506 await this.SendLastItem(Service, Node, Jid, FromBareJid, Language);
4514 KeyValuePair<CaseInsensitiveString, string>[] NodeVerPerFullJid;
4516 lock (this.nodeVerByFullJidByBareJid)
4518 if (!this.nodeVerByFullJidByBareJid.TryGetValue(BareJid, out NodeVerInfo NodeVerInfo))
4521 NodeVerPerFullJid = NodeVerInfo.NodeVerPerFullJidStatic;
4524 string Feature = Node.Name +
"+notify";
4525 List<CaseInsensitiveString> FullJids =
null;
4527 foreach (KeyValuePair<CaseInsensitiveString, string> P
in NodeVerPerFullJid)
4529 if (!this.featuresByNodeVer.
TryGetValue(P.Value, out Dictionary<string, bool> Features))
4532 if (!Features.ContainsKey(Feature))
4535 if (FullJids is
null)
4536 FullJids =
new List<CaseInsensitiveString>();
4538 FullJids.Add(P.Key);
4541 return FullJids?.ToArray();
4544 private class NodeVerInfo
4546 public Dictionary<CaseInsensitiveString, string> NodeVerPerFullJid;
4547 public KeyValuePair<CaseInsensitiveString, string>[] NodeVerPerFullJidStatic;
4549 public void UpdateStaticLocked()
4553 this.NodeVerPerFullJidStatic =
new KeyValuePair<CaseInsensitiveString, string>[this.NodeVerPerFullJid.Count];
4555 foreach (KeyValuePair<CaseInsensitiveString, string> P
in this.NodeVerPerFullJid)
4556 this.NodeVerPerFullJidStatic[i++] = P;
4581 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.
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 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.
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
Implements an HTTP server.
int[] OpenHttpPorts
HTTP Ports successfully opened.
HttpServer(params ISniffer[] Sniffers)
Implements an HTTPS server.
const int DefaultHttpPort
Default HTTP Port (80).
int[] OpenHttpsPorts
HTTPS Ports successfully opened.
const int DefaultHttpsPort
Default HTTPS port (443).
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 async Task DeleteLazy(object Object)
Deletes an object in the database, if unlocked. If locked, object will be deleted at next opportunity...
static async Task InsertLazy(object Object)
Inserts an object into the database, if unlocked. If locked, object will be inserted at next opportun...
static IDatabaseProvider Provider
Registered database provider.
static async Task UpdateLazy(object Object)
Updates an object in the database, if unlocked. If locked, object will be updated at next opportunity...
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)
Provisioning and registry service component.
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