/*
* Copyright (C) 1999-2008 Jive Software. All rights reserved.
*
* 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.
*/
package org.jivesoftware.openfire.plugin.spark;
import java.util.Collection;
import java.util.Iterator;
import org.dom4j.Element;
import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.openfire.interceptor.InterceptorManager;
import org.jivesoftware.openfire.interceptor.PacketInterceptor;
import org.jivesoftware.openfire.interceptor.PacketRejectedException;
import org.jivesoftware.openfire.session.Session;
import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.IQ;
import org.xmpp.packet.JID;
import org.xmpp.packet.Packet;
/**
* Intercepts Bookmark Storage requests and appends all server based Bookmarks to
* the result.
*
* @author Derek DeMoro
*/
public class BookmarkInterceptor implements PacketInterceptor {
private static final Logger Log = LoggerFactory.getLogger(BookmarkInterceptor.class);
/**
* Initializes the BookmarkInterceptor and needed Server instances.
*/
public BookmarkInterceptor() {
}
public void interceptPacket(Packet packet, Session session, boolean incoming, boolean processed) throws PacketRejectedException {
if (!processed && packet instanceof IQ && !incoming) {
// Check for the Bookmark Storage element and hand off to the Bookmark engine.
IQ iq = (IQ)packet;
Element childElement = iq.getChildElement();
if (childElement == null || iq.getType() != IQ.Type.result) {
return;
}
String namespace = childElement.getNamespaceURI();
if ("jabber:iq:private".equals(namespace)) {
// In private data, when a user is attempting to retrieve bookmark
// information, there will be a storage:bookmarks namespace.
Element storageElement = childElement.element("storage");
if (storageElement == null) {
return;
}
namespace = storageElement.getNamespaceURI();
if ("storage:bookmarks".equals(namespace)) {
// Append Server defined bookmarks for user.
JID toJID = iq.getTo();
addBookmarks(toJID, storageElement);
}
}
}
}
/**
* Add this interceptor to the interceptor manager.
*/
public void start() {
InterceptorManager.getInstance().addInterceptor(this);
}
/**
* Remove this interceptor from the interceptor manager.
*/
public void stop() {
InterceptorManager.getInstance().removeInterceptor(this);
}
/**
* Adds all server defined bookmarks to the users requested
* bookmarks.
*
* @param jid the jid of the user requesting the bookmark(s)
* @param storageElement the JEP-0048 compliant storage element.
*/
private void addBookmarks(JID jid, Element storageElement) {
final Collection<Bookmark> bookmarks = BookmarkManager.getBookmarks();
for (Bookmark bookmark : bookmarks) {
// Check to see if the bookmark should be appended for this
// particular user.
boolean addBookmarkForUser = bookmark.isGlobalBookmark() || isBookmarkForJID(jid, bookmark);
if (addBookmarkForUser) {
// Add bookmark element.
addBookmarkElement(jid, bookmark, storageElement);
}
}
}
/**
* True if the specified bookmark should be appended to the users list of
* bookmarks.
*
* @param jid the jid of the user.
* @param bookmark the bookmark.
* @return true if bookmark should be appended.
*/
private static boolean isBookmarkForJID(JID jid, Bookmark bookmark) {
String username = jid.getNode();
if (bookmark.getUsers().contains(username)) {
return true;
}
Collection<String> groups = bookmark.getGroups();
if (groups != null && !groups.isEmpty()) {
GroupManager groupManager = GroupManager.getInstance();
for (String groupName : groups) {
try {
Group group = groupManager.getGroup(groupName);
if (group.isUser(jid.getNode())) {
return true;
}
}
catch (GroupNotFoundException e) {
Log.debug(e.getMessage(), e);
}
}
}
return false;
}
/**
* Adds a Bookmark to the users defined list of bookmarks.
*
* @param jid the users jid.
* @param bookmark the bookmark to be added.
* @param element the storage element to append to.
*/
private void addBookmarkElement(JID jid, Bookmark bookmark, Element element) {
final UserManager userManager = UserManager.getInstance();
try {
userManager.getUser(jid.getNode());
}
catch (UserNotFoundException e) {
return;
}
// If this is a URL Bookmark, check to make sure we
// do not add duplicate bookmarks.
if (bookmark.getType() == Bookmark.Type.url) {
Element urlBookmarkElement = urlExists(element, bookmark.getValue());
if (urlBookmarkElement == null) {
urlBookmarkElement = element.addElement("url");
urlBookmarkElement.addAttribute("name", bookmark.getName());
urlBookmarkElement.addAttribute("url", bookmark.getValue());
// Add an RSS attribute to the bookmark if it's defined. RSS isn't an
// official part of the Bookmark JEP, but we define it as a logical
// extension.
boolean rss = Boolean.valueOf(bookmark.getProperty("rss"));
if (rss) {
urlBookmarkElement.addAttribute("rss", Boolean.toString(rss));
}
}
appendSharedElement(urlBookmarkElement);
}
// Otherwise it's a conference bookmark.
else {
try {
Element conferenceElement = conferenceExists(element, bookmark.getValue());
// If the conference bookmark does not exist, add it to the current
// reply.
if (conferenceElement == null) {
conferenceElement = element.addElement("conference");
conferenceElement.addAttribute("name", bookmark.getName());
boolean autojoin = Boolean.valueOf(bookmark.getProperty("autojoin"));
conferenceElement.addAttribute("autojoin", Boolean.toString(autojoin));
conferenceElement.addAttribute("jid", bookmark.getValue());
boolean nameasnick = Boolean.valueOf(bookmark.getProperty("nameasnick"));
if (nameasnick) {
User currentUser = userManager.getUser(jid.getNode());
Element nick = conferenceElement.addElement("nick");
nick.addText(currentUser.getName());
}
}
appendSharedElement(conferenceElement);
}
catch (Exception e) {
Log.error(e.getMessage(), e);
}
}
}
/**
* Adds the shared namespace element to indicate to clients that this bookmark is a shared bookmark.
*
* @param bookmarkElement the bookmark to add the shared element to.
*/
private static void appendSharedElement(Element bookmarkElement) {
bookmarkElement.addElement("shared_bookmark", "http://jivesoftware.com/jeps/bookmarks");
}
/**
* Checks if the bookmark has already been defined in the users private storage.
*
* @param element the private storage element.
* @param url the url to search for.
* @return true if the bookmark already exists.
*/
private static Element urlExists(Element element, String url) {
// Iterate through current elements to see if this url already exists.
// If one does not exist, then add the bookmark.
final Iterator<Element> urlBookmarks = element.elementIterator("url");
while (urlBookmarks.hasNext()) {
Element urlElement = urlBookmarks.next();
String urlValue = urlElement.attributeValue("url");
if (urlValue.equalsIgnoreCase(url)) {
return urlElement;
}
}
return null;
}
/**
* Checks if the conference bookmark has already been defined in the users private storage.
*
* @param element the private storage element.
* @param roomJID the JID of the room to find.
* @return true if the bookmark exists.
*/
private Element conferenceExists(Element element, String roomJID) {
// Iterate through current elements to see if the conference bookmark
// already exists.
final Iterator<Element> conferences = element.elementIterator("conference");
while (conferences.hasNext()) {
final Element conferenceElement = conferences.next();
String jidValue = conferenceElement.attributeValue("jid");
if (jidValue != null && roomJID != null && jidValue.equalsIgnoreCase(roomJID)) {
return conferenceElement;
}
}
return null;
}
}