📄 searchplugin.java
字号:
/** * Copyright (C) 2008 Jive Software. All rights reserved. * * This software is published under the terms of the GNU Public License (GPL), * a copy of which is included in this distribution, or a commercial license * agreement with Jive. */package org.jivesoftware.openfire.plugin;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.dom4j.QName;import org.jivesoftware.openfire.XMPPServer;import org.jivesoftware.openfire.container.Plugin;import org.jivesoftware.openfire.container.PluginManager;import org.jivesoftware.openfire.disco.IQDiscoInfoHandler;import org.jivesoftware.openfire.disco.IQDiscoItemsHandler;import org.jivesoftware.openfire.forms.DataForm;import org.jivesoftware.openfire.forms.FormField;import org.jivesoftware.openfire.forms.spi.XDataFormImpl;import org.jivesoftware.openfire.forms.spi.XFormFieldImpl;import org.jivesoftware.openfire.resultsetmanager.ResultSet;import org.jivesoftware.openfire.resultsetmanager.ResultSetImpl;import org.jivesoftware.openfire.user.User;import org.jivesoftware.openfire.user.UserManager;import org.jivesoftware.openfire.user.UserNotFoundException;import org.jivesoftware.util.*;import org.xmpp.component.Component;import org.xmpp.component.ComponentException;import org.xmpp.component.ComponentManager;import org.xmpp.component.ComponentManagerFactory;import org.xmpp.packet.IQ;import org.xmpp.packet.IQ.Type;import org.xmpp.packet.JID;import org.xmpp.packet.Packet;import org.xmpp.packet.PacketError;import org.xmpp.packet.PacketError.Condition;import java.io.File;import java.util.*;import java.util.Map.Entry;/** * Provides support for Jabber Search * (<a href="http://www.xmpp.org/extensions/xep-0055.html">XEP-0055</a>).<p> * <p/> * The basic functionality is to query an information repository * regarding the possible search fields, to send a search query, * and to receive search results. This implementation was primarily designed to use * <a href="http://www.xmpp.org/extensions/xep-0004.html">Data Forms</a>, but * also supports non-dataform searches. * <p/> * * @author <a href="mailto:ryan@version2software.com">Ryan Graham</a> */public class SearchPlugin implements Component, Plugin, PropertyEventListener { public static final String NAMESPACE_JABBER_IQ_SEARCH = "jabber:iq:search"; public static final String SERVICENAME = "plugin.search.serviceName"; public static final String SERVICEENABLED = "plugin.search.serviceEnabled"; public static final String EXCLUDEDFIELDS = "plugin.search.excludedFields"; private UserManager userManager; private ComponentManager componentManager; private PluginManager pluginManager; private String serviceName; private boolean serviceEnabled; private Collection<String> exculudedFields; private static String serverName; private TreeMap<String, String> fieldLookup = new TreeMap<String, String>(new CaseInsensitiveComparator()); private Map<String, String> reverseFieldLookup = new HashMap<String, String>(); /** * A list of field names that are valid in jabber:iq:search */ public final static Collection<String> validSearchRequestFields = new ArrayList<String>(); static { validSearchRequestFields.add("first"); validSearchRequestFields.add("last"); validSearchRequestFields.add("nick"); validSearchRequestFields.add("email"); validSearchRequestFields.add("x"); // extended info // result set management (XEP-0059) validSearchRequestFields.add("set"); } public SearchPlugin() { serviceName = JiveGlobals.getProperty(SERVICENAME, "search"); serviceEnabled = JiveGlobals.getBooleanProperty(SERVICEENABLED, true); exculudedFields = StringUtils.stringToCollection(JiveGlobals.getProperty(EXCLUDEDFIELDS, "")); serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain(); userManager = UserManager.getInstance(); // Some clients, such as Miranda, are hard-coded to search specific fields, // so we map those fields to the fields that Openfire actually supports. fieldLookup.put("jid", "Username"); fieldLookup.put("username", "Username"); fieldLookup.put("first", "Name"); fieldLookup.put("last", "Name"); fieldLookup.put("nick", "Name"); fieldLookup.put("name", "Name"); fieldLookup.put("email", "Email"); } /* * (non-Javadoc) * * @see org.xmpp.component.Component#getName() */ public String getName() { return pluginManager.getName(this); } /* * (non-Javadoc) * * @see org.xmpp.component.Component#getDescription() */ public String getDescription() { return pluginManager.getDescription(this); } /* * (non-Javadoc) * * @see org.jivesoftware.openfire.container.Plugin#initializePlugin(org.jivesoftware.openfire.container.PluginManager, * java.io.File) */ public void initializePlugin(PluginManager manager, File pluginDirectory) { pluginManager = manager; componentManager = ComponentManagerFactory.getComponentManager(); try { componentManager.addComponent(serviceName, this); } catch (ComponentException e) { componentManager.getLog().error(e); } PropertyEventDispatcher.addListener(this); } /* * (non-Javadoc) * * @see org.xmpp.component.Component#initialize(org.xmpp.packet.JID, * org.xmpp.component.ComponentManager) */ public void initialize(JID jid, ComponentManager componentManager) { } /* * (non-Javadoc) * * @see org.xmpp.component.Component#start() */ public void start() { } /* * (non-Javadoc) * * @see org.jivesoftware.openfire.container.Plugin#destroyPlugin() */ public void destroyPlugin() { PropertyEventDispatcher.removeListener(this); pluginManager = null; try { componentManager.removeComponent(serviceName); componentManager = null; } catch (Exception e) { if (componentManager != null) { componentManager.getLog().error(e); } } serviceName = null; userManager = null; exculudedFields = null; serverName = null; fieldLookup = null; reverseFieldLookup = null; } /* * (non-Javadoc) * * @see org.xmpp.component.Component#shutdown() */ public void shutdown() { } /* * (non-Javadoc) * * @see org.xmpp.component.Component#processPacket(org.xmpp.packet.Packet) */ public void processPacket(Packet p) { if (!(p instanceof IQ)) { return; } final IQ packet = (IQ) p; if (packet.getType().equals(IQ.Type.error) || packet.getType().equals(IQ.Type.result)) { return; } // Packet p is an IQ stanza of type GET or SET. Therefor, it _must_ be // replied to. final IQ replyPacket = handleIQRequest(packet); try { componentManager.sendPacket(this, replyPacket); } catch (ComponentException e) { componentManager.getLog().error(e); } } /** * Handles IQ requests. This method throws an IllegalArgumentException if an * IQ stanza is supplied that is not a request (if the stanza is not of type * 'get' or 'set'). This method will either throw an Exception, or return a * non-null IQ stanza of type 'error' or 'result', as XMPP Core specifies * that <strong>all</strong> IQ request stanza's (type 'get' or 'set') MUST * be replied to. * * @param iq The IQ stanza that forms the request. * @return The response to the request. */ private IQ handleIQRequest(IQ iq) { final IQ replyPacket; // 'final' to ensure that it is set. if (iq == null) { throw new IllegalArgumentException("Argument 'iq' cannot be null."); } final IQ.Type type = iq.getType(); if (type != IQ.Type.get && type != IQ.Type.set) { throw new IllegalArgumentException( "Argument 'iq' must be of type 'get' or 'set'"); } final Element childElement = iq.getChildElement(); if (childElement == null) { replyPacket = IQ.createResultIQ(iq); replyPacket .setError(new PacketError( Condition.bad_request, org.xmpp.packet.PacketError.Type.modify, "IQ stanzas of type 'get' and 'set' MUST contain one and only one child element (RFC 3920 section 9.2.3).")); return replyPacket; } final String namespace = childElement.getNamespaceURI(); if (namespace == null) { replyPacket = IQ.createResultIQ(iq); replyPacket.setError(Condition.feature_not_implemented); return replyPacket; } if (namespace.equals(NAMESPACE_JABBER_IQ_SEARCH)) { replyPacket = handleSearchRequest(iq); } else if (namespace.equals(IQDiscoInfoHandler.NAMESPACE_DISCO_INFO)) { replyPacket = handleDiscoInfo(iq); } else if (namespace.equals(IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS)) { replyPacket = IQ.createResultIQ(iq); replyPacket.setChildElement("query", IQDiscoItemsHandler.NAMESPACE_DISCO_ITEMS); } else { // don't known what to do with this. replyPacket = IQ.createResultIQ(iq); replyPacket.setError(Condition.feature_not_implemented); } return replyPacket; } /** * Creates a response specific to the search plugin to Disco#Info requests. * * @param iq The IQ stanza that contains the request. * @return An IQ stanza, formulated as an answer to the received request. */ private static IQ handleDiscoInfo(IQ iq) { if (iq == null) { throw new IllegalArgumentException("Argument 'iq' cannot be null."); } if (!iq.getChildElement().getNamespaceURI().equals( IQDiscoInfoHandler.NAMESPACE_DISCO_INFO) || iq.getType() != Type.get) { throw new IllegalArgumentException( "This is not a valid disco#info request."); } final IQ replyPacket = IQ.createResultIQ(iq); final Element responseElement = replyPacket.setChildElement("query", IQDiscoInfoHandler.NAMESPACE_DISCO_INFO); responseElement.addElement("identity").addAttribute("category", "directory").addAttribute("type", "user").addAttribute("name", "User Search"); responseElement.addElement("feature").addAttribute("var", NAMESPACE_JABBER_IQ_SEARCH); responseElement.addElement("feature").addAttribute("var", IQDiscoInfoHandler.NAMESPACE_DISCO_INFO); responseElement.addElement("feature").addAttribute("var", ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT); return replyPacket; } private IQ handleSearchRequest(IQ packet) { if (!serviceEnabled) { return replyDisabled(packet); } switch (packet.getType()) { case get: return processGetPacket(packet); case set: return processSetPacket(packet); default: // we can safely ignore 'error' and 'result' typed iq stanzas. return null; } } /** * Constructs a IQ result stanza, based on the request stanza that is * provided as an argument. The stanza tells the recipient that this service * is currently unavailable. * * @param packet The request IQ stanza to which a result will be returned. * @return A result stanza, telling the user that this service is * unavailable.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -