MUCRoomHistory.java 9.88 KB
Newer Older
1
/*
2
 * Copyright (C) 2004-2008 Jive Software. All rights reserved.
3
 *
4 5 6 7 8 9 10 11 12 13 14
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
15 16
 */

17
package org.jivesoftware.openfire.muc;
18

19
import org.dom4j.Attribute;
20
import org.dom4j.Element;
21 22
import org.dom4j.Namespace;
import org.dom4j.io.SAXReader;
23
import org.jivesoftware.openfire.user.UserNotFoundException;
24
import org.jivesoftware.util.XMPPDateTimeFormat;
25 26
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
27 28 29
import org.xmpp.packet.JID;
import org.xmpp.packet.Message;

30
import java.io.StringReader;
31 32
import java.util.Date;
import java.util.Iterator;
33
import java.util.List;
34 35 36 37 38 39 40 41 42
import java.util.ListIterator;

/**
 * Represent the data model for one <code>MUCRoom</code> history. Including chat transcript,
 * joining and leaving times.
 * 
 * @author Gaston Dombiak
 */
public final class MUCRoomHistory {
43
    private static final Logger Log = LoggerFactory.getLogger(MUCRoomHistory.class);
44 45 46 47 48 49 50 51 52 53 54 55 56 57

    private MUCRoom room;

    private HistoryStrategy historyStrategy;

    private boolean isNonAnonymousRoom;

    public MUCRoomHistory(MUCRoom mucRoom, HistoryStrategy historyStrategy) {
        this.room = mucRoom;
        this.isNonAnonymousRoom = mucRoom.canAnyoneDiscoverJID();
        this.historyStrategy = historyStrategy;
    }

    public void addMessage(Message packet) {
58 59
    	boolean isSubjectChangeRequest = isSubjectChangeRequest(packet);
    	JID fromJID = packet.getFrom();
60 61
        // Don't keep messages whose sender is the room itself (thus address without resource)
        // unless the message is changing the room's subject
62 63 64
        if (!isSubjectChangeRequest &&
        	(fromJID == null || fromJID.toString().length() == 0 ||
        	 fromJID.equals(room.getRole().getRoleAddress()))) {
65 66
            return;
        }
67 68 69
        // Do not store regular messages if there is no message strategy (keep subject change requests)
        if (!isSubjectChangeRequest && !historyStrategy.isHistoryEnabled()) {
            return;
70 71
        }

72 73 74
        // Ignore empty messages (no subject AND no body)
        if (!isSubjectChangeRequest &&
            (packet.getBody() == null || packet.getBody().trim().length() == 0)) {
75 76 77
            return;
        }

78
        Message packetToAdd = packet.createCopy();
79 80 81 82 83 84

        // Check if the room has changed its configuration
        if (isNonAnonymousRoom != room.canAnyoneDiscoverJID()) {
            isNonAnonymousRoom = room.canAnyoneDiscoverJID();
            // Update the "from" attribute of the delay information in the history
            // TODO Make this update in a separate thread
85 86
            for (Iterator<Message> it = getMessageHistory(); it.hasNext();) {
                Message message = it.next();
87
                Element delayElement = message.getChildElement("delay", "urn:xmpp:delay");
88 89 90 91
                if (room.canAnyoneDiscoverJID()) {
                    // Set the Full JID as the "from" attribute
                    try {
                        MUCRole role = room.getOccupant(message.getFrom().getResource());
92
                        delayElement.addAttribute("from", role.getUserAddress().toString());
93 94
                    }
                    catch (UserNotFoundException e) {
95
                        // Ignore.
96 97 98 99 100 101 102 103 104 105 106
                    }
                }
                else {
                    // Set the Room JID as the "from" attribute
                    delayElement.addAttribute("from", message.getFrom().toString());
                }
            }

        }

        // Add the delay information to the message
107
        Element delayInformation = packetToAdd.addChildElement("delay", "urn:xmpp:delay");
108
        Date current = new Date();
109
        delayInformation.addAttribute("stamp", XMPPDateTimeFormat.format(current));
110 111 112 113
        if (room.canAnyoneDiscoverJID()) {
            // Set the Full JID as the "from" attribute
            try {
                MUCRole role = room.getOccupant(packet.getFrom().getResource());
114
                delayInformation.addAttribute("from", role.getUserAddress().toString());
115 116
            }
            catch (UserNotFoundException e) {
117
                // Ignore.
118 119 120 121 122 123 124 125 126
            }
        }
        else {
            // Set the Room JID as the "from" attribute
            delayInformation.addAttribute("from", packet.getFrom().toString());
        }
        historyStrategy.addMessage(packetToAdd);
    }

127
    public Iterator<Message> getMessageHistory() {
128 129 130 131 132 133 134 135 136 137
        return historyStrategy.getMessageHistory();
    }

    /**
     * Obtain the current history to be iterated in reverse mode. This means that the returned list
     * iterator will be positioned at the end of the history so senders of this message must
     * traverse the list in reverse mode.
     * 
     * @return A list iterator of Message objects positioned at the end of the list.
     */
138
    public ListIterator<Message> getReverseMessageHistory() {
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
        return historyStrategy.getReverseMessageHistory();
    }

    /**
     * Creates a new message and adds it to the history. The new message will be created based on
     * the provided information. This information will likely come from the database when loading
     * the room history from the database.
     *
     * @param senderJID the sender's JID of the message to add to the history.
     * @param nickname the sender's nickname of the message to add to the history.
     * @param sentDate the date when the message was sent to the room.
     * @param subject the subject included in the message.
     * @param body the body of the message.
     */
    public void addOldMessage(String senderJID, String nickname, Date sentDate, String subject,
154
            String body, String stanza)
155 156 157
    {
        Message message = new Message();
        message.setType(Message.Type.groupchat);
158 159 160 161 162 163 164 165 166 167 168 169
        if (stanza != null) {
            // payload initialized as XML string from DB
            SAXReader xmlReader = new SAXReader();
            xmlReader.setEncoding("UTF-8");
            try {
                Element element = xmlReader.read(new StringReader(stanza)).getRootElement();
                for (Element child : (List<Element>)element.elements()) {
                    Namespace ns = child.getNamespace();
                    if (ns == null || ns.getURI().equals("jabber:client") || ns.getURI().equals("jabber:server")) {
                        continue;
                    }
                    Element added = message.addChildElement(child.getName(), child.getNamespaceURI());
170 171 172
                    if (!child.getText().isEmpty()) {
                        added.setText(child.getText());
                    }
173
                    for (Attribute attr : (List<Attribute>)child.attributes()) {
174
                        added.addAttribute(attr.getQName(), attr.getValue());
175 176
                    }
                    for (Element el : (List<Element>)child.elements()) {
177
                        added.add(el.createCopy());
178 179
                    }
                }
180 181 182
                if (element.attribute("id") != null) {
                    message.setID(element.attributeValue("id"));
                }
183
            } catch (Exception ex) {
184
                Log.error("Failed to parse payload XML", ex);
185 186
            }
        }
187 188 189 190 191 192
        message.setSubject(subject);
        message.setBody(body);
        // Set the sender of the message
        if (nickname != null && nickname.trim().length() > 0) {
            JID roomJID = room.getRole().getRoleAddress();
            // Recreate the sender address based on the nickname and room's JID
193
            message.setFrom(new JID(roomJID.getNode(), roomJID.getDomain(), nickname, true));
194 195 196 197 198 199 200
        }
        else {
            // Set the room as the sender of the message
            message.setFrom(room.getRole().getRoleAddress());
        }

        // Add the delay information to the message
201
        Element delayInformation = message.addChildElement("delay", "urn:xmpp:delay");
202
        delayInformation.addAttribute("stamp", XMPPDateTimeFormat.format(sentDate));
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
        if (room.canAnyoneDiscoverJID()) {
            // Set the Full JID as the "from" attribute
            delayInformation.addAttribute("from", senderJID);
        }
        else {
            // Set the Room JID as the "from" attribute
            delayInformation.addAttribute("from", room.getRole().getRoleAddress().toString());
        }
        historyStrategy.addMessage(message);
    }

    /**
     * Returns true if there is a message within the history of the room that has changed the
     * room's subject.
     *
     * @return true if there is a message within the history of the room that has changed the
     *         room's subject.
     */
    public boolean hasChangedSubject() {
        return historyStrategy.hasChangedSubject();
    }
224 225 226 227 228 229 230 231 232 233

    /**
     * Returns the message within the history of the room that has changed the
     * room's subject.
     * 
     * @return the latest room subject change or null if none exists yet.
     */
    public Message getChangedSubject() {
        return historyStrategy.getChangedSubject();
    }
234 235 236 237 238 239 240 241 242

    /**
     * Returns true if the given message qualifies as a subject change request, per XEP-0045.
     * 
     * @return true if the given packet is a subject change request
     */
    public boolean isSubjectChangeRequest(Message message) {
        return historyStrategy.isSubjectChangeRequest(message);
    }
243
}