diff options
Diffstat (limited to 'src/com/isode/stroke/roster')
-rw-r--r-- | src/com/isode/stroke/roster/GetRosterRequest.java | 32 | ||||
-rw-r--r-- | src/com/isode/stroke/roster/RosterMemoryStorage.java | 22 | ||||
-rw-r--r-- | src/com/isode/stroke/roster/RosterPushResponder.java | 34 | ||||
-rw-r--r-- | src/com/isode/stroke/roster/RosterStorage.java | 13 | ||||
-rw-r--r-- | src/com/isode/stroke/roster/SetRosterRequest.java | 36 | ||||
-rw-r--r-- | src/com/isode/stroke/roster/XMPPRoster.java | 80 | ||||
-rw-r--r-- | src/com/isode/stroke/roster/XMPPRosterController.java | 116 | ||||
-rw-r--r-- | src/com/isode/stroke/roster/XMPPRosterImpl.java | 95 | ||||
-rw-r--r-- | src/com/isode/stroke/roster/XMPPRosterItem.java | 48 |
9 files changed, 476 insertions, 0 deletions
diff --git a/src/com/isode/stroke/roster/GetRosterRequest.java b/src/com/isode/stroke/roster/GetRosterRequest.java new file mode 100644 index 0000000..18e885b --- /dev/null +++ b/src/com/isode/stroke/roster/GetRosterRequest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2015, Isode Limited, London, England. + * All rights reserved. + */ + +package com.isode.stroke.roster; + +import com.isode.stroke.elements.IQ.Type; +import com.isode.stroke.elements.RosterPayload; +import com.isode.stroke.jid.JID; +import com.isode.stroke.queries.GenericRequest; +import com.isode.stroke.queries.IQRouter; + +public class GetRosterRequest extends GenericRequest<RosterPayload> { + public GetRosterRequest(JID target, IQRouter iqRouter) { + super(Type.Get, target, new RosterPayload(), iqRouter); + } + + public GetRosterRequest(IQRouter iqRouter) { + super(Type.Get, new JID(), new RosterPayload(), iqRouter); + } + + public static GetRosterRequest create(IQRouter router) { + return new GetRosterRequest(router); + } + + public static GetRosterRequest create(IQRouter router, String version) { + GetRosterRequest request = new GetRosterRequest(router); + request.getPayloadGeneric().setVersion(version); + return request; + } +} diff --git a/src/com/isode/stroke/roster/RosterMemoryStorage.java b/src/com/isode/stroke/roster/RosterMemoryStorage.java new file mode 100644 index 0000000..9c6d55f --- /dev/null +++ b/src/com/isode/stroke/roster/RosterMemoryStorage.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011-2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.roster; + +import com.isode.stroke.elements.RosterPayload; + +public class RosterMemoryStorage implements RosterStorage { + private RosterPayload roster = new RosterPayload(); + + @Override + public RosterPayload getRoster() { + return roster; + } + + @Override + public void setRoster(RosterPayload r) { + roster = r; + } + +} diff --git a/src/com/isode/stroke/roster/RosterPushResponder.java b/src/com/isode/stroke/roster/RosterPushResponder.java new file mode 100644 index 0000000..18b20b5 --- /dev/null +++ b/src/com/isode/stroke/roster/RosterPushResponder.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2010-2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.roster; + +import com.isode.stroke.elements.ErrorPayload; +import com.isode.stroke.elements.RosterPayload; +import com.isode.stroke.jid.JID; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.queries.SetResponder; +import com.isode.stroke.signals.Signal1; + +public class RosterPushResponder extends SetResponder<RosterPayload> { + + final Signal1<RosterPayload> onRosterReceived = new Signal1<RosterPayload>(); + + public RosterPushResponder(IQRouter router) { + super(new RosterPayload(), router); + } + + @Override + protected boolean handleSetRequest(JID from, JID to, String id, RosterPayload payload) { + if (getIQRouter().isAccountJID(from)) { + onRosterReceived.emit(payload); + sendResponse(from, id, new RosterPayload()); + } else { + sendError(from, id, ErrorPayload.Condition.NotAuthorized, ErrorPayload.Type.Cancel); + } + return true; + } + + +} diff --git a/src/com/isode/stroke/roster/RosterStorage.java b/src/com/isode/stroke/roster/RosterStorage.java new file mode 100644 index 0000000..df048cc --- /dev/null +++ b/src/com/isode/stroke/roster/RosterStorage.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2011-2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.roster; + +import com.isode.stroke.elements.RosterPayload; + +public interface RosterStorage { + + RosterPayload getRoster(); + void setRoster(RosterPayload roster); +} diff --git a/src/com/isode/stroke/roster/SetRosterRequest.java b/src/com/isode/stroke/roster/SetRosterRequest.java new file mode 100644 index 0000000..4d52676 --- /dev/null +++ b/src/com/isode/stroke/roster/SetRosterRequest.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2010-2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.roster; + +import com.isode.stroke.elements.ErrorPayload; +import com.isode.stroke.elements.IQ; +import com.isode.stroke.elements.Payload; +import com.isode.stroke.elements.RosterPayload; +import com.isode.stroke.jid.JID; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.queries.Request; +import com.isode.stroke.signals.Signal1; + +public class SetRosterRequest extends Request { + + static SetRosterRequest create(RosterPayload payload, IQRouter router) { + return new SetRosterRequest(new JID(), payload, router); + } + + static SetRosterRequest create(RosterPayload payload, final JID to, IQRouter router) { + return new SetRosterRequest(to, payload, router); + } + + private SetRosterRequest(final JID to, RosterPayload payload, IQRouter router) { + super(IQ.Type.Set, to, payload, router); + } + + public void handleResponse(Payload payload, ErrorPayload error) { + onResponse.emit(error); + } + + final Signal1<ErrorPayload> onResponse = new Signal1<ErrorPayload>(); + +} diff --git a/src/com/isode/stroke/roster/XMPPRoster.java b/src/com/isode/stroke/roster/XMPPRoster.java new file mode 100644 index 0000000..a2c3cd3 --- /dev/null +++ b/src/com/isode/stroke/roster/XMPPRoster.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010-2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.roster; + +import java.util.Set; +import java.util.Collection; + +import com.isode.stroke.elements.RosterItemPayload; +import com.isode.stroke.jid.JID; +import com.isode.stroke.signals.Signal; +import com.isode.stroke.signals.Signal1; +import com.isode.stroke.signals.Signal3; + +public abstract class XMPPRoster { + /** + * Checks whether the bare jid of the given jid is in the roster. + */ + public abstract boolean containsJID(final JID jid); + + /** + * Retrieves the subscription state for the given jid. + */ + public abstract RosterItemPayload.Subscription getSubscriptionStateForJID(final JID jid); + + /** + * Retrieves the stored roster name for the given jid. + */ + public abstract String getNameForJID(final JID jid); + + /** + * Returns the list of groups for the given JID. + */ + public abstract Collection<String> getGroupsForJID(final JID jid); + + /** + * Retrieve the items in the roster. + */ + public abstract Collection<XMPPRosterItem> getItems(); + + /** + * Retrieve the item with the given JID. + */ + public abstract XMPPRosterItem getItem(final JID jid); + + /** + * Retrieve the list of (existing) groups. + */ + public abstract Set<String> getGroups(); + + /** + * Emitted when the given JID is added to the roster. + */ + public final Signal1<JID> onJIDAdded = new Signal1<JID>(); + + /** + * Emitted when the given JID is removed from the roster. + */ + public final Signal1<JID> onJIDRemoved = new Signal1<JID>(); + + /** + * Emitted when the name or the groups of the roster item with the + * given JID changes. + */ + public final Signal3<JID, String, Collection<String>> onJIDUpdated = new Signal3<JID, String, Collection<String>>(); + + /** + * Emitted when the roster is reset (e.g. due to logging in/logging out). + * After this signal is emitted, the roster is empty. It will be repopulated through + * onJIDAdded and onJIDRemoved events. + */ + public final Signal onRosterCleared = new Signal(); + + /** + * Emitted after the last contact of the initial roster request response + * was added. + */ + public final Signal onInitialRosterPopulated = new Signal(); +} diff --git a/src/com/isode/stroke/roster/XMPPRosterController.java b/src/com/isode/stroke/roster/XMPPRosterController.java new file mode 100644 index 0000000..6ad03f2 --- /dev/null +++ b/src/com/isode/stroke/roster/XMPPRosterController.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2010-2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.roster; + +import java.util.Collection; + +import com.isode.stroke.elements.ErrorPayload; +import com.isode.stroke.elements.RosterItemPayload; +import com.isode.stroke.elements.RosterPayload; +import com.isode.stroke.roster.GetRosterRequest; +import com.isode.stroke.queries.IQRouter; +import com.isode.stroke.signals.Slot1; +import com.isode.stroke.signals.Slot2; + +public class XMPPRosterController { + private IQRouter iqRouter_; + private RosterPushResponder rosterPushResponder_; + private XMPPRosterImpl xmppRoster_; + private RosterStorage rosterStorage_; + private boolean useVersioning; + + + /** + * The controller does not gain ownership of these parameters. + */ + public XMPPRosterController(IQRouter iqRouter, XMPPRosterImpl xmppRoster, RosterStorage rosterStorage) { + iqRouter_ = iqRouter; + rosterPushResponder_ = new RosterPushResponder(iqRouter); + xmppRoster_ = xmppRoster; + rosterStorage_ = rosterStorage; + useVersioning = false; + + rosterPushResponder_.onRosterReceived.connect(new Slot1<RosterPayload>() { + @Override + public void call(RosterPayload p1) { + handleRosterReceived(p1, false, new RosterPayload()); + } + }); + rosterPushResponder_.start(); + } + + public void delete() { + rosterPushResponder_.stop(); + } + + public void requestRoster() { + xmppRoster_.clear(); + + final RosterPayload storedRoster = rosterStorage_.getRoster(); + GetRosterRequest rosterRequest; + if (useVersioning) { + String version = ""; + if (storedRoster != null && storedRoster.getVersion() != null) { + version = storedRoster.getVersion(); + } + rosterRequest = GetRosterRequest.create(iqRouter_, version); + } + else { + rosterRequest = GetRosterRequest.create(iqRouter_); + } + + rosterRequest.onResponse.connect(new Slot2<RosterPayload, ErrorPayload>() { + @Override + public void call(RosterPayload p1, ErrorPayload p2) { + handleRosterReceived(p1, true, storedRoster); + } + }); + rosterRequest.send(); + } + + void handleRosterReceived(RosterPayload rosterPayload, boolean initial, RosterPayload previousRoster) { + if (rosterPayload != null) { + for (final RosterItemPayload item : rosterPayload.getItems()) { + //Don't worry about the updated case, the XMPPRoster sorts that out. + if (item.getSubscription() == RosterItemPayload.Subscription.Remove) { + xmppRoster_.removeContact(item.getJID()); + } else { + xmppRoster_.addContact(item.getJID(), item.getName(), item.getGroups(), item.getSubscription()); + } + } + } + else if (previousRoster != null) { + // The cached version hasn't changed; emit all items + for (final RosterItemPayload item : previousRoster.getItems()) { + if (item.getSubscription() != RosterItemPayload.Subscription.Remove) { + xmppRoster_.addContact(item.getJID(), item.getName(), item.getGroups(), item.getSubscription()); + } + else { + System.err.println("ERROR: Stored invalid roster item"); + } + } + } + if (initial) { + xmppRoster_.onInitialRosterPopulated.emit(); + } + if (rosterPayload != null && rosterPayload.getVersion() != null && useVersioning) { + saveRoster(rosterPayload.getVersion()); + } + } + + void saveRoster(final String version) { + Collection<XMPPRosterItem> items = xmppRoster_.getItems(); + RosterPayload roster = new RosterPayload(); + roster.setVersion(version); + for (final XMPPRosterItem item : items) { + roster.addItem(new RosterItemPayload(item.getJID(), item.getName(), item.getSubscription(), item.getGroups())); + } + rosterStorage_.setRoster(roster); + } + + public void setUseVersioning(boolean b) { + useVersioning = b; + } +} diff --git a/src/com/isode/stroke/roster/XMPPRosterImpl.java b/src/com/isode/stroke/roster/XMPPRosterImpl.java new file mode 100644 index 0000000..61d0bc3 --- /dev/null +++ b/src/com/isode/stroke/roster/XMPPRosterImpl.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010-2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.roster; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.isode.stroke.elements.RosterItemPayload; +import com.isode.stroke.elements.RosterItemPayload.Subscription; +import com.isode.stroke.jid.JID; + +public class XMPPRosterImpl extends XMPPRoster { + + private Map<JID, XMPPRosterItem> entries_ = new HashMap<JID, XMPPRosterItem>(); + + void addContact(final JID jid, final String name, final Collection<String> groups, RosterItemPayload.Subscription subscription) { + JID bareJID = jid.toBare(); + XMPPRosterItem item = entries_.get(bareJID); + + if (item != null) { + String oldName = item.getName(); + Collection<String> oldGroups = item.getGroups(); + entries_.put(bareJID, new XMPPRosterItem(jid, name, groups, subscription)); + onJIDUpdated.emit(bareJID, oldName, oldGroups); + } + else { + entries_.put(bareJID, new XMPPRosterItem(jid, name, groups, subscription)); + onJIDAdded.emit(bareJID); + } + } + + void removeContact(final JID jid) { + entries_.remove(jid.toBare()); + onJIDRemoved.emit(jid); + } + + void clear() { + entries_.clear(); + onRosterCleared.emit(); + } + + @Override + public boolean containsJID(JID jid) { + return entries_.containsKey(jid); + } + + @Override + public Subscription getSubscriptionStateForJID(JID jid) { + XMPPRosterItem item = entries_.get(jid.toBare()); + if (item != null) return item.getSubscription(); + return RosterItemPayload.Subscription.None; + } + + @Override + public String getNameForJID(JID jid) { + XMPPRosterItem item = entries_.get(jid.toBare()); + if (item != null) return item.getName(); + return ""; + } + + @Override + public Collection<String> getGroupsForJID(JID jid) { + XMPPRosterItem item = entries_.get(jid.toBare()); + if (item != null) return item.getGroups(); + return new ArrayList<String>(); + } + + @Override + public Collection<XMPPRosterItem> getItems() { + return entries_.values(); + } + + @Override + public XMPPRosterItem getItem(JID jid) { + XMPPRosterItem item = entries_.get(jid.toBare()); + if (item != null) return item; + + return null; + } + + @Override + public Set<String> getGroups() { + Set<String> groups = new HashSet<String>(); + for (XMPPRosterItem item : entries_.values()) + groups.addAll(item.getGroups()); + return groups; + } + +} diff --git a/src/com/isode/stroke/roster/XMPPRosterItem.java b/src/com/isode/stroke/roster/XMPPRosterItem.java new file mode 100644 index 0000000..1412f83 --- /dev/null +++ b/src/com/isode/stroke/roster/XMPPRosterItem.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2015, Isode Limited, London, England. + * All rights reserved. + */ +package com.isode.stroke.roster; + +import java.util.Collection; + +import com.isode.stroke.elements.RosterItemPayload; +import com.isode.stroke.jid.JID; + +public class XMPPRosterItem { + private JID jid; + private String name; + private Collection<String> groups; + private RosterItemPayload.Subscription subscription; + + public XMPPRosterItem(final JID jid, final String name, final Collection<String> groups, RosterItemPayload.Subscription subscription) { + this.jid = jid; + this.name = name; + this.groups = groups; + this.subscription = subscription; + } + + public final JID getJID() { + return jid; + } + + public final String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public final Collection<String> getGroups() { + return groups; + } + + public void setGroups(final Collection<String> groups) { + this.groups = groups; + } + + public RosterItemPayload.Subscription getSubscription() { + return subscription; + } +} |