package org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.set; import java.util.Collection; import java.util.Date; import java.util.concurrent.BlockingQueue; import org.apache.log4j.Logger; import org.buddycloud.channelserver.Configuration; import org.buddycloud.channelserver.channel.ChannelManager; import org.buddycloud.channelserver.channel.ValidatePayload; import org.buddycloud.channelserver.channel.validate.PayloadValidator; import org.buddycloud.channelserver.channel.validate.UnknownContentTypeException; import org.buddycloud.channelserver.db.exception.NodeStoreException; import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.JabberPubsub; import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.PubSubElementProcessorAbstract; import org.buddycloud.channelserver.pubsub.affiliation.Affiliations; import org.buddycloud.channelserver.pubsub.model.NodeMembership; import org.buddycloud.channelserver.pubsub.model.NodeSubscription; import org.buddycloud.channelserver.pubsub.model.impl.NodeItemImpl; import org.buddycloud.channelserver.pubsub.subscription.Subscriptions; import org.buddycloud.channelserver.utils.XMLConstants; import org.dom4j.Element; import org.dom4j.dom.DOMElement; import org.xmpp.packet.IQ; import org.xmpp.packet.IQ.Type; import org.xmpp.packet.JID; import org.xmpp.packet.Message; import org.xmpp.packet.Packet; import org.xmpp.packet.PacketError; import org.xmpp.resultsetmanagement.ResultSet; public class Publish extends PubSubElementProcessorAbstract { private static final Logger LOGGER = Logger.getLogger(Publish.class); private Element entry; private JID publishersJID; private Element item; private PayloadValidator validator; public Publish(BlockingQueue<Packet> outQueue, ChannelManager channelManager) { this.outQueue = outQueue; this.channelManager = channelManager; acceptedElementName = XMLConstants.PUBLISH_ELEM; } @Override public void process(Element elm, JID actorJID, IQ reqIQ, Element rsm) throws Exception { request = reqIQ; response = IQ.createResultIQ(reqIQ); publishersJID = request.getFrom(); node = request.getChildElement().element(XMLConstants.PUBLISH_ELEM).attributeValue(XMLConstants.NODE_ATTR); if (!checkNode()) { return; } boolean isLocalSubscriber = false; if (actorJID != null) { publishersJID = actorJID; } else { isLocalSubscriber = Configuration.getInstance().isLocalJID(publishersJID); // Check that user is registered. if (!isLocalSubscriber) { // If the packet did not have actor, and the sender is not a // local user. // publishing is not allowed. /* * <iq type='error' from='pubsub.shakespeare.lit' to='hamlet@denmark.lit/elsinore' * id='create1'> <error type='auth'> <registration-required * xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/> </error> </iq> */ response.setType(Type.error); PacketError pe = new PacketError(org.xmpp.packet.PacketError.Condition.registration_required, org.xmpp.packet.PacketError.Type.auth); response.setError(pe); outQueue.put(response); return; } } try { if (!nodeExists()) { return; } if (!userCanPost()) { return; } if (!isRequestValid()) { return; } saveNodeItem(); updateThreadParent(); sendResponseStanza(); sendNotifications(); } catch (NodeStoreException e) { LOGGER.error("Problem with node store", e); setErrorCondition(PacketError.Type.wait, PacketError.Condition.internal_server_error); outQueue.put(response); } catch (UnknownContentTypeException e) { LOGGER.error(e); createExtendedErrorReply(PacketError.Type.modify, PacketError.Condition.not_acceptable, ValidatePayload.UNSUPPORTED_CONTENT_TYPE); } } private void updateThreadParent() throws NodeStoreException { if (null == this.validator.getInReplyTo()) { return; } channelManager.updateThreadParent(node, this.validator.getInReplyTo()); } private void saveNodeItem() throws NodeStoreException { // Let's store the new item. entry = validator.getPayload(); channelManager.addNodeItem(new NodeItemImpl(node, this.validator.getLocalItemId(), new Date(), entry.asXML(), this.validator.getInReplyTo())); } public void setEntryValidator(PayloadValidator validator) { this.validator = validator; } private PayloadValidator getPayloadValidator() throws Exception { if (null == this.validator) { this.validator = new ValidatePayload(channelManager, node).getValidator(); } return this.validator; } private void sendInvalidPayloadResponse() throws InterruptedException { LOGGER.info("Payload is not valid: '" + validator.getErrorMessage() + "'."); createExtendedErrorReply(PacketError.Type.modify, PacketError.Condition.bad_request, validator.getErrorMessage()); outQueue.put(response); } private boolean isRequestValid() throws Exception { item = request.getChildElement().element(acceptedElementName).element(XMLConstants.ITEM_ELEM); if (null == item) { createExtendedErrorReply(PacketError.Type.modify, PacketError.Condition.bad_request, XMLConstants.ITEM_REQUIRED_ELEM); outQueue.put(response); return false; } validator = getPayloadValidator(); validator.setPayload(item); validator.setUser(publishersJID); validator.setTo(request.getTo().toBareJID()); validator.setNode(node); validator.setChannelManager(channelManager); if (!validator.isValid()) { sendInvalidPayloadResponse(); return false; } return true; } private boolean userCanPost() throws NodeStoreException, InterruptedException { NodeMembership membership = channelManager.getNodeMembership(node, publishersJID); if ((!membership.getSubscription().equals(Subscriptions.subscribed)) || (!membership.getAffiliation().in(Affiliations.moderator, Affiliations.owner, Affiliations.publisher))) { response.setType(Type.error); PacketError error = new PacketError(PacketError.Condition.forbidden, PacketError.Type.auth); response.setError(error); outQueue.put(response); return false; } return true; } private void sendResponseStanza() throws InterruptedException { /* * Success, let's response as defined in * http://xmpp.org/extensions/xep-0060.html#publisher-publish - 7.1.2 Success Case */ Element pubsub = new DOMElement(XMLConstants.PUBSUB_ELEM, new org.dom4j.Namespace("", JabberPubsub.NAMESPACE_URI)); Element publish = pubsub.addElement(XMLConstants.PUBLISH_ELEM); publish.addAttribute(XMLConstants.NODE_ATTR, node); Element newItem = publish.addElement(XMLConstants.ITEM_ELEM); newItem.addAttribute(XMLConstants.ID_ATTR, validator.getGlobalItemId()); response.setChildElement(pubsub); outQueue.put(response); } private boolean nodeExists() throws Exception { if (true == channelManager.nodeExists(node)) { return true; } response.setType(Type.error); PacketError error = new PacketError(PacketError.Condition.item_not_found, PacketError.Type.cancel); response.setError(error); outQueue.put(response); return false; } private boolean checkNode() throws InterruptedException, NodeStoreException { if (node == null || "".equals(node)) { response.setType(Type.error); Element badRequest = new DOMElement(PacketError.Condition.bad_request.toXMPP(), new org.dom4j.Namespace("", JabberPubsub.NS_XMPP_STANZAS)); Element nodeIdRequired = new DOMElement(XMLConstants.NODE_ID_REQUIRED, new org.dom4j.Namespace("", JabberPubsub.NS_PUBSUB_ERROR)); Element error = new DOMElement(XMLConstants.ERROR_ELEM); error.addAttribute(XMLConstants.TYPE_ATTR, PacketError.Type.modify.toXMPP()); error.add(badRequest); error.add(nodeIdRequired); response.setChildElement(error); outQueue.put(response); return false; } boolean isLocalNode = false; try { isLocalNode = Configuration.getInstance().isLocalNode(node); } catch (IllegalArgumentException e) { response.setType(Type.error); PacketError pe = new PacketError(PacketError.Condition.bad_request, PacketError.Type.modify); response.setError(pe); LOGGER.error(e); outQueue.put(response); return false; } if (!isLocalNode) { makeRemoteRequest(); return false; } return true; } private void sendNotifications() throws NodeStoreException, InterruptedException { // Let's send notifications as defined in 7.1.2.1 Notification With // Payload Message msg = new Message(); msg.getElement().addAttribute("remote-server-discover", "false"); msg.setType(Message.Type.headline); msg.setFrom(request.getTo()); Element event = msg.addChildElement("event", JabberPubsub.NS_PUBSUB_EVENT); Element items = event.addElement("items"); items.addAttribute("node", node); Element i = items.addElement("item"); i.addAttribute("id", validator.getGlobalItemId()); i.add(entry.createCopy()); ResultSet<NodeSubscription> cur = channelManager.getNodeSubscriptionListeners(node); for (NodeSubscription ns : cur) { JID to = ns.getUser(); if (ns.getSubscription().equals(Subscriptions.subscribed)) { LOGGER.debug("Sending post notification to " + to.toBareJID()); msg.setTo(ns.getListener()); outQueue.put(msg.createCopy()); } } Collection<JID> admins = getAdminUsers(); for (JID admin : admins) { msg.setTo(admin); outQueue.put(msg.createCopy()); } } }