PrivateStorage.java 5.96 KB
Newer Older
Matt Tucker's avatar
Matt Tucker committed
1 2 3 4 5
/**
 * $RCSfile$
 * $Revision$
 * $Date$
 *
Matt Tucker's avatar
Matt Tucker committed
6
 * Copyright (C) 2004 Jive Software. All rights reserved.
Matt Tucker's avatar
Matt Tucker committed
7
 *
Matt Tucker's avatar
Matt Tucker committed
8 9
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
Matt Tucker's avatar
Matt Tucker committed
10
 */
Matt Tucker's avatar
Matt Tucker committed
11

12
package org.jivesoftware.messenger;
Matt Tucker's avatar
Matt Tucker committed
13 14

import org.jivesoftware.messenger.container.BasicModule;
15
import org.jivesoftware.database.DbConnectionManager;
Matt Tucker's avatar
Matt Tucker committed
16
import org.jivesoftware.util.Log;
17
import org.jivesoftware.util.LocaleUtils;
Matt Tucker's avatar
Matt Tucker committed
18
import org.jivesoftware.util.XPPReader;
19 20
import org.dom4j.Element;
import org.dom4j.Document;
Matt Tucker's avatar
Matt Tucker committed
21

22
import java.sql.*;
Matt Tucker's avatar
Matt Tucker committed
23
import java.sql.Connection;
24 25
import java.io.StringWriter;
import java.io.StringReader;
Matt Tucker's avatar
Matt Tucker committed
26 27

/**
28 29
 * Private storage for user accounts (JEP-0049). It is used by some XMPP systems
 * for saving client settings on the server.
Matt Tucker's avatar
Matt Tucker committed
30 31 32
 *
 * @author Iain Shigeoka
 */
33
public class PrivateStorage extends BasicModule {
Matt Tucker's avatar
Matt Tucker committed
34

35
    private static final String LOAD_PRIVATE =
36
        "SELECT value FROM jivePrivate WHERE username=? AND namespace=?";
37
    private static final String INSERT_PRIVATE =
38
        "INSERT INTO jivePrivate (value,name,username,namespace) VALUES (?,?,?,?)";
39
    private static final String UPDATE_PRIVATE =
40
        "UPDATE jivePrivate SET value=?, name=? WHERE username=? AND namespace=?";
Matt Tucker's avatar
Matt Tucker committed
41

42 43 44 45
    // Currently no delete supported, we can detect an add of an empty element and
    // use that to signal a delete but that optimization doesn't seem necessary.
    // private static final String DELETE_PRIVATE =
    //     "DELETE FROM jivePrivate WHERE userID=? AND name=? AND namespace=?";
Matt Tucker's avatar
Matt Tucker committed
46

47
    private boolean enabled = JiveGlobals.getBooleanProperty("xmpp.privateStorageEnabled", true);
Matt Tucker's avatar
Matt Tucker committed
48

49 50 51 52
    /**
     * Constructs a new PrivateStore instance.
     */
    public PrivateStorage() {
Matt Tucker's avatar
Matt Tucker committed
53 54 55
        super("Private user data storage");
    }

56 57 58 59 60
    /**
     * Returns true if private storage is enabled.
     *
     * @return true if private storage is enabled.
     */
Matt Tucker's avatar
Matt Tucker committed
61 62 63 64
    public boolean isEnabled() {
        return enabled;
    }

65 66 67 68 69
    /**
     * Sets whether private storage is enabled.
     *
     * @param enabled true if this private store is enabled.
     */
Matt Tucker's avatar
Matt Tucker committed
70
    public void setEnabled(boolean enabled) {
71 72
        this.enabled = enabled;
        JiveGlobals.setProperty("xmpp.privateStorageEnabled", Boolean.toString(enabled));
Matt Tucker's avatar
Matt Tucker committed
73 74
    }

75 76 77 78 79 80 81 82
    /**
     * Stores private data. If the name and namespace of the element matches another
     * stored private data XML document, then replace it with the new one.
     *
     * @param data the data to store (XML element)
     * @param username the username of the account where private data is being stored
     */
    public void add(String username, Element data) {
Matt Tucker's avatar
Matt Tucker committed
83
        if (enabled) {
84
            java.sql.Connection con = null;
Matt Tucker's avatar
Matt Tucker committed
85 86 87 88 89 90
            PreparedStatement pstmt = null;
            try {
                StringWriter writer = new StringWriter();
                data.write(writer);
                con = DbConnectionManager.getConnection();
                pstmt = con.prepareStatement(LOAD_PRIVATE);
91
                pstmt.setString(1, username);
Matt Tucker's avatar
Matt Tucker committed
92 93
                pstmt.setString(2, data.getNamespaceURI());
                ResultSet rs = pstmt.executeQuery();
94
                boolean update = false;
Matt Tucker's avatar
Matt Tucker committed
95
                if (rs.next()) {
96 97 98 99
                    update = true;
                }
                rs.close();
                pstmt.close();
100

101
                if (update) {
Matt Tucker's avatar
Matt Tucker committed
102 103 104 105 106 107 108
                    pstmt = con.prepareStatement(UPDATE_PRIVATE);
                }
                else {
                    pstmt = con.prepareStatement(INSERT_PRIVATE);
                }
                pstmt.setString(1, writer.toString());
                pstmt.setString(2, data.getName());
109
                pstmt.setString(3, username);
Matt Tucker's avatar
Matt Tucker committed
110
                pstmt.setString(4, data.getNamespaceURI());
Derek DeMoro's avatar
Derek DeMoro committed
111
                pstmt.executeUpdate();
Matt Tucker's avatar
Matt Tucker committed
112 113 114 115 116
            }
            catch (Exception e) {
                Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
            }
            finally {
117 118 119 120
                try { if (pstmt != null) { pstmt.close(); } }
                catch (Exception e) { Log.error(e); }
                try { if (con != null) { con.close(); } }
                catch (Exception e) { Log.error(e); }
Matt Tucker's avatar
Matt Tucker committed
121 122 123 124
            }
        }
    }

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    /**
     * Returns the data stored under a key corresponding to the name and namespace
     * of the given element. The Element must be in the form:<p>
     *
     * <code>&lt;name xmlns='namespace'/&gt;</code><p>
     *
     * If no data is currently stored under the given key, an empty element will be
     * returned.
     *
     * @param data an XML document who's element name and namespace is used to
     *      match previously stored private data.
     * @param username the username of the account where private data is being stored.
     * @return the data stored under the given key or the data element.
     */
    public Element get(String username, Element data) {
Matt Tucker's avatar
Matt Tucker committed
140 141 142 143 144 145 146
        data.clearContent();
        if (enabled) {
            Connection con = null;
            PreparedStatement pstmt = null;
            try {
                con = DbConnectionManager.getConnection();
                pstmt = con.prepareStatement(LOAD_PRIVATE);
147
                pstmt.setString(1, username);
Matt Tucker's avatar
Matt Tucker committed
148 149 150 151 152
                pstmt.setString(2, data.getNamespaceURI());
                ResultSet rs = pstmt.executeQuery();
                if (rs.next()) {
                    StringReader reader = new StringReader(rs.getString(1).trim());
                    Document doc = XPPReader.parseDocument(reader, this.getClass());
153
                    data = doc.getRootElement();
Matt Tucker's avatar
Matt Tucker committed
154
                }
155
                rs.close();
Matt Tucker's avatar
Matt Tucker committed
156 157 158 159 160
            }
            catch (Exception e) {
                Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
            }
            finally {
161 162 163 164
                try { if (pstmt != null) { pstmt.close(); } }
                catch (Exception e) { Log.error(e); }
                try { if (con != null) { con.close(); } }
                catch (Exception e) { Log.error(e); }
Matt Tucker's avatar
Matt Tucker committed
165 166 167 168
            }
        }
        return data;
    }
169
}