0001: /**
0002: * $RCSfile: $
0003: * $Revision: $
0004: * $Date: $
0005: *
0006: * Copyright (C) 2006 Jive Software. All rights reserved.
0007: *
0008: * This software is published under the terms of the GNU Public License (GPL),
0009: * a copy of which is included in this distribution.
0010: */package org.jivesoftware.openfire.pubsub;
0011:
0012: import org.dom4j.DocumentHelper;
0013: import org.dom4j.Element;
0014: import org.dom4j.QName;
0015: import org.jivesoftware.openfire.PacketRouter;
0016: import org.jivesoftware.openfire.XMPPServer;
0017: import org.jivesoftware.openfire.XMPPServerListener;
0018: import org.jivesoftware.openfire.pubsub.models.AccessModel;
0019: import org.jivesoftware.openfire.user.UserManager;
0020: import org.jivesoftware.util.Log;
0021: import org.jivesoftware.util.StringUtils;
0022: import org.xmpp.forms.DataForm;
0023: import org.xmpp.forms.FormField;
0024: import org.xmpp.packet.*;
0025:
0026: import java.util.*;
0027: import java.util.concurrent.ConcurrentHashMap;
0028:
0029: /**
0030: * A PubSubEngine is responsible for handling packets sent to a pub-sub service.
0031: *
0032: * @author Matt Tucker
0033: */
0034: public class PubSubEngine {
0035:
0036: /**
0037: * The packet router for the server.
0038: */
0039: private PacketRouter router = null;
0040:
0041: public PubSubEngine(PacketRouter router) {
0042: this .router = router;
0043: }
0044:
0045: /**
0046: * Handles IQ packets sent to the pubsub service. Requests of disco#info and disco#items
0047: * are not being handled by the engine. Instead the service itself should handle disco packets.
0048: *
0049: * @param service the PubSub service this action is to be performed for.
0050: * @param iq the IQ packet sent to the pubsub service.
0051: * @return true if the IQ packet was handled by the engine.
0052: */
0053: public boolean process(PubSubService service, IQ iq) {
0054: // Ignore IQs of type ERROR or RESULT
0055: if (IQ.Type.error == iq.getType()
0056: || IQ.Type.result == iq.getType()) {
0057: return true;
0058: }
0059: Element childElement = iq.getChildElement();
0060: String namespace = null;
0061:
0062: if (childElement != null) {
0063: namespace = childElement.getNamespaceURI();
0064: }
0065: if ("http://jabber.org/protocol/pubsub".equals(namespace)) {
0066: Element action = childElement.element("publish");
0067: if (action != null) {
0068: // Entity publishes an item
0069: publishItemsToNode(service, iq, action);
0070: return true;
0071: }
0072: action = childElement.element("subscribe");
0073: if (action != null) {
0074: // Entity subscribes to a node
0075: subscribeNode(service, iq, childElement, action);
0076: return true;
0077: }
0078: action = childElement.element("options");
0079: if (action != null) {
0080: if (IQ.Type.get == iq.getType()) {
0081: // Subscriber requests subscription options form
0082: getSubscriptionConfiguration(service, iq,
0083: childElement, action);
0084: } else {
0085: // Subscriber submits completed options form
0086: configureSubscription(service, iq, action);
0087: }
0088: return true;
0089: }
0090: action = childElement.element("create");
0091: if (action != null) {
0092: // Entity is requesting to create a new node
0093: createNode(service, iq, childElement, action);
0094: return true;
0095: }
0096: action = childElement.element("unsubscribe");
0097: if (action != null) {
0098: // Entity unsubscribes from a node
0099: unsubscribeNode(service, iq, action);
0100: return true;
0101: }
0102: action = childElement.element("subscriptions");
0103: if (action != null) {
0104: // Entity requests all current subscriptions
0105: getSubscriptions(service, iq, childElement);
0106: return true;
0107: }
0108: action = childElement.element("affiliations");
0109: if (action != null) {
0110: // Entity requests all current affiliations
0111: getAffiliations(service, iq, childElement);
0112: return true;
0113: }
0114: action = childElement.element("items");
0115: if (action != null) {
0116: // Subscriber requests all active items
0117: getPublishedItems(service, iq, action);
0118: return true;
0119: }
0120: action = childElement.element("retract");
0121: if (action != null) {
0122: // Entity deletes an item
0123: deleteItems(service, iq, action);
0124: return true;
0125: }
0126: // Unknown action requested
0127: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
0128: return true;
0129: } else if ("http://jabber.org/protocol/pubsub#owner"
0130: .equals(namespace)) {
0131: Element action = childElement.element("configure");
0132: if (action != null) {
0133: String nodeID = action.attributeValue("node");
0134: if (nodeID == null) {
0135: // if user is not sysadmin then return nodeid-required error
0136: if (!service.isServiceAdmin(iq.getFrom())
0137: || !service.isCollectionNodesSupported()) {
0138: // Configure elements must have a node attribute so answer an error
0139: Element pubsubError = DocumentHelper
0140: .createElement(QName
0141: .get("nodeid-required",
0142: "http://jabber.org/protocol/pubsub#errors"));
0143: sendErrorPacket(iq,
0144: PacketError.Condition.bad_request,
0145: pubsubError);
0146: return true;
0147: } else {
0148: // Sysadmin is trying to configure root collection node
0149: nodeID = service.getRootCollectionNode()
0150: .getNodeID();
0151: }
0152: }
0153: if (IQ.Type.get == iq.getType()) {
0154: // Owner requests configuration form of a node
0155: getNodeConfiguration(service, iq, childElement,
0156: nodeID);
0157: } else {
0158: // Owner submits or cancels node configuration form
0159: configureNode(service, iq, action, nodeID);
0160: }
0161: return true;
0162: }
0163: action = childElement.element("default");
0164: if (action != null) {
0165: // Owner requests default configuration options for
0166: // leaf or collection nodes
0167: getDefaultNodeConfiguration(service, iq, childElement,
0168: action);
0169: return true;
0170: }
0171: action = childElement.element("delete");
0172: if (action != null) {
0173: // Owner deletes a node
0174: deleteNode(service, iq, action);
0175: return true;
0176: }
0177: action = childElement.element("subscriptions");
0178: if (action != null) {
0179: if (IQ.Type.get == iq.getType()) {
0180: // Owner requests all affiliated entities
0181: getNodeSubscriptions(service, iq, action);
0182: } else {
0183: modifyNodeSubscriptions(service, iq, action);
0184: }
0185: return true;
0186: }
0187: action = childElement.element("affiliations");
0188: if (action != null) {
0189: if (IQ.Type.get == iq.getType()) {
0190: // Owner requests all affiliated entities
0191: getNodeAffiliations(service, iq, action);
0192: } else {
0193: modifyNodeAffiliations(service, iq, action);
0194: }
0195: return true;
0196: }
0197: action = childElement.element("purge");
0198: if (action != null) {
0199: // Owner purges items from a node
0200: purgeNode(service, iq, action);
0201: return true;
0202: }
0203: // Unknown action requested so return error to sender
0204: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
0205: return true;
0206: } else if ("http://jabber.org/protocol/commands"
0207: .equals(namespace)) {
0208: // Process ad-hoc command
0209: IQ reply = service.getManager().process(iq);
0210: router.route(reply);
0211: return true;
0212: }
0213: return false;
0214: }
0215:
0216: /**
0217: * Handles Presence packets sent to the pubsub service. Only process available and not
0218: * available presences.
0219: *
0220: * @param service the PubSub service this action is to be performed for.
0221: * @param presence the Presence packet sent to the pubsub service.
0222: */
0223: public void process(PubSubService service, Presence presence) {
0224: if (presence.isAvailable()) {
0225: JID subscriber = presence.getFrom();
0226: Map<String, String> fullPresences = service
0227: .getBarePresences().get(subscriber.toBareJID());
0228: if (fullPresences == null) {
0229: synchronized (subscriber.toBareJID().intern()) {
0230: fullPresences = service.getBarePresences().get(
0231: subscriber.toBareJID());
0232: if (fullPresences == null) {
0233: fullPresences = new ConcurrentHashMap<String, String>();
0234: service.getBarePresences().put(
0235: subscriber.toBareJID(), fullPresences);
0236: }
0237: }
0238: }
0239: Presence.Show show = presence.getShow();
0240: fullPresences.put(subscriber.toString(),
0241: show == null ? "online" : show.name());
0242: } else if (presence.getType() == Presence.Type.unavailable) {
0243: JID subscriber = presence.getFrom();
0244: Map<String, String> fullPresences = service
0245: .getBarePresences().get(subscriber.toBareJID());
0246: if (fullPresences != null) {
0247: fullPresences.remove(subscriber.toString());
0248: if (fullPresences.isEmpty()) {
0249: service.getBarePresences().remove(
0250: subscriber.toBareJID());
0251: }
0252: }
0253: }
0254: }
0255:
0256: /**
0257: * Handles Message packets sent to the pubsub service. Messages may be of type error
0258: * when an event notification was sent to a susbcriber whose address is no longer available.<p>
0259: *
0260: * Answers to authorization requests sent to node owners to approve pending subscriptions
0261: * will also be processed by this method.
0262: *
0263: * @param service the PubSub service this action is to be performed for.
0264: * @param message the Message packet sent to the pubsub service.
0265: */
0266: public void process(PubSubService service, Message message) {
0267: if (message.getType() == Message.Type.error) {
0268: // Process Messages of type error to identify possible subscribers that no longer exist
0269: if (message.getError().getType() == PacketError.Type.cancel) {
0270: // TODO Assuming that owner is the bare JID (as defined in the JEP). This can be replaced with an explicit owner specified in the packet
0271: JID owner = new JID(message.getFrom().toBareJID());
0272: // Terminate the subscription of the entity to all nodes hosted at the service
0273: cancelAllSubscriptions(service, owner);
0274: } else if (message.getError().getType() == PacketError.Type.auth) {
0275: // TODO Queue the message to be sent again later (will retry a few times and
0276: // will be discarded when the retry limit is reached)
0277: }
0278: } else if (message.getType() == Message.Type.normal) {
0279: // Check that this is an answer to an authorization request
0280: DataForm authForm = (DataForm) message.getExtension("x",
0281: "jabber:x:data");
0282: if (authForm != null
0283: && authForm.getType() == DataForm.Type.submit) {
0284: String formType = authForm.getField("FORM_TYPE")
0285: .getValues().get(0);
0286: // Check that completed data form belongs to an authorization request
0287: if ("http://jabber.org/protocol/pubsub#subscribe_authorization"
0288: .equals(formType)) {
0289: // Process the answer to the authorization request
0290: processAuthorizationAnswer(service, authForm,
0291: message);
0292: }
0293: }
0294: }
0295: }
0296:
0297: private void publishItemsToNode(PubSubService service, IQ iq,
0298: Element publishElement) {
0299: String nodeID = publishElement.attributeValue("node");
0300: Node node;
0301: if (nodeID == null) {
0302: // No node was specified. Return bad_request error
0303: Element pubsubError = DocumentHelper
0304: .createElement(QName.get("nodeid-required",
0305: "http://jabber.org/protocol/pubsub#errors"));
0306: sendErrorPacket(iq, PacketError.Condition.bad_request,
0307: pubsubError);
0308: return;
0309: } else {
0310: // Look for the specified node
0311: node = service.getNode(nodeID);
0312: if (node == null) {
0313: // Node does not exist. Return item-not-found error
0314: sendErrorPacket(iq,
0315: PacketError.Condition.item_not_found, null);
0316: return;
0317: }
0318: }
0319:
0320: JID from = iq.getFrom();
0321: // TODO Assuming that owner is the bare JID (as defined in the JEP). This can be replaced with an explicit owner specified in the packet
0322: JID owner = new JID(from.toBareJID());
0323: if (!node.getPublisherModel().canPublish(node, owner)
0324: && !service.isServiceAdmin(owner)) {
0325: // Entity does not have sufficient privileges to publish to node
0326: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
0327: return;
0328: }
0329:
0330: if (node.isCollectionNode()) {
0331: // Node is a collection node. Return feature-not-implemented error
0332: Element pubsubError = DocumentHelper
0333: .createElement(QName.get("unsupported",
0334: "http://jabber.org/protocol/pubsub#errors"));
0335: pubsubError.addAttribute("feature", "publish");
0336: sendErrorPacket(iq,
0337: PacketError.Condition.feature_not_implemented,
0338: pubsubError);
0339: return;
0340: }
0341:
0342: LeafNode leafNode = (LeafNode) node;
0343: Iterator itemElements = publishElement.elementIterator("item");
0344:
0345: // Check that an item was included if node persist items or includes payload
0346: if (!itemElements.hasNext() && leafNode.isItemRequired()) {
0347: Element pubsubError = DocumentHelper
0348: .createElement(QName.get("item-required",
0349: "http://jabber.org/protocol/pubsub#errors"));
0350: sendErrorPacket(iq, PacketError.Condition.bad_request,
0351: pubsubError);
0352: return;
0353: }
0354: // Check that no item was included if node doesn't persist items and doesn't
0355: // includes payload
0356: if (itemElements.hasNext() && !leafNode.isItemRequired()) {
0357: Element pubsubError = DocumentHelper
0358: .createElement(QName.get("item-forbidden",
0359: "http://jabber.org/protocol/pubsub#errors"));
0360: sendErrorPacket(iq, PacketError.Condition.bad_request,
0361: pubsubError);
0362: return;
0363: }
0364: List<Element> items = new ArrayList<Element>();
0365: List entries;
0366: Element payload;
0367: while (itemElements.hasNext()) {
0368: Element item = (Element) itemElements.next();
0369: entries = item.elements();
0370: payload = entries.isEmpty() ? null : (Element) entries
0371: .get(0);
0372: // Check that a payload was included if node is configured to include payload
0373: // in notifications
0374: if (payload == null && leafNode.isPayloadDelivered()) {
0375: Element pubsubError = DocumentHelper
0376: .createElement(QName
0377: .get("payload-required",
0378: "http://jabber.org/protocol/pubsub#errors"));
0379: sendErrorPacket(iq, PacketError.Condition.bad_request,
0380: pubsubError);
0381: return;
0382: }
0383: // Check that the payload (if any) contains only one child element
0384: if (entries.size() > 1) {
0385: Element pubsubError = DocumentHelper
0386: .createElement(QName
0387: .get("invalid-payload",
0388: "http://jabber.org/protocol/pubsub#errors"));
0389: sendErrorPacket(iq, PacketError.Condition.bad_request,
0390: pubsubError);
0391: return;
0392: }
0393: items.add(item);
0394: }
0395:
0396: // Return success operation
0397: router.route(IQ.createResultIQ(iq));
0398: // Publish item and send event notifications to subscribers
0399: leafNode.publishItems(from, items);
0400: }
0401:
0402: private void deleteItems(PubSubService service, IQ iq,
0403: Element retractElement) {
0404: String nodeID = retractElement.attributeValue("node");
0405: Node node;
0406: if (nodeID == null) {
0407: // No node was specified. Return bad_request error
0408: Element pubsubError = DocumentHelper
0409: .createElement(QName.get("nodeid-required",
0410: "http://jabber.org/protocol/pubsub#errors"));
0411: sendErrorPacket(iq, PacketError.Condition.bad_request,
0412: pubsubError);
0413: return;
0414: } else {
0415: // Look for the specified node
0416: node = service.getNode(nodeID);
0417: if (node == null) {
0418: // Node does not exist. Return item-not-found error
0419: sendErrorPacket(iq,
0420: PacketError.Condition.item_not_found, null);
0421: return;
0422: }
0423: }
0424: // Get the items to delete
0425: Iterator itemElements = retractElement.elementIterator("item");
0426: if (!itemElements.hasNext()) {
0427: Element pubsubError = DocumentHelper
0428: .createElement(QName.get("item-required",
0429: "http://jabber.org/protocol/pubsub#errors"));
0430: sendErrorPacket(iq, PacketError.Condition.bad_request,
0431: pubsubError);
0432: return;
0433: }
0434:
0435: if (node.isCollectionNode()) {
0436: // Cannot delete items from a collection node. Return an error.
0437: Element pubsubError = DocumentHelper
0438: .createElement(QName.get("unsupported",
0439: "http://jabber.org/protocol/pubsub#errors"));
0440: pubsubError.addAttribute("feature", "persistent-items");
0441: sendErrorPacket(iq,
0442: PacketError.Condition.feature_not_implemented,
0443: pubsubError);
0444: return;
0445: }
0446: LeafNode leafNode = (LeafNode) node;
0447:
0448: if (!leafNode.isItemRequired()) {
0449: // Cannot delete items from a leaf node that doesn't handle itemIDs. Return an error.
0450: Element pubsubError = DocumentHelper
0451: .createElement(QName.get("unsupported",
0452: "http://jabber.org/protocol/pubsub#errors"));
0453: pubsubError.addAttribute("feature", "persistent-items");
0454: sendErrorPacket(iq,
0455: PacketError.Condition.feature_not_implemented,
0456: pubsubError);
0457: return;
0458: }
0459:
0460: List<PublishedItem> items = new ArrayList<PublishedItem>();
0461: while (itemElements.hasNext()) {
0462: Element itemElement = (Element) itemElements.next();
0463: String itemID = itemElement.attributeValue("id");
0464: if (itemID != null) {
0465: PublishedItem item = node.getPublishedItem(itemID);
0466: if (item == null) {
0467: // ItemID does not exist. Return item-not-found error
0468: sendErrorPacket(iq,
0469: PacketError.Condition.item_not_found, null);
0470: return;
0471: } else {
0472: if (item.canDelete(iq.getFrom())) {
0473: items.add(item);
0474: } else {
0475: // Publisher does not have sufficient privileges to delete this item
0476: sendErrorPacket(iq,
0477: PacketError.Condition.forbidden, null);
0478: return;
0479: }
0480: }
0481: } else {
0482: // No item ID was specified so return a bad_request error
0483: Element pubsubError = DocumentHelper
0484: .createElement(QName
0485: .get("item-required",
0486: "http://jabber.org/protocol/pubsub#errors"));
0487: sendErrorPacket(iq, PacketError.Condition.bad_request,
0488: pubsubError);
0489: return;
0490: }
0491: }
0492: // Send reply with success
0493: router.route(IQ.createResultIQ(iq));
0494: // Delete items and send subscribers a notification
0495: leafNode.deleteItems(items);
0496: }
0497:
0498: private void subscribeNode(PubSubService service, IQ iq,
0499: Element childElement, Element subscribeElement) {
0500: String nodeID = subscribeElement.attributeValue("node");
0501: Node node;
0502: if (nodeID == null) {
0503: if (service.isCollectionNodesSupported()) {
0504: // Entity subscribes to root collection node
0505: node = service.getRootCollectionNode();
0506: } else {
0507: // Service does not have a root collection node so return a nodeid-required error
0508: Element pubsubError = DocumentHelper
0509: .createElement(QName
0510: .get("nodeid-required",
0511: "http://jabber.org/protocol/pubsub#errors"));
0512: sendErrorPacket(iq, PacketError.Condition.bad_request,
0513: pubsubError);
0514: return;
0515: }
0516: } else {
0517: // Look for the specified node
0518: node = service.getNode(nodeID);
0519: if (node == null) {
0520: // Node does not exist. Return item-not-found error
0521: sendErrorPacket(iq,
0522: PacketError.Condition.item_not_found, null);
0523: return;
0524: }
0525: }
0526: // Check if sender and subscriber JIDs match or if a valid "trusted proxy" is being used
0527: JID from = iq.getFrom();
0528: JID subscriberJID = new JID(subscribeElement
0529: .attributeValue("jid"));
0530: if (!from.toBareJID().equals(subscriberJID.toBareJID())
0531: && !service.isServiceAdmin(from)) {
0532: // JIDs do not match and requestor is not a service admin so return an error
0533: Element pubsubError = DocumentHelper
0534: .createElement(QName.get("invalid-jid",
0535: "http://jabber.org/protocol/pubsub#errors"));
0536: sendErrorPacket(iq, PacketError.Condition.bad_request,
0537: pubsubError);
0538: return;
0539: }
0540: // TODO Assumed that the owner of the subscription is the bare JID of the subscription JID. Waiting StPeter answer for explicit field.
0541: JID owner = new JID(subscriberJID.toBareJID());
0542: // Check if the node's access model allows the subscription to proceed
0543: AccessModel accessModel = node.getAccessModel();
0544: if (!accessModel.canSubscribe(node, owner, subscriberJID)) {
0545: sendErrorPacket(iq, accessModel.getSubsriptionError(),
0546: accessModel.getSubsriptionErrorDetail());
0547: return;
0548: }
0549: // Check if the subscriber is an anonymous user
0550: if (!UserManager.getInstance().isRegisteredUser(subscriberJID)) {
0551: // Anonymous users cannot subscribe to the node. Return forbidden error
0552: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
0553: return;
0554: }
0555: // Check if the subscription owner is a user with outcast affiliation
0556: NodeAffiliate nodeAffiliate = node.getAffiliate(owner);
0557: if (nodeAffiliate != null
0558: && nodeAffiliate.getAffiliation() == NodeAffiliate.Affiliation.outcast) {
0559: // Subscriber is an outcast. Return forbidden error
0560: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
0561: return;
0562: }
0563: // Check that subscriptions to the node are enabled
0564: if (!node.isSubscriptionEnabled()
0565: && !service.isServiceAdmin(from)) {
0566: // Sender is not a sysadmin and subscription is disabled so return an error
0567: sendErrorPacket(iq, PacketError.Condition.not_allowed, null);
0568: return;
0569: }
0570:
0571: // Get any configuration form included in the options element (if any)
0572: DataForm optionsForm = null;
0573: Element options = childElement.element("options");
0574: if (options != null) {
0575: Element formElement = options.element(QName.get("x",
0576: "jabber:x:data"));
0577: if (formElement != null) {
0578: optionsForm = new DataForm(formElement);
0579: }
0580: }
0581:
0582: // If leaf node does not support multiple subscriptions then check whether subscriber is
0583: // creating another subscription or not
0584: if (!node.isCollectionNode()
0585: && !node.isMultipleSubscriptionsEnabled()) {
0586: NodeSubscription existingSubscription = node
0587: .getSubscription(subscriberJID);
0588: if (existingSubscription != null) {
0589: // User is trying to create another subscription so
0590: // return current subscription state
0591: existingSubscription.sendSubscriptionState(iq);
0592: return;
0593: }
0594: }
0595:
0596: // Check if subscribing twice to a collection node using same subscription type
0597: if (node.isCollectionNode()) {
0598: // By default assume that new subscription is of type node
0599: boolean isNodeType = true;
0600: if (optionsForm != null) {
0601: FormField field = optionsForm
0602: .getField("pubsub#subscription_type");
0603: if (field != null) {
0604: if ("items".equals(field.getValues().get(0))) {
0605: isNodeType = false;
0606: }
0607: }
0608: }
0609: if (nodeAffiliate != null) {
0610: for (NodeSubscription subscription : nodeAffiliate
0611: .getSubscriptions()) {
0612: if (isNodeType) {
0613: // User is requesting a subscription of type "nodes"
0614: if (NodeSubscription.Type.nodes == subscription
0615: .getType()) {
0616: // Cannot have 2 subscriptions of the same type. Return conflict error
0617: sendErrorPacket(iq,
0618: PacketError.Condition.conflict,
0619: null);
0620: return;
0621: }
0622: } else if (!node.isMultipleSubscriptionsEnabled()) {
0623: // User is requesting a subscription of type "items" and
0624: // multiple subscriptions is not allowed
0625: if (NodeSubscription.Type.items == subscription
0626: .getType()) {
0627: // User is trying to create another subscription so
0628: // return current subscription state
0629: subscription.sendSubscriptionState(iq);
0630: return;
0631: }
0632: }
0633: }
0634: }
0635: }
0636:
0637: // Create a subscription and an affiliation if the subscriber doesn't have one
0638: node.createSubscription(iq, owner, subscriberJID, accessModel
0639: .isAuthorizationRequired(), optionsForm);
0640: }
0641:
0642: private void unsubscribeNode(PubSubService service, IQ iq,
0643: Element unsubscribeElement) {
0644: String nodeID = unsubscribeElement.attributeValue("node");
0645: String subID = unsubscribeElement.attributeValue("subid");
0646: Node node;
0647: if (nodeID == null) {
0648: if (service.isCollectionNodesSupported()) {
0649: // Entity unsubscribes from root collection node
0650: node = service.getRootCollectionNode();
0651: } else {
0652: // Service does not have a root collection node so return a nodeid-required error
0653: Element pubsubError = DocumentHelper
0654: .createElement(QName
0655: .get("nodeid-required",
0656: "http://jabber.org/protocol/pubsub#errors"));
0657: sendErrorPacket(iq, PacketError.Condition.bad_request,
0658: pubsubError);
0659: return;
0660: }
0661: } else {
0662: // Look for the specified node
0663: node = service.getNode(nodeID);
0664: if (node == null) {
0665: // Node does not exist. Return item-not-found error
0666: sendErrorPacket(iq,
0667: PacketError.Condition.item_not_found, null);
0668: return;
0669: }
0670: }
0671: NodeSubscription subscription;
0672: if (node.isMultipleSubscriptionsEnabled()) {
0673: if (subID == null) {
0674: // No subid was specified and the node supports multiple subscriptions
0675: Element pubsubError = DocumentHelper
0676: .createElement(QName
0677: .get("subid-required",
0678: "http://jabber.org/protocol/pubsub#errors"));
0679: sendErrorPacket(iq, PacketError.Condition.bad_request,
0680: pubsubError);
0681: return;
0682: } else {
0683: // Check if the specified subID belongs to an existing node subscription
0684: subscription = node.getSubscription(subID);
0685: if (subscription == null) {
0686: Element pubsubError = DocumentHelper
0687: .createElement(QName
0688: .get("invalid-subid",
0689: "http://jabber.org/protocol/pubsub#errors"));
0690: sendErrorPacket(iq,
0691: PacketError.Condition.not_acceptable,
0692: pubsubError);
0693: return;
0694: }
0695: }
0696: } else {
0697: String jidAttribute = unsubscribeElement
0698: .attributeValue("jid");
0699: // Check if the specified JID has a subscription with the node
0700: if (jidAttribute == null) {
0701: // No JID was specified so return an error indicating that jid is required
0702: Element pubsubError = DocumentHelper
0703: .createElement(QName
0704: .get("jid-required",
0705: "http://jabber.org/protocol/pubsub#errors"));
0706: sendErrorPacket(iq, PacketError.Condition.bad_request,
0707: pubsubError);
0708: return;
0709: }
0710: JID subscriberJID = new JID(jidAttribute);
0711: subscription = node.getSubscription(subscriberJID);
0712: if (subscription == null) {
0713: Element pubsubError = DocumentHelper
0714: .createElement(QName
0715: .get("not-subscribed",
0716: "http://jabber.org/protocol/pubsub#errors"));
0717: sendErrorPacket(iq,
0718: PacketError.Condition.unexpected_request,
0719: pubsubError);
0720: return;
0721: }
0722: }
0723: JID from = iq.getFrom();
0724: // Check that unsubscriptions to the node are enabled
0725: if (!node.isSubscriptionEnabled()
0726: && !service.isServiceAdmin(from)) {
0727: // Sender is not a sysadmin and unsubscription is disabled so return an error
0728: sendErrorPacket(iq, PacketError.Condition.not_allowed, null);
0729: return;
0730: }
0731:
0732: // TODO Assumed that the owner of the subscription is the bare JID of the subscription JID. Waiting StPeter answer for explicit field.
0733: JID owner = new JID(from.toBareJID());
0734: // A subscription was found so check if the user is allowed to cancel the subscription
0735: if (!subscription.canModify(from)
0736: && !subscription.canModify(owner)) {
0737: // Requestor is prohibited from unsubscribing entity
0738: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
0739: return;
0740: }
0741:
0742: // Cancel subscription
0743: node.cancelSubscription(subscription);
0744: // Send reply with success
0745: router.route(IQ.createResultIQ(iq));
0746: }
0747:
0748: private void getSubscriptionConfiguration(PubSubService service,
0749: IQ iq, Element childElement, Element optionsElement) {
0750: String nodeID = optionsElement.attributeValue("node");
0751: String subID = optionsElement.attributeValue("subid");
0752: Node node;
0753: if (nodeID == null) {
0754: if (service.isCollectionNodesSupported()) {
0755: // Entity requests subscription options of root collection node
0756: node = service.getRootCollectionNode();
0757: } else {
0758: // Service does not have a root collection node so return a nodeid-required error
0759: Element pubsubError = DocumentHelper
0760: .createElement(QName
0761: .get("nodeid-required",
0762: "http://jabber.org/protocol/pubsub#errors"));
0763: sendErrorPacket(iq, PacketError.Condition.bad_request,
0764: pubsubError);
0765: return;
0766: }
0767: } else {
0768: // Look for the specified node
0769: node = service.getNode(nodeID);
0770: if (node == null) {
0771: // Node does not exist. Return item-not-found error
0772: sendErrorPacket(iq,
0773: PacketError.Condition.item_not_found, null);
0774: return;
0775: }
0776: }
0777: NodeSubscription subscription;
0778: if (node.isMultipleSubscriptionsEnabled()) {
0779: if (subID == null) {
0780: // No subid was specified and the node supports multiple subscriptions
0781: Element pubsubError = DocumentHelper
0782: .createElement(QName
0783: .get("subid-required",
0784: "http://jabber.org/protocol/pubsub#errors"));
0785: sendErrorPacket(iq, PacketError.Condition.bad_request,
0786: pubsubError);
0787: return;
0788: } else {
0789: // Check if the specified subID belongs to an existing node subscription
0790: subscription = node.getSubscription(subID);
0791: if (subscription == null) {
0792: Element pubsubError = DocumentHelper
0793: .createElement(QName
0794: .get("invalid-subid",
0795: "http://jabber.org/protocol/pubsub#errors"));
0796: sendErrorPacket(iq,
0797: PacketError.Condition.not_acceptable,
0798: pubsubError);
0799: return;
0800: }
0801: }
0802: } else {
0803: // Check if the specified JID has a subscription with the node
0804: String jidAttribute = optionsElement.attributeValue("jid");
0805: if (jidAttribute == null) {
0806: // No JID was specified so return an error indicating that jid is required
0807: Element pubsubError = DocumentHelper
0808: .createElement(QName
0809: .get("jid-required",
0810: "http://jabber.org/protocol/pubsub#errors"));
0811: sendErrorPacket(iq, PacketError.Condition.bad_request,
0812: pubsubError);
0813: return;
0814: }
0815: JID subscriberJID = new JID(jidAttribute);
0816: subscription = node.getSubscription(subscriberJID);
0817: if (subscription == null) {
0818: Element pubsubError = DocumentHelper
0819: .createElement(QName
0820: .get("not-subscribed",
0821: "http://jabber.org/protocol/pubsub#errors"));
0822: sendErrorPacket(iq,
0823: PacketError.Condition.unexpected_request,
0824: pubsubError);
0825: return;
0826: }
0827: }
0828:
0829: // A subscription was found so check if the user is allowed to get the subscription options
0830: if (!subscription.canModify(iq.getFrom())) {
0831: // Requestor is prohibited from getting the subscription options
0832: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
0833: return;
0834: }
0835:
0836: // Return data form containing subscription configuration to the subscriber
0837: IQ reply = IQ.createResultIQ(iq);
0838: Element replyChildElement = childElement.createCopy();
0839: reply.setChildElement(replyChildElement);
0840: replyChildElement.element("options").add(
0841: subscription.getConfigurationForm().getElement());
0842: router.route(reply);
0843: }
0844:
0845: private void configureSubscription(PubSubService service, IQ iq,
0846: Element optionsElement) {
0847: String nodeID = optionsElement.attributeValue("node");
0848: String subID = optionsElement.attributeValue("subid");
0849: Node node;
0850: if (nodeID == null) {
0851: if (service.isCollectionNodesSupported()) {
0852: // Entity submits new subscription options of root collection node
0853: node = service.getRootCollectionNode();
0854: } else {
0855: // Service does not have a root collection node so return a nodeid-required error
0856: Element pubsubError = DocumentHelper
0857: .createElement(QName
0858: .get("nodeid-required",
0859: "http://jabber.org/protocol/pubsub#errors"));
0860: sendErrorPacket(iq, PacketError.Condition.bad_request,
0861: pubsubError);
0862: return;
0863: }
0864: } else {
0865: // Look for the specified node
0866: node = service.getNode(nodeID);
0867: if (node == null) {
0868: // Node does not exist. Return item-not-found error
0869: sendErrorPacket(iq,
0870: PacketError.Condition.item_not_found, null);
0871: return;
0872: }
0873: }
0874: NodeSubscription subscription;
0875: if (node.isMultipleSubscriptionsEnabled()) {
0876: if (subID == null) {
0877: // No subid was specified and the node supports multiple subscriptions
0878: Element pubsubError = DocumentHelper
0879: .createElement(QName
0880: .get("subid-required",
0881: "http://jabber.org/protocol/pubsub#errors"));
0882: sendErrorPacket(iq, PacketError.Condition.bad_request,
0883: pubsubError);
0884: return;
0885: } else {
0886: // Check if the specified subID belongs to an existing node subscription
0887: subscription = node.getSubscription(subID);
0888: if (subscription == null) {
0889: Element pubsubError = DocumentHelper
0890: .createElement(QName
0891: .get("invalid-subid",
0892: "http://jabber.org/protocol/pubsub#errors"));
0893: sendErrorPacket(iq,
0894: PacketError.Condition.not_acceptable,
0895: pubsubError);
0896: return;
0897: }
0898: }
0899: } else {
0900: // Check if the specified JID has a subscription with the node
0901: String jidAttribute = optionsElement.attributeValue("jid");
0902: if (jidAttribute == null) {
0903: // No JID was specified so return an error indicating that jid is required
0904: Element pubsubError = DocumentHelper
0905: .createElement(QName
0906: .get("jid-required",
0907: "http://jabber.org/protocol/pubsub#errors"));
0908: sendErrorPacket(iq, PacketError.Condition.bad_request,
0909: pubsubError);
0910: return;
0911: }
0912: JID subscriberJID = new JID(jidAttribute);
0913: subscription = node.getSubscription(subscriberJID);
0914: if (subscription == null) {
0915: Element pubsubError = DocumentHelper
0916: .createElement(QName
0917: .get("not-subscribed",
0918: "http://jabber.org/protocol/pubsub#errors"));
0919: sendErrorPacket(iq,
0920: PacketError.Condition.unexpected_request,
0921: pubsubError);
0922: return;
0923: }
0924: }
0925:
0926: // A subscription was found so check if the user is allowed to submits
0927: // new subscription options
0928: if (!subscription.canModify(iq.getFrom())) {
0929: // Requestor is prohibited from setting new subscription options
0930: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
0931: return;
0932: }
0933:
0934: Element formElement = optionsElement.element(QName.get("x",
0935: "jabber:x:data"));
0936: if (formElement != null) {
0937: // Change the subscription configuration based on the completed form
0938: subscription.configure(iq, new DataForm(formElement));
0939: } else {
0940: // No data form was included so return bad request error
0941: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
0942: }
0943: }
0944:
0945: private void getSubscriptions(PubSubService service, IQ iq,
0946: Element childElement) {
0947: // TODO Assuming that owner is the bare JID (as defined in the JEP). This can be replaced with an explicit owner specified in the packet
0948: JID owner = new JID(iq.getFrom().toBareJID());
0949: // Collect subscriptions of owner for all nodes at the service
0950: Collection<NodeSubscription> subscriptions = new ArrayList<NodeSubscription>();
0951: for (Node node : service.getNodes()) {
0952: subscriptions.addAll(node.getSubscriptions(owner));
0953: }
0954: // Create reply to send
0955: IQ reply = IQ.createResultIQ(iq);
0956: Element replyChildElement = childElement.createCopy();
0957: reply.setChildElement(replyChildElement);
0958: if (subscriptions.isEmpty()) {
0959: // User does not have any affiliation or subscription with the pubsub service
0960: reply.setError(PacketError.Condition.item_not_found);
0961: } else {
0962: Element affiliationsElement = replyChildElement
0963: .element("subscriptions");
0964: // Add information about subscriptions including existing affiliations
0965: for (NodeSubscription subscription : subscriptions) {
0966: Element subElement = affiliationsElement
0967: .addElement("subscription");
0968: Node node = subscription.getNode();
0969: NodeAffiliate nodeAffiliate = subscription
0970: .getAffiliate();
0971: // Do not include the node id when node is the root collection node
0972: if (!node.isRootCollectionNode()) {
0973: subElement.addAttribute("node", node.getNodeID());
0974: }
0975: subElement.addAttribute("jid", subscription.getJID()
0976: .toString());
0977: subElement.addAttribute("affiliation", nodeAffiliate
0978: .getAffiliation().name());
0979: subElement.addAttribute("subscription", subscription
0980: .getState().name());
0981: if (node.isMultipleSubscriptionsEnabled()) {
0982: subElement.addAttribute("subid", subscription
0983: .getID());
0984: }
0985: }
0986: }
0987: // Send reply
0988: router.route(reply);
0989: }
0990:
0991: private void getAffiliations(PubSubService service, IQ iq,
0992: Element childElement) {
0993: // TODO Assuming that owner is the bare JID (as defined in the JEP). This can be replaced with an explicit owner specified in the packet
0994: JID owner = new JID(iq.getFrom().toBareJID());
0995: // Collect affiliations of owner for all nodes at the service
0996: Collection<NodeAffiliate> affiliations = new ArrayList<NodeAffiliate>();
0997: for (Node node : service.getNodes()) {
0998: NodeAffiliate nodeAffiliate = node.getAffiliate(owner);
0999: if (nodeAffiliate != null) {
1000: affiliations.add(nodeAffiliate);
1001: }
1002: }
1003: // Create reply to send
1004: IQ reply = IQ.createResultIQ(iq);
1005: Element replyChildElement = childElement.createCopy();
1006: reply.setChildElement(replyChildElement);
1007: if (affiliations.isEmpty()) {
1008: // User does not have any affiliation or subscription with the pubsub service
1009: reply.setError(PacketError.Condition.item_not_found);
1010: } else {
1011: Element affiliationsElement = replyChildElement
1012: .element("affiliations");
1013: // Add information about affiliations without subscriptions
1014: for (NodeAffiliate affiliate : affiliations) {
1015: Element affiliateElement = affiliationsElement
1016: .addElement("affiliation");
1017: // Do not include the node id when node is the root collection node
1018: if (!affiliate.getNode().isRootCollectionNode()) {
1019: affiliateElement.addAttribute("node", affiliate
1020: .getNode().getNodeID());
1021: }
1022: affiliateElement.addAttribute("jid", affiliate.getJID()
1023: .toString());
1024: affiliateElement.addAttribute("affiliation", affiliate
1025: .getAffiliation().name());
1026: }
1027: }
1028: // Send reply
1029: router.route(reply);
1030: }
1031:
1032: private void getPublishedItems(PubSubService service, IQ iq,
1033: Element itemsElement) {
1034: String nodeID = itemsElement.attributeValue("node");
1035: String subID = itemsElement.attributeValue("subid");
1036: Node node;
1037: if (nodeID == null) {
1038: // User must specify a leaf node ID so return a nodeid-required error
1039: Element pubsubError = DocumentHelper
1040: .createElement(QName.get("nodeid-required",
1041: "http://jabber.org/protocol/pubsub#errors"));
1042: sendErrorPacket(iq, PacketError.Condition.bad_request,
1043: pubsubError);
1044: return;
1045: } else {
1046: // Look for the specified node
1047: node = service.getNode(nodeID);
1048: if (node == null) {
1049: // Node does not exist. Return item-not-found error
1050: sendErrorPacket(iq,
1051: PacketError.Condition.item_not_found, null);
1052: return;
1053: }
1054: }
1055: if (node.isCollectionNode()) {
1056: // Node is a collection node. Return feature-not-implemented error
1057: Element pubsubError = DocumentHelper
1058: .createElement(QName.get("unsupported",
1059: "http://jabber.org/protocol/pubsub#errors"));
1060: pubsubError.addAttribute("feature", "retrieve-items");
1061: sendErrorPacket(iq,
1062: PacketError.Condition.feature_not_implemented,
1063: pubsubError);
1064: return;
1065: }
1066: // Check if sender and subscriber JIDs match or if a valid "trusted proxy" is being used
1067: JID subscriberJID = iq.getFrom();
1068: // TODO Assumed that the owner of the subscription is the bare JID of the subscription JID. Waiting StPeter answer for explicit field.
1069: JID owner = new JID(subscriberJID.toBareJID());
1070: // Check if the node's access model allows the subscription to proceed
1071: AccessModel accessModel = node.getAccessModel();
1072: if (!accessModel.canAccessItems(node, owner, subscriberJID)) {
1073: sendErrorPacket(iq, accessModel.getSubsriptionError(),
1074: accessModel.getSubsriptionErrorDetail());
1075: return;
1076: }
1077: // Check that the requester is not an outcast
1078: NodeAffiliate affiliate = node.getAffiliate(owner);
1079: if (affiliate != null
1080: && affiliate.getAffiliation() == NodeAffiliate.Affiliation.outcast) {
1081: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1082: return;
1083: }
1084:
1085: // Get the user's subscription
1086: NodeSubscription subscription = null;
1087: if (node.isMultipleSubscriptionsEnabled()
1088: && !node.getSubscriptions(owner).isEmpty()) {
1089: if (subID == null) {
1090: // No subid was specified and the node supports multiple subscriptions
1091: Element pubsubError = DocumentHelper
1092: .createElement(QName
1093: .get("subid-required",
1094: "http://jabber.org/protocol/pubsub#errors"));
1095: sendErrorPacket(iq, PacketError.Condition.bad_request,
1096: pubsubError);
1097: return;
1098: } else {
1099: // Check if the specified subID belongs to an existing node subscription
1100: subscription = node.getSubscription(subID);
1101: if (subscription == null) {
1102: Element pubsubError = DocumentHelper
1103: .createElement(QName
1104: .get("invalid-subid",
1105: "http://jabber.org/protocol/pubsub#errors"));
1106: sendErrorPacket(iq,
1107: PacketError.Condition.not_acceptable,
1108: pubsubError);
1109: return;
1110: }
1111: }
1112: }
1113:
1114: if (subscription != null && !subscription.isActive()) {
1115: Element pubsubError = DocumentHelper
1116: .createElement(QName.get("not-subscribed",
1117: "http://jabber.org/protocol/pubsub#errors"));
1118: sendErrorPacket(iq, PacketError.Condition.not_authorized,
1119: pubsubError);
1120: return;
1121: }
1122:
1123: LeafNode leafNode = (LeafNode) node;
1124: // Get list of items to send to the user
1125: boolean forceToIncludePayload = false;
1126: List<PublishedItem> items;
1127: String max_items = itemsElement.attributeValue("max_items");
1128: int recentItems = 0;
1129: if (max_items != null) {
1130: try {
1131: // Parse the recent number of items requested
1132: recentItems = Integer.parseInt(max_items);
1133: } catch (NumberFormatException e) {
1134: // There was an error parsing the number so assume that all items were requested
1135: Log.warn("Assuming that all items were requested", e);
1136: max_items = null;
1137: }
1138: }
1139: if (max_items != null) {
1140: // Get the N most recent published items
1141: items = new ArrayList<PublishedItem>(leafNode
1142: .getPublishedItems(recentItems));
1143: } else {
1144: List requestedItems = itemsElement.elements("item");
1145: if (requestedItems.isEmpty()) {
1146: // Get all the active items that were published to the node
1147: items = new ArrayList<PublishedItem>(leafNode
1148: .getPublishedItems());
1149: } else {
1150: items = new ArrayList<PublishedItem>();
1151: // Indicate that payload should be included (if exists) no matter
1152: // the node configuration
1153: forceToIncludePayload = true;
1154: // Get the items as requested by the user
1155: for (Iterator it = requestedItems.iterator(); it
1156: .hasNext();) {
1157: Element element = (Element) it.next();
1158: String itemID = element.attributeValue("id");
1159: PublishedItem item = leafNode
1160: .getPublishedItem(itemID);
1161: if (item != null) {
1162: items.add(item);
1163: }
1164: }
1165: }
1166: }
1167:
1168: if (subscription != null && subscription.getKeyword() != null) {
1169: // Filter items that do not match the subscription keyword
1170: for (Iterator<PublishedItem> it = items.iterator(); it
1171: .hasNext();) {
1172: PublishedItem item = it.next();
1173: if (!subscription.isKeywordMatched(item)) {
1174: // Remove item that does not match keyword
1175: it.remove();
1176: }
1177: }
1178: }
1179:
1180: // Send items to the user
1181: leafNode.sendPublishedItems(iq, items, forceToIncludePayload);
1182: }
1183:
1184: private void createNode(PubSubService service, IQ iq,
1185: Element childElement, Element createElement) {
1186: // Get sender of the IQ packet
1187: JID from = iq.getFrom();
1188: // Verify that sender has permissions to create nodes
1189: if (!service.canCreateNode(from)
1190: || !UserManager.getInstance().isRegisteredUser(from)) {
1191: // The user is not allowed to create nodes so return an error
1192: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1193: return;
1194: }
1195: DataForm completedForm = null;
1196: CollectionNode parentNode = null;
1197: String nodeID = createElement.attributeValue("node");
1198: String newNodeID = nodeID;
1199: if (nodeID == null) {
1200: // User requested an instant node
1201: if (!service.isInstantNodeSupported()) {
1202: // Instant nodes creation is not allowed so return an error
1203: Element pubsubError = DocumentHelper
1204: .createElement(QName
1205: .get("nodeid-required",
1206: "http://jabber.org/protocol/pubsub#errors"));
1207: sendErrorPacket(iq,
1208: PacketError.Condition.not_acceptable,
1209: pubsubError);
1210: return;
1211: }
1212: do {
1213: // Create a new nodeID and make sure that the random generated string does not
1214: // match an existing node. Probability to match an existing node are very very low
1215: // but they exist :)
1216: newNodeID = StringUtils.randomString(15);
1217: } while (service.getNode(newNodeID) != null);
1218: }
1219: boolean collectionType = false;
1220: // Check if user requested to configure the node (using a data form)
1221: Element configureElement = childElement.element("configure");
1222: if (configureElement != null) {
1223: // Get the data form that contains the parent nodeID
1224: completedForm = getSentConfigurationForm(configureElement);
1225: if (completedForm != null) {
1226: // Calculate newNodeID when new node is affiliated with a Collection
1227: FormField field = completedForm
1228: .getField("pubsub#collection");
1229: if (field != null) {
1230: List<String> values = field.getValues();
1231: if (!values.isEmpty()) {
1232: String parentNodeID = values.get(0);
1233: Node tempNode = service.getNode(parentNodeID);
1234: if (tempNode == null) {
1235: // Requested parent node was not found so return an error
1236: sendErrorPacket(
1237: iq,
1238: PacketError.Condition.item_not_found,
1239: null);
1240: return;
1241: } else if (!tempNode.isCollectionNode()) {
1242: // Requested parent node is not a collection node so return an error
1243: sendErrorPacket(
1244: iq,
1245: PacketError.Condition.not_acceptable,
1246: null);
1247: return;
1248: }
1249: parentNode = (CollectionNode) tempNode;
1250: }
1251: }
1252: field = completedForm.getField("pubsub#node_type");
1253: if (field != null) {
1254: // Check if user requested to create a new collection node
1255: List<String> values = field.getValues();
1256: if (!values.isEmpty()) {
1257: collectionType = "collection".equals(values
1258: .get(0));
1259: }
1260: }
1261: }
1262: }
1263: // If no parent was defined then use the root collection node
1264: if (parentNode == null && service.isCollectionNodesSupported()) {
1265: parentNode = service.getRootCollectionNode();
1266: }
1267: // Check that the requested nodeID does not exist
1268: Node existingNode = service.getNode(newNodeID);
1269: if (existingNode != null) {
1270: // There is a conflict since a node with the same ID already exists
1271: sendErrorPacket(iq, PacketError.Condition.conflict, null);
1272: return;
1273: }
1274:
1275: if (collectionType && !service.isCollectionNodesSupported()) {
1276: // Cannot create a collection node since the service doesn't support it
1277: Element pubsubError = DocumentHelper
1278: .createElement(QName.get("unsupported",
1279: "http://jabber.org/protocol/pubsub#errors"));
1280: pubsubError.addAttribute("feature", "collections");
1281: sendErrorPacket(iq,
1282: PacketError.Condition.feature_not_implemented,
1283: pubsubError);
1284: return;
1285: }
1286:
1287: if (parentNode != null && !collectionType) {
1288: // Check if requester is allowed to add a new leaf child node to the parent node
1289: if (!parentNode.isAssociationAllowed(from)) {
1290: // User is not allowed to add child leaf node to parent node. Return an error.
1291: sendErrorPacket(iq, PacketError.Condition.forbidden,
1292: null);
1293: return;
1294: }
1295: // Check if number of child leaf nodes has not been exceeded
1296: if (parentNode.isMaxLeafNodeReached()) {
1297: // Max number of child leaf nodes has been reached. Return an error.
1298: Element pubsubError = DocumentHelper
1299: .createElement(QName
1300: .get("max-nodes-exceeded",
1301: "http://jabber.org/protocol/pubsub#errors"));
1302: sendErrorPacket(iq, PacketError.Condition.conflict,
1303: pubsubError);
1304: return;
1305: }
1306: }
1307:
1308: // Create and configure the node
1309: boolean conflict = false;
1310: Node newNode = null;
1311: try {
1312: // TODO Assumed that the owner of the subscription is the bare JID of the subscription JID. Waiting StPeter answer for explicit field.
1313: JID owner = new JID(from.toBareJID());
1314: synchronized (newNodeID.intern()) {
1315: if (service.getNode(newNodeID) == null) {
1316: // Create the node
1317: if (collectionType) {
1318: newNode = new CollectionNode(service,
1319: parentNode, newNodeID, from);
1320: } else {
1321: newNode = new LeafNode(service, parentNode,
1322: newNodeID, from);
1323: }
1324: // Add the creator as the node owner
1325: newNode.addOwner(owner);
1326: // Configure and save the node to the backend store
1327: if (completedForm != null) {
1328: newNode.configure(completedForm);
1329: } else {
1330: newNode.saveToDB();
1331: }
1332: } else {
1333: conflict = true;
1334: }
1335: }
1336: if (conflict) {
1337: // There is a conflict since a node with the same ID already exists
1338: sendErrorPacket(iq, PacketError.Condition.conflict,
1339: null);
1340: } else {
1341: // Return success to the node owner
1342: IQ reply = IQ.createResultIQ(iq);
1343: // Include new nodeID if it has changed from the original nodeID
1344: if (!newNode.getNodeID().equals(nodeID)) {
1345: Element elem = reply.setChildElement("pubsub",
1346: "http://jabber.org/protocol/pubsub");
1347: elem.addElement("create").addAttribute("node",
1348: newNode.getNodeID());
1349: }
1350: router.route(reply);
1351: }
1352: } catch (NotAcceptableException e) {
1353: // Node should have at least one owner. Return not-acceptable error.
1354: sendErrorPacket(iq, PacketError.Condition.not_acceptable,
1355: null);
1356: }
1357: }
1358:
1359: private void getNodeConfiguration(PubSubService service, IQ iq,
1360: Element childElement, String nodeID) {
1361: Node node = service.getNode(nodeID);
1362: if (node == null) {
1363: // Node does not exist. Return item-not-found error
1364: sendErrorPacket(iq, PacketError.Condition.item_not_found,
1365: null);
1366: return;
1367: }
1368: if (!node.isAdmin(iq.getFrom())) {
1369: // Requesting entity is prohibited from configuring this node. Return forbidden error
1370: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1371: return;
1372: }
1373:
1374: // Return data form containing node configuration to the owner
1375: IQ reply = IQ.createResultIQ(iq);
1376: Element replyChildElement = childElement.createCopy();
1377: reply.setChildElement(replyChildElement);
1378: replyChildElement.element("configure").add(
1379: node.getConfigurationForm().getElement());
1380: router.route(reply);
1381: }
1382:
1383: private void getDefaultNodeConfiguration(PubSubService service,
1384: IQ iq, Element childElement, Element defaultElement) {
1385: String type = defaultElement.attributeValue("type");
1386: type = type == null ? "leaf" : type;
1387:
1388: boolean isLeafType = "leaf".equals(type);
1389: DefaultNodeConfiguration config = service
1390: .getDefaultNodeConfiguration(isLeafType);
1391: if (config == null) {
1392: // Service does not support the requested node type so return an error
1393: Element pubsubError = DocumentHelper
1394: .createElement(QName.get("unsupported",
1395: "http://jabber.org/protocol/pubsub#errors"));
1396: pubsubError.addAttribute("feature", isLeafType ? "leaf"
1397: : "collections");
1398: sendErrorPacket(iq,
1399: PacketError.Condition.feature_not_implemented,
1400: pubsubError);
1401: return;
1402: }
1403:
1404: // Return data form containing default node configuration
1405: IQ reply = IQ.createResultIQ(iq);
1406: Element replyChildElement = childElement.createCopy();
1407: reply.setChildElement(replyChildElement);
1408: replyChildElement.element("default").add(
1409: config.getConfigurationForm().getElement());
1410: router.route(reply);
1411: }
1412:
1413: private void configureNode(PubSubService service, IQ iq,
1414: Element configureElement, String nodeID) {
1415: Node node = service.getNode(nodeID);
1416: if (node == null) {
1417: // Node does not exist. Return item-not-found error
1418: sendErrorPacket(iq, PacketError.Condition.item_not_found,
1419: null);
1420: return;
1421: }
1422: if (!node.isAdmin(iq.getFrom())) {
1423: // Requesting entity is not allowed to get node configuration. Return forbidden error
1424: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1425: return;
1426: }
1427:
1428: // Get the data form that contains the parent nodeID
1429: DataForm completedForm = getSentConfigurationForm(configureElement);
1430: if (completedForm != null) {
1431: try {
1432: // Update node configuration with the provided data form
1433: // (and update the backend store)
1434: node.configure(completedForm);
1435: // Return that node configuration was successful
1436: router.route(IQ.createResultIQ(iq));
1437: } catch (NotAcceptableException e) {
1438: // Node should have at least one owner. Return not-acceptable error.
1439: sendErrorPacket(iq,
1440: PacketError.Condition.not_acceptable, null);
1441: }
1442: } else {
1443: // No data form was included so return bad-request error
1444: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
1445: }
1446: }
1447:
1448: private void deleteNode(PubSubService service, IQ iq,
1449: Element deleteElement) {
1450: String nodeID = deleteElement.attributeValue("node");
1451: if (nodeID == null) {
1452: // NodeID was not provided. Return bad-request error
1453: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
1454: return;
1455: }
1456: Node node = service.getNode(nodeID);
1457: if (node == null) {
1458: // Node does not exist. Return item-not-found error
1459: sendErrorPacket(iq, PacketError.Condition.item_not_found,
1460: null);
1461: return;
1462: }
1463: if (!node.isAdmin(iq.getFrom())) {
1464: // Requesting entity is prohibited from deleting this node. Return forbidden error
1465: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1466: return;
1467: }
1468: if (node.isRootCollectionNode()) {
1469: // Root collection node cannot be deleted. Return not-allowed error
1470: sendErrorPacket(iq, PacketError.Condition.not_allowed, null);
1471: return;
1472: }
1473:
1474: // Delete the node
1475: if (node.delete()) {
1476: // Return that node was deleted successfully
1477: router.route(IQ.createResultIQ(iq));
1478: } else {
1479: // Some error occured while trying to delete the node
1480: sendErrorPacket(iq,
1481: PacketError.Condition.internal_server_error, null);
1482: }
1483: }
1484:
1485: private void purgeNode(PubSubService service, IQ iq,
1486: Element purgeElement) {
1487: String nodeID = purgeElement.attributeValue("node");
1488: if (nodeID == null) {
1489: // NodeID was not provided. Return bad-request error
1490: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
1491: return;
1492: }
1493: Node node = service.getNode(nodeID);
1494: if (node == null) {
1495: // Node does not exist. Return item-not-found error
1496: sendErrorPacket(iq, PacketError.Condition.item_not_found,
1497: null);
1498: return;
1499: }
1500: if (!node.isAdmin(iq.getFrom())) {
1501: // Requesting entity is prohibited from configuring this node. Return forbidden error
1502: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1503: return;
1504: }
1505: if (!((LeafNode) node).isPersistPublishedItems()) {
1506: // Node does not persist items. Return feature-not-implemented error
1507: Element pubsubError = DocumentHelper
1508: .createElement(QName.get("unsupported",
1509: "http://jabber.org/protocol/pubsub#errors"));
1510: pubsubError.addAttribute("feature", "persistent-items");
1511: sendErrorPacket(iq,
1512: PacketError.Condition.feature_not_implemented,
1513: pubsubError);
1514: return;
1515: }
1516: if (node.isCollectionNode()) {
1517: // Node is a collection node. Return feature-not-implemented error
1518: Element pubsubError = DocumentHelper
1519: .createElement(QName.get("unsupported",
1520: "http://jabber.org/protocol/pubsub#errors"));
1521: pubsubError.addAttribute("feature", "purge-nodes");
1522: sendErrorPacket(iq,
1523: PacketError.Condition.feature_not_implemented,
1524: pubsubError);
1525: return;
1526: }
1527:
1528: // Purge the node
1529: ((LeafNode) node).purge();
1530: // Return that node purged successfully
1531: router.route(IQ.createResultIQ(iq));
1532: }
1533:
1534: private void getNodeSubscriptions(PubSubService service, IQ iq,
1535: Element affiliationsElement) {
1536: String nodeID = affiliationsElement.attributeValue("node");
1537: if (nodeID == null) {
1538: // NodeID was not provided. Return bad-request error.
1539: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
1540: return;
1541: }
1542: Node node = service.getNode(nodeID);
1543: if (node == null) {
1544: // Node does not exist. Return item-not-found error.
1545: sendErrorPacket(iq, PacketError.Condition.item_not_found,
1546: null);
1547: return;
1548: }
1549: if (!node.isAdmin(iq.getFrom())) {
1550: // Requesting entity is prohibited from getting affiliates list. Return forbidden error.
1551: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1552: return;
1553: }
1554:
1555: // Ask the node to send the list of subscriptions to the owner
1556: node.sendSubscriptions(iq);
1557: }
1558:
1559: private void modifyNodeSubscriptions(PubSubService service, IQ iq,
1560: Element entitiesElement) {
1561: String nodeID = entitiesElement.attributeValue("node");
1562: if (nodeID == null) {
1563: // NodeID was not provided. Return bad-request error.
1564: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
1565: return;
1566: }
1567: Node node = service.getNode(nodeID);
1568: if (node == null) {
1569: // Node does not exist. Return item-not-found error.
1570: sendErrorPacket(iq, PacketError.Condition.item_not_found,
1571: null);
1572: return;
1573: }
1574: if (!node.isAdmin(iq.getFrom())) {
1575: // Requesting entity is prohibited from getting affiliates list. Return forbidden error.
1576: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1577: return;
1578: }
1579:
1580: IQ reply = IQ.createResultIQ(iq);
1581:
1582: // Process modifications or creations of affiliations and subscriptions.
1583: for (Iterator it = entitiesElement
1584: .elementIterator("subscription"); it.hasNext();) {
1585: Element entity = (Element) it.next();
1586: JID subscriber = new JID(entity.attributeValue("jid"));
1587: // TODO Assumed that the owner of the subscription is the bare JID of the subscription JID. Waiting StPeter answer for explicit field.
1588: JID owner = new JID(subscriber.toBareJID());
1589: String subStatus = entity.attributeValue("subscription");
1590: String subID = entity.attributeValue("subid");
1591: // Process subscriptions changes
1592: // Get current subscription (if any)
1593: NodeSubscription subscription = null;
1594: if (node.isMultipleSubscriptionsEnabled()) {
1595: if (subID != null) {
1596: subscription = node.getSubscription(subID);
1597: }
1598: } else {
1599: subscription = node.getSubscription(subscriber);
1600: }
1601: if ("none".equals(subStatus) && subscription != null) {
1602: // Owner is cancelling an existing subscription
1603: node.cancelSubscription(subscription);
1604: } else if ("subscribed".equals(subStatus)) {
1605: if (subscription != null) {
1606: // Owner is approving a subscription (i.e. making active)
1607: node.approveSubscription(subscription, true);
1608: } else {
1609: // Owner is creating a subscription for an entity to the node
1610: node.createSubscription(null, owner, subscriber,
1611: false, null);
1612: }
1613: }
1614: }
1615:
1616: // Send reply
1617: router.route(reply);
1618: }
1619:
1620: private void getNodeAffiliations(PubSubService service, IQ iq,
1621: Element affiliationsElement) {
1622: String nodeID = affiliationsElement.attributeValue("node");
1623: if (nodeID == null) {
1624: // NodeID was not provided. Return bad-request error.
1625: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
1626: return;
1627: }
1628: Node node = service.getNode(nodeID);
1629: if (node == null) {
1630: // Node does not exist. Return item-not-found error.
1631: sendErrorPacket(iq, PacketError.Condition.item_not_found,
1632: null);
1633: return;
1634: }
1635: if (!node.isAdmin(iq.getFrom())) {
1636: // Requesting entity is prohibited from getting affiliates list. Return forbidden error.
1637: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1638: return;
1639: }
1640:
1641: // Ask the node to send the list of affiliations to the owner
1642: node.sendAffiliations(iq);
1643: }
1644:
1645: private void modifyNodeAffiliations(PubSubService service, IQ iq,
1646: Element entitiesElement) {
1647: String nodeID = entitiesElement.attributeValue("node");
1648: if (nodeID == null) {
1649: // NodeID was not provided. Return bad-request error.
1650: sendErrorPacket(iq, PacketError.Condition.bad_request, null);
1651: return;
1652: }
1653: Node node = service.getNode(nodeID);
1654: if (node == null) {
1655: // Node does not exist. Return item-not-found error.
1656: sendErrorPacket(iq, PacketError.Condition.item_not_found,
1657: null);
1658: return;
1659: }
1660: if (!node.isAdmin(iq.getFrom())) {
1661: // Requesting entity is prohibited from getting affiliates list. Return forbidden error.
1662: sendErrorPacket(iq, PacketError.Condition.forbidden, null);
1663: return;
1664: }
1665:
1666: IQ reply = IQ.createResultIQ(iq);
1667: Collection<JID> invalidAffiliates = new ArrayList<JID>();
1668:
1669: // Process modifications or creations of affiliations
1670: for (Iterator it = entitiesElement
1671: .elementIterator("affiliation"); it.hasNext();) {
1672: Element affiliation = (Element) it.next();
1673: JID owner = new JID(affiliation.attributeValue("jid"));
1674: String newAffiliation = affiliation
1675: .attributeValue("affiliation");
1676: // Get current affiliation of this user (if any)
1677: NodeAffiliate affiliate = node.getAffiliate(owner);
1678:
1679: // Check that we are not removing the only owner of the node
1680: if (affiliate != null
1681: && !affiliate.getAffiliation().name().equals(
1682: newAffiliation)) {
1683: // Trying to modify an existing affiliation
1684: if (affiliate.getAffiliation() == NodeAffiliate.Affiliation.owner
1685: && node.getOwners().size() == 1) {
1686: // Trying to remove the unique owner of the node. Include in error answer.
1687: invalidAffiliates.add(owner);
1688: continue;
1689: }
1690: }
1691:
1692: // Owner is setting affiliations for new entities or modifying
1693: // existing affiliations
1694: if ("owner".equals(newAffiliation)) {
1695: node.addOwner(owner);
1696: } else if ("publisher".equals(newAffiliation)) {
1697: node.addPublisher(owner);
1698: } else if ("none".equals(newAffiliation)) {
1699: node.addNoneAffiliation(owner);
1700: } else {
1701: node.addOutcast(owner);
1702: }
1703: }
1704:
1705: // Process invalid entities that tried to remove node owners. Send original affiliation
1706: // of the invalid entities.
1707: if (!invalidAffiliates.isEmpty()) {
1708: reply.setError(PacketError.Condition.not_acceptable);
1709: Element child = reply.setChildElement("pubsub",
1710: "http://jabber.org/protocol/pubsub#owner");
1711: Element entities = child.addElement("affiliations");
1712: if (!node.isRootCollectionNode()) {
1713: entities.addAttribute("node", node.getNodeID());
1714: }
1715: for (JID affiliateJID : invalidAffiliates) {
1716: NodeAffiliate affiliate = node
1717: .getAffiliate(affiliateJID);
1718: Element entity = entities.addElement("affiliation");
1719: entity.addAttribute("jid", affiliate.getJID()
1720: .toString());
1721: entity.addAttribute("affiliation", affiliate
1722: .getAffiliation().name());
1723: }
1724: }
1725: // Send reply
1726: router.route(reply);
1727: }
1728:
1729: /**
1730: * Terminates the subscription of the specified entity to all nodes hosted at the service.
1731: * The affiliation with the node will be removed if the entity was not a node owner or
1732: * publisher.
1733: *
1734: * @param service the PubSub service this action is to be performed for.
1735: * @param user the entity that no longer exists.
1736: */
1737: private void cancelAllSubscriptions(PubSubService service, JID user) {
1738: for (Node node : service.getNodes()) {
1739: NodeAffiliate affiliate = node.getAffiliate(user);
1740: if (affiliate == null) {
1741: continue;
1742: }
1743: for (NodeSubscription subscription : affiliate
1744: .getSubscriptions()) {
1745: // Cancel subscription
1746: node.cancelSubscription(subscription);
1747: }
1748: }
1749: }
1750:
1751: private void processAuthorizationAnswer(PubSubService service,
1752: DataForm authForm, Message message) {
1753: String nodeID = authForm.getField("pubsub#node").getValues()
1754: .get(0);
1755: String subID = authForm.getField("pubsub#subid").getValues()
1756: .get(0);
1757: String allow = authForm.getField("pubsub#allow").getValues()
1758: .get(0);
1759: boolean approved;
1760: if ("1".equals(allow) || "true".equals(allow)) {
1761: approved = true;
1762: } else if ("0".equals(allow) || "false".equals(allow)) {
1763: approved = false;
1764: } else {
1765: // Unknown allow value. Ignore completed form
1766: Log
1767: .warn("Invalid allow value in completed authorization form: "
1768: + message.toXML());
1769: return;
1770: }
1771: // Approve or cancel the pending subscription to the node
1772: Node node = service.getNode(nodeID);
1773: if (node != null) {
1774: NodeSubscription subscription = node.getSubscription(subID);
1775: if (subscription != null) {
1776: node.approveSubscription(subscription, approved);
1777: }
1778: }
1779: }
1780:
1781: /**
1782: * Generate a conflict packet to indicate that the nickname being requested/used is already in
1783: * use by another user.
1784: *
1785: * @param packet the packet to be bounced.
1786: */
1787: void sendErrorPacket(IQ packet, PacketError.Condition error,
1788: Element pubsubError) {
1789: IQ reply = IQ.createResultIQ(packet);
1790: reply.setChildElement(packet.getChildElement().createCopy());
1791: reply.setError(error);
1792: if (pubsubError != null) {
1793: // Add specific pubsub error if available
1794: reply.getError().getElement().add(pubsubError);
1795: }
1796: router.route(reply);
1797: }
1798:
1799: /**
1800: * Returns the data form included in the configure element sent by the node owner or
1801: * <tt>null</tt> if none was included or access model was defined. If the
1802: * owner just wants to set the access model to use for the node and optionally set the
1803: * list of roster groups (i.e. contacts present in the node owner roster in the
1804: * specified groups are allowed to access the node) allowed to access the node then
1805: * instead of including a data form the owner can just specify the "access" attribute
1806: * of the configure element and optionally include a list of group elements. In this case,
1807: * the method will create a data form including the specified data. This is a nice way
1808: * to accept both ways to configure a node but always returning a data form.
1809: *
1810: * @param configureElement the configure element sent by the owner.
1811: * @return the data form included in the configure element sent by the node owner or
1812: * <tt>null</tt> if none was included or access model was defined.
1813: */
1814: private DataForm getSentConfigurationForm(Element configureElement) {
1815: DataForm completedForm = null;
1816: FormField formField;
1817: Element formElement = configureElement.element(QName.get("x",
1818: "jabber:x:data"));
1819: if (formElement != null) {
1820: completedForm = new DataForm(formElement);
1821: }
1822: String accessModel = configureElement.attributeValue("access");
1823: if (accessModel != null) {
1824: if (completedForm == null) {
1825: // Create a form (i.e. simulate that the user sent a form with roster groups)
1826: completedForm = new DataForm(DataForm.Type.submit);
1827: // Add the hidden field indicating that this is a node config form
1828: formField = completedForm.addField();
1829: formField.setVariable("FORM_TYPE");
1830: formField.setType(FormField.Type.hidden);
1831: formField
1832: .addValue("http://jabber.org/protocol/pubsub#node_config");
1833: }
1834: if (completedForm.getField("pubsub#access_model") == null) {
1835: // Add the field that will specify the access model of the node
1836: formField = completedForm.addField();
1837: formField.setVariable("pubsub#access_model");
1838: formField.addValue(accessModel);
1839: } else {
1840: Log
1841: .debug("PubSubEngine: Owner sent access model in data form and as attribute: "
1842: + configureElement.asXML());
1843: }
1844: // Check if a list of groups was specified
1845: List groups = configureElement.elements("group");
1846: if (!groups.isEmpty()) {
1847: // Add the field that will contain the specified groups
1848: formField = completedForm.addField();
1849: formField.setVariable("pubsub#roster_groups_allowed");
1850: // Add each group as a value of the groups field
1851: for (Iterator it = groups.iterator(); it.hasNext();) {
1852: formField.addValue(((Element) it.next())
1853: .getTextTrim());
1854: }
1855: }
1856: }
1857: return completedForm;
1858: }
1859:
1860: public void start(final PubSubService service) {
1861: // Probe presences of users that this service has subscribed to (once the server
1862: // has started)
1863:
1864: if (XMPPServer.getInstance().isStarted()) {
1865: probePresences(service);
1866: } else {
1867: XMPPServer.getInstance().addServerListener(
1868: new XMPPServerListener() {
1869: public void serverStarted() {
1870: probePresences(service);
1871: }
1872:
1873: public void serverStopping() {
1874: }
1875: });
1876: }
1877: }
1878:
1879: private void probePresences(final PubSubService service) {
1880: Set<JID> affiliates = new HashSet<JID>();
1881: for (Node node : service.getNodes()) {
1882: affiliates.addAll(node.getPresenceBasedSubscribers());
1883: }
1884: for (JID jid : affiliates) {
1885: // Send probe presence
1886: Presence subscription = new Presence(Presence.Type.probe);
1887: subscription.setTo(jid);
1888: subscription.setFrom(service.getAddress());
1889: service.send(subscription);
1890: }
1891: }
1892:
1893: public void shutdown(PubSubService service) {
1894: // Stop the maintenance processes
1895: service.getTimer().cancel();
1896: // Delete from the database items contained in the itemsToDelete queue
1897: PublishedItem entry;
1898: while (!service.getItemsToDelete().isEmpty()) {
1899: entry = service.getItemsToDelete().poll();
1900: if (entry != null) {
1901: PubSubPersistenceManager.removePublishedItem(service,
1902: entry);
1903: }
1904: }
1905: // Save to the database items contained in the itemsToAdd queue
1906: while (!service.getItemsToAdd().isEmpty()) {
1907: entry = service.getItemsToAdd().poll();
1908: if (entry != null) {
1909: PubSubPersistenceManager.createPublishedItem(service,
1910: entry);
1911: }
1912: }
1913: // Stop executing ad-hoc commands
1914: service.getManager().stop();
1915: }
1916:
1917: /*******************************************************************************
1918: * Methods related to presence subscriptions to subscribers' presence.
1919: ******************************************************************************/
1920:
1921: /**
1922: * Returns the show values of the last know presence of all connected resources of the
1923: * specified subscriber. When the subscriber JID is a bare JID then the answered collection
1924: * will have many entries one for each connected resource. Moreover, if the user
1925: * is offline then an empty collectin is returned. Available show status is represented
1926: * by a <tt>online</tt> value. The rest of the possible show values as defined in RFC 3921.
1927: *
1928: * @param service the PubSub service this action is to be performed for.
1929: * @param subscriber the JID of the subscriber. This is not the JID of the affiliate.
1930: * @return an empty collection when offline. Otherwise, a collection with the show value
1931: * of each connected resource.
1932: */
1933: public static Collection<String> getShowPresences(
1934: PubSubService service, JID subscriber) {
1935: Map<String, String> fullPresences = service.getBarePresences()
1936: .get(subscriber.toBareJID());
1937: if (fullPresences == null) {
1938: // User is offline so return empty list
1939: return Collections.emptyList();
1940: }
1941: if (subscriber.getResource() == null) {
1942: // Subscriber used bared JID so return show value of all connected resources
1943: return fullPresences.values();
1944: } else {
1945: // Look for the show value using the full JID
1946: String show = fullPresences.get(subscriber.toString());
1947: if (show == null) {
1948: // User at the specified resource is offline so return empty list
1949: return Collections.emptyList();
1950: }
1951: // User is connected at specified resource so answer list with presence show value
1952: return Arrays.asList(show);
1953: }
1954: }
1955:
1956: /**
1957: * Requests the pubsub service to subscribe to the presence of the user. If the service
1958: * has already subscribed to the user's presence then do nothing.
1959: *
1960: * @param service the PubSub service this action is to be performed for.
1961: * @param node the node that originated the subscription request.
1962: * @param user the JID of the affiliate to subscribe to his presence.
1963: */
1964: public static void presenceSubscriptionNotRequired(
1965: PubSubService service, Node node, JID user) {
1966: // Check that no node is requiring to be subscribed to this user
1967: for (Node hostedNode : service.getNodes()) {
1968: if (hostedNode.isPresenceBasedDelivery(user)) {
1969: // Do not unsubscribe since presence subscription is still required
1970: return;
1971: }
1972: }
1973: // Unscribe from the user presence
1974: Presence subscription = new Presence(Presence.Type.unsubscribe);
1975: subscription.setTo(user);
1976: subscription.setFrom(service.getAddress());
1977: service.send(subscription);
1978: }
1979:
1980: /**
1981: * Requests the pubsub service to unsubscribe from the presence of the user. If the service
1982: * was not subscribed to the user's presence or any node still requires to be subscribed to
1983: * the user presence then do nothing.
1984: *
1985: * @param service the PubSub service this action is to be performed for.
1986: * @param node the node that originated the unsubscription request.
1987: * @param user the JID of the affiliate to unsubscribe from his presence.
1988: */
1989: public static void presenceSubscriptionRequired(
1990: PubSubService service, Node node, JID user) {
1991: Map<String, String> fullPresences = service.getBarePresences()
1992: .get(user.toString());
1993: if (fullPresences == null || fullPresences.isEmpty()) {
1994: Presence subscription = new Presence(
1995: Presence.Type.subscribe);
1996: subscription.setTo(user);
1997: subscription.setFrom(service.getAddress());
1998: service.send(subscription);
1999: // Sending subscription requests based on received presences may generate
2000: // that a sunscription request is sent to an offline user (since offline
2001: // presences are not stored in the service's "barePresences"). However, this
2002: // not optimal algorithm shouldn't bother the user since the user's server
2003: // should reply when already subscribed to the user's presence instead of
2004: // asking the user to accept the subscription request.
2005: }
2006: }
2007:
2008: /*******************************************************************************
2009: * Methods related to PubSub maintenance tasks. Such as
2010: * saving or deleting published items.
2011: ******************************************************************************/
2012:
2013: /**
2014: * Schedules the maintenance task for repeated <i>fixed-delay execution</i>,
2015: * beginning after the specified delay. Subsequent executions take place
2016: * at approximately regular intervals separated by the specified period.
2017: *
2018: * @param service the PubSub service this action is to be performed for.
2019: * @param timeout the new frequency of the maintenance task.
2020: */
2021: void setPublishedItemTaskTimeout(PubSubService service, int timeout) {
2022: int items_task_timeout = service.getItemsTaskTimeout();
2023: if (items_task_timeout == timeout) {
2024: return;
2025: }
2026: // Cancel the existing task because the timeout has changed
2027: PublishedItemTask publishedItemTask = service
2028: .getPublishedItemTask();
2029: if (publishedItemTask != null) {
2030: publishedItemTask.cancel();
2031: }
2032: service.setItemsTaskTimeout(timeout);
2033: // Create a new task and schedule it with the new timeout
2034: service.setPublishedItemTask(new PublishedItemTask(service));
2035: service.getTimer().schedule(publishedItemTask,
2036: items_task_timeout, items_task_timeout);
2037: }
2038:
2039: /**
2040: * Adds the item to the queue of items to remove from the database. The queue is going
2041: * to be processed by another thread.
2042: *
2043: * @param service the PubSub service this action is to be performed for.
2044: * @param removedItem the item to remove from the database.
2045: */
2046: public static void queueItemToRemove(PubSubService service,
2047: PublishedItem removedItem) {
2048: // Remove the removed item from the queue of items to add to the database
2049: if (!service.getItemsToAdd().remove(removedItem)) {
2050: // The item is already present in the database so add the removed item
2051: // to the queue of items to delete from the database
2052: service.getItemsToDelete().add(removedItem);
2053: }
2054: }
2055:
2056: /**
2057: * Adds the item to the queue of items to add to the database. The queue is going
2058: * to be processed by another thread.
2059: *
2060: * @param service the PubSub service this action is to be performed for.
2061: * @param newItem the item to add to the database.
2062: */
2063: public static void queueItemToAdd(PubSubService service,
2064: PublishedItem newItem) {
2065: service.getItemsToAdd().add(newItem);
2066: }
2067:
2068: /**
2069: * Cancels any queued operation for the specified list of items. This operation is
2070: * usually required when a node was deleted so any pending operation of the node items
2071: * should be cancelled.
2072: *
2073: * @param service the PubSub service this action is to be performed for.
2074: * @param items the list of items to remove the from queues.
2075: */
2076: void cancelQueuedItems(PubSubService service,
2077: Collection<PublishedItem> items) {
2078: for (PublishedItem item : items) {
2079: service.getItemsToAdd().remove(item);
2080: service.getItemsToDelete().remove(item);
2081: }
2082: }
2083:
2084: }
|