summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/isode/stroke/roster')
-rw-r--r--src/com/isode/stroke/roster/GetRosterRequest.java32
-rw-r--r--src/com/isode/stroke/roster/RosterMemoryStorage.java22
-rw-r--r--src/com/isode/stroke/roster/RosterPushResponder.java34
-rw-r--r--src/com/isode/stroke/roster/RosterStorage.java13
-rw-r--r--src/com/isode/stroke/roster/SetRosterRequest.java36
-rw-r--r--src/com/isode/stroke/roster/XMPPRoster.java80
-rw-r--r--src/com/isode/stroke/roster/XMPPRosterController.java116
-rw-r--r--src/com/isode/stroke/roster/XMPPRosterImpl.java95
-rw-r--r--src/com/isode/stroke/roster/XMPPRosterItem.java48
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;
+ }
+}