@Override
    public IQ handleIQ(IQ packet) {
        // Create a copy of the sent pack that will be used as the reply
        // we only need to add the requested items to the reply if any otherwise add 
        // a not found error
        IQ reply = IQ.createResultIQ(packet);

        // TODO Implement publishing client items
        if (IQ.Type.set == packet.getType()) {
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(PacketError.Condition.feature_not_implemented);
            return reply;
        }

        // Look for a DiscoItemsProvider associated with the requested entity.
        // We consider the host of the recipient JID of the packet as the entity. It's the 
        // DiscoItemsProvider responsibility to provide the items associated with the JID's name  
        // together with any possible requested node.
        DiscoItemsProvider itemsProvider = null;
        
        if((packet.getTo() == null) || (packet.getTo().asBareJID().equals(packet.getFrom().asBareJID()))) {
            itemsProvider = getProvider(XMPPServer.getInstance().getServerInfo().getXMPPDomain());
        } else {
            itemsProvider = getProvider(packet.getTo().getDomain());
        }

        if (itemsProvider != null) {
            // Get the JID's name
            String name = packet.getTo() == null ? null : packet.getTo().getNode();
            if (name == null || name.trim().isEmpty()) {
                name = null;
            }
            // Get the requested node
            Element iq = packet.getChildElement();
            String node = iq.attributeValue("node");

            // Check if we have items associated with the requested name and node
            Iterator<DiscoItem> itemsItr = itemsProvider.getItems(name, node, packet.getFrom());
            if (itemsItr != null) {
                reply.setChildElement(iq.createCopy());
                Element queryElement = reply.getChildElement();

                // See if the requesting entity would like to apply 'result set
                // management'
                final Element rsmElement = packet.getChildElement().element(
                        QName.get("set",
                                ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT));

                // apply RSM only if the element exists, and the (total) results
                // set is not empty.
                final boolean applyRSM = rsmElement != null
                        && itemsItr.hasNext();

                if (applyRSM) {
                    if (!ResultSet.isValidRSMRequest(rsmElement))
                    {
                        reply.setError(PacketError.Condition.bad_request);
                        return reply;
                    }
                    
                    // Calculate which results to include.
                    final List<DiscoItem> rsmResults;
                    final List<DiscoItem> allItems = new ArrayList<>();
                    while (itemsItr.hasNext()) {
                        allItems.add(itemsItr.next());
                    }
                    final ResultSet<DiscoItem> rs = new ResultSetImpl<>(
                            allItems);
                    try {
                        rsmResults = rs.applyRSMDirectives(rsmElement);
                    } catch (NullPointerException e) {
                        final IQ itemNotFound = IQ.createResultIQ(packet);
                        itemNotFound.setError(PacketError.Condition.item_not_found);
                        return itemNotFound;
                    }

                    // add the applicable results to the IQ-result
                    for (DiscoItem item : rsmResults) {
                        final Element resultElement = item.getElement();
                        resultElement.setQName(new QName(resultElement
                                .getName(), queryElement.getNamespace()));
                        queryElement.add(resultElement.createCopy());
                    }

                    // overwrite the 'set' element.
                    queryElement.elements(QName.get("set", ResultSet.NAMESPACE_RESULT_SET_MANAGEMENT))
                        .forEach(queryElement::remove);
                    queryElement.add(rs.generateSetElementFromResults(rsmResults));
                } else {
                    // don't apply RSM:
                    // Add to the reply all the items provided by the DiscoItemsProvider
                    Element item;
                    while (itemsItr.hasNext()) {
                        item = itemsItr.next().getElement();
                        item.setQName(new QName(item.getName(), queryElement.getNamespace()));
                        queryElement.add(item.createCopy());
                    }
                 }
            }
            else {
                // If the DiscoItemsProvider has no items for the requested name and node 
                // then answer a not found error
                reply.setChildElement(packet.getChildElement().createCopy());
                reply.setError(PacketError.Condition.item_not_found);
            }
        }
        else {
            // If we didn't find a DiscoItemsProvider then answer a not found error
            reply.setChildElement(packet.getChildElement().createCopy());
            reply.setError(PacketError.Condition.item_not_found);
        }

        return reply;
    }
XEP-0030 Section 8 contradict RFC 6121 8.5.1. "No Such User" in its definition on how the server must
respond to a request made against a non-existing user. This method overrides the default implementation of
RFC 6121's definition (from the super class) with that of XEP-0030:

<blockquote>The following rules apply to the handling of service discovery requests sent to bare JIDs: [...] In
response to a disco#items request, the server MUST return an empty result set if: [...] The target entity does
not exist (no matter if the request specifies a node or not).</blockquote>

@see <a href="https://xmpp.org/extensions/xep-0030.html#security">XEP-0030 8 "Security Considerations"</a>
@see <a href="https://xmpp.org/rfcs/rfc6121.html#rules-localpart-nosuchuser">RFC 6121 8.5.1. "No Such User"</a>
@see <a href="https://igniterealtime.atlassian.net/jira/software/c/projects/OF/issues/OF-880">OF-880</a>
xmppserver/src/main/java/org/jivesoftware/openfire/disco/IQDiscoItemsHandler.java