public void storeOffline(Message message) {
        if (message != null) {
            // Do nothing if the message was sent to the server itself, an anonymous user or a non-existent user
            // Also ignore message carbons
            JID recipientJID = message.getTo();
            if (recipientJID == null || serverAddress.equals(recipientJID) ||
                    recipientJID.getNode() == null ||
                    message.getExtension("received", "urn:xmpp:carbons:2") != null ||
                    !(UserManager.getInstance().isRegisteredUser(recipientJID, false) || UserManager.isPotentialFutureLocalUser(recipientJID))
            ) {
                return;
            }

            // Do not store messages if communication is blocked
            PrivacyList list =
                    PrivacyListManager.getInstance().getDefaultPrivacyList(recipientJID.getNode());
            if (list != null && list.shouldBlockPacket(message)) {
                if (message.getType() == Message.Type.error){
                    Log.debug("Avoid generating an error in response to a stanza that itself is an error (to avoid the chance of entering an endless back-and-forth of exchanging errors). Suppress sending an {} error in response to: {}", PacketError.Condition.service_unavailable, message);
                    return;
                }
                Log.trace("Responding with 'service-unavailable' as the message was blocked by a privacy list, to: {}", message);
                Message result = message.createCopy();
                result.setTo(message.getFrom());
                result.setFrom(message.getTo());
                result.setError(PacketError.Condition.service_unavailable);

                XMPPServer.getInstance().getPacketRouter().route(result);
                return;
            }

            // 8.5.2.  localpart@domainpart
            // 8.5.2.2.  No Available or Connected Resources
            if (recipientJID.getResource() == null) {
                if (message.getType() == Message.Type.headline || message.getType() == Message.Type.error) {
                    // For a message stanza of type "headline" or "error", the server MUST silently ignore the message.
                    return;
                }
                // // For a message stanza of type "groupchat", the server MUST return an error to the sender, which SHOULD be <service-unavailable/>.
                else if (message.getType() == Message.Type.groupchat) {
                    bounce(message);
                    return;
                }
            } else {
                // 8.5.3.  localpart@domainpart/resourcepart
                // 8.5.3.2.1.  Message

                // For a message stanza of type "normal", "groupchat", or "headline", the server MUST either (a) silently ignore the stanza
                // or (b) return an error stanza to the sender, which SHOULD be <service-unavailable/>.
                if (message.getType() == Message.Type.normal || message.getType() == Message.Type.groupchat || message.getType() == Message.Type.headline) {
                    // Depending on the OfflineMessageStragey, we may silently ignore or bounce
                    if (type == Type.bounce) {
                        bounce(message);
                    }
                    // Either bounce or silently ignore, never store such messages
                    return;
                }
                // For a message stanza of type "error", the server MUST silently ignore the stanza.
                else if (message.getType() == Message.Type.error) {
                    return;
                }
            }

            switch (type) {
            case bounce:
                bounce(message);
                break;
            case store:
                store(message);
                break;
            case store_and_bounce:
                if (underQuota(message)) {
                    store(message);
                }
                else {
                    Log.debug( "Unable to store, as user is over storage quota. Bouncing message instead: " + message.toXML() );
                    bounce(message);
                }
                break;
            case store_and_drop:
                if (underQuota(message)) {
                    store(message);
                } else {
                    Log.debug( "Unable to store, as user is over storage quota. Silently dropping message: " + message.toXML() );
                }
                break;
            case drop:
                // Drop essentially means silently ignore/do nothing
                break;
            }
        }
    }
Controls what is done with offline messages.

@author Iain Shigeoka
xmppserver/src/main/java/org/jivesoftware/openfire/OfflineMessageStrategy.java