package org.buddycloud.channelserver.packetprocessor.iq.namespace.search;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
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.db.CloseableIterator;
import org.buddycloud.channelserver.db.exception.NodeStoreException;
import org.buddycloud.channelserver.packetprocessor.PacketProcessor;
import org.buddycloud.channelserver.pubsub.model.NodeItem;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.tree.DefaultElement;
import org.xmpp.forms.DataForm;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
import org.xmpp.packet.PacketError;
public class SearchSet implements PacketProcessor<IQ> {
private ChannelManager channelManager;
private BlockingQueue<Packet> outQueue;
private IQ responseIq;
private Element x;
private IQ requestIq;
private ArrayList<String> content = new ArrayList<String>();
private int page = 1;
private int rpp = 25;
private JID author;
private JID searcher;
public static final Logger LOGGER = Logger.getLogger(SearchSet.class);
public SearchSet(BlockingQueue<Packet> outQueue, ChannelManager channelManager) {
this.channelManager = channelManager;
this.outQueue = outQueue;
}
@Override
public void process(IQ request) throws Exception {
searcher = request.getFrom();
responseIq = IQ.createResultIQ(request);
this.requestIq = request;
if (false == isValidRequest()) {
return;
}
if (false == processForm()) {
return;
}
try {
runSearch();
} catch (NodeStoreException e) {
LOGGER.error(e);
sendErrorResponse(PacketError.Type.wait, PacketError.Condition.internal_server_error);
return;
}
outQueue.put(responseIq);
}
private boolean isValidRequest() throws Exception {
if (false == Configuration.getInstance().isLocalJID(searcher)) {
sendErrorResponse(PacketError.Type.cancel, PacketError.Condition.not_allowed);
return false;
}
if (false == hasDataForm() || false == dataFormCorrect()) {
return false;
}
return true;
}
private boolean hasDataForm() throws Exception {
x = requestIq.getElement().element("query").element("x");
if (null == x || !DataForm.NAMESPACE.equals(x.getNamespaceURI()) || !"submit".equals(x.attributeValue("type"))) {
sendErrorResponse(PacketError.Type.modify, PacketError.Condition.bad_request);
return false;
}
return true;
}
private boolean dataFormCorrect() throws Exception {
if (!hasCorrectFormElement() || !hasEnoughFormFields()) {
sendErrorResponse(PacketError.Type.modify, PacketError.Condition.bad_request);
return false;
}
return true;
}
private boolean hasCorrectFormElement() throws Exception {
List<Element> elements = x.elements("field");
if (elements.size() > 0) {
for (Element field : elements) {
if (!"FORM_TYPE".equals(field.attributeValue("var"))) {
continue;
}
String value = field.elementText("value");
if (null == value || !Search.NAMESPACE_URI.equals(value)) {
return false;
}
return true;
}
}
return false;
}
private boolean hasEnoughFormFields() throws Exception {
List<Element> elements = x.elements("field");
if (elements.size() < 2) {
return false;
}
boolean hasContentOrAuthor = false;
String var;
for (Element field : elements) {
var = field.attributeValue("var");
if ("content".equals(var) || "author".equals(var)) {
hasContentOrAuthor = true;
}
}
return hasContentOrAuthor;
}
private boolean processForm() throws Exception {
try {
extractFieldValues();
} catch (NumberFormatException e) {
return false;
}
if (false == checkFieldValues()) {
return false;
}
return true;
}
private void runSearch() throws NodeStoreException {
CloseableIterator<NodeItem> results = channelManager.performSearch(searcher, content, author, page, rpp);
Element query = responseIq.getElement().addElement("query");
query.addAttribute("xmlns", Search.NAMESPACE_URI);
Element x = new DefaultElement("x");
int resultCounter = 0;
NodeItem nodeItem;
Element entry;
SAXReader xmlReader = new SAXReader();
while (results.hasNext()) {
if (0 == resultCounter) {
addFormField(x);
addReportedFields(x);
}
nodeItem = results.next();
try {
entry = xmlReader.read(new StringReader(nodeItem.getPayload())).getRootElement();
Element item = x.addElement("item");
item.addElement("field").addAttribute("var", "node").addElement("value").setText(nodeItem.getNodeId());
item.addElement("field").addAttribute("var", "id").addElement("value").setText(nodeItem.getId());
item.addElement("field").addAttribute("var", "entry").addElement("value").add(entry);
} catch (DocumentException e) {
LOGGER.error("Error parsing a node entry, ignoring. " + nodeItem);
}
resultCounter++;
}
if (resultCounter > 0) {
query.add(x);
}
}
private void addFormField(Element x) {
x.addElement("field").addAttribute("type", "hidden").addAttribute("var", "FORM_TYPE").addElement("value").setText(Search.NAMESPACE_URI);
}
private void addReportedFields(Element x) {
Element reported = x.addElement("reported");
reported.addElement("field").addAttribute("var", "node").addAttribute("label", "Node").addAttribute("type", "text-single");
reported.addElement("field").addAttribute("var", "id").addAttribute("label", "Item ID").addAttribute("type", "text-single");
reported.addElement("field").addAttribute("var", "entry").addAttribute("label", "Item").addAttribute("type", "xml");
}
private void extractFieldValues() {
List<Element> elements = x.elements("field");
String var;
for (Element field : elements) {
var = field.attributeValue("var");
if ("content".equals(var)) {
content = getValuesAsList(field);
} else if ("author".equals(var)) {
String authorStr = field.elementText("value");
if (authorStr.length() > 0) {
author = new JID(authorStr);
}
} else if ("page".equals(var)) {
page = getValueAsNumber(field);
} else if ("rpp".equals(var)) {
rpp = getValueAsNumber(field);
}
}
}
private boolean checkFieldValues() throws Exception {
if (((null != content && content.size() > 0) || (null != author && author.toBareJID().length() > 0)) && (page > 0 && rpp > 0)) {
return true;
}
sendErrorResponse(PacketError.Type.modify, PacketError.Condition.bad_request);
return false;
}
private ArrayList<String> getValuesAsList(Element field) {
ArrayList<String> rtn = new ArrayList<String>();
String valueText;
for (Element value : (List<Element>) field.elements("value")) {
valueText = value.getText();
if (valueText.length() == 0) {
continue;
}
rtn.add(valueText);
}
return rtn;
}
private Integer getValueAsNumber(Element field) throws NumberFormatException {
String valueStr = field.elementText("value");
return Integer.parseInt(valueStr);
}
private void sendErrorResponse(PacketError.Type type, PacketError.Condition condition) throws InterruptedException {
responseIq.setType(IQ.Type.error);
PacketError error = new PacketError(condition, type);
responseIq.setError(error);
outQueue.put(responseIq);
}
}