IQvCardHandler.java 6.59 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

Matt Tucker's avatar
Matt Tucker committed
12 13 14 15 16 17 18 19 20 21
package org.jivesoftware.messenger.handler;

import org.jivesoftware.messenger.*;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.UserNotFoundException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
Matt Tucker's avatar
Matt Tucker committed
22 23
import java.util.Collection;

Matt Tucker's avatar
Matt Tucker committed
24 25
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
26
import org.dom4j.QName;
Matt Tucker's avatar
Matt Tucker committed
27 28 29
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.PacketError;
Matt Tucker's avatar
Matt Tucker committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

/**
 * Implements the TYPE_IQ vcard-temp protocol. Clients
 * use this protocol to set and retrieve the vCard information
 * associated with someone's account.
 * <p/>
 * A 'get' query retrieves the vcard for the addressee.
 * A 'set' query sets the vcard information for the sender's account.
 * <p/>
 * Currently an empty implementation to allow usage with normal
 * clients. Future implementation needed.
 * <p/>
 * <h2>Assumptions</h2>
 * This handler assumes that the request is addressed to the server.
 * An appropriate TYPE_IQ tag matcher should be placed in front of this
 * one to route TYPE_IQ requests not addressed to the server to
 * another channel (probably for direct delivery to the recipient).
 * <p/>
 * <h2>Warning</h2>
 * There should be a way of determining whether a session has
 * authorization to access this feature. I'm not sure it is a good
 * idea to do authorization in each handler. It would be nice if
 * the framework could assert authorization policies across channels.
 * <p/>
 * <h2>Warning</h2>
 * I have noticed incompatibility between vCard XML used by Exodus and Psi.
 * There is a new vCard standard going through the JSF JEP process. We might
 * want to start either standardizing on clients (probably the most practical),
 * sending notices for non-conformance (useful),
 * or attempting to translate between client versions (not likely).
 *
 * @author Iain Shigeoka
 */
public class IQvCardHandler extends IQHandler {

    private IQHandlerInfo info;
66
    private UserManager userManager;
Matt Tucker's avatar
Matt Tucker committed
67 68 69

    public IQvCardHandler() {
        super("XMPP vCard Handler");
70
        info = new IQHandlerInfo("vCard", "vcard-temp");
Matt Tucker's avatar
Matt Tucker committed
71 72
    }

73
    public IQ handleIQ(IQ packet) throws UnauthorizedException, PacketException {
Matt Tucker's avatar
Matt Tucker committed
74 75
        IQ result = null;
        try {
Matt Tucker's avatar
Matt Tucker committed
76 77 78
            IQ.Type type = packet.getType();
            if (type.equals(IQ.Type.set)) {
                User user = userManager.getUser(packet.getFrom().getNode());
Matt Tucker's avatar
Matt Tucker committed
79
                // Proper format
Matt Tucker's avatar
Matt Tucker committed
80
                Element vcard = packet.getChildElement();
Matt Tucker's avatar
Matt Tucker committed
81 82 83 84
                if (vcard != null) {
                    List nameStack = new ArrayList(5);
                    readVCard(vcard, nameStack, user);
                }
Matt Tucker's avatar
Matt Tucker committed
85
                result = IQ.createResultIQ(packet);
Matt Tucker's avatar
Matt Tucker committed
86
            }
Matt Tucker's avatar
Matt Tucker committed
87
            else if (type.equals(IQ.Type.get)) {
88 89 90 91 92 93
                JID recipient = packet.getTo();
                // If no TO was specified then get the vCard of the sender of the packet
                if (recipient == null) {
                    recipient = packet.getFrom();
                }

Matt Tucker's avatar
Matt Tucker committed
94
                result = IQ.createResultIQ(packet);
Matt Tucker's avatar
Matt Tucker committed
95

96
                Element vcard = DocumentHelper.createElement(QName.get("vCard", "vcard-temp"));
Matt Tucker's avatar
Matt Tucker committed
97
                result.setChildElement(vcard);
98
                // Only try to get the vCard values of non-anonymous users 
Gaston Dombiak's avatar
Gaston Dombiak committed
99
                if (recipient != null && recipient.getNode() != null) {
100 101 102 103 104 105 106 107
                    User user = userManager.getUser(recipient.getNode());
                    VCardManager vManager = VCardManager.getInstance();
                    Collection<String> names = vManager.getVCardPropertyNames(user.getUsername());
                    for (String name : names) {
                        String path = name.replace(':', '/');
                        Element node = DocumentHelper.makeElement(vcard, path);
                        node.setText(vManager.getVCardProperty(user.getUsername(), name));
                    }
Matt Tucker's avatar
Matt Tucker committed
108 109 110
                }
            }
            else {
Matt Tucker's avatar
Matt Tucker committed
111
                result = IQ.createResultIQ(packet);
112
                result.setChildElement(packet.getChildElement().createCopy());
Matt Tucker's avatar
Matt Tucker committed
113
                result.setError(PacketError.Condition.not_acceptable);
Matt Tucker's avatar
Matt Tucker committed
114 115 116
            }
        }
        catch (UserNotFoundException e) {
Matt Tucker's avatar
Matt Tucker committed
117
            result = IQ.createResultIQ(packet);
118 119
            result.setChildElement(packet.getChildElement().createCopy());
            result.setError(PacketError.Condition.item_not_found);
Matt Tucker's avatar
Matt Tucker committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
        }
        return result;
    }

    /**
     * We need to build names from heirarchical position in the DOM tree.
     *
     * @param element   The element to interrogate for text nodes
     * @param nameStack The current name of the vcard property (list as a stack)
     * @param user      the user getting their vcard set
     */
    private void readVCard(Element element, List nameStack, User user) throws UnauthorizedException {
        Iterator children = element.elementIterator();
        while (children.hasNext()) {
            Element child = (Element)children.next();
            nameStack.add(child.getName());
            String value = child.getTextTrim();
            if (value != null) {
                if (!"".equals(value)) {
Matt Tucker's avatar
Matt Tucker committed
139 140
                    VCardManager.getInstance().setVCardProperty(user.getUsername(),
                            createName(nameStack), value);
Matt Tucker's avatar
Matt Tucker committed
141
                }
142 143 144 145
                else {
                    VCardManager.getInstance().deleteVCardProperty(user.getUsername(),
                            createName(nameStack));
                }
Matt Tucker's avatar
Matt Tucker committed
146 147 148 149 150 151 152 153 154 155 156 157 158
            }
            readVCard(child, nameStack, user);
            nameStack.remove(nameStack.size() - 1);
        }
    }

    /**
     * Generate a name for the given name stack values
     *
     * @param nameStack
     * @return The name concatenating the values with the ':' character
     */
    private String createName(List nameStack) {
159
        StringBuilder buf = new StringBuilder();
Matt Tucker's avatar
Matt Tucker committed
160 161 162 163 164 165 166 167 168 169
        Iterator iter = nameStack.iterator();
        while (iter.hasNext()) {
            if (buf.length() > 0) {
                buf.append(':');
            }
            buf.append(iter.next());
        }
        return buf.toString();
    }

170 171 172
    public void initialize(XMPPServer server) {
        super.initialize(server);
        userManager = server.getUserManager();
Matt Tucker's avatar
Matt Tucker committed
173 174 175 176 177 178
    }

    public IQHandlerInfo getInfo() {
        return info;
    }
}