package org.buddycloud.channelserver.packetprocessor.iq.namespace.mam;
import java.io.StringReader;
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.Conf;
import org.buddycloud.channelserver.db.CloseableIterator;
import org.buddycloud.channelserver.db.exception.NodeStoreException;
import org.buddycloud.channelserver.packetprocessor.PacketProcessor;
import org.buddycloud.channelserver.packetprocessor.iq.namespace.pubsub.JabberPubsub;
import org.buddycloud.channelserver.pubsub.affiliation.Affiliations;
import org.buddycloud.channelserver.pubsub.model.NodeAffiliation;
import org.buddycloud.channelserver.pubsub.model.NodeItem;
import org.buddycloud.channelserver.pubsub.model.NodeSubscription;
import org.buddycloud.channelserver.pubsub.subscription.Subscriptions;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xmpp.packet.IQ;
import org.xmpp.packet.IQ.Type;
import org.xmpp.packet.Message;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
import org.xmpp.packet.PacketError.Condition;
import org.xmpp.resultsetmanagement.ResultSet;
public class MessageArchiveManagement implements PacketProcessor<IQ> {
public static final String NAMESPACE_MAM = "urn:xmpp:mam:tmp";
public static final String NAMESPACE_FORWARDED = "urn:xmpp:forward:0";
public static final String NAMESPACE_DELAY = "urn:xmpp:delay";
public static final String ELEMENT_NAME = "query";
private static final Logger logger = Logger.getLogger(MessageArchiveManagement.class);
private final BlockingQueue<Packet> outQueue;
private ChannelManager channelManager;
private SAXReader xmlReader = new SAXReader();
private Message wrapper;
private Date startTimestamp;
private Date endTimestamp = new Date();
private IQ requestIq;
private IQ reply;
public MessageArchiveManagement(BlockingQueue<Packet> outQueue, ChannelManager channelManager) {
this.outQueue = outQueue;
this.channelManager = channelManager;
}
@Override
public void process(IQ reqIQ) throws Exception {
requestIq = reqIQ;
reply = IQ.createResultIQ(requestIq);
if (false == Configuration.getInstance().isLocalJID(requestIq.getFrom())) {
this.sendNotHandledStanza();
return;
}
if (false == isValidRequest()) {
return;
}
generateMessageWrapper();
sendSubscriptionUpdates();
sendAffiliationUpdated();
sendItemUpdates();
outQueue.put(IQ.createResultIQ(this.requestIq));
}
private void generateMessageWrapper() {
wrapper = new Message();
wrapper.setFrom(requestIq.getTo());
wrapper.setTo(requestIq.getFrom());
Element result = wrapper.addChildElement("result", NAMESPACE_MAM);
result.addAttribute("id", requestIq.getID());
Element forwarded = wrapper.addChildElement("forwarded", NAMESPACE_FORWARDED);
Element delay = forwarded.addElement("delay", NAMESPACE_DELAY);
delay.addAttribute("stamp", Conf.formatDate(new Date()));
Element message = forwarded.addElement("msg");
message.addAttribute("type", Message.Type.headline.toString());
message.addAttribute("to", requestIq.getFrom().toFullJID());
message.addAttribute("from", requestIq.getTo().toString());
}
private boolean isValidRequest() throws InterruptedException {
try {
Element query = requestIq.getChildElement();
startTimestamp = Conf.parseDate("1970-01-01T00:00:00Z");
if (query.element("start") != null) {
startTimestamp = Conf.parseDate(query.elementText("start"));
}
if (query.element("end") != null) {
endTimestamp = Conf.parseDate(query.elementText("end"));
}
return true;
} catch (IllegalArgumentException e) {
logger.error(e);
sendErrorPacket(PacketError.Type.modify, PacketError.Condition.bad_request);
return false;
}
}
private void sendItemUpdates() {
try {
CloseableIterator<NodeItem> items = channelManager.getNewNodeItemsForUser(requestIq.getFrom(), startTimestamp, endTimestamp);
if (false == items.hasNext()) {
return;
}
Message notification = wrapper.createCopy();
Element forwarded = notification.getElement().element("forwarded");
notification.getElement().addAttribute("remote-server-discover", "false");
Element event = forwarded.addElement("event");
event.addNamespace("", JabberPubsub.NS_PUBSUB_EVENT);
Element itemsElement = event.addElement("items");
Element i = itemsElement.addElement("item");
NodeItem item;
while (items.hasNext()) {
item = items.next();
itemsElement.addAttribute("node", item.getNodeId());
i.addAttribute("id", item.getId());
if (null != i.element("entry")) {
i.remove(i.element("entry"));
}
i.add(xmlReader.read(new StringReader(item.getPayload())).getRootElement());
forwarded.element("delay").addAttribute("stamp", Conf.formatDate(item.getUpdated()));
outQueue.put(notification.createCopy());
}
} catch (NodeStoreException e) {
logger.error(e);
} catch (InterruptedException e) {
logger.error(e);
} catch (DocumentException e) {
logger.error(e);
}
}
private void sendAffiliationUpdated() {
try {
ResultSet<NodeAffiliation> changes = channelManager.getAffiliationChanges(requestIq.getFrom(), startTimestamp, endTimestamp);
if (0 == changes.size()) {
return;
}
Message notification = wrapper.createCopy();
Element forwarded = notification.getElement().element("forwarded");
Element event = forwarded.addElement("event");
Element affiliations = event.addElement("affiliations");
Element affiliationElement = affiliations.addElement("affiliation");
event.addNamespace("", JabberPubsub.NS_PUBSUB_EVENT);
Affiliations affiliation;
for (NodeAffiliation change : changes) {
affiliation = change.getAffiliation();
if ((true == isOwnerModerator(change.getNodeId())) && (true == Affiliations.outcast.equals(change.getAffiliation()))) {
affiliation = Affiliations.none;
}
affiliations.addAttribute("node", change.getNodeId());
affiliationElement.addAttribute("jid", change.getUser().toBareJID());
affiliationElement.addAttribute("affiliation", affiliation.toString());
forwarded.element("delay").addAttribute("stamp", Conf.formatDate(change.getLastUpdated()));
outQueue.put(notification.createCopy());
}
} catch (NodeStoreException e) {
logger.error(e);
} catch (InterruptedException e) {
logger.error(e);
}
}
private void sendSubscriptionUpdates() {
try {
ResultSet<NodeSubscription> changes = channelManager.getSubscriptionChanges(requestIq.getFrom(), endTimestamp, endTimestamp);
if (0 == changes.size()) {
return;
}
Message notification = wrapper.createCopy();
Element forwarded = notification.getElement().element("forwarded");
Element event = forwarded.addElement("event");
event.addNamespace("", JabberPubsub.NS_PUBSUB_EVENT);
Element subscription = event.addElement("subscription");
for (NodeSubscription change : changes) {
if ((false == isOwnerModerator(change.getNodeId())) && (true == Subscriptions.invited.equals(change.getSubscription()))) {
continue;
}
subscription.addAttribute("node", change.getNodeId());
subscription.addAttribute("jid", change.getUser().toBareJID());
subscription.addAttribute("subscription", change.getSubscription().toString());
if ((null != change.getInvitedBy()) && Subscriptions.invited.equals(change.getSubscription())) {
subscription.addAttribute("invited-by", change.getInvitedBy().toBareJID());
}
forwarded.element("delay").addAttribute("stamp", Conf.formatDate(change.getLastUpdated()));
outQueue.put(notification.createCopy());
}
} catch (NodeStoreException e) {
logger.error(e);
} catch (InterruptedException e) {
logger.error(e);
}
}
private void sendNotHandledStanza() throws InterruptedException {
sendErrorPacket(PacketError.Type.cancel, PacketError.Condition.service_unavailable);
}
private void sendErrorPacket(PacketError.Type type, Condition condition) throws InterruptedException {
reply.setChildElement(requestIq.getChildElement().createCopy());
reply.setType(Type.error);
PacketError pe = new PacketError(condition, type);
reply.setError(pe);
outQueue.put(reply);
}
private boolean isOwnerModerator(String node) throws NodeStoreException {
return channelManager.getNodeMembership(node, requestIq.getFrom()).getAffiliation().canAuthorize();
}
}