summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/isode')
-rw-r--r--src/com/isode/stroke/base/SafeByteArray.java9
-rw-r--r--src/com/isode/stroke/client/Client.java132
-rw-r--r--src/com/isode/stroke/client/CoreClient.java14
-rw-r--r--src/com/isode/stroke/client/MemoryStorages.java49
-rw-r--r--src/com/isode/stroke/client/NickManager.java14
-rw-r--r--src/com/isode/stroke/client/NickManagerImpl.java62
-rw-r--r--src/com/isode/stroke/client/NickResolver.java98
-rw-r--r--src/com/isode/stroke/client/Storages.java18
-rw-r--r--src/com/isode/stroke/crypto/CryptoProvider.java35
-rw-r--r--src/com/isode/stroke/crypto/Hash.java15
-rw-r--r--src/com/isode/stroke/disco/CapsInfoGenerator.java73
-rw-r--r--src/com/isode/stroke/disco/CapsManager.java144
-rw-r--r--src/com/isode/stroke/disco/CapsMemoryStorage.java26
-rw-r--r--src/com/isode/stroke/disco/CapsProvider.java14
-rw-r--r--src/com/isode/stroke/disco/CapsStorage.java12
-rw-r--r--src/com/isode/stroke/disco/ClientDiscoManager.java76
-rw-r--r--src/com/isode/stroke/disco/DiscoInfoResponder.java54
-rw-r--r--src/com/isode/stroke/disco/EntityCapsManager.java93
-rw-r--r--src/com/isode/stroke/disco/EntityCapsProvider.java21
-rw-r--r--src/com/isode/stroke/disco/GetDiscoInfoRequest.java32
-rw-r--r--src/com/isode/stroke/elements/ChatState.java4
-rw-r--r--src/com/isode/stroke/elements/Idle.java29
-rw-r--r--src/com/isode/stroke/elements/MUCInvitationPayload.java24
-rw-r--r--src/com/isode/stroke/elements/Presence.java10
-rw-r--r--src/com/isode/stroke/elements/PrivateStorage.java10
-rw-r--r--src/com/isode/stroke/elements/Replace.java31
-rw-r--r--src/com/isode/stroke/elements/RosterItemPayload.java14
-rw-r--r--src/com/isode/stroke/elements/RosterPayload.java18
-rw-r--r--src/com/isode/stroke/elements/SecurityLabel.java62
-rw-r--r--src/com/isode/stroke/elements/SecurityLabelsCatalog.java83
-rw-r--r--src/com/isode/stroke/elements/Stanza.java18
-rw-r--r--src/com/isode/stroke/elements/StatusShow.java33
-rw-r--r--src/com/isode/stroke/elements/VCard.java311
-rw-r--r--src/com/isode/stroke/muc/MUC.java33
-rw-r--r--src/com/isode/stroke/muc/MUCBookmarkManager.java8
-rw-r--r--src/com/isode/stroke/network/JavaConnection.java3
-rw-r--r--src/com/isode/stroke/network/JavaCryptoProvider.java90
-rw-r--r--src/com/isode/stroke/network/JavaNetworkFactories.java10
-rw-r--r--src/com/isode/stroke/network/NetworkFactories.java8
-rw-r--r--src/com/isode/stroke/parser/XMPPParser.java8
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/ForwardedParser.java15
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java27
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/IdleParser.java44
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/MAMQueryParser.java11
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/MAMResultParser.java9
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/MUCInvitationPayloadParser.java9
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/PrivateStorageParser.java57
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/PrivateStorageParserFactory.java29
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/PubSubEventParser.java18
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/PubSubOwnerPubSubParser.java (renamed from src/com/isode/stroke/parser/PubSubOwnerPubSubParser.java)36
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/PubSubParser.java34
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/ReplaceParser.java39
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/ResultSetParser.java21
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/StatusParser.java35
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/StatusShowParser.java49
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/StorageParser.java76
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/SubjectParser.java35
-rw-r--r--src/com/isode/stroke/parser/payloadparsers/VCardParser.java301
-rw-r--r--src/com/isode/stroke/presence/PayloadAddingPresenceSender.java62
-rw-r--r--src/com/isode/stroke/presence/PresenceOracle.java127
-rw-r--r--src/com/isode/stroke/presence/SubscriptionManager.java68
-rw-r--r--src/com/isode/stroke/queries/GetRosterRequest.java24
-rw-r--r--src/com/isode/stroke/queries/IQRouter.java18
-rw-r--r--src/com/isode/stroke/queries/SetResponder.java20
-rw-r--r--src/com/isode/stroke/queries/requests/GetSecurityLabelsCatalogRequest.java22
-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
-rw-r--r--src/com/isode/stroke/serializer/PresenceSerializer.java7
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/CapsInfoSerializer.java4
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/FullPayloadSerializerCollection.java18
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/IdleSerializer.java27
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/MUCInvitationPayloadSerializer.java9
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/PrivateStorageSerializer.java39
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/ReplaceSerializer.java25
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/StatusSerializer.java23
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/StatusShowSerializer.java35
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/StorageSerializer.java51
-rw-r--r--src/com/isode/stroke/serializer/payloadserializers/VCardSerializer.java254
-rw-r--r--src/com/isode/stroke/serializer/xml/XMLTextNode.java8
-rw-r--r--src/com/isode/stroke/signals/Signal7.java66
-rw-r--r--src/com/isode/stroke/signals/Slot7.java30
-rw-r--r--src/com/isode/stroke/stringcodecs/Base64.java11
-rw-r--r--src/com/isode/stroke/vcards/GetVCardRequest.java22
-rw-r--r--src/com/isode/stroke/vcards/SetVCardRequest.java23
-rw-r--r--src/com/isode/stroke/vcards/VCardManager.java115
-rw-r--r--src/com/isode/stroke/vcards/VCardMemoryStorage.java31
-rw-r--r--src/com/isode/stroke/vcards/VCardStorage.java33
94 files changed, 4039 insertions, 252 deletions
diff --git a/src/com/isode/stroke/base/SafeByteArray.java b/src/com/isode/stroke/base/SafeByteArray.java
new file mode 100644
index 0000000..9f91afb
--- /dev/null
+++ b/src/com/isode/stroke/base/SafeByteArray.java
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2011-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.base;
+
+public class SafeByteArray {
+
+}
diff --git a/src/com/isode/stroke/client/Client.java b/src/com/isode/stroke/client/Client.java
index 4f5d6c7..0ea6ad7 100644
--- a/src/com/isode/stroke/client/Client.java
+++ b/src/com/isode/stroke/client/Client.java
@@ -1,22 +1,31 @@
/*
- * Copyright (c) 2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.client;
+import com.isode.stroke.disco.CapsManager;
+import com.isode.stroke.disco.ClientDiscoManager;
+import com.isode.stroke.disco.EntityCapsManager;
+import com.isode.stroke.disco.EntityCapsProvider;
+import com.isode.stroke.elements.Presence;
import com.isode.stroke.jid.JID;
import com.isode.stroke.muc.MUCManager;
import com.isode.stroke.muc.MUCRegistry;
import com.isode.stroke.network.NetworkFactories;
import com.isode.stroke.presence.DirectedPresenceSender;
+import com.isode.stroke.presence.PresenceOracle;
+import com.isode.stroke.presence.PresenceSender;
import com.isode.stroke.presence.StanzaChannelPresenceSender;
+import com.isode.stroke.presence.SubscriptionManager;
import com.isode.stroke.pubsub.PubSubManager;
import com.isode.stroke.pubsub.PubSubManagerImpl;
import com.isode.stroke.queries.responders.SoftwareVersionResponder;
+import com.isode.stroke.roster.XMPPRoster;
+import com.isode.stroke.roster.XMPPRosterController;
+import com.isode.stroke.roster.XMPPRosterImpl;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.vcards.VCardManager;
/**
* Provides the core functionality for writing XMPP client software.
@@ -32,7 +41,21 @@ public class Client extends CoreClient {
private final DirectedPresenceSender directedPresenceSender; //NOPMD, this is not better as a local variable
private final StanzaChannelPresenceSender stanzaChannelPresenceSender; //NOPMD, this is not better as a local variable
private final SoftwareVersionResponder softwareVersionResponder;
- private final PubSubManager pubSubManager;
+ private final PubSubManager pubSubManager;
+ private final XMPPRosterImpl roster;
+ private final XMPPRosterController rosterController;
+ private final PresenceOracle presenceOracle;
+ private final Storages storages;
+ private final MemoryStorages memoryStorages;
+ private final VCardManager vcardManager;
+ private final CapsManager capsManager;
+ private final EntityCapsManager entityCapsManager;
+ private final NickManager nickManager;
+ private final NickResolver nickResolver;
+ private final SubscriptionManager subscriptionManager;
+ private final ClientDiscoManager discoManager;
+
+ final Signal1<Presence> onPresenceChange = new Signal1<Presence>();
/**
* Constructor.
@@ -50,18 +73,42 @@ public class Client extends CoreClient {
* @param networkFactories An implementation of network interaction, must
* not be null.
*/
- public Client(final JID jid, final String password, final NetworkFactories networkFactories) {
+ public Client(final JID jid, final String password, final NetworkFactories networkFactories, Storages storages) {
super(jid, password, networkFactories);
+
+ this.storages = storages;
+ memoryStorages = new MemoryStorages(networkFactories.getCryptoProvider());
+
+ softwareVersionResponder = new SoftwareVersionResponder(getIQRouter());
+ softwareVersionResponder.start();
+
+ roster = new XMPPRosterImpl();
+ rosterController = new XMPPRosterController(getIQRouter(), roster, getStorages().getRosterStorage());
+
+ subscriptionManager = new SubscriptionManager(getStanzaChannel());
+
+ presenceOracle = new PresenceOracle(getStanzaChannel());
+ presenceOracle.onPresenceChange.connect(onPresenceChange);
+
stanzaChannelPresenceSender = new StanzaChannelPresenceSender(getStanzaChannel());
directedPresenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender);
+ discoManager = new ClientDiscoManager(getIQRouter(), directedPresenceSender, networkFactories.getCryptoProvider());
mucRegistry = new MUCRegistry();
mucManager = new MUCManager(getStanzaChannel(), getIQRouter(), directedPresenceSender, mucRegistry);
- softwareVersionResponder = new SoftwareVersionResponder(getIQRouter());
- softwareVersionResponder.start();
-
- pubSubManager = new PubSubManagerImpl(getStanzaChannel(), getIQRouter());
+ vcardManager = new VCardManager(jid, getIQRouter(), getStorages().getVCardStorage());
+ capsManager = new CapsManager(getStorages().getCapsStorage(), getStanzaChannel(), getIQRouter(), networkFactories.getCryptoProvider());
+ entityCapsManager = new EntityCapsManager(capsManager, getStanzaChannel());
+
+ nickManager = new NickManagerImpl(jid.toBare(), vcardManager);
+ nickResolver = new NickResolver(jid.toBare(), roster, vcardManager, mucRegistry);
+
+ pubSubManager = new PubSubManagerImpl(getStanzaChannel(), getIQRouter());
+ }
+
+ public Client(final JID jid, final String password, final NetworkFactories networkFactories) {
+ this(jid, password, networkFactories, null);
}
/**
@@ -88,6 +135,10 @@ public class Client extends CoreClient {
return pubSubManager;
}
+ public XMPPRoster getRoster() {
+ return roster;
+ }
+
/**
* Sets the software version of the client.
*
@@ -96,4 +147,63 @@ public class Client extends CoreClient {
public void setSoftwareVersion(final String name, final String version, final String os) {
softwareVersionResponder.setVersion(name, version, os);
}
+
+
+ public void requestRoster() {
+ // FIXME: We should set this once when the session is finished, but there
+ // is currently no callback for this
+ if (getSession() != null) {
+ rosterController.setUseVersioning(getSession().getRosterVersioningSuported());
+ }
+ rosterController.requestRoster();
+ }
+
+ public Presence getLastPresence(final JID jid) {
+ return presenceOracle.getLastPresence(jid);
+ }
+
+ public Presence getHighestPriorityPresence(final JID bareJID) {
+ return presenceOracle.getHighestPriorityPresence(bareJID);
+ }
+
+ public PresenceOracle getPresenceOracle() {
+ return presenceOracle;
+ }
+
+ public NickManager getNickManager() {
+ return nickManager;
+ }
+
+ public NickResolver getNickResolver() {
+ return nickResolver;
+ }
+
+ public SubscriptionManager getSubscriptionManager() {
+ return subscriptionManager;
+ }
+
+ public ClientDiscoManager getDiscoManager() {
+ return discoManager;
+ }
+
+ public VCardManager getVCardManager() {
+ return vcardManager;
+ }
+
+ private Storages getStorages() {
+ if (storages != null) {
+ return storages;
+ }
+ return memoryStorages;
+ }
+
+ public PresenceSender getPresenceSender() {
+ return discoManager.getPresenceSender();
+ }
+
+ public EntityCapsProvider getEntityCapsProvider() {
+ return entityCapsManager;
+ }
+
+
}
diff --git a/src/com/isode/stroke/client/CoreClient.java b/src/com/isode/stroke/client/CoreClient.java
index a1c6d0a..84ea673 100644
--- a/src/com/isode/stroke/client/CoreClient.java
+++ b/src/com/isode/stroke/client/CoreClient.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2010-2014, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010-2014, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.client;
@@ -286,6 +282,10 @@ public class CoreClient {
connector_.stop();
}
}
+
+ public boolean isActive() {
+ return (session_ != null && !session_.isFinished()) || connector_ != null;
+ }
public void setCertificate(final CertificateWithKey certificate) {
certificate_ = certificate;
@@ -477,6 +477,10 @@ public class CoreClient {
}
connector_ = null;
}
+
+ protected ClientSession getSession() {
+ return session_;
+ }
private void resetSession() {
session_.onFinished.disconnectAll();
diff --git a/src/com/isode/stroke/client/MemoryStorages.java b/src/com/isode/stroke/client/MemoryStorages.java
new file mode 100644
index 0000000..9bd97a8
--- /dev/null
+++ b/src/com/isode/stroke/client/MemoryStorages.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.client;
+
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.disco.CapsMemoryStorage;
+import com.isode.stroke.disco.CapsStorage;
+import com.isode.stroke.roster.RosterMemoryStorage;
+import com.isode.stroke.roster.RosterStorage;
+import com.isode.stroke.vcards.VCardMemoryStorage;
+import com.isode.stroke.vcards.VCardStorage;
+
+public class MemoryStorages implements Storages {
+ private VCardStorage vcardStorage;
+// private AvatarStorage avatarStorage;
+ private CapsStorage capsStorage;
+ private RosterStorage rosterStorage;
+// private HistoryStorage historyStorage;
+
+ public MemoryStorages(CryptoProvider crypto) {
+ vcardStorage = new VCardMemoryStorage(crypto);
+ capsStorage = new CapsMemoryStorage();
+// avatarStorage = new AvatarMemoryStorage();
+ rosterStorage = new RosterMemoryStorage();
+// #ifdef SWIFT_EXPERIMENTAL_HISTORY
+// historyStorage = new SQLiteHistoryStorage(":memory:");
+// #else
+// historyStorage = NULL;
+
+ }
+
+ @Override
+ public VCardStorage getVCardStorage() {
+ return vcardStorage;
+ }
+
+ @Override
+ public RosterStorage getRosterStorage() {
+ return rosterStorage;
+ }
+
+ @Override
+ public CapsStorage getCapsStorage() {
+ return capsStorage;
+ }
+
+}
diff --git a/src/com/isode/stroke/client/NickManager.java b/src/com/isode/stroke/client/NickManager.java
new file mode 100644
index 0000000..b88dd61
--- /dev/null
+++ b/src/com/isode/stroke/client/NickManager.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.client;
+
+import com.isode.stroke.signals.Signal1;
+
+public abstract class NickManager {
+ public abstract String getOwnNick();
+ public abstract void setOwnNick(final String nick);
+
+ public final Signal1<String> onOwnNickChanged = new Signal1<String>();
+}
diff --git a/src/com/isode/stroke/client/NickManagerImpl.java b/src/com/isode/stroke/client/NickManagerImpl.java
new file mode 100644
index 0000000..17f9d6b
--- /dev/null
+++ b/src/com/isode/stroke/client/NickManagerImpl.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.client;
+
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Slot2;
+import com.isode.stroke.vcards.VCardManager;
+
+public class NickManagerImpl extends NickManager {
+ private JID ownJID = new JID();
+ private String ownNick = "";
+ private SignalConnection vCardChangedSignal;
+
+ NickManagerImpl(final JID ownJID, VCardManager vcardManager) {
+ this.ownJID = ownJID;
+
+ vCardChangedSignal = vcardManager.onVCardChanged.connect(new Slot2<JID, VCard>() {
+ @Override
+ public void call(JID p1, VCard p2) {
+ handleVCardReceived(p1, p2);
+ }
+ });
+
+ updateOwnNickFromVCard(vcardManager.getVCard(ownJID.toBare()));
+ }
+
+ public void delete() {
+ vCardChangedSignal.disconnect();
+ }
+
+ @Override
+ public String getOwnNick() {
+ return ownNick;
+ }
+
+ @Override
+ public void setOwnNick(final String nick) {
+ }
+
+ void handleVCardReceived(final JID jid, VCard vcard) {
+ if (jid.compare(ownJID, JID.CompareType.WithoutResource) != 0) {
+ return;
+ }
+ updateOwnNickFromVCard(vcard);
+ }
+
+ void updateOwnNickFromVCard(VCard vcard) {
+ String nick = null;
+ if (vcard != null && !vcard.getNickname().isEmpty()) {
+ nick = vcard.getNickname();
+ }
+ if (ownNick != nick && nick != null && !nick.equals(ownNick)) {
+ ownNick = nick;
+ onOwnNickChanged.emit(ownNick);
+ }
+ }
+
+}
diff --git a/src/com/isode/stroke/client/NickResolver.java b/src/com/isode/stroke/client/NickResolver.java
new file mode 100644
index 0000000..fe2b129
--- /dev/null
+++ b/src/com/isode/stroke/client/NickResolver.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.client;
+
+import java.util.Collection;
+
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.muc.MUCRegistry;
+import com.isode.stroke.roster.XMPPRoster;
+import com.isode.stroke.signals.Signal2;
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.Slot2;
+import com.isode.stroke.signals.Slot3;
+import com.isode.stroke.vcards.VCardManager;
+
+public class NickResolver {
+ private JID ownJID_;
+ private String ownNick_;
+ private XMPPRoster xmppRoster_;
+ private MUCRegistry mucRegistry_;
+ private VCardManager vcardManager_;
+
+ public final Signal2<JID, String> onNickChanged = new Signal2<JID, String>();
+
+ public NickResolver(final JID ownJID, XMPPRoster xmppRoster, VCardManager vcardManager, MUCRegistry mucRegistry) {
+ ownJID_ = ownJID;
+ xmppRoster_ = xmppRoster;
+ vcardManager_ = vcardManager;
+ if (vcardManager_ != null) {
+ vcardManager_.onVCardChanged.connect(new Slot2<JID, VCard>() {
+ @Override
+ public void call(JID p1, VCard p2) {
+ handleVCardReceived(p1, p2);
+ }
+ });
+ }
+ mucRegistry_ = mucRegistry;
+ xmppRoster_.onJIDUpdated.connect(new Slot3<JID, String, Collection<String>>() {
+ @Override
+ public void call(JID p1, String p2, Collection<String> p3) {
+ handleJIDUpdated(p1, p2, p3);
+ }
+ });
+ xmppRoster_.onJIDAdded.connect(new Slot1<JID>() {
+ @Override
+ public void call(JID p1) {
+ handleJIDAdded(p1);
+ }
+ });
+ }
+
+ void handleJIDUpdated(final JID jid, final String previousNick, final Collection<String> groups) {
+ onNickChanged.emit(jid, previousNick);
+ }
+
+ void handleJIDAdded(final JID jid) {
+ String oldNick= jidToNick(jid);
+ onNickChanged.emit(jid, oldNick);
+ }
+
+ public String jidToNick(final JID jid) {
+ if (jid.toBare().equals(ownJID_)) {
+ if (ownNick_ != null && !ownNick_.isEmpty()) {
+ return ownNick_;
+ }
+ }
+
+ if (mucRegistry_ != null && mucRegistry_.isMUC(jid.toBare()) ) {
+ return jid.getResource().isEmpty() ? jid.toBare().toString() : jid.getResource();
+ }
+
+ if (xmppRoster_.containsJID(jid) && !xmppRoster_.getNameForJID(jid).isEmpty()) {
+ return xmppRoster_.getNameForJID(jid);
+ }
+
+ return jid.toBare().toString();
+ }
+
+ void handleVCardReceived(final JID jid, VCard ownVCard) {
+ if (jid.compare(ownJID_, JID.CompareType.WithoutResource) != 0) {
+ return;
+ }
+ ownNick_ = ownJID_.toString();
+ if (ownVCard != null) {
+ if (!ownVCard.getNickname().isEmpty()) {
+ ownNick_ = ownVCard.getNickname();
+ } else if (!ownVCard.getGivenName().isEmpty()) {
+ ownNick_ = ownVCard.getGivenName();
+ } else if (!ownVCard.getFullName().isEmpty()) {
+ ownNick_ = ownVCard.getFullName();
+ }
+ }
+ }
+
+}
diff --git a/src/com/isode/stroke/client/Storages.java b/src/com/isode/stroke/client/Storages.java
new file mode 100644
index 0000000..188d1c6
--- /dev/null
+++ b/src/com/isode/stroke/client/Storages.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.client;
+
+import com.isode.stroke.disco.CapsStorage;
+import com.isode.stroke.roster.RosterStorage;
+import com.isode.stroke.vcards.VCardStorage;
+
+public interface Storages {
+ VCardStorage getVCardStorage();
+// AvatarStorage getAvatarStorage();
+// CapsStorage getCapsStorage();
+ RosterStorage getRosterStorage();
+// HistoryStorage getHistoryStorage();
+ CapsStorage getCapsStorage();
+}
diff --git a/src/com/isode/stroke/crypto/CryptoProvider.java b/src/com/isode/stroke/crypto/CryptoProvider.java
new file mode 100644
index 0000000..1c1a29e
--- /dev/null
+++ b/src/com/isode/stroke/crypto/CryptoProvider.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.crypto;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.SafeByteArray;
+
+public abstract class CryptoProvider {
+
+ public abstract Hash createSHA1();
+ public abstract Hash createMD5();
+ public abstract ByteArray getHMACSHA1(final SafeByteArray key, final ByteArray data);
+ public abstract ByteArray getHMACSHA1(final ByteArray key, final ByteArray data);
+ public abstract boolean isMD5AllowedForCrypto();
+
+ // Convenience
+ public ByteArray getSHA1Hash(final SafeByteArray data) {
+ return createSHA1().update(data).getHash();
+ }
+
+ public ByteArray getSHA1Hash(final ByteArray data) {
+ return createSHA1().update(data).getHash();
+ }
+
+ public ByteArray getMD5Hash(final SafeByteArray data) {
+ return createMD5().update(data).getHash();
+ }
+
+ public ByteArray getMD5Hash(final ByteArray data) {
+ return createMD5().update(data).getHash();
+ }
+
+}
diff --git a/src/com/isode/stroke/crypto/Hash.java b/src/com/isode/stroke/crypto/Hash.java
new file mode 100644
index 0000000..0d3f7cb
--- /dev/null
+++ b/src/com/isode/stroke/crypto/Hash.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2013-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.crypto;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.base.SafeByteArray;
+
+public interface Hash {
+ Hash update(final ByteArray data);
+ Hash update(final SafeByteArray data);
+
+ ByteArray getHash();
+}
diff --git a/src/com/isode/stroke/disco/CapsInfoGenerator.java b/src/com/isode/stroke/disco/CapsInfoGenerator.java
new file mode 100644
index 0000000..2bbd843
--- /dev/null
+++ b/src/com/isode/stroke/disco/CapsInfoGenerator.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.elements.CapsInfo;
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.elements.Form;
+import com.isode.stroke.elements.FormField;
+import com.isode.stroke.stringcodecs.Base64;
+
+public class CapsInfoGenerator {
+ private String node_;
+ private CryptoProvider crypto_;
+
+ private final static Comparator<FormField> compareFields = new Comparator<FormField>() {
+ @Override
+ public int compare(FormField lhs, FormField rhs) {
+ return lhs.getName().compareTo(rhs.getName());
+ }
+ };
+
+ CapsInfoGenerator(final String node, CryptoProvider crypto) {
+ this.node_ = node;
+ this.crypto_ = crypto;
+ }
+
+ CapsInfo generateCapsInfo(final DiscoInfo discoInfo) {
+ String serializedCaps = "";
+
+ List<DiscoInfo.Identity> identities = discoInfo.getIdentities();
+ Collections.sort(identities);
+ for (final DiscoInfo.Identity identity : identities) {
+ serializedCaps += identity.getCategory() + "/" + identity.getType()
+ + "/" + identity.getLanguage() + "/" + identity.getName()
+ + "<";
+ }
+
+ List<String> features = discoInfo.getFeatures();
+ Collections.sort(features);
+ for (final String feature : features) {
+ serializedCaps += feature + "<";
+ }
+
+ for (Form extension : discoInfo.getExtensions()) {
+ serializedCaps += extension.getFormType() + "<";
+ List<FormField> fields = extension.getFields();
+ Collections.sort(fields, compareFields);
+ for (FormField field : fields) {
+ if ("FORM_TYPE".equals(field.getName())) {
+ continue;
+ }
+ serializedCaps += field.getName() + "<";
+ List<String> values = field.getValues();
+ Collections.sort(values);
+ for (final String value : values) {
+ serializedCaps += value + "<";
+ }
+ }
+ }
+
+ String version = Base64.encode(crypto_ .getSHA1Hash(new ByteArray(serializedCaps)));
+ return new CapsInfo(node_, version, "sha-1");
+ }
+
+}
diff --git a/src/com/isode/stroke/disco/CapsManager.java b/src/com/isode/stroke/disco/CapsManager.java
new file mode 100644
index 0000000..2e4c45b
--- /dev/null
+++ b/src/com/isode/stroke/disco/CapsManager.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.elements.CapsInfo;
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.elements.Presence;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Slot1;
+import com.isode.stroke.signals.Slot2;
+
+public class CapsManager extends CapsProvider {
+
+ private final IQRouter iqRouter;
+ private final CryptoProvider crypto;
+ private final CapsStorage capsStorage;
+ private boolean warnOnInvalidHash;
+ private Set<String> requestedDiscoInfos = new HashSet<String>();
+ private Set<CapsPair> failingCaps = new HashSet<CapsPair>();
+ private Map<String, Set<CapsPair>> fallbacks = new HashMap<String, Set<CapsPair>>();
+
+ private class CapsPair {
+ JID jid;
+ String node;
+
+ CapsPair(JID j, String n) {jid = j; node = n;}
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof CapsPair)) return false;
+ CapsPair o1 = (CapsPair) o;
+ return jid.equals(o1.jid) && node.equals(o1.node);
+ }
+
+ @Override public int hashCode() {return jid.hashCode() * 5 + node.hashCode();}
+ }
+
+ public CapsManager(CapsStorage capsStorage, StanzaChannel stanzaChannel,
+ IQRouter iqRouter, CryptoProvider crypto) {
+ this.iqRouter = iqRouter;
+ this.crypto = crypto;
+ this.capsStorage = capsStorage;
+ this.warnOnInvalidHash = true;
+
+ stanzaChannel.onPresenceReceived.connect(new Slot1<Presence>() {
+ @Override
+ public void call(Presence p1) {
+ handlePresenceReceived(p1);
+ }
+ });
+ stanzaChannel.onAvailableChanged.connect(new Slot1<Boolean>() {
+ @Override
+ public void call(Boolean p1) {
+ handleStanzaChannelAvailableChanged(p1);
+ }
+ });
+ }
+
+ private void handlePresenceReceived(Presence presence) {
+ CapsInfo capsInfo = presence.getPayload(new CapsInfo());
+ if (capsInfo == null || !capsInfo.getHash().equals("sha-1")
+ || presence.getPayload(new ErrorPayload()) != null) {
+ return;
+ }
+ String hash = capsInfo.getVersion();
+ if (capsStorage.getDiscoInfo(hash) != null) {
+ return;
+ }
+ if (failingCaps.contains(new CapsPair(presence.getFrom(), hash))) {
+ return;
+ }
+ if (requestedDiscoInfos.contains(hash)) {
+ Set<CapsPair> fallback = fallbacks.get(hash);
+ if (fallback == null) fallbacks.put(hash, fallback = new HashSet<CapsPair>());
+ fallback.add(new CapsPair(presence.getFrom(), capsInfo.getNode()));
+ return;
+ }
+ requestDiscoInfo(presence.getFrom(), capsInfo.getNode(), hash);
+ }
+
+ private void handleStanzaChannelAvailableChanged(boolean available) {
+ if (available) {
+ failingCaps.clear();
+ fallbacks.clear();
+ requestedDiscoInfos.clear();
+ }
+ }
+
+ private void handleDiscoInfoReceived(final JID from, final String hash, DiscoInfo discoInfo, ErrorPayload error) {
+ requestedDiscoInfos.remove(hash);
+ if (error != null || discoInfo == null
+ || !new CapsInfoGenerator("", crypto).generateCapsInfo(discoInfo).getVersion().equals(hash)) {
+ if (warnOnInvalidHash && error == null && discoInfo != null) {
+// std.cerr << "Warning: Caps from " << from.toString() << " do not verify" << std.endl;
+ }
+ failingCaps.add(new CapsPair(from, hash));
+ Set<CapsPair> i = fallbacks.get(hash);
+ if (i != null && !i.isEmpty()) {
+ CapsPair fallbackAndNode = i.iterator().next();
+ i.remove(fallbackAndNode);
+ requestDiscoInfo(fallbackAndNode.jid, fallbackAndNode.node, hash);
+ }
+ return;
+ }
+ fallbacks.remove(hash);
+ capsStorage.setDiscoInfo(hash, discoInfo);
+ onCapsAvailable.emit(hash);
+ }
+
+ private void requestDiscoInfo(final JID jid, final String node, final String hash) {
+ GetDiscoInfoRequest request = GetDiscoInfoRequest.create(jid, node
+ + "#" + hash, iqRouter);
+ request.onResponse.connect(new Slot2<DiscoInfo, ErrorPayload>() {
+ @Override
+ public void call(DiscoInfo p1, ErrorPayload p2) {
+ handleDiscoInfoReceived(jid, hash, p1, p2);
+ }
+ });
+ requestedDiscoInfos.add(hash);
+ request.send();
+ }
+
+ @Override
+ DiscoInfo getCaps(String hash) {
+ return capsStorage.getDiscoInfo(hash);
+ }
+
+ // Mainly for testing purposes
+ void setWarnOnInvalidHash(boolean b) {
+ warnOnInvalidHash = b;
+ }
+
+}
diff --git a/src/com/isode/stroke/disco/CapsMemoryStorage.java b/src/com/isode/stroke/disco/CapsMemoryStorage.java
new file mode 100644
index 0000000..81e0e77
--- /dev/null
+++ b/src/com/isode/stroke/disco/CapsMemoryStorage.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.isode.stroke.elements.DiscoInfo;
+
+public class CapsMemoryStorage implements CapsStorage {
+ private Map<String, DiscoInfo> caps = new HashMap<String, DiscoInfo>();
+
+ @Override
+ public DiscoInfo getDiscoInfo(String s) {
+ return caps.get(s);
+ }
+
+ @Override
+ public void setDiscoInfo(String s, DiscoInfo d) {
+ caps.put(s, d);
+
+ }
+
+}
diff --git a/src/com/isode/stroke/disco/CapsProvider.java b/src/com/isode/stroke/disco/CapsProvider.java
new file mode 100644
index 0000000..d83eebc
--- /dev/null
+++ b/src/com/isode/stroke/disco/CapsProvider.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.signals.Signal1;
+
+public abstract class CapsProvider {
+ abstract DiscoInfo getCaps(final String hash);
+
+ public final Signal1<String> onCapsAvailable = new Signal1<String>();
+}
diff --git a/src/com/isode/stroke/disco/CapsStorage.java b/src/com/isode/stroke/disco/CapsStorage.java
new file mode 100644
index 0000000..07c1cd7
--- /dev/null
+++ b/src/com/isode/stroke/disco/CapsStorage.java
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import com.isode.stroke.elements.DiscoInfo;
+
+public interface CapsStorage {
+ DiscoInfo getDiscoInfo(final String s);
+ void setDiscoInfo(final String s, DiscoInfo d);
+}
diff --git a/src/com/isode/stroke/disco/ClientDiscoManager.java b/src/com/isode/stroke/disco/ClientDiscoManager.java
new file mode 100644
index 0000000..3770fbc
--- /dev/null
+++ b/src/com/isode/stroke/disco/ClientDiscoManager.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.elements.CapsInfo;
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.presence.PayloadAddingPresenceSender;
+import com.isode.stroke.presence.PresenceSender;
+import com.isode.stroke.queries.IQRouter;
+
+public class ClientDiscoManager {
+ private PayloadAddingPresenceSender presenceSender;
+ private CryptoProvider crypto;
+ private DiscoInfoResponder discoInfoResponder;
+ private String capsNode;
+ private CapsInfo capsInfo;
+
+ /**
+ * Constructs the manager
+ *
+ * \param iqRouter the router on which requests will be answered \param
+ * presenceSender the presence sender to which all outgoing presence (with
+ * caps information) will be sent.
+ */
+ public ClientDiscoManager(IQRouter iqRouter, PresenceSender presenceSender,
+ CryptoProvider crypto) {
+ this.crypto = crypto;
+ discoInfoResponder = new DiscoInfoResponder(iqRouter);
+ discoInfoResponder.start();
+ this.presenceSender = new PayloadAddingPresenceSender(presenceSender);
+ }
+
+ void delete() {
+ discoInfoResponder.stop();
+ }
+
+ /**
+ * Needs to be called before calling setDiscoInfo().
+ */
+ public void setCapsNode(final String node) {
+ capsNode = node;
+ }
+
+ /**
+ * Sets the capabilities of the client.
+ */
+ public void setDiscoInfo(final DiscoInfo discoInfo) {
+ capsInfo = new CapsInfoGenerator(capsNode, crypto).generateCapsInfo(discoInfo);
+ discoInfoResponder.clearDiscoInfo();
+ discoInfoResponder.setDiscoInfo(discoInfo);
+ discoInfoResponder.setDiscoInfo(
+ capsInfo.getNode() + "#" + capsInfo.getVersion(), discoInfo);
+ presenceSender.setPayload(capsInfo);
+ }
+
+ /**
+ * Returns the presence sender through which all outgoing presence should be
+ * sent. The manager will add the necessary caps information, and forward it
+ * to the presence sender passed at construction time.
+ */
+ public PresenceSender getPresenceSender() {
+ return presenceSender;
+ }
+
+ /**
+ * Called when the client is connected. This resets the presence sender,
+ * such that it assumes initial presence hasn't been sent yet.
+ */
+ void handleConnected() {
+ presenceSender.reset();
+ }
+
+}
diff --git a/src/com/isode/stroke/disco/DiscoInfoResponder.java b/src/com/isode/stroke/disco/DiscoInfoResponder.java
new file mode 100644
index 0000000..c0edca2
--- /dev/null
+++ b/src/com/isode/stroke/disco/DiscoInfoResponder.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.GetResponder;
+import com.isode.stroke.queries.IQRouter;
+
+public class DiscoInfoResponder extends GetResponder<DiscoInfo> {
+ private DiscoInfo info_;
+ private Map<String, DiscoInfo> nodeInfo_ = new HashMap<String, DiscoInfo>();
+
+ public DiscoInfoResponder(IQRouter router) {
+ super(new DiscoInfo(), router);
+ }
+
+ void clearDiscoInfo() {
+ info_ = new DiscoInfo();
+ nodeInfo_.clear();
+ }
+
+ void setDiscoInfo(final DiscoInfo info) {
+ info_ = info;
+ }
+
+ void setDiscoInfo(final String node, final DiscoInfo info) {
+ DiscoInfo newInfo = info;
+ newInfo.setNode(node);
+ nodeInfo_.put(node, newInfo);
+ }
+
+ protected boolean handleGetRequest(final JID from, final JID j, final String id, DiscoInfo info) {
+ if (info.getNode().isEmpty()) {
+ sendResponse(from, id, info_);
+ }
+ else {
+ DiscoInfo i = nodeInfo_.get(info.getNode());
+ if (i != null) {
+ sendResponse(from, id, i);
+ }
+ else {
+ sendError(from, id, ErrorPayload.Condition.ItemNotFound, ErrorPayload.Type.Cancel);
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/com/isode/stroke/disco/EntityCapsManager.java b/src/com/isode/stroke/disco/EntityCapsManager.java
new file mode 100644
index 0000000..a41ec11
--- /dev/null
+++ b/src/com/isode/stroke/disco/EntityCapsManager.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.elements.CapsInfo;
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.elements.Presence;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.signals.Slot1;
+
+public class EntityCapsManager extends EntityCapsProvider {
+ private final CapsProvider capsProvider;
+ private final Map<JID, String> caps = new HashMap<JID, String>();
+
+ public EntityCapsManager(CapsProvider capsProvider, StanzaChannel stanzaChannel) {
+ this.capsProvider = capsProvider;
+
+ stanzaChannel.onPresenceReceived.connect(new Slot1<Presence>() {
+ @Override
+ public void call(Presence p1) {
+ handlePresenceReceived(p1);
+ }
+ });
+ stanzaChannel.onAvailableChanged.connect(new Slot1<Boolean>() {
+ @Override
+ public void call(Boolean p1) {
+ handleStanzaChannelAvailableChanged(p1);
+ }
+ });
+ capsProvider.onCapsAvailable.connect(new Slot1<String>() {
+ @Override
+ public void call(String p1) {
+ handleCapsAvailable(p1);
+ }
+ });
+ }
+
+ private void handlePresenceReceived(Presence presence) {
+ JID from = presence.getFrom();
+ if (presence.isAvailable()) {
+ CapsInfo capsInfo = presence.getPayload(new CapsInfo());
+ if (capsInfo == null || !capsInfo.getHash().equals("sha-1") || presence.getPayload(new ErrorPayload()) != null) {
+ return;
+ }
+ String hash = capsInfo.getVersion();
+ String i = caps.get(from);
+ if (!hash.equals(i)) {
+ caps.put(from, hash);
+ DiscoInfo disco = capsProvider.getCaps(hash);
+ if (disco != null || i != null) {
+ onCapsChanged.emit(from);
+ }
+ }
+ }
+ else {
+ if (caps.remove(from) != null) {
+ onCapsChanged.emit(from);
+ }
+ }
+ }
+
+ private void handleStanzaChannelAvailableChanged(boolean available) {
+ if (available) {
+ for (JID i : caps.keySet()) {
+ onCapsChanged.emit(i);
+ }
+ caps.clear();
+ }
+ }
+
+ private void handleCapsAvailable(final String hash) {
+ // TODO: Use Boost.Bimap ?
+ for (JID i : caps.keySet()) {
+ if (caps.get(i).equals(hash)) {
+ onCapsChanged.emit(i);
+ }
+ }
+ }
+
+ public DiscoInfo getCaps(final JID jid) {
+ if (caps.containsKey(jid)) {
+ return capsProvider.getCaps(caps.get(jid));
+ }
+ return new DiscoInfo();
+ }
+}
diff --git a/src/com/isode/stroke/disco/EntityCapsProvider.java b/src/com/isode/stroke/disco/EntityCapsProvider.java
new file mode 100644
index 0000000..fd30173
--- /dev/null
+++ b/src/com/isode/stroke/disco/EntityCapsProvider.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.signals.Signal1;
+
+public abstract class EntityCapsProvider {
+ /**
+ * Returns the service discovery information of the given JID.
+ */
+ public abstract DiscoInfo getCaps(final JID jid);
+
+ /**
+ * Emitted when the capabilities of a JID changes.
+ */
+ public final Signal1<JID> onCapsChanged = new Signal1<JID>();
+}
diff --git a/src/com/isode/stroke/disco/GetDiscoInfoRequest.java b/src/com/isode/stroke/disco/GetDiscoInfoRequest.java
new file mode 100644
index 0000000..f820b2f
--- /dev/null
+++ b/src/com/isode/stroke/disco/GetDiscoInfoRequest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.disco;
+
+import com.isode.stroke.elements.DiscoInfo;
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+
+public class GetDiscoInfoRequest extends GenericRequest<DiscoInfo> {
+
+ public static GetDiscoInfoRequest create(final JID jid, IQRouter router) {
+ return new GetDiscoInfoRequest(jid, router);
+ }
+
+ public static GetDiscoInfoRequest create(final JID jid, final String node, IQRouter router) {
+ return new GetDiscoInfoRequest(jid, node, router);
+ }
+
+ private GetDiscoInfoRequest(final JID jid, IQRouter router) {
+ super(IQ.Type.Get, jid, new DiscoInfo(), router);
+ }
+
+ private GetDiscoInfoRequest(final JID jid, final String node, IQRouter router) {
+ this(jid, router);
+ getPayloadGeneric().setNode(node);
+ }
+
+}
diff --git a/src/com/isode/stroke/elements/ChatState.java b/src/com/isode/stroke/elements/ChatState.java
index 151eec4..b602b8e 100644
--- a/src/com/isode/stroke/elements/ChatState.java
+++ b/src/com/isode/stroke/elements/ChatState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2015 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -51,4 +51,4 @@ public class ChatState extends Payload {
NotNull.exceptIfNull(state, "state");
state_ = state;
}
-} \ No newline at end of file
+}
diff --git a/src/com/isode/stroke/elements/Idle.java b/src/com/isode/stroke/elements/Idle.java
new file mode 100644
index 0000000..f76cf8a
--- /dev/null
+++ b/src/com/isode/stroke/elements/Idle.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+package com.isode.stroke.elements;
+
+import java.util.Date;
+
+public class Idle extends Payload {
+ public Idle() {}
+ public Idle(Date since) {
+ since_ = since;
+ }
+
+ public void setSince(Date since) {
+ since_ = since;
+ }
+
+ public Date getSince() {
+ return since_;
+ }
+
+ private Date since_;
+}
diff --git a/src/com/isode/stroke/elements/MUCInvitationPayload.java b/src/com/isode/stroke/elements/MUCInvitationPayload.java
index 9d0195b..464e5ff 100644
--- a/src/com/isode/stroke/elements/MUCInvitationPayload.java
+++ b/src/com/isode/stroke/elements/MUCInvitationPayload.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2011, Kevin Smith
+ * Copyright (c) 2011-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.elements;
@@ -20,6 +16,8 @@ public class MUCInvitationPayload extends Payload {
private String password_;
private String reason_;
private String thread_;
+ private boolean impromptu_;
+
/**
* Create the payload
@@ -45,6 +43,22 @@ public class MUCInvitationPayload extends Payload {
}
/**
+ * Set the impromptu value
+ * @param b value to set
+ */
+ public void setIsImpromptu(boolean b) {
+ impromptu_ = b;
+ }
+
+ /**
+ * Get the impromptu value
+ * @return impromptu value
+ */
+ public boolean getIsImpromptu() {
+ return impromptu_;
+ }
+
+ /**
* Set the jabber ID
* @param jid jabber Id, not null
*/
diff --git a/src/com/isode/stroke/elements/Presence.java b/src/com/isode/stroke/elements/Presence.java
index 392573f..ff3b0df 100644
--- a/src/com/isode/stroke/elements/Presence.java
+++ b/src/com/isode/stroke/elements/Presence.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2010-2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.elements;
@@ -119,6 +115,10 @@ public class Presence extends Stanza {
public void setPriority(int priority) {
updatePayload(new Priority(priority));
}
+
+ public boolean isAvailable() {
+ return type_ != null && type_ == Type.Available;
+ }
@Override
public String toString() {
diff --git a/src/com/isode/stroke/elements/PrivateStorage.java b/src/com/isode/stroke/elements/PrivateStorage.java
index 171e56e..09e173f 100644
--- a/src/com/isode/stroke/elements/PrivateStorage.java
+++ b/src/com/isode/stroke/elements/PrivateStorage.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.elements;
@@ -15,7 +11,9 @@ package com.isode.stroke.elements;
*/
public class PrivateStorage extends Payload {
- /**
+ public PrivateStorage() {}
+
+ /**
* Constructor
* @param p payload, not null
*/
diff --git a/src/com/isode/stroke/elements/Replace.java b/src/com/isode/stroke/elements/Replace.java
new file mode 100644
index 0000000..65c5216
--- /dev/null
+++ b/src/com/isode/stroke/elements/Replace.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 2011 Vlad Voicu
+ * Licensed under the Simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+package com.isode.stroke.elements;
+
+public class Replace extends Payload {
+ private String replaceID_;
+
+ public Replace() {
+ this("");
+ }
+
+ public Replace(String id) {
+ replaceID_ = id;
+ }
+
+ public String getID() {
+ return replaceID_;
+ }
+
+ public void setID(String id) {
+ replaceID_ = id;
+ }
+
+}
diff --git a/src/com/isode/stroke/elements/RosterItemPayload.java b/src/com/isode/stroke/elements/RosterItemPayload.java
index a80ecc6..c080915 100644
--- a/src/com/isode/stroke/elements/RosterItemPayload.java
+++ b/src/com/isode/stroke/elements/RosterItemPayload.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Isode Limited, London, England.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
/*
@@ -23,15 +23,23 @@ public class RosterItemPayload {
};
public RosterItemPayload() {
+ jid_ = new JID();
+ name_ = "";
subscription_ = Subscription.None;
ask_ = false;
+ groups_ = new ArrayList<String>();
}
- public RosterItemPayload(JID jid, String name, Subscription subscription) {
+ public RosterItemPayload(JID jid, String name, Subscription subscription, Collection<String> groups) {
jid_ = jid;
name_ = name;
subscription_ = subscription;
ask_ = false;
+ groups_ = groups;
+ }
+
+ public RosterItemPayload(JID jid, String name, Subscription subscription) {
+ this(jid, name, subscription, new ArrayList<String>());
}
public void setJID(JID jid) {
@@ -81,6 +89,6 @@ public class RosterItemPayload {
private JID jid_;
private String name_;
private Subscription subscription_;
- private ArrayList<String> groups_ = new ArrayList<String>();
+ private Collection<String> groups_;
private boolean ask_;
}
diff --git a/src/com/isode/stroke/elements/RosterPayload.java b/src/com/isode/stroke/elements/RosterPayload.java
index 6208216..5ca32f8 100644
--- a/src/com/isode/stroke/elements/RosterPayload.java
+++ b/src/com/isode/stroke/elements/RosterPayload.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Isode Limited, London, England.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
/*
@@ -9,13 +9,16 @@
package com.isode.stroke.elements;
import com.isode.stroke.jid.JID;
+
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.List;
/**
* Roster.
*/
public class RosterPayload extends Payload {
+ private final ArrayList<RosterItemPayload> items_ = new ArrayList<RosterItemPayload>();
+ private String version_;
public RosterPayload() {
}
@@ -33,9 +36,16 @@ public class RosterPayload extends Payload {
items_.add(item);
}
- public Collection<RosterItemPayload> getItems() {
+ public List<RosterItemPayload> getItems() {
return items_;
}
- private final ArrayList<RosterItemPayload> items_ = new ArrayList<RosterItemPayload>();
+ public String getVersion() {
+ return version_;
+ }
+
+ public void setVersion(String version) {
+ this.version_ = version;
+ }
+
}
diff --git a/src/com/isode/stroke/elements/SecurityLabel.java b/src/com/isode/stroke/elements/SecurityLabel.java
new file mode 100644
index 0000000..8134811
--- /dev/null
+++ b/src/com/isode/stroke/elements/SecurityLabel.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.elements;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class SecurityLabel extends Payload {
+
+ private Collection<String> equivalentLabels = new ArrayList<String>();
+ private String foregroundColor;
+ private String displayMarking;
+ private String backgroundColor;
+ private String label;
+
+ final Collection<String> getEquivalentLabels() {
+ return equivalentLabels;
+ }
+
+ void setEquivalentLabels(final Collection<String> value) {
+ this.equivalentLabels = value ;
+ }
+
+ void addEquivalentLabel(final String value) {
+ this.equivalentLabels.add(value);
+ }
+
+ final String getForegroundColor() {
+ return foregroundColor;
+ }
+
+ void setForegroundColor(final String value) {
+ this.foregroundColor = value ;
+ }
+
+ final String getDisplayMarking() {
+ return displayMarking;
+ }
+
+ void setDisplayMarking(final String value) {
+ this.displayMarking = value ;
+ }
+
+ final String getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ void setBackgroundColor(final String value) {
+ this.backgroundColor = value ;
+ }
+
+ final String getLabel() {
+ return label;
+ }
+
+ void setLabel(final String value) {
+ this.label = value ;
+ }
+
+}
diff --git a/src/com/isode/stroke/elements/SecurityLabelsCatalog.java b/src/com/isode/stroke/elements/SecurityLabelsCatalog.java
new file mode 100644
index 0000000..4bb9493
--- /dev/null
+++ b/src/com/isode/stroke/elements/SecurityLabelsCatalog.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.elements;
+
+import java.util.Collection;
+
+import com.isode.stroke.jid.JID;
+
+public class SecurityLabelsCatalog extends Payload {
+ private JID to_;
+ private String name_;
+ private String description_;
+ private Collection<Item> items_;
+
+ public class Item {
+ private SecurityLabel label_;
+ private String selector_;
+ private boolean default_;
+
+ public SecurityLabel getLabel() {
+ return label_;
+ }
+
+ void setLabel(SecurityLabel label) {
+ label_ = label;
+ }
+
+ final String getSelector() { return selector_; }
+
+ void setSelector(final String selector) {
+ selector_ = selector;
+ }
+
+ boolean getIsDefault() { return default_; }
+
+ void setIsDefault(boolean isDefault) {
+ default_ = isDefault;
+ }
+ };
+
+ public SecurityLabelsCatalog() {
+ this(new JID());
+ }
+
+ public SecurityLabelsCatalog(final JID to) {
+ to_ = to;
+ }
+
+ public final Collection<Item> getItems() {
+ return items_;
+ }
+
+ public void addItem(final Item item) {
+ items_.add(item);
+ }
+
+ public final JID getTo() {
+ return to_;
+ }
+
+ public void setTo(final JID to) {
+ to_ = to;
+ }
+
+ public final String getName() {
+ return name_;
+ }
+
+ public void setName(final String name) {
+ name_ = name;
+ }
+
+ public final String getDescription() {
+ return description_;
+ }
+
+ public void setDescription(final String description) {
+ description_ = description;
+ }
+
+}
diff --git a/src/com/isode/stroke/elements/Stanza.java b/src/com/isode/stroke/elements/Stanza.java
index c542e8b..9a4b907 100644
--- a/src/com/isode/stroke/elements/Stanza.java
+++ b/src/com/isode/stroke/elements/Stanza.java
@@ -1,15 +1,13 @@
/*
- * Copyright (c) 2010-2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.elements;
import com.isode.stroke.jid.JID;
+
+import java.util.Date;
import java.util.Vector;
/**
@@ -48,7 +46,8 @@ public abstract class Stanza implements Element {
* @param type payload type object instance, not null
* @return payload of given type, can be null
*/
- public <T extends Payload> T getPayload(T type) {
+ @SuppressWarnings("unchecked")
+ public <T extends Payload> T getPayload(T type) {
for (Payload payload : payloads_) {
if (payload.getClass().isAssignableFrom(type.getClass())) {
return (T)payload;
@@ -63,7 +62,8 @@ public abstract class Stanza implements Element {
* @param type payload type object instance, not null
* @return list of payloads of given type, not null but can be empty
*/
- public <T extends Payload> Vector<T> getPayloads(T type) {
+ @SuppressWarnings("unchecked")
+ public <T extends Payload> Vector<T> getPayloads(T type) {
Vector<T> results = new Vector<T>();
for (Payload payload : payloads_) {
if (payload.getClass().isAssignableFrom(type.getClass())) {
@@ -168,4 +168,8 @@ public abstract class Stanza implements Element {
" id=\"" + id_ + "\"";
}
+ public Date getTimestamp() {
+ Delay delay = getPayload(new Delay());
+ return delay != null ? delay.getStamp() : null;
+ }
}
diff --git a/src/com/isode/stroke/elements/StatusShow.java b/src/com/isode/stroke/elements/StatusShow.java
index 14cfe2c..c6b2b14 100644
--- a/src/com/isode/stroke/elements/StatusShow.java
+++ b/src/com/isode/stroke/elements/StatusShow.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2010, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.elements;
@@ -25,11 +21,11 @@ public class StatusShow extends Payload {
type_ = type;
}
- void setType(Type type) {
+ public void setType(Type type) {
type_ = type;
}
- Type getType() {
+ public Type getType() {
return type_;
}
@@ -50,4 +46,27 @@ public class StatusShow extends Payload {
}
return "Unknown";
}
+
+ /**
+ * Can be used for rough ordering of Types.
+ * Greater magnitude = more available.
+ */
+ public static int typeToAvailabilityOrdering(Type type) {
+ switch (type) {
+ case Online: return 4;
+ case FFC: return 5;
+ case Away: return 2;
+ case XA: return 1;
+ case DND: return 3;
+ case None: return 0;
+ }
+ assert(false);
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "StatusShow : " + type_.toString();
+ }
+
}
diff --git a/src/com/isode/stroke/elements/VCard.java b/src/com/isode/stroke/elements/VCard.java
new file mode 100644
index 0000000..f9b294b
--- /dev/null
+++ b/src/com/isode/stroke/elements/VCard.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.elements;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.jid.JID;
+
+public class VCard extends Payload implements Serializable {
+ private String version_ = "";
+ private String fullName_ = "";
+ private String familyName_ = "";
+ private String givenName_ = "";
+ private String middleName_ = "";
+ private String prefix_ = "";
+ private String suffix_ = "";
+// private //String email_;
+ private ByteArray photo_;
+ private String photoType_ = "";
+ private String nick_ = "";
+ private Date birthday_;
+ private String unknownContent_ = "";
+ private List<EMailAddress> emailAddresses_;
+ private List<Telephone> telephones_;
+ private List<Address> addresses_;
+ private List<AddressLabel> addressLabels_;
+ private List<JID> jids_;
+ private String description_ = "";
+ private List<Organization> organizations_;
+ private List<String> titles_;
+ private List<String> roles_;
+ private List<String> urls_;
+
+ public static class EMailAddress {
+ public boolean isHome;
+ public boolean isWork;
+ public boolean isInternet;
+ public boolean isPreferred;
+ public boolean isX400;
+ public String address;
+ };
+
+ public static class Telephone {
+ public boolean isHome;
+ public boolean isWork;
+ public boolean isVoice;
+ public boolean isFax;
+ public boolean isPager;
+ public boolean isMSG;
+ public boolean isCell;
+ public boolean isVideo;
+ public boolean isBBS;
+ public boolean isModem;
+ public boolean isISDN;
+ public boolean isPCS;
+ public boolean isPreferred;
+ public String number;
+ };
+
+ public static enum DeliveryType {
+ DomesticDelivery,
+ InternationalDelivery,
+ None
+ };
+
+ public static class Address {
+ public boolean isHome;
+ public boolean isWork;
+ public boolean isPostal;
+ public boolean isParcel;
+ public DeliveryType deliveryType;
+ public boolean isPreferred;
+
+ public String poBox;
+ public String addressExtension;
+ public String street;
+ public String locality;
+ public String region;
+ public String postalCode;
+ public String country;
+ };
+
+ public static class AddressLabel {
+ public boolean isHome;
+ public boolean isWork;
+ public boolean isPostal;
+ public boolean isParcel;
+ public DeliveryType deliveryType;
+ public boolean isPreferred;
+ public List<String> lines = new ArrayList<String>();
+ };
+
+ public static class Organization {
+ public String name;
+ public List<String> units = new ArrayList<String>();
+ };
+
+ public VCard() {}
+
+ public void setVersion(final String version) { version_ = version; }
+ public final String getVersion() { return version_; }
+
+ public void setFullName(final String fullName) { fullName_ = fullName; }
+ public final String getFullName() { return fullName_; }
+
+ public void setFamilyName(final String familyName) { familyName_ = familyName; }
+ public final String getFamilyName() { return familyName_; }
+
+ public void setGivenName(final String givenName) { givenName_ = givenName; }
+ public final String getGivenName() { return givenName_; }
+
+ public void setMiddleName(final String middleName) { middleName_ = middleName; }
+ public final String getMiddleName() { return middleName_; }
+
+ public void setPrefix(final String prefix) { prefix_ = prefix; }
+ public final String getPrefix() { return prefix_; }
+
+ public void setSuffix(final String suffix) { suffix_ = suffix; }
+ public final String getSuffix() { return suffix_; }
+
+
+ //void setEMailAddress(final String email) { email_ = email; }
+ //final String getEMailAddress() { return email_; }
+
+ public void setNickname(final String nick) { nick_ = nick; }
+ public final String getNickname() { return nick_; }
+
+ public void setPhoto(final ByteArray photo) { photo_ = photo; }
+ public final ByteArray getPhoto() { return photo_; }
+
+ public void setPhotoType(final String photoType) { photoType_ = photoType; }
+ public final String getPhotoType() { return photoType_; }
+
+ public final String getUnknownContent() { return unknownContent_; }
+ public void addUnknownContent(final String c) {
+ unknownContent_ += c;
+ }
+
+ public final List<EMailAddress> getEMailAddresses() {
+ return emailAddresses_;
+ }
+
+ public void addEMailAddress(final EMailAddress email) {
+ if (emailAddresses_ == null) emailAddresses_ = new ArrayList<EMailAddress>();
+ emailAddresses_.add(email);
+ }
+
+ public void clearEMailAddresses() {
+ if (emailAddresses_ != null) emailAddresses_.clear();
+ }
+
+ public void setBirthday(final Date birthday) {
+ birthday_ = birthday;
+ }
+
+ public final Date getBirthday() {
+ return birthday_;
+ }
+
+ public void addTelephone(final Telephone phone) {
+ if (telephones_ == null) telephones_ = new ArrayList<Telephone>();
+ telephones_.add(phone);
+ }
+
+ public void clearTelephones() {
+ if (telephones_ != null) telephones_.clear();
+ }
+
+ public final List<Address> getAddresses() {
+ return addresses_;
+ }
+
+ public void addAddress(final Address address) {
+ if (addresses_ == null) addresses_ = new ArrayList<Address>();
+ addresses_.add(address);
+ }
+
+ public void clearAddresses() {
+ if (addresses_ != null) addresses_.clear();
+ }
+
+ public final List<AddressLabel> getAddressLabels() {
+ return addressLabels_;
+ }
+
+ public void addAddressLabel(final AddressLabel addressLabel) {
+ if (addressLabels_ == null) addressLabels_ = new ArrayList<AddressLabel>();
+ addressLabels_.add(addressLabel);
+ }
+
+ public void clearAddressLabels() {
+ if (addressLabels_ != null) addressLabels_.clear();
+ }
+
+ public final List<JID> getJIDs() {
+ if (jids_ == null) jids_ = new ArrayList<JID>();
+ return jids_;
+ }
+
+ public void clearJIDs() {
+ if (jids_ != null) jids_.clear();
+ }
+
+ public final String getDescription() {
+ return description_;
+ }
+
+ public void setDescription(final String description) {
+ this.description_ = description;
+ }
+
+ public final List<Organization> getOrganizations() {
+ return organizations_;
+ }
+
+ public void addOrganization(final Organization organization) {
+ if (organizations_ == null) organizations_ = new ArrayList<Organization>();
+ organizations_.add(organization);
+ }
+
+ public void clearOrganizations() {
+ if (organizations_ != null) organizations_.clear();
+ }
+
+ public final List<String> getTitles() {
+ return titles_;
+ }
+
+ public void addTitle(final String title) {
+ if (titles_ == null) titles_ = new ArrayList<String>();
+ titles_.add(title);
+ }
+
+ public void clearTitles() {
+ if (titles_ != null) titles_.clear();
+ }
+
+ public final List<String> getRoles() {
+ return roles_;
+ }
+
+ public void addRole(final String role) {
+ if (roles_ == null) roles_ = new ArrayList<String>();
+ roles_.add(role);
+ }
+
+ public void clearRoles() {
+ if (roles_ != null) roles_.clear();
+ }
+
+ public final List<String> getURLs() {
+ return urls_;
+ }
+
+ public void addURL(final String url) {
+ if (urls_ == null) urls_ = new ArrayList<String>();
+ urls_.add(url);
+ }
+
+ public void clearURLs() {
+ if (urls_ != null) urls_.clear();
+ }
+
+ public boolean isEmpty() {
+ boolean empty = version_.isEmpty() && fullName_.isEmpty() && familyName_.isEmpty() && givenName_.isEmpty() && middleName_.isEmpty() && prefix_.isEmpty() && suffix_.isEmpty();
+ empty &= photo_ == null || photo_.isEmpty();
+ empty &= photoType_.isEmpty();
+ empty &= nick_.isEmpty();
+ empty &= birthday_ == null;
+ empty &= unknownContent_.isEmpty();
+ empty &= emailAddresses_ == null || emailAddresses_.isEmpty();
+ empty &= telephones_ == null || telephones_.isEmpty();
+ empty &= addresses_ == null || addresses_.isEmpty();
+ empty &= addressLabels_ == null || addressLabels_.isEmpty();
+ empty &= jids_ == null || jids_.isEmpty();
+ empty &= description_.isEmpty();
+ empty &= organizations_ == null || organizations_.isEmpty();
+ empty &= titles_ == null || titles_.isEmpty();
+ empty &= roles_ == null || roles_.isEmpty();
+ empty &= urls_ == null || urls_.isEmpty();
+ return empty;
+ }
+
+ public EMailAddress getPreferredEMailAddress() {
+ for (final EMailAddress address : emailAddresses_) {
+ if (address.isPreferred) {
+ return address;
+ }
+ }
+ if (emailAddresses_ != null && !emailAddresses_.isEmpty()) {
+ return emailAddresses_.get(0);
+ }
+ return new EMailAddress();
+ }
+
+ public void addJID(JID jid) {
+ jids_.add(jid);
+
+ }
+
+ public List<Telephone> getTelephones() {
+ return telephones_;
+ }
+
+}
diff --git a/src/com/isode/stroke/muc/MUC.java b/src/com/isode/stroke/muc/MUC.java
index 8df1b89..900bb7d 100644
--- a/src/com/isode/stroke/muc/MUC.java
+++ b/src/com/isode/stroke/muc/MUC.java
@@ -1,13 +1,10 @@
/*
- * Copyright (c) 2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Kevin Smith
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.muc;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -32,6 +29,7 @@ import com.isode.stroke.jid.JID.CompareType;
import com.isode.stroke.presence.DirectedPresenceSender;
import com.isode.stroke.queries.GenericRequest;
import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Signal;
import com.isode.stroke.signals.Signal1;
import com.isode.stroke.signals.Signal2;
import com.isode.stroke.signals.Signal3;
@@ -70,6 +68,8 @@ public class MUC {
public Signal3<ErrorPayload,JID,MUCOccupant.Role> onRoleChangeFailed =
new Signal3<ErrorPayload,JID,MUCOccupant.Role>();
+ public final Signal onUnlocked = new Signal();
+
private boolean createAsReservedIfNew;
private IQRouter iqRouter_;
private boolean joinComplete_;
@@ -253,25 +253,44 @@ public class MUC {
return occupants.containsKey(nick);
}
+ public Map<String, MUCOccupant> getOccupants() {
+ return Collections.unmodifiableMap(occupants);
+ }
+
/**
* Invite the person with give JID to the chat room
* @param person jabber ID o the person to invite,not nul
*/
public void invitePerson(JID person) {
- invitePerson(person,"");
+ invitePerson(person, "", false, false);
+ }
+
+ /**
+ * Send an invite for the person to join the MUC
+ * @param person jabber ID of the person to invite, not null
+ * @param reason join reason, not null
+ * @param isImpromptu
+ */
+ public void invitePerson(JID person, String reason, boolean isImpromptu) {
+ invitePerson(person, reason, isImpromptu, false);
}
+
/**
* Send an invite for the person to join the MUC
* @param person jabber ID of the person to invite, not null
* @param reason join reason, not null
+ * @param isImpromptu
+ * @param isReuseChat
*/
- public void invitePerson(JID person, String reason) {
+ public void invitePerson(JID person, String reason, boolean isImpromptu, boolean isReuseChat) {
Message message = new Message();
message.setTo(person);
message.setType(Message.Type.Normal);
MUCInvitationPayload invite = new MUCInvitationPayload();
invite.setReason(reason);
+ invite.setIsImpromptu(isImpromptu);
+ invite.setIsContinuation(isReuseChat);
invite.setJID(ownMUCJID.toBare());
message.addPayload(invite);
stanzaChannel.sendMessage(message);
diff --git a/src/com/isode/stroke/muc/MUCBookmarkManager.java b/src/com/isode/stroke/muc/MUCBookmarkManager.java
index 78bddea..36e6d94 100644
--- a/src/com/isode/stroke/muc/MUCBookmarkManager.java
+++ b/src/com/isode/stroke/muc/MUCBookmarkManager.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.muc;
@@ -116,7 +112,7 @@ public class MUCBookmarkManager {
storage = payload;
Vector<MUCBookmark> receivedBookmarks = new Vector<MUCBookmark>();
- for (Storage.Room room : payload.getRooms()) {
+ if (payload != null) for (Storage.Room room : payload.getRooms()) {
receivedBookmarks.add(new MUCBookmark(room));
}
diff --git a/src/com/isode/stroke/network/JavaConnection.java b/src/com/isode/stroke/network/JavaConnection.java
index f1d72bb..9b171d9 100644
--- a/src/com/isode/stroke/network/JavaConnection.java
+++ b/src/com/isode/stroke/network/JavaConnection.java
@@ -160,6 +160,7 @@ public class JavaConnection extends Connection implements EventOwner {
if(selector_ != null) {
try {
selector_.close();
+ selector_ = null;
} catch (IOException e) {
}
}
@@ -382,7 +383,7 @@ public class JavaConnection extends Connection implements EventOwner {
private boolean disconnecting_ = false;
private boolean disconnected_ = false;
private SocketChannel socketChannel_;
- private Selector selector_;
+ private volatile Selector selector_;
private SelectionKey selectionKey_;
private Worker worker_;
diff --git a/src/com/isode/stroke/network/JavaCryptoProvider.java b/src/com/isode/stroke/network/JavaCryptoProvider.java
new file mode 100644
index 0000000..b7dff5d
--- /dev/null
+++ b/src/com/isode/stroke/network/JavaCryptoProvider.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2011-2015 Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.network;
+
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+import com.isode.stroke.base.ByteArray;
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.crypto.Hash;
+import com.isode.stroke.base.SafeByteArray;
+
+public class JavaCryptoProvider extends CryptoProvider {
+
+ private static class HashProvider implements Hash {
+ private final MessageDigest digest;
+
+ HashProvider(String algorithm) throws NoSuchAlgorithmException {
+ digest = MessageDigest.getInstance("SHA-1");
+ }
+
+ @Override
+ public Hash update(ByteArray data) {
+ digest.update(data.getData());
+ return this;
+ }
+
+ @Override
+ public Hash update(SafeByteArray data) {
+// digest.update(data.getData());
+ return this;
+ }
+
+ @Override
+ public ByteArray getHash() {
+ return new ByteArray(digest.digest());
+ }
+
+ }
+
+ @Override
+ public Hash createSHA1() {
+ try {
+ return new HashProvider("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public Hash createMD5() {
+ try {
+ return new HashProvider("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ }
+
+// @Override
+ public ByteArray getHMACSHA1(SafeByteArray key, ByteArray data) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public ByteArray getHMACSHA1(ByteArray key, ByteArray data) {
+ Mac mac;
+ try {
+ mac = Mac.getInstance("HmacSHA1");
+ mac.init(new SecretKeySpec(key.getData(), mac.getAlgorithm()));
+ return new ByteArray(mac.doFinal(data.getData()));
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ } catch (InvalidKeyException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isMD5AllowedForCrypto() {
+ return false;
+ }
+
+}
diff --git a/src/com/isode/stroke/network/JavaNetworkFactories.java b/src/com/isode/stroke/network/JavaNetworkFactories.java
index aaffea3..2276a2a 100644
--- a/src/com/isode/stroke/network/JavaNetworkFactories.java
+++ b/src/com/isode/stroke/network/JavaNetworkFactories.java
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2011-2013 Isode Limited, London, England.
+ * Copyright (c) 2011-2015 Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.network;
+import com.isode.stroke.crypto.CryptoProvider;
import com.isode.stroke.eventloop.EventLoop;
import com.isode.stroke.tls.PlatformTLSFactories;
import com.isode.stroke.tls.TLSContextFactory;
@@ -16,6 +17,7 @@ public class JavaNetworkFactories implements NetworkFactories {
connections_ = new JavaConnectionFactory(eventLoop_);
dns_ = new PlatformDomainNameResolver(eventLoop_);
platformTLSFactories_ = new PlatformTLSFactories();
+ cryptoProvider_ = new JavaCryptoProvider();
}
public TimerFactory getTimerFactory() {
@@ -34,9 +36,15 @@ public class JavaNetworkFactories implements NetworkFactories {
return platformTLSFactories_.getTLSContextFactory();
}
+ @Override
+ public CryptoProvider getCryptoProvider() {
+ return cryptoProvider_;
+ }
+
private final EventLoop eventLoop_;
private final JavaTimerFactory timers_;
private final JavaConnectionFactory connections_;
private final PlatformDomainNameResolver dns_;
private final PlatformTLSFactories platformTLSFactories_;
+ private final CryptoProvider cryptoProvider_;
}
diff --git a/src/com/isode/stroke/network/NetworkFactories.java b/src/com/isode/stroke/network/NetworkFactories.java
index 678a3b7..fe20214 100644
--- a/src/com/isode/stroke/network/NetworkFactories.java
+++ b/src/com/isode/stroke/network/NetworkFactories.java
@@ -1,13 +1,10 @@
/*
- * Copyright (c) 2011-2013 Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010 Remko Tronçon.
+ * Copyright (c) 2010-2015 Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.network;
+import com.isode.stroke.crypto.CryptoProvider;
import com.isode.stroke.tls.TLSContextFactory;
public interface NetworkFactories {
@@ -16,5 +13,6 @@ public interface NetworkFactories {
ConnectionFactory getConnectionFactory();
DomainNameResolver getDomainNameResolver();
TLSContextFactory getTLSContextFactory();
+ CryptoProvider getCryptoProvider();
}
diff --git a/src/com/isode/stroke/parser/XMPPParser.java b/src/com/isode/stroke/parser/XMPPParser.java
index c2c25e9..85d0e1f 100644
--- a/src/com/isode/stroke/parser/XMPPParser.java
+++ b/src/com/isode/stroke/parser/XMPPParser.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2010, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.parser;
@@ -35,7 +31,7 @@ public class XMPPParser implements XMLParserClient {
xmlParseResult = xmlParser_.parse(data);
} catch (Exception e) {
parseErrorOccurred_ = true;
- logger_.warning("Data " + data + " caused:\n" + e.getMessage());
+ logger_.log(java.util.logging.Level.WARNING, "Data " + data + " caused:\n" + e.getMessage(), e);
}
if (parseErrorOccurred_ || !xmlParseResult) {
logger_.warning(String.format("When parsing, %b and %b",
diff --git a/src/com/isode/stroke/parser/payloadparsers/ForwardedParser.java b/src/com/isode/stroke/parser/payloadparsers/ForwardedParser.java
index b7a023e..2819566 100644
--- a/src/com/isode/stroke/parser/payloadparsers/ForwardedParser.java
+++ b/src/com/isode/stroke/parser/payloadparsers/ForwardedParser.java
@@ -1,10 +1,5 @@
/*
- * Copyright (c) 2014 Kevin Smith and Remko Tronçon
- * All rights reserved.
- */
-
-/*
- * Copyright (c) 2014, Isode Limited, London, England.
+ * Copyright (c) 2014-2015, Isode Limited, London, England.
* All rights reserved.
*/
@@ -28,13 +23,13 @@ public class ForwardedParser extends GenericPayloadParser<Forwarded> {
public void handleStartElement(String element, String ns, AttributeMap attributes) {
if (level_ == 1) {
- if (element == "iq") { /* begin parsing a nested stanza? */
+ if ("iq".equals(element)) { /* begin parsing a nested stanza? */
childParser_ = new IQParser(factories_);
- } else if (element == "message") {
+ } else if ("message".equals(element)) {
childParser_ = new MessageParser(factories_);
- } else if (element == "presence") {
+ } else if ("presence".equals(element)) {
childParser_ = new PresenceParser(factories_);
- } else if (element == "delay" && ns == "urn:xmpp:delay") {
+ } else if ("delay".equals(element) && "urn:xmpp:delay".equals(ns)) {
delayParser_ = new DelayParser();
}
}
diff --git a/src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java b/src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java
index 5236d42..e443a00 100644
--- a/src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java
+++ b/src/com/isode/stroke/parser/payloadparsers/FullPayloadParserFactoryCollection.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2010-2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.parser.payloadparsers;
@@ -12,27 +8,27 @@ import com.isode.stroke.parser.GenericPayloadParserFactory;
import com.isode.stroke.parser.GenericPayloadParserFactory2;
import com.isode.stroke.parser.PayloadParserFactory;
import com.isode.stroke.parser.PayloadParserFactoryCollection;
-import com.isode.stroke.parser.PubSubOwnerPubSubParser;
+import com.isode.stroke.parser.payloadparsers.PubSubOwnerPubSubParser;
public class FullPayloadParserFactoryCollection extends PayloadParserFactoryCollection {
public FullPayloadParserFactoryCollection() {
/* TODO: Port more */
//addFactory(new GenericPayloadParserFactory<IBBParser>("", "http://jabber.org/protocol/ibb"));
- //addFactory(new GenericPayloadParserFactory<StatusShowParser>("show", StatusShowParser.class));
- //addFactory(new GenericPayloadParserFactory<StatusParser>("status", StatusParser.class));
- //addFactory(new GenericPayloadParserFactory<ReplaceParser>("replace", "http://swift.im/protocol/replace"));
+ addFactory(new GenericPayloadParserFactory<StatusShowParser>("show", StatusShowParser.class));
+ addFactory(new GenericPayloadParserFactory<StatusParser>("status", StatusParser.class));
+ addFactory(new GenericPayloadParserFactory<ReplaceParser>("replace", "http://swift.im/protocol/replace", ReplaceParser.class));
+ addFactory(new GenericPayloadParserFactory<ReplaceParser>("replace", "urn:xmpp:message-correct:0", ReplaceParser.class));
addFactory(new GenericPayloadParserFactory<LastParser>("query", "jabber:iq:last", LastParser.class));
addFactory(new GenericPayloadParserFactory<BodyParser>("body", BodyParser.class));
- //addFactory(new GenericPayloadParserFactory<SubjectParser>("subject", SubjectParser.class));
+ addFactory(new GenericPayloadParserFactory<SubjectParser>("subject", SubjectParser.class));
//addFactory(new GenericPayloadParserFactory<PriorityParser>("priority", PriorityParser.class));
addFactory(new ErrorParserFactory(this));
addFactory(new SoftwareVersionParserFactory());
- //addFactory(new StorageParserFactory());
+ addFactory(new GenericPayloadParserFactory<StorageParser>("storage", "storage:bookmarks", StorageParser.class));
addFactory(new RosterParserFactory());
addFactory(new GenericPayloadParserFactory<DiscoInfoParser>("query", "http://jabber.org/protocol/disco#info", DiscoInfoParser.class));
addFactory(new GenericPayloadParserFactory<DiscoItemsParser>("query", "http://jabber.org/protocol/disco#items", DiscoItemsParser.class));
addFactory(new GenericPayloadParserFactory<CapsInfoParser> ("c", "http://jabber.org/protocol/caps", CapsInfoParser.class));
- //addFactory(new CapsInfoParserFactory());
addFactory(new ResourceBindParserFactory());
addFactory(new StartSessionParserFactory());
//addFactory(new SecurityLabelParserFactory());
@@ -45,9 +41,9 @@ public class FullPayloadParserFactoryCollection extends PayloadParserFactoryColl
//addFactory(new StreamInitiationParserFactory());
//addFactory(new BytestreamsParserFactory());
//addFactory(new VCardUpdateParserFactory());
- //addFactory(new VCardParserFactory());
- //addFactory(new PrivateStorageParserFactory(this));
- addFactory(new ChatStateParserFactory());
+ addFactory(new GenericPayloadParserFactory<VCardParser>("vCard", "vcard-temp", VCardParser.class));
+ addFactory(new PrivateStorageParserFactory(this));
+ addFactory(new ChatStateParserFactory());
//addFactory(new DelayParserFactory());
addFactory(new MUCUserPayloadParserFactory(this));
addFactory(new MUCOwnerPayloadParserFactory(this));
@@ -59,6 +55,7 @@ public class FullPayloadParserFactoryCollection extends PayloadParserFactoryColl
"http://jabber.org/protocol/muc#user",MUCDestroyPayloadParser.class));
addFactory(new GenericPayloadParserFactory<MUCDestroyPayloadParser>("destroy",
"http://jabber.org/protocol/muc#owner",MUCDestroyPayloadParser.class));
+ addFactory(new GenericPayloadParserFactory<IdleParser>("idle", "urn:xmpp:idle:1",IdleParser.class));
addFactory(new GenericPayloadParserFactory2<PubSubParser>("pubsub", "http://jabber.org/protocol/pubsub", this, PubSubParser.class));
addFactory(new GenericPayloadParserFactory2<PubSubOwnerPubSubParser>("pubsub", "http://jabber.org/protocol/pubsub#owner", this, PubSubOwnerPubSubParser.class));
diff --git a/src/com/isode/stroke/parser/payloadparsers/IdleParser.java b/src/com/isode/stroke/parser/payloadparsers/IdleParser.java
new file mode 100644
index 0000000..3986168
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/IdleParser.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2011-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.parser.payloadparsers;
+
+import java.util.Date;
+
+import com.isode.stroke.base.DateTime;
+import com.isode.stroke.elements.Idle;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+
+public class IdleParser extends GenericPayloadParser<Idle> {
+
+ private int level_ = 0;
+
+ public IdleParser() {
+ super(new Idle());
+ }
+
+ public void handleStartElement(String element, String ns, AttributeMap attributes) {
+ if (level_ == 0) {
+ Date since = DateTime.stringToDate(attributes.getAttribute("since"));
+ getPayloadInternal().setSince(since);
+ }
+ ++level_;
+ }
+
+ public void handleEndElement(String element, String ns) {
+ --level_;
+ }
+
+ public void handleCharacterData(String data) {
+
+ }
+
+}
diff --git a/src/com/isode/stroke/parser/payloadparsers/MAMQueryParser.java b/src/com/isode/stroke/parser/payloadparsers/MAMQueryParser.java
index 2543a68..dc41e9d 100644
--- a/src/com/isode/stroke/parser/payloadparsers/MAMQueryParser.java
+++ b/src/com/isode/stroke/parser/payloadparsers/MAMQueryParser.java
@@ -1,10 +1,5 @@
/*
-* Copyright (c) 2014 Kevin Smith and Remko Tronçon
-* All rights reserved.
-*/
-
-/*
-* Copyright (c) 2014, Isode Limited, London, England.
+* Copyright (c) 2014-2015, Isode Limited, London, England.
* All rights reserved.
*/
@@ -34,9 +29,9 @@ public class MAMQueryParser extends GenericPayloadParser<MAMQuery> {
payloadInternal.setNode(nodeValue);
}
} else if (level_ == 1) {
- if (element == "x" && ns == "jabber:x:data") {
+ if ("x".equals(element) && "jabber:x:data".equals(ns)) {
formParser_ = new FormParser();
- } else if (element == "set" && ns == "http://jabber.org/protocol/rsm") {
+ } else if ("set".equals(element) && "http://jabber.org/protocol/rsm".equals(ns)) {
resultSetParser_ = new ResultSetParser();
}
}
diff --git a/src/com/isode/stroke/parser/payloadparsers/MAMResultParser.java b/src/com/isode/stroke/parser/payloadparsers/MAMResultParser.java
index cb3d7fd..3e71712 100644
--- a/src/com/isode/stroke/parser/payloadparsers/MAMResultParser.java
+++ b/src/com/isode/stroke/parser/payloadparsers/MAMResultParser.java
@@ -1,10 +1,5 @@
/*
-* Copyright (c) 2014 Kevin Smith and Remko Tronçon
-* All rights reserved.
-*/
-
-/*
-* Copyright (c) 2014, Isode Limited, London, England.
+* Copyright (c) 2014-2015, Isode Limited, London, England.
* All rights reserved.
*/
@@ -33,7 +28,7 @@ public class MAMResultParser extends GenericPayloadParser<MAMResult> {
getPayloadInternal().setQueryID(attributeValue);
}
} else if (level_ == 1) {
- if (element == "forwarded" && ns == "urn:xmpp:forward:0") {
+ if ("forwarded".equals(element) && "urn:xmpp:forward:0".equals(ns)) {
payloadParser_ = new ForwardedParser(factories_);
}
}
diff --git a/src/com/isode/stroke/parser/payloadparsers/MUCInvitationPayloadParser.java b/src/com/isode/stroke/parser/payloadparsers/MUCInvitationPayloadParser.java
index 27ab0d8..473bcdd 100644
--- a/src/com/isode/stroke/parser/payloadparsers/MUCInvitationPayloadParser.java
+++ b/src/com/isode/stroke/parser/payloadparsers/MUCInvitationPayloadParser.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2011, Kevin Smith
+ * Copyright (c) 2011-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.parser.payloadparsers;
@@ -11,6 +7,7 @@ package com.isode.stroke.parser.payloadparsers;
import com.isode.stroke.elements.MUCInvitationPayload;
import com.isode.stroke.jid.JID;
import com.isode.stroke.parser.GenericPayloadTreeParser;
+import com.isode.stroke.parser.tree.NullParserElement;
import com.isode.stroke.parser.tree.ParserElement;
/**
@@ -31,5 +28,7 @@ public class MUCInvitationPayloadParser extends GenericPayloadTreeParser<MUCInvi
invite.setPassword(root.getAttributes().getAttribute("password"));
invite.setReason(root.getAttributes().getAttribute("reason"));
invite.setThread(root.getAttributes().getAttribute("thread"));
+ ParserElement impromptuNode = root.getChild("impromptu", "http://swift.im/impromptu");
+ invite.setIsImpromptu(!(impromptuNode instanceof NullParserElement));
}
}
diff --git a/src/com/isode/stroke/parser/payloadparsers/PrivateStorageParser.java b/src/com/isode/stroke/parser/payloadparsers/PrivateStorageParser.java
new file mode 100644
index 0000000..df62938
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/PrivateStorageParser.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.parser.payloadparsers;
+
+import com.isode.stroke.elements.PrivateStorage;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+import com.isode.stroke.parser.PayloadParser;
+import com.isode.stroke.parser.PayloadParserFactory;
+import com.isode.stroke.parser.PayloadParserFactoryCollection;
+
+public class PrivateStorageParser extends GenericPayloadParser<PrivateStorage> {
+
+ private PayloadParserFactoryCollection factories;
+ private int level;
+ private PayloadParser currentPayloadParser;
+
+ public PrivateStorageParser(PayloadParserFactoryCollection factories) {
+ super(new PrivateStorage());
+ this.factories = factories;
+ }
+
+ public void handleStartElement(String element, String ns, AttributeMap attributes) {
+ if (level == 1) {
+ PayloadParserFactory payloadParserFactory = factories.getPayloadParserFactory(element, ns, attributes);
+ if (payloadParserFactory != null) {
+ currentPayloadParser = payloadParserFactory.createPayloadParser();
+ }
+ }
+
+ if (level >= 1 && currentPayloadParser != null) {
+ currentPayloadParser.handleStartElement(element, ns, attributes);
+ }
+ ++level;
+ }
+
+ public void handleEndElement(String element, String ns) {
+ --level;
+ if (currentPayloadParser != null) {
+ if (level >= 1) {
+ currentPayloadParser.handleEndElement(element, ns);
+ }
+
+ if (level == 1) {
+ getPayloadInternal().setPayload(currentPayloadParser.getPayload());
+ }
+ }
+ }
+
+ public void handleCharacterData(String data) {
+ if (level > 1 && currentPayloadParser != null) {
+ currentPayloadParser.handleCharacterData(data);
+ }
+ }
+}
diff --git a/src/com/isode/stroke/parser/payloadparsers/PrivateStorageParserFactory.java b/src/com/isode/stroke/parser/payloadparsers/PrivateStorageParserFactory.java
new file mode 100644
index 0000000..b2b3db0
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/PrivateStorageParserFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010-2015 Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.parser.payloadparsers;
+
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.PayloadParser;
+import com.isode.stroke.parser.PayloadParserFactory;
+import com.isode.stroke.parser.PayloadParserFactoryCollection;
+
+public class PrivateStorageParserFactory implements PayloadParserFactory {
+
+ private PayloadParserFactoryCollection factories;
+
+ public PrivateStorageParserFactory(PayloadParserFactoryCollection factories) {
+ this.factories = factories;
+ }
+
+ @Override
+ public boolean canParse(String element, String ns, AttributeMap map) {
+ return "query".equals(element) && "jabber:iq:private".equals(ns);
+ }
+
+ @Override
+ public PayloadParser createPayloadParser() {
+ return new PrivateStorageParser(factories);
+ }
+}
diff --git a/src/com/isode/stroke/parser/payloadparsers/PubSubEventParser.java b/src/com/isode/stroke/parser/payloadparsers/PubSubEventParser.java
index d7bdbe6..f1f7701 100644
--- a/src/com/isode/stroke/parser/payloadparsers/PubSubEventParser.java
+++ b/src/com/isode/stroke/parser/payloadparsers/PubSubEventParser.java
@@ -1,9 +1,5 @@
/*
-* Copyright (c) 2014, Isode Limited, London, England.
-* All rights reserved.
-*/
-/*
-* Copyright (c) 2014, Remko Tronçon.
+* Copyright (c) 2013-2015, Isode Limited, London, England.
* All rights reserved.
*/
@@ -25,22 +21,22 @@ public PubSubEventParser(PayloadParserFactoryCollection parser) {
@Override
public void handleStartElement(String element, String ns, AttributeMap attributes) {
if (level_ == 1) {
- if (element == "items" && ns == "http://jabber.org/protocol/pubsub#event") {
+ if ("items".equals(element) && "http://jabber.org/protocol/pubsub#event".equals(ns)) {
currentPayloadParser_ = new PubSubEventItemsParser(parsers_);
}
- if (element == "collection" && ns == "http://jabber.org/protocol/pubsub#event") {
+ if ("collection".equals(element) && "http://jabber.org/protocol/pubsub#event".equals(ns)) {
currentPayloadParser_ = new PubSubEventCollectionParser(parsers_);
}
- if (element == "purge" && ns == "http://jabber.org/protocol/pubsub#event") {
+ if ("purge".equals(element) && "http://jabber.org/protocol/pubsub#event".equals(ns)) {
currentPayloadParser_ = new PubSubEventPurgeParser(parsers_);
}
- if (element == "configuration" && ns == "http://jabber.org/protocol/pubsub#event") {
+ if ("configuration".equals(element) && "http://jabber.org/protocol/pubsub#event".equals(ns)) {
currentPayloadParser_ = new PubSubEventConfigurationParser(parsers_);
}
- if (element == "delete" && ns == "http://jabber.org/protocol/pubsub#event") {
+ if ("delete".equals(element) && "http://jabber.org/protocol/pubsub#event".equals(ns)) {
currentPayloadParser_ = new PubSubEventDeleteParser(parsers_);
}
- if (element == "subscription" && ns == "http://jabber.org/protocol/pubsub#event") {
+ if ("subscription".equals(element) && "http://jabber.org/protocol/pubsub#event".equals(ns)) {
currentPayloadParser_ = new PubSubEventSubscriptionParser(parsers_);
}
}
diff --git a/src/com/isode/stroke/parser/PubSubOwnerPubSubParser.java b/src/com/isode/stroke/parser/payloadparsers/PubSubOwnerPubSubParser.java
index ac0c4fe..d711cf1 100644
--- a/src/com/isode/stroke/parser/PubSubOwnerPubSubParser.java
+++ b/src/com/isode/stroke/parser/payloadparsers/PubSubOwnerPubSubParser.java
@@ -1,16 +1,16 @@
/*
- * Copyright (c) 2014, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2014, Remko Tronçon.
+ * Copyright (c) 2013-2015, Isode Limited, London, England.
* All rights reserved.
*/
-package com.isode.stroke.parser;
+package com.isode.stroke.parser.payloadparsers;
import com.isode.stroke.elements.PubSubOwnerPayload;
import com.isode.stroke.elements.PubSubOwnerPubSub;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+import com.isode.stroke.parser.PayloadParser;
+import com.isode.stroke.parser.PayloadParserFactoryCollection;
import com.isode.stroke.parser.payloadparsers.PubSubOwnerAffiliationsParser;
import com.isode.stroke.parser.payloadparsers.PubSubOwnerConfigureParser;
import com.isode.stroke.parser.payloadparsers.PubSubOwnerDefaultParser;
@@ -30,30 +30,30 @@ public class PubSubOwnerPubSubParser extends
public void handleStartElement(String element, String ns,
AttributeMap attributes) {
if (level_ == 1) {
- if (element == "configure"
- && ns == "http://jabber.org/protocol/pubsub#owner") {
+ if ("configure".equals(element)
+ && "http://jabber.org/protocol/pubsub#owner".equals(ns)) {
currentPayloadParser_ = new PubSubOwnerConfigureParser(parsers_);
}
- if (element == "subscriptions"
- && ns == "http://jabber.org/protocol/pubsub#owner") {
+ if ("subscriptions".equals(element)
+ && "http://jabber.org/protocol/pubsub#owner".equals(ns)) {
currentPayloadParser_ = new PubSubOwnerSubscriptionsParser(
parsers_);
}
- if (element == "default"
- && ns == "http://jabber.org/protocol/pubsub#owner") {
+ if ("default".equals(element)
+ && "http://jabber.org/protocol/pubsub#owner".equals(ns)) {
currentPayloadParser_ = new PubSubOwnerDefaultParser(parsers_);
}
- if (element == "purge"
- && ns == "http://jabber.org/protocol/pubsub#owner") {
+ if ("purge".equals(element)
+ && "http://jabber.org/protocol/pubsub#owner".equals(ns)) {
currentPayloadParser_ = new PubSubOwnerPurgeParser(parsers_);
}
- if (element == "affiliations"
- && ns == "http://jabber.org/protocol/pubsub#owner") {
+ if ("affiliations".equals(element)
+ && "http://jabber.org/protocol/pubsub#owner".equals(ns)) {
currentPayloadParser_ = new PubSubOwnerAffiliationsParser(
parsers_);
}
- if (element == "delete"
- && ns == "http://jabber.org/protocol/pubsub#owner") {
+ if ("delete".equals(element)
+ && "http://jabber.org/protocol/pubsub#owner".equals(ns)) {
currentPayloadParser_ = new PubSubOwnerDeleteParser(parsers_);
}
}
diff --git a/src/com/isode/stroke/parser/payloadparsers/PubSubParser.java b/src/com/isode/stroke/parser/payloadparsers/PubSubParser.java
index 134d654..8497555 100644
--- a/src/com/isode/stroke/parser/payloadparsers/PubSubParser.java
+++ b/src/com/isode/stroke/parser/payloadparsers/PubSubParser.java
@@ -1,9 +1,5 @@
/*
-* Copyright (c) 2014, Isode Limited, London, England.
-* All rights reserved.
-*/
-/*
-* Copyright (c) 2014, Remko Tronçon.
+* Copyright (c) 2013-2015, Isode Limited, London, England.
* All rights reserved.
*/
@@ -30,40 +26,40 @@ public class PubSubParser extends GenericPayloadParser<PubSub> {
public void handleStartElement(String element, String ns, AttributeMap attributes) {
if (level_ == 1) {
- if (element == "items" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("items".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubItemsParser(parsers_);
}
- if (element == "create" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("create".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubCreateParser(parsers_);
}
- if (element == "publish" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("publish".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubPublishParser(parsers_);
}
- if (element == "affiliations" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("affiliations".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubAffiliationsParser(parsers_);
}
- if (element == "retract" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("retract".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubRetractParser(parsers_);
}
- if (element == "options" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("options".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubOptionsParser(parsers_);
}
- if (element == "configure" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("configure".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubConfigureParser(parsers_);
}
- if (element == "default" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("default".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubDefaultParser(parsers_);
}
- if (element == "subscriptions" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("subscriptions".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubSubscriptionsParser(parsers_);
}
- if (element == "subscribe" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("subscribe".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubSubscribeParser(parsers_);
}
- if (element == "unsubscribe" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("unsubscribe".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubUnsubscribeParser(parsers_);
}
- if (element == "subscription" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("subscription".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
currentPayloadParser_ = new PubSubSubscriptionParser(parsers_);
}
}
@@ -83,10 +79,10 @@ public class PubSubParser extends GenericPayloadParser<PubSub> {
if (level_ == 1) {
if (currentPayloadParser_ != null) {
- if (element == "options" && ns == "http://jabber.org/protocol/pubsub") {
+ if ("options".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
optionsPayload_ = (PubSubOptions)currentPayloadParser_.getPayload();
}
- else if (element == "configure" && ns == "http://jabber.org/protocol/pubsub") {
+ else if ("configure".equals(element) && "http://jabber.org/protocol/pubsub".equals(ns)) {
configurePayload_ = (PubSubConfigure)currentPayloadParser_.getPayload();
}
else {
diff --git a/src/com/isode/stroke/parser/payloadparsers/ReplaceParser.java b/src/com/isode/stroke/parser/payloadparsers/ReplaceParser.java
new file mode 100644
index 0000000..601ab7d
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/ReplaceParser.java
@@ -0,0 +1,39 @@
+/*
+* Copyright (c) 2014-2015, Isode Limited, London, England.
+* All rights reserved.
+*/
+/*
+ * Copyright (c) 2011 Vlad Voicu
+ * Licensed under the Simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.parser.payloadparsers;
+
+import com.isode.stroke.elements.Replace;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+
+public class ReplaceParser extends GenericPayloadParser<Replace> {
+
+ public ReplaceParser() {
+ super(new Replace());
+ }
+
+ public void handleStartElement(String element, String ns, AttributeMap attributes) {
+ if (level_ == 0) {
+ String id = attributes.getAttribute("id");
+ getPayloadInternal().setID(id);
+ }
+ ++level_;
+ }
+
+ public void handleEndElement(String element, String ns) {
+ --level_;
+ }
+
+ public void handleCharacterData(String data) {
+ }
+
+ private int level_;
+}
diff --git a/src/com/isode/stroke/parser/payloadparsers/ResultSetParser.java b/src/com/isode/stroke/parser/payloadparsers/ResultSetParser.java
index 26343a0..628c522 100644
--- a/src/com/isode/stroke/parser/payloadparsers/ResultSetParser.java
+++ b/src/com/isode/stroke/parser/payloadparsers/ResultSetParser.java
@@ -1,10 +1,5 @@
/*
-* Copyright (c) 2014 Kevin Smith and Remko Tronçon
-* All rights reserved.
-*/
-
-/*
-* Copyright (c) 2014, Isode Limited, London, England.
+* Copyright (c) 2014-2015, Isode Limited, London, England.
* All rights reserved.
*/
@@ -23,7 +18,7 @@ public class ResultSetParser extends GenericPayloadParser<ResultSet> {
public void handleStartElement(String element, String ns, AttributeMap attributes) {
currentText_ = "";
if (level_ == 1) {
- if (element == "first" && ns == "http://jabber.org/protocol/rsm") {
+ if ("first".equals(element) && "http://jabber.org/protocol/rsm".equals(ns)) {
String attributeValue = attributes.getAttributeValue("index");
if (attributeValue != null) {
getPayloadInternal().setFirstIDIndex(Long.parseLong(attributeValue));
@@ -36,17 +31,17 @@ public class ResultSetParser extends GenericPayloadParser<ResultSet> {
public void handleEndElement(String element, String ns) {
--level_;
if (level_ == 1) {
- if (element == "max") {
+ if ("max".equals(element)) {
getPayloadInternal().setMaxItems(Long.parseLong(currentText_));
- } else if (element == "count") {
+ } else if ("count".equals(element)) {
getPayloadInternal().setCount(Long.parseLong(currentText_));
- } else if (element == "first") {
+ } else if ("first".equals(element)) {
getPayloadInternal().setFirstID(currentText_);
- } else if (element == "last") {
+ } else if ("last".equals(element)) {
getPayloadInternal().setLastID(currentText_);
- } else if (element == "after") {
+ } else if ("after".equals(element)) {
getPayloadInternal().setAfter(currentText_);
- } else if (element == "before") {
+ } else if ("before".equals(element)) {
getPayloadInternal().setBefore(currentText_);
}
}
diff --git a/src/com/isode/stroke/parser/payloadparsers/StatusParser.java b/src/com/isode/stroke/parser/payloadparsers/StatusParser.java
new file mode 100644
index 0000000..5a463ef
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/StatusParser.java
@@ -0,0 +1,35 @@
+/*
+* Copyright (c) 2010-2015, Isode Limited, London, England.
+* All rights reserved.
+*/
+
+package com.isode.stroke.parser.payloadparsers;
+
+import com.isode.stroke.elements.Status;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+
+public class StatusParser extends GenericPayloadParser<Status> {
+
+ public StatusParser() {
+ super(new Status());
+ }
+
+ public void handleStartElement(String element, String ns, AttributeMap attributes) {
+ ++level_;
+ }
+
+ public void handleEndElement(String element, String ns) {
+ --level_;
+ if (level_ == 0) {
+ getPayloadInternal().setText(text_);
+ }
+ }
+
+ public void handleCharacterData(String data) {
+ text_ += data;
+ }
+
+ private int level_;
+ private String text_ = "";
+}
diff --git a/src/com/isode/stroke/parser/payloadparsers/StatusShowParser.java b/src/com/isode/stroke/parser/payloadparsers/StatusShowParser.java
new file mode 100644
index 0000000..abe8692
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/StatusShowParser.java
@@ -0,0 +1,49 @@
+/*
+* Copyright (c) 2010-2015, Isode Limited, London, England.
+* All rights reserved.
+*/
+
+package com.isode.stroke.parser.payloadparsers;
+
+import com.isode.stroke.elements.StatusShow;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+
+public class StatusShowParser extends GenericPayloadParser<StatusShow> {
+
+ public StatusShowParser() {
+ super(new StatusShow());
+ }
+
+ public void handleStartElement(String element, String ns, AttributeMap attributes) {
+ ++level_;
+ }
+
+ public void handleEndElement(String element, String ns) {
+ --level_;
+ if (level_ == 0) {
+ if ("away".equals(text_)) {
+ getPayloadInternal().setType(StatusShow.Type.Away);
+ }
+ else if ("chat".equals(text_)) {
+ getPayloadInternal().setType(StatusShow.Type.FFC);
+ }
+ else if ("xa".equals(text_)) {
+ getPayloadInternal().setType(StatusShow.Type.XA);
+ }
+ else if ("dnd".equals(text_)) {
+ getPayloadInternal().setType(StatusShow.Type.DND);
+ }
+ else {
+ getPayloadInternal().setType(StatusShow.Type.Online);
+ }
+ }
+ }
+
+ public void handleCharacterData(String data) {
+ text_ = text_ == null ? data : text_ + data;
+ }
+
+ private int level_;
+ private String text_;
+}
diff --git a/src/com/isode/stroke/parser/payloadparsers/StorageParser.java b/src/com/isode/stroke/parser/payloadparsers/StorageParser.java
new file mode 100644
index 0000000..7898938
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/StorageParser.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.parser.payloadparsers;
+
+import com.isode.stroke.elements.Storage;
+import com.isode.stroke.elements.Storage.Room;
+import com.isode.stroke.elements.Storage.URL;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+
+public class StorageParser extends GenericPayloadParser<Storage> {
+
+ private final static int BookmarkLevel = 1;
+ private static final int DetailLevel = 2;
+ private int level;
+ private String currentText;
+ private Room room;
+ private URL url;
+
+ public StorageParser() {
+ super(new Storage());
+ }
+
+ public void handleStartElement(String element, String ns, AttributeMap attributes) {
+ if (level == BookmarkLevel) {
+ if ("conference".equals(element)) {
+ assert(room == null);
+ room = new Storage.Room();
+ room.autoJoin = attributes.getBoolAttribute("autojoin", false);
+ room.jid = new JID(attributes.getAttribute("jid"));
+ room.name = attributes.getAttribute("name");
+ }
+ else if ("url".equals(element)) {
+ assert(url == null);
+ url = new Storage.URL();
+ url.name = attributes.getAttribute("name");
+ url.url = attributes.getAttribute("url");
+ }
+ }
+ else if (level == DetailLevel) {
+ currentText = "";
+ }
+ ++level;
+ }
+
+ public void handleEndElement(String element, String ns) {
+ --level;
+ if (level == BookmarkLevel) {
+ if ("conference".equals(element)) {
+ assert(room != null);
+ getPayloadInternal().addRoom(room);
+ room = null;
+ }
+ else if ("url".equals(element)) {
+ assert(url != null);
+ getPayloadInternal().addURL(url);
+ url = null;
+ }
+ }
+ else if (level == DetailLevel && room != null) {
+ if ("nick".equals(element)) {
+ room.nick = currentText;
+ }
+ else if ("password".equals(element)) {
+ room.password = currentText;
+ }
+ }
+ }
+
+ public void handleCharacterData(String data) {
+ currentText += data;
+ }
+}
diff --git a/src/com/isode/stroke/parser/payloadparsers/SubjectParser.java b/src/com/isode/stroke/parser/payloadparsers/SubjectParser.java
new file mode 100644
index 0000000..5aa69c5
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/SubjectParser.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.parser.payloadparsers;
+
+import com.isode.stroke.elements.Subject;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+
+public class SubjectParser extends GenericPayloadParser<Subject> {
+
+
+ private int level_;
+ private String text_ = "";
+
+ public SubjectParser() {
+ super(new Subject());
+ }
+
+ public void handleStartElement(String element, String ns, AttributeMap attributes) {
+ ++level_;
+ }
+
+ public void handleEndElement(String element, String ns) {
+ --level_;
+ if (level_ == 0) {
+ getPayloadInternal().setText(text_);
+ }
+ }
+
+ public void handleCharacterData(String data) {
+ text_ += data;
+ }
+}
diff --git a/src/com/isode/stroke/parser/payloadparsers/VCardParser.java b/src/com/isode/stroke/parser/payloadparsers/VCardParser.java
new file mode 100644
index 0000000..b955dc4
--- /dev/null
+++ b/src/com/isode/stroke/parser/payloadparsers/VCardParser.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.parser.payloadparsers;
+
+import java.util.Stack;
+
+import com.isode.stroke.base.DateTime;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.parser.AttributeMap;
+import com.isode.stroke.parser.GenericPayloadParser;
+import com.isode.stroke.parser.SerializingParser;
+import com.isode.stroke.stringcodecs.Base64;
+
+public class VCardParser extends GenericPayloadParser<VCard> {
+
+ Stack<String> elementStack_ = new Stack<String>();
+ VCard.EMailAddress currentEMailAddress_;
+ VCard.Telephone currentTelephone_;
+ VCard.Address currentAddress_;
+ VCard.AddressLabel currentAddressLabel_;
+ VCard.Organization currentOrganization_;
+ SerializingParser unknownContentParser_;
+ String currentText_ = "";
+
+ public VCardParser() {
+ super(new VCard());
+ }
+
+ public void handleStartElement(String element, String ns, AttributeMap attributes) {
+ elementStack_.add(element);
+ String elementHierarchy = getElementHierarchy();
+ if ("/vCard/EMAIL".equals(elementHierarchy)) {
+ currentEMailAddress_ = new VCard.EMailAddress();
+ }
+ if ("/vCard/TEL".equals(elementHierarchy)) {
+ currentTelephone_ = new VCard.Telephone();
+ }
+ if ("/vCard/ADR".equals(elementHierarchy)) {
+ currentAddress_ = new VCard.Address();
+ }
+ if ("/vCard/LABEL".equals(elementHierarchy)) {
+ currentAddressLabel_ = new VCard.AddressLabel();
+ }
+ if ("/vCard/ORG".equals(elementHierarchy)) {
+ currentOrganization_ = new VCard.Organization();
+ }
+ if (elementStack_.size() == 2) {
+ assert(unknownContentParser_ == null);
+ unknownContentParser_ = new SerializingParser();
+ unknownContentParser_.handleStartElement(element, ns, attributes);
+ }
+ else if (unknownContentParser_ != null) {
+ unknownContentParser_.handleStartElement(element, ns, attributes);
+ }
+
+ currentText_ = "";
+ }
+
+ public void handleEndElement(String element, String ns) {
+ if (unknownContentParser_ != null) {
+ unknownContentParser_.handleEndElement(element, ns);
+ }
+
+ String elementHierarchy = getElementHierarchy();
+ if ("/vCard/VERSION".equals(elementHierarchy)) {
+ getPayloadInternal().setVersion(currentText_);
+ }
+ else if ("/vCard/FN".equals(elementHierarchy)) {
+ getPayloadInternal().setFullName(currentText_);
+ }
+ else if ("/vCard/N/FAMILY".equals(elementHierarchy)) {
+ getPayloadInternal().setFamilyName(currentText_);
+ }
+ else if ("/vCard/N/GIVEN".equals(elementHierarchy)) {
+ getPayloadInternal().setGivenName(currentText_);
+ }
+ else if ("/vCard/N/MIDDLE".equals(elementHierarchy)) {
+ getPayloadInternal().setMiddleName(currentText_);
+ }
+ else if ("/vCard/N/PREFIX".equals(elementHierarchy)) {
+ getPayloadInternal().setPrefix(currentText_);
+ }
+ else if ("/vCard/N/SUFFIX".equals(elementHierarchy)) {
+ getPayloadInternal().setSuffix(currentText_);
+ }
+ else if ("/vCard/N".equals(elementHierarchy)) {
+ }
+ else if ("/vCard/NICKNAME".equals(elementHierarchy)) {
+ getPayloadInternal().setNickname(currentText_);
+ }
+ else if ("/vCard/PHOTO/TYPE".equals(elementHierarchy)) {
+ getPayloadInternal().setPhotoType(currentText_);
+ }
+ else if ("/vCard/PHOTO/BINVAL".equals(elementHierarchy)) {
+ getPayloadInternal().setPhoto(Base64.decode(currentText_.replace("\n", "").replace("\r", "")));
+ }
+ else if ("/vCard/PHOTO".equals(elementHierarchy)) {
+ }
+ else if ("/vCard/EMAIL/USERID".equals(elementHierarchy)) {
+ currentEMailAddress_.address = currentText_;
+ }
+ else if ("/vCard/EMAIL/HOME".equals(elementHierarchy)) {
+ currentEMailAddress_.isHome = true;
+ }
+ else if ("/vCard/EMAIL/WORK".equals(elementHierarchy)) {
+ currentEMailAddress_.isWork = true;
+ }
+ else if ("/vCard/EMAIL/INTERNET".equals(elementHierarchy)) {
+ currentEMailAddress_.isInternet = true;
+ }
+ else if ("/vCard/EMAIL/X400".equals(elementHierarchy)) {
+ currentEMailAddress_.isX400 = true;
+ }
+ else if ("/vCard/EMAIL/PREF".equals(elementHierarchy)) {
+ currentEMailAddress_.isPreferred = true;
+ }
+ else if ("/vCard/EMAIL".equals(elementHierarchy) && currentEMailAddress_.address != null && !currentEMailAddress_.address.isEmpty()) {
+ getPayloadInternal().addEMailAddress(currentEMailAddress_);
+ }
+ else if ("/vCard/BDAY".equals(elementHierarchy) && !currentText_.isEmpty()) {
+ getPayloadInternal().setBirthday(DateTime.stringToDate(currentText_));
+ }
+ else if ("/vCard/TEL/NUMBER".equals(elementHierarchy)) {
+ currentTelephone_.number = currentText_;
+ }
+ else if ("/vCard/TEL/HOME".equals(elementHierarchy)) {
+ currentTelephone_.isHome = true;
+ }
+ else if ("/vCard/TEL/WORK".equals(elementHierarchy)) {
+ currentTelephone_.isWork = true;
+ }
+ else if ("/vCard/TEL/VOICE".equals(elementHierarchy)) {
+ currentTelephone_.isVoice = true;
+ }
+ else if ("/vCard/TEL/FAX".equals(elementHierarchy)) {
+ currentTelephone_.isFax = true;
+ }
+ else if ("/vCard/TEL/PAGER".equals(elementHierarchy)) {
+ currentTelephone_.isPager = true;
+ }
+ else if ("/vCard/TEL/MSG".equals(elementHierarchy)) {
+ currentTelephone_.isMSG = true;
+ }
+ else if ("/vCard/TEL/CELL".equals(elementHierarchy)) {
+ currentTelephone_.isCell = true;
+ }
+ else if ("/vCard/TEL/VIDEO".equals(elementHierarchy)) {
+ currentTelephone_.isVideo = true;
+ }
+ else if ("/vCard/TEL/BBS".equals(elementHierarchy)) {
+ currentTelephone_.isBBS = true;
+ }
+ else if ("/vCard/TEL/MODEM".equals(elementHierarchy)) {
+ currentTelephone_.isModem = true;
+ }
+ else if ("/vCard/TEL/ISDN".equals(elementHierarchy)) {
+ currentTelephone_.isISDN = true;
+ }
+ else if ("/vCard/TEL/PCS".equals(elementHierarchy)) {
+ currentTelephone_.isPCS = true;
+ }
+ else if ("/vCard/TEL/PREF".equals(elementHierarchy)) {
+ currentTelephone_.isPreferred = true;
+ }
+ else if ("/vCard/TEL".equals(elementHierarchy) && currentTelephone_.number != null && !currentTelephone_.number.isEmpty()) {
+ getPayloadInternal().addTelephone(currentTelephone_);
+ }
+ else if ("/vCard/ADR/HOME".equals(elementHierarchy)) {
+ currentAddress_.isHome = true;
+ }
+ else if ("/vCard/ADR/WORK".equals(elementHierarchy)) {
+ currentAddress_.isWork = true;
+ }
+ else if ("/vCard/ADR/POSTAL".equals(elementHierarchy)) {
+ currentAddress_.isPostal = true;
+ }
+ else if ("/vCard/ADR/PARCEL".equals(elementHierarchy)) {
+ currentAddress_.isParcel = true;
+ }
+ else if ("/vCard/ADR/DOM".equals(elementHierarchy)) {
+ currentAddress_.deliveryType = VCard.DeliveryType.DomesticDelivery;
+ }
+ else if ("/vCard/ADR/INTL".equals(elementHierarchy)) {
+ currentAddress_.deliveryType = VCard.DeliveryType.InternationalDelivery;
+ }
+ else if ("/vCard/ADR/PREF".equals(elementHierarchy)) {
+ currentAddress_.isPreferred = true;
+ }
+ else if ("/vCard/ADR/POBOX".equals(elementHierarchy)) {
+ currentAddress_.poBox = currentText_;
+ }
+ else if ("/vCard/ADR/EXTADD".equals(elementHierarchy)) {
+ currentAddress_.addressExtension = currentText_;
+ }
+ else if ("/vCard/ADR/STREET".equals(elementHierarchy)) {
+ currentAddress_.street = currentText_;
+ }
+ else if ("/vCard/ADR/LOCALITY".equals(elementHierarchy)) {
+ currentAddress_.locality = currentText_;
+ }
+ else if ("/vCard/ADR/REGION".equals(elementHierarchy)) {
+ currentAddress_.region = currentText_;
+ }
+ else if ("/vCard/ADR/PCODE".equals(elementHierarchy)) {
+ currentAddress_.postalCode = currentText_;
+ }
+ else if ("/vCard/ADR/CTRY".equals(elementHierarchy)) {
+ currentAddress_.country = currentText_;
+ }
+ else if ("/vCard/ADR".equals(elementHierarchy)) {
+ if (currentAddress_.poBox != null && !currentAddress_.poBox.isEmpty()
+ || currentAddress_.addressExtension != null && !currentAddress_.addressExtension.isEmpty()
+ || currentAddress_.street != null && !currentAddress_.street.isEmpty()
+ || currentAddress_.locality != null && !currentAddress_.locality.isEmpty()
+ || currentAddress_.region != null && !currentAddress_.region.isEmpty()
+ || currentAddress_.postalCode != null && !currentAddress_.postalCode.isEmpty()
+ || currentAddress_.country != null && !currentAddress_.country.isEmpty()) {
+ getPayloadInternal().addAddress(currentAddress_);
+ }
+ }
+ else if ("/vCard/LABEL/HOME".equals(elementHierarchy)) {
+ currentAddressLabel_.isHome = true;
+ }
+ else if ("/vCard/LABEL/WORK".equals(elementHierarchy)) {
+ currentAddressLabel_.isWork = true;
+ }
+ else if ("/vCard/LABEL/POSTAL".equals(elementHierarchy)) {
+ currentAddressLabel_.isPostal = true;
+ }
+ else if ("/vCard/LABEL/PARCEL".equals(elementHierarchy)) {
+ currentAddressLabel_.isParcel = true;
+ }
+ else if ("/vCard/LABEL/DOM".equals(elementHierarchy)) {
+ currentAddressLabel_.deliveryType = VCard.DeliveryType.DomesticDelivery;
+ }
+ else if ("/vCard/LABEL/INTL".equals(elementHierarchy)) {
+ currentAddressLabel_.deliveryType = VCard.DeliveryType.InternationalDelivery;
+ }
+ else if ("/vCard/LABEL/PREF".equals(elementHierarchy)) {
+ currentAddressLabel_.isPreferred = true;
+ }
+ else if ("/vCard/LABEL/LINE".equals(elementHierarchy)) {
+ currentAddressLabel_.lines.add(currentText_);
+ }
+ else if ("/vCard/LABEL".equals(elementHierarchy)) {
+ getPayloadInternal().addAddressLabel(currentAddressLabel_);
+ }
+ else if ("/vCard/JID".equals(elementHierarchy) && !currentText_.isEmpty()) {
+ getPayloadInternal().addJID(new JID(currentText_));
+ }
+ else if ("/vCard/DESC".equals(elementHierarchy)) {
+ getPayloadInternal().setDescription(currentText_);
+ }
+ else if ("/vCard/ORG/ORGNAME".equals(elementHierarchy)) {
+ currentOrganization_.name = currentText_;
+ }
+ else if ("/vCard/ORG/ORGUNIT".equals(elementHierarchy) && !currentText_.isEmpty()) {
+ currentOrganization_.units.add(currentText_);
+ }
+ else if ("/vCard/ORG".equals(elementHierarchy)) {
+ if (!currentOrganization_.name.isEmpty() || !currentOrganization_.units.isEmpty()) {
+ getPayloadInternal().addOrganization(currentOrganization_);
+ }
+ }
+ else if ("/vCard/TITLE".equals(elementHierarchy) && !currentText_.isEmpty()) {
+ getPayloadInternal().addTitle(currentText_);
+ }
+ else if ("/vCard/ROLE".equals(elementHierarchy) && !currentText_.isEmpty()) {
+ getPayloadInternal().addRole(currentText_);
+ }
+ else if ("/vCard/URL".equals(elementHierarchy) && !currentText_.isEmpty()) {
+ getPayloadInternal().addURL(currentText_);
+ }
+ else if (elementStack_.size() == 2 && unknownContentParser_ != null) {
+ getPayloadInternal().addUnknownContent(unknownContentParser_.getResult());
+ }
+
+ if (elementStack_.size() == 2 && unknownContentParser_ != null) {
+ unknownContentParser_ = null;
+ }
+ elementStack_.pop();
+ }
+
+ public void handleCharacterData(String text) {
+ if (unknownContentParser_ != null) {
+ unknownContentParser_.handleCharacterData(text);
+ }
+ currentText_ += text;
+ }
+
+ private String getElementHierarchy() {
+ String result = "";
+ for(String element : elementStack_) {
+ result += "/" + element;
+ }
+ return result;
+ }
+}
diff --git a/src/com/isode/stroke/presence/PayloadAddingPresenceSender.java b/src/com/isode/stroke/presence/PayloadAddingPresenceSender.java
new file mode 100644
index 0000000..ed6106b
--- /dev/null
+++ b/src/com/isode/stroke/presence/PayloadAddingPresenceSender.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.presence;
+
+import com.isode.stroke.elements.Payload;
+import com.isode.stroke.elements.Presence;
+
+public class PayloadAddingPresenceSender implements PresenceSender {
+ private Presence lastSentPresence;
+ private final PresenceSender sender;
+ private Payload payload;
+
+ public PayloadAddingPresenceSender(PresenceSender sender) {
+ this.sender = sender;
+ }
+
+ public void sendPresence(Presence presence) {
+ if (presence.isAvailable()) {
+ if (presence.getTo() != null && !presence.getTo().isValid()) {
+ lastSentPresence = presence;
+ }
+ } else {
+ lastSentPresence = null;
+ }
+ if (payload != null) {
+ Presence sentPresence = presence;
+ sentPresence.updatePayload(payload);
+ sender.sendPresence(sentPresence);
+ } else {
+ sender.sendPresence(presence);
+ }
+ }
+
+ public boolean isAvailable() {
+ return sender.isAvailable();
+ }
+
+ /**
+ * Sets the payload to be added to outgoing presences. If initial presence
+ * has been sent, this will resend the last sent presence with an updated
+ * payload. Initial presence is reset when unavailable presence is sent, or
+ * when reset() is called.
+ */
+ public void setPayload(Payload payload) {
+ this.payload = payload;
+ if (lastSentPresence != null) {
+ sendPresence(lastSentPresence);
+ }
+ }
+
+ /**
+ * Resets the presence sender. This puts the presence sender back in the
+ * initial state (before initial presence has been sent). This also resets
+ * the chained sender.
+ */
+ public void reset() {
+ lastSentPresence = null;
+ }
+
+}
diff --git a/src/com/isode/stroke/presence/PresenceOracle.java b/src/com/isode/stroke/presence/PresenceOracle.java
new file mode 100644
index 0000000..8d63f59
--- /dev/null
+++ b/src/com/isode/stroke/presence/PresenceOracle.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.presence;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.elements.Presence;
+import com.isode.stroke.elements.StatusShow;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Slot1;
+
+public class PresenceOracle {
+ private final Map<JID,Map<JID,Presence>> entries_ = new HashMap<JID,Map<JID,Presence>>();
+ private final StanzaChannel stanzaChannel_;
+ private final SignalConnection onPresenceReceivedSignal;
+ private final SignalConnection onAvailableChangedSignal;
+
+
+ public final Signal1<Presence> onPresenceChange = new Signal1<Presence>();
+
+ public PresenceOracle(StanzaChannel stanzaChannel) {
+ stanzaChannel_ = stanzaChannel;
+ onPresenceReceivedSignal = stanzaChannel_.onPresenceReceived.connect(new Slot1<Presence>() {
+ @Override
+ public void call(Presence p1) {
+ handleIncomingPresence(p1);
+ }
+ });
+ onAvailableChangedSignal = stanzaChannel_.onAvailableChanged.connect(new Slot1<Boolean>() {
+ @Override
+ public void call(Boolean p1) {
+ handleStanzaChannelAvailableChanged(p1);
+ }
+ });
+ }
+
+ void delete() {
+ onPresenceReceivedSignal.disconnect();
+ onAvailableChangedSignal.disconnect();
+ }
+
+ void handleStanzaChannelAvailableChanged(boolean available) {
+ if (available) {
+ entries_.clear();
+ }
+ }
+
+
+ void handleIncomingPresence(Presence presence) {
+ JID bareJID = presence.getFrom().toBare();
+ if (presence.getType() == Presence.Type.Subscribe) {
+ }
+ else {
+ Presence passedPresence = presence;
+ if (presence.getType() == Presence.Type.Unsubscribe) {
+ /* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */
+ passedPresence = new Presence();
+ passedPresence.setType(Presence.Type.Unavailable);
+ passedPresence.setFrom(bareJID);
+ passedPresence.setStatus(presence.getStatus());
+ }
+ Map<JID,Presence> jidMap = entries_.get(bareJID);
+ if (jidMap == null) jidMap = new HashMap<JID,Presence>();
+ if (passedPresence.getFrom().isBare() && presence.getType() == Presence.Type.Unavailable) {
+ /* Have a bare-JID only presence of offline */
+ jidMap.clear();
+ } else if (passedPresence.getType() == Presence.Type.Available) {
+ /* Don't have a bare-JID only offline presence once there are available presences */
+ jidMap.remove(bareJID);
+ }
+ if (passedPresence.getType() == Presence.Type.Unavailable && jidMap.size() > 1) {
+ jidMap.remove(passedPresence.getFrom());
+ } else {
+ jidMap.put(passedPresence.getFrom(), passedPresence);
+ }
+ entries_.put(bareJID, jidMap);
+ onPresenceChange.emit(passedPresence);
+ }
+ }
+
+ public Presence getLastPresence(final JID jid) {
+ Map<JID,Presence> presenceMap = entries_.get(jid.toBare());
+ if (presenceMap == null) return new Presence();
+
+ Presence i = presenceMap.get(jid);
+ if (i != null) {
+ return i;
+ } else {
+ return new Presence();
+ }
+ }
+
+ public Collection<Presence> getAllPresence(final JID bareJID) {
+ Collection<Presence> results = new ArrayList<Presence>();
+
+ Map<JID,Presence> presenceMap = entries_.get(bareJID);
+ if (presenceMap == null) return results;
+
+ results.addAll(presenceMap.values());
+ return results;
+ }
+
+ public Presence getHighestPriorityPresence(final JID bareJID) {
+ Map<JID,Presence> presenceMap = entries_.get(bareJID);
+ if (presenceMap == null) return new Presence();
+
+ Presence highest = null;
+ for (Presence current : presenceMap.values()) {
+ if (highest == null
+ || current.getPriority() > highest.getPriority()
+ || (current.getPriority() == highest.getPriority()
+ && StatusShow.typeToAvailabilityOrdering(current.getShow()) > StatusShow.typeToAvailabilityOrdering(highest.getShow()))) {
+ highest = current;
+ }
+
+ }
+ return highest;
+ }
+}
diff --git a/src/com/isode/stroke/presence/SubscriptionManager.java b/src/com/isode/stroke/presence/SubscriptionManager.java
new file mode 100644
index 0000000..91075fc
--- /dev/null
+++ b/src/com/isode/stroke/presence/SubscriptionManager.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.presence;
+
+import com.isode.stroke.client.StanzaChannel;
+import com.isode.stroke.elements.Presence;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.signals.Signal2;
+import com.isode.stroke.signals.Signal3;
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Slot1;
+
+public class SubscriptionManager {
+ private StanzaChannel stanzaChannel;
+ private SignalConnection onPresenceReceivedConnection;
+
+ public final Signal3<JID, String, Presence> onPresenceSubscriptionRequest = new Signal3<JID, String, Presence>();
+
+ public final Signal2<JID, String> onPresenceSubscriptionRevoked = new Signal2<JID, String>();
+
+ public SubscriptionManager(StanzaChannel channel) {
+ stanzaChannel = channel;
+ onPresenceReceivedConnection = stanzaChannel.onPresenceReceived.connect(new Slot1<Presence>() {
+ @Override
+ public void call(Presence p1) {
+ handleIncomingPresence(p1);
+ }
+ });
+ }
+
+ void delete() {
+ onPresenceReceivedConnection.disconnect();
+ }
+
+ public void cancelSubscription(final JID jid) {
+ Presence stanza = new Presence();
+ stanza.setType(Presence.Type.Unsubscribed);
+ stanza.setTo(jid);
+ stanzaChannel.sendPresence(stanza);
+ }
+
+ public void confirmSubscription(final JID jid) {
+ Presence stanza = new Presence();
+ stanza.setType(Presence.Type.Subscribed);
+ stanza.setTo(jid);
+ stanzaChannel.sendPresence(stanza);
+ }
+
+
+ public void requestSubscription(final JID jid) {
+ Presence stanza = new Presence();
+ stanza.setType(Presence.Type.Subscribe);
+ stanza.setTo(jid);
+ stanzaChannel.sendPresence(stanza);
+ }
+
+ void handleIncomingPresence(Presence presence) {
+ JID bareJID = presence.getFrom().toBare();
+ if (presence.getType() == Presence.Type.Subscribe) {
+ onPresenceSubscriptionRequest.emit(bareJID, presence.getStatus(), presence);
+ }
+ else if (presence.getType() == Presence.Type.Unsubscribe) {
+ onPresenceSubscriptionRevoked.emit(bareJID, presence.getStatus());
+ }
+ }
+}
diff --git a/src/com/isode/stroke/queries/GetRosterRequest.java b/src/com/isode/stroke/queries/GetRosterRequest.java
deleted file mode 100644
index 4501a94..0000000
--- a/src/com/isode/stroke/queries/GetRosterRequest.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 2010, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
- * All rights reserved.
- */
-
-package com.isode.stroke.queries;
-
-import com.isode.stroke.elements.IQ.Type;
-import com.isode.stroke.elements.RosterPayload;
-import com.isode.stroke.jid.JID;
-
-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);
- }
-}
diff --git a/src/com/isode/stroke/queries/IQRouter.java b/src/com/isode/stroke/queries/IQRouter.java
index 409d02b..0dd55f2 100644
--- a/src/com/isode/stroke/queries/IQRouter.java
+++ b/src/com/isode/stroke/queries/IQRouter.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2010-2014, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.queries;
@@ -63,7 +59,17 @@ public class IQRouter {
return channel_.isAvailable();
}
- private void handleIQ(IQ iq) {
+ /**
+ * Checks whether the given jid is the account JID (i.e. it is either
+ * the bare JID, or it is the empty JID).
+ * Can be used to check whether a stanza is sent by the server on behalf
+ * of the user's account.
+ */
+ public boolean isAccountJID(final JID jid) {
+ return jid.isValid() ? jid_.toBare().compare(jid, JID.CompareType.WithResource) == 0 : true;
+ }
+
+ private void handleIQ(IQ iq) {
boolean handled = false;
synchronized (handlers_) {
for (IQHandler handler : handlers_) {
diff --git a/src/com/isode/stroke/queries/SetResponder.java b/src/com/isode/stroke/queries/SetResponder.java
new file mode 100644
index 0000000..c042385
--- /dev/null
+++ b/src/com/isode/stroke/queries/SetResponder.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.queries;
+
+import com.isode.stroke.elements.Payload;
+import com.isode.stroke.jid.JID;
+
+public abstract class SetResponder<T extends Payload> extends Responder<T> {
+
+ public SetResponder(T payloadType, IQRouter router) {
+ super(payloadType, router);
+ }
+
+ @Override
+ protected boolean handleGetRequest(JID from, JID to, String id, T payload) {
+ return false;
+ }
+}
diff --git a/src/com/isode/stroke/queries/requests/GetSecurityLabelsCatalogRequest.java b/src/com/isode/stroke/queries/requests/GetSecurityLabelsCatalogRequest.java
new file mode 100644
index 0000000..fdadc9b
--- /dev/null
+++ b/src/com/isode/stroke/queries/requests/GetSecurityLabelsCatalogRequest.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.queries.requests;
+
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.SecurityLabelsCatalog;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+
+public class GetSecurityLabelsCatalogRequest extends GenericRequest<SecurityLabelsCatalog>{
+
+ public static GetSecurityLabelsCatalogRequest create(JID recipient, IQRouter router) {
+ return new GetSecurityLabelsCatalogRequest(recipient, router);
+ }
+
+ private GetSecurityLabelsCatalogRequest(JID recipient, IQRouter router) {
+ super(IQ.Type.Get, new JID(), new SecurityLabelsCatalog(recipient), router);
+ }
+}
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;
+ }
+}
diff --git a/src/com/isode/stroke/serializer/PresenceSerializer.java b/src/com/isode/stroke/serializer/PresenceSerializer.java
index f5a2556..6611c8c 100644
--- a/src/com/isode/stroke/serializer/PresenceSerializer.java
+++ b/src/com/isode/stroke/serializer/PresenceSerializer.java
@@ -1,16 +1,11 @@
/*
- * Copyright (c) 2010, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.serializer;
import com.isode.stroke.elements.Presence;
-import com.isode.stroke.elements.Stanza;
import com.isode.stroke.serializer.xml.XMLElement;
public class PresenceSerializer extends GenericStanzaSerializer<Presence> {
diff --git a/src/com/isode/stroke/serializer/payloadserializers/CapsInfoSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/CapsInfoSerializer.java
index b98a741..cfe1c25 100644
--- a/src/com/isode/stroke/serializer/payloadserializers/CapsInfoSerializer.java
+++ b/src/com/isode/stroke/serializer/payloadserializers/CapsInfoSerializer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Isode Limited.
+ * Copyright (c) 2010-2015 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -34,4 +34,4 @@ public class CapsInfoSerializer extends GenericPayloadSerializer<CapsInfo> {
capsElement.setAttribute("ver", capsInfo.getVersion());
return capsElement.serialize();
}
-} \ No newline at end of file
+}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/FullPayloadSerializerCollection.java b/src/com/isode/stroke/serializer/payloadserializers/FullPayloadSerializerCollection.java
index 0b62e0f..f4caa29 100644
--- a/src/com/isode/stroke/serializer/payloadserializers/FullPayloadSerializerCollection.java
+++ b/src/com/isode/stroke/serializer/payloadserializers/FullPayloadSerializerCollection.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2010-2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.serializer.payloadserializers;
@@ -34,8 +30,8 @@ public class FullPayloadSerializerCollection extends PayloadSerializerCollection
addSerializer(new MUCOwnerPayloadSerializer(this));
addSerializer(new MUCUserPayloadSerializer(this));
addSerializer(new SoftwareVersionSerializer());
- //addSerializer(new StatusSerializer());
- //addSerializer(new StatusShowSerializer());
+ addSerializer(new StatusSerializer());
+ addSerializer(new StatusShowSerializer());
addSerializer(new DiscoInfoSerializer());
addSerializer(new DiscoItemsSerializer());
addSerializer(new CapsInfoSerializer());
@@ -45,17 +41,19 @@ public class FullPayloadSerializerCollection extends PayloadSerializerCollection
//addSerializer(new SecurityLabelsCatalogSerializer());
//addSerializer(new StreamInitiationSerializer());
//addSerializer(new BytestreamsSerializer());
- //addSerializer(new VCardSerializer());
+ addSerializer(new VCardSerializer());
//addSerializer(new VCardUpdateSerializer());
addSerializer(new RawXMLPayloadSerializer());
- //addSerializer(new StorageSerializer());
+ addSerializer(new StorageSerializer());
addSerializer(new DelaySerializer());
addSerializer(new FormSerializer());
- //addSerializer(new PrivateStorageSerializer(this));
+ addSerializer(new PrivateStorageSerializer(this));
addSerializer(new CommandSerializer());
//addSerializer(new NicknameSerializer());
addSerializer(new SearchPayloadSerializer());
+ addSerializer(new ReplaceSerializer());
addSerializer(new LastSerializer());
+ addSerializer(new IdleSerializer());
addSerializer(new PubSubSerializer(this));
addSerializer(new PubSubEventSerializer(this));
diff --git a/src/com/isode/stroke/serializer/payloadserializers/IdleSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/IdleSerializer.java
new file mode 100644
index 0000000..6d1ae93
--- /dev/null
+++ b/src/com/isode/stroke/serializer/payloadserializers/IdleSerializer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 Isode Limited, London, England.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+package com.isode.stroke.serializer.payloadserializers;
+
+import com.isode.stroke.base.DateTime;
+import com.isode.stroke.elements.Idle;
+import com.isode.stroke.serializer.GenericPayloadSerializer;
+
+public class IdleSerializer extends GenericPayloadSerializer<Idle> {
+
+ public IdleSerializer() {
+ super(Idle.class);
+ }
+
+ @Override
+ protected String serializePayload(Idle idle) {
+ return "<idle xmlns='urn:xmpp:idle:1' since='" + DateTime.dateToString(idle.getSince()) + "'/>";
+ }
+}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/MUCInvitationPayloadSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/MUCInvitationPayloadSerializer.java
index e149aa7..3281b0e 100644
--- a/src/com/isode/stroke/serializer/payloadserializers/MUCInvitationPayloadSerializer.java
+++ b/src/com/isode/stroke/serializer/payloadserializers/MUCInvitationPayloadSerializer.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2011, Kevin Smith
+ * Copyright (c) 2011-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.serializer.payloadserializers;
@@ -41,6 +37,9 @@ public class MUCInvitationPayloadSerializer extends GenericPayloadSerializer<MUC
if (payload.getThread() != null && !payload.getThread().isEmpty()) {
mucElement.setAttribute("thread", payload.getThread());
}
+ if (payload.getIsImpromptu()) {
+ mucElement.addNode(new XMLElement("impromptu", "http://swift.im/impromptu"));
+ }
return mucElement.serialize();
}
}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/PrivateStorageSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/PrivateStorageSerializer.java
new file mode 100644
index 0000000..c863f71
--- /dev/null
+++ b/src/com/isode/stroke/serializer/payloadserializers/PrivateStorageSerializer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+
+package com.isode.stroke.serializer.payloadserializers;
+
+import com.isode.stroke.elements.Payload;
+import com.isode.stroke.elements.PrivateStorage;
+import com.isode.stroke.serializer.GenericPayloadSerializer;
+import com.isode.stroke.serializer.PayloadSerializer;
+import com.isode.stroke.serializer.PayloadSerializerCollection;
+import com.isode.stroke.serializer.xml.XMLElement;
+import com.isode.stroke.serializer.xml.XMLRawTextNode;
+
+class PrivateStorageSerializer extends GenericPayloadSerializer<PrivateStorage>{
+
+ private final PayloadSerializerCollection serializers;
+
+ public PrivateStorageSerializer(PayloadSerializerCollection serializers) {
+ super(PrivateStorage.class);
+ this.serializers = serializers;
+ }
+
+
+ @Override
+ protected String serializePayload(PrivateStorage storage) {
+ XMLElement storageElement = new XMLElement("query", "jabber:iq:private");
+ Payload payload = storage.getPayload();
+ if (payload != null) {
+ PayloadSerializer serializer = serializers.getPayloadSerializer(payload);
+ if (serializer != null) {
+ storageElement.addNode(new XMLRawTextNode(serializer.serialize(payload)));
+ }
+ }
+ return storageElement.serialize();
+ }
+
+}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/ReplaceSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/ReplaceSerializer.java
new file mode 100644
index 0000000..b6b49e2
--- /dev/null
+++ b/src/com/isode/stroke/serializer/payloadserializers/ReplaceSerializer.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2012-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+/*
+ * Copyright (c) 2011 Vlad Voicu
+ * Licensed under the Simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+package com.isode.stroke.serializer.payloadserializers;
+
+import com.isode.stroke.elements.Replace;
+import com.isode.stroke.serializer.GenericPayloadSerializer;
+
+public class ReplaceSerializer extends GenericPayloadSerializer<Replace> {
+
+ public ReplaceSerializer() {
+ super(Replace.class);
+ }
+
+ protected String serializePayload(Replace replace) {
+ return "<replace id = '" + replace.getID() + "' xmlns='urn:xmpp:message-correct:0'/>";
+ }
+
+}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/StatusSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/StatusSerializer.java
new file mode 100644
index 0000000..6a9bb23
--- /dev/null
+++ b/src/com/isode/stroke/serializer/payloadserializers/StatusSerializer.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.serializer.payloadserializers;
+
+import com.isode.stroke.elements.Status;
+import com.isode.stroke.serializer.GenericPayloadSerializer;
+import com.isode.stroke.serializer.xml.XMLElement;
+import com.isode.stroke.serializer.xml.XMLTextNode;
+
+public class StatusSerializer extends GenericPayloadSerializer<Status> {
+
+ public StatusSerializer() {
+ super(Status.class);
+ }
+
+ protected String serializePayload(Status status) {
+ XMLElement element = new XMLElement("status");
+ element.addNode(new XMLTextNode(status.getText()));
+ return element.serialize();
+ }
+}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/StatusShowSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/StatusShowSerializer.java
new file mode 100644
index 0000000..e7cb587
--- /dev/null
+++ b/src/com/isode/stroke/serializer/payloadserializers/StatusShowSerializer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.serializer.payloadserializers;
+
+import com.isode.stroke.elements.StatusShow;
+import com.isode.stroke.serializer.GenericPayloadSerializer;
+
+public class StatusShowSerializer extends GenericPayloadSerializer<StatusShow> {
+
+ public StatusShowSerializer() {
+ super(StatusShow.class);
+ }
+
+ protected String serializePayload(StatusShow statusShow) {
+ if (statusShow.getType () == StatusShow.Type.Online || statusShow.getType() == StatusShow.Type.None) {
+ return "";
+ }
+ else {
+ String result = "<show>";
+ switch (statusShow.getType()) {
+ case Away: result += "away"; break;
+ case XA: result += "xa"; break;
+ case FFC: result += "chat"; break;
+ case DND: result += "dnd"; break;
+ case Online: assert(false); break;
+ case None: assert(false); break;
+ }
+ result += "</show>";
+ return result;
+ }
+ }
+
+}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/StorageSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/StorageSerializer.java
new file mode 100644
index 0000000..2a072ad
--- /dev/null
+++ b/src/com/isode/stroke/serializer/payloadserializers/StorageSerializer.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+
+package com.isode.stroke.serializer.payloadserializers;
+
+import com.isode.stroke.elements.Storage;
+import com.isode.stroke.serializer.GenericPayloadSerializer;
+import com.isode.stroke.serializer.xml.XMLElement;
+import com.isode.stroke.serializer.xml.XMLTextNode;
+
+class StorageSerializer extends GenericPayloadSerializer<Storage>{
+
+ public StorageSerializer() {
+ super(Storage.class);
+ }
+
+ @Override
+ protected String serializePayload(Storage storage) {
+ XMLElement storageElement = new XMLElement("storage", "storage:bookmarks");
+
+ for (final Storage.Room room : storage.getRooms()) {
+ XMLElement conferenceElement = new XMLElement("conference");
+ conferenceElement.setAttribute("name", room.name);
+ conferenceElement.setAttribute("jid", room.jid.toString());
+ conferenceElement.setAttribute("autojoin", room.autoJoin ? "1" : "0");
+ if (room.nick != null && !room.nick.isEmpty()) {
+ XMLElement nickElement = new XMLElement("nick");
+ nickElement.addNode(new XMLTextNode(room.nick));
+ conferenceElement.addNode(nickElement);
+ }
+ if (room.password != null) {
+ XMLElement passwordElement = new XMLElement("password");
+ passwordElement.addNode(new XMLTextNode(room.password));
+ conferenceElement.addNode(passwordElement);
+ }
+ storageElement.addNode(conferenceElement);
+ }
+
+ for (final Storage.URL url : storage.getURLs()) {
+ XMLElement urlElement = new XMLElement("url");
+ urlElement.setAttribute("name", url.name);
+ urlElement.setAttribute("url", url.url);
+ storageElement.addNode(urlElement);
+ }
+
+ return storageElement.serialize();
+ }
+
+}
diff --git a/src/com/isode/stroke/serializer/payloadserializers/VCardSerializer.java b/src/com/isode/stroke/serializer/payloadserializers/VCardSerializer.java
new file mode 100644
index 0000000..8a98f46
--- /dev/null
+++ b/src/com/isode/stroke/serializer/payloadserializers/VCardSerializer.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+
+package com.isode.stroke.serializer.payloadserializers;
+
+import com.isode.stroke.base.DateTime;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.serializer.GenericPayloadSerializer;
+import com.isode.stroke.serializer.xml.XMLElement;
+import com.isode.stroke.serializer.xml.XMLRawTextNode;
+import com.isode.stroke.stringcodecs.Base64;
+
+class VCardSerializer extends GenericPayloadSerializer<VCard>{
+
+ public VCardSerializer() {
+ super(VCard.class);
+ }
+
+ @Override
+ protected String serializePayload(VCard vcard) {
+ XMLElement queryElement = new XMLElement("vCard", "vcard-temp");
+ if (vcard.getVersion() != null && !vcard.getVersion().isEmpty()) {
+ queryElement.addNode(new XMLElement("VERSION", "", vcard.getVersion()));
+ }
+ if (vcard.getFullName() != null && !vcard.getFullName().isEmpty()) {
+ queryElement.addNode(new XMLElement("FN", "", vcard.getFullName()));
+ }
+ if (vcard.getGivenName() != null && !vcard.getGivenName().isEmpty()
+ || vcard.getFamilyName() != null && !vcard.getFamilyName().isEmpty()
+ || vcard.getMiddleName() != null && !vcard.getMiddleName().isEmpty()
+ || vcard.getPrefix() != null && !vcard.getPrefix().isEmpty()
+ || vcard.getSuffix() != null && !vcard.getSuffix().isEmpty()) {
+ XMLElement nameElement = new XMLElement("N");
+ if (vcard.getFamilyName() != null && !vcard.getFamilyName().isEmpty()) {
+ nameElement.addNode(new XMLElement("FAMILY", "", vcard.getFamilyName()));
+ }
+ if (vcard.getGivenName() != null && !vcard.getGivenName().isEmpty()) {
+ nameElement.addNode(new XMLElement("GIVEN", "", vcard.getGivenName()));
+ }
+ if (vcard.getMiddleName() != null && !vcard.getMiddleName().isEmpty()) {
+ nameElement.addNode(new XMLElement("MIDDLE", "", vcard.getMiddleName()));
+ }
+ if (vcard.getPrefix() != null && !vcard.getPrefix().isEmpty()) {
+ nameElement.addNode(new XMLElement("PREFIX", "", vcard.getPrefix()));
+ }
+ if (vcard.getSuffix() != null && !vcard.getSuffix().isEmpty()) {
+ nameElement.addNode(new XMLElement("SUFFIX", "", vcard.getSuffix()));
+ }
+ queryElement.addNode(nameElement);
+ }
+ if (vcard.getEMailAddresses() != null) for (final VCard.EMailAddress emailAddress : vcard.getEMailAddresses()) {
+ XMLElement emailElement = new XMLElement("EMAIL");
+ emailElement.addNode(new XMLElement("USERID", "", emailAddress.address));
+ if (emailAddress.isHome) {
+ emailElement.addNode(new XMLElement("HOME"));
+ }
+ if (emailAddress.isWork) {
+ emailElement.addNode(new XMLElement("WORK"));
+ }
+ if (emailAddress.isInternet) {
+ emailElement.addNode(new XMLElement("INTERNET"));
+ }
+ if (emailAddress.isPreferred) {
+ emailElement.addNode(new XMLElement("PREF"));
+ }
+ if (emailAddress.isX400) {
+ emailElement.addNode(new XMLElement("X400"));
+ }
+ queryElement.addNode(emailElement);
+ }
+ if (vcard.getNickname() != null && !vcard.getNickname().isEmpty()) {
+ queryElement.addNode(new XMLElement("NICKNAME", "", vcard.getNickname()));
+ }
+ if (vcard.getPhoto() != null && !vcard.getPhoto().isEmpty() || vcard.getPhotoType() != null && !vcard.getPhotoType().isEmpty()) {
+ XMLElement photoElement = new XMLElement("PHOTO");
+ if (vcard.getPhotoType() != null && !vcard.getPhotoType().isEmpty()) {
+ photoElement.addNode(new XMLElement("TYPE", "", vcard.getPhotoType()));
+ }
+ if (vcard.getPhoto() != null && !vcard.getPhoto().isEmpty()) {
+ photoElement.addNode(new XMLElement("BINVAL", "", Base64.encode(vcard.getPhoto())));
+ }
+ queryElement.addNode(photoElement);
+ }
+ if (vcard.getBirthday() != null) {
+ queryElement.addNode(new XMLElement("BDAY", "", DateTime.dateToString(vcard.getBirthday())));
+ }
+
+ if (vcard.getTelephones() != null) for (final VCard.Telephone telephone : vcard.getTelephones()) {
+ XMLElement telElement = new XMLElement("TEL");
+ telElement.addNode(new XMLElement("NUMBER", "", telephone.number));
+ if (telephone.isHome) {
+ telElement.addNode(new XMLElement("HOME"));
+ }
+ if (telephone.isWork) {
+ telElement.addNode(new XMLElement("WORK"));
+ }
+ if (telephone.isVoice) {
+ telElement.addNode(new XMLElement("VOICE"));
+ }
+ if (telephone.isFax) {
+ telElement.addNode(new XMLElement("FAX"));
+ }
+ if (telephone.isPager) {
+ telElement.addNode(new XMLElement("PAGER"));
+ }
+ if (telephone.isMSG) {
+ telElement.addNode(new XMLElement("MSG"));
+ }
+ if (telephone.isCell) {
+ telElement.addNode(new XMLElement("CELL"));
+ }
+ if (telephone.isVideo) {
+ telElement.addNode(new XMLElement("VIDEO"));
+ }
+ if (telephone.isBBS) {
+ telElement.addNode(new XMLElement("BBS"));
+ }
+ if (telephone.isModem) {
+ telElement.addNode(new XMLElement("MODEM"));
+ }
+ if (telephone.isISDN) {
+ telElement.addNode(new XMLElement("ISDN"));
+ }
+ if (telephone.isPCS) {
+ telElement.addNode(new XMLElement("PCS"));
+ }
+ if (telephone.isPreferred) {
+ telElement.addNode(new XMLElement("PREF"));
+ }
+ queryElement.addNode(telElement);
+ }
+
+ if (vcard.getAddresses() != null) for (final VCard.Address address : vcard.getAddresses()) {
+ XMLElement adrElement = new XMLElement("ADR");
+ if (!address.poBox.isEmpty()) {
+ adrElement.addNode(new XMLElement("POBOX", "", address.poBox));
+ }
+ if (!address.addressExtension.isEmpty()) {
+ adrElement.addNode(new XMLElement("EXTADD", "", address.addressExtension));
+ }
+ if (!address.street.isEmpty()) {
+ adrElement.addNode(new XMLElement("STREET", "", address.street));
+ }
+ if (!address.locality.isEmpty()) {
+ adrElement.addNode(new XMLElement("LOCALITY", "", address.locality));
+ }
+ if (!address.region.isEmpty()) {
+ adrElement.addNode(new XMLElement("REGION", "", address.region));
+ }
+ if (!address.postalCode.isEmpty()) {
+ adrElement.addNode(new XMLElement("PCODE", "", address.postalCode));
+ }
+ if (!address.country.isEmpty()) {
+ adrElement.addNode(new XMLElement("CTRY", "", address.country));
+ }
+
+ if (address.isHome) {
+ adrElement.addNode(new XMLElement("HOME"));
+ }
+ if (address.isWork) {
+ adrElement.addNode(new XMLElement("WORK"));
+ }
+ if (address.isPostal) {
+ adrElement.addNode(new XMLElement("POSTAL"));
+ }
+ if (address.isParcel) {
+ adrElement.addNode(new XMLElement("PARCEL"));
+ }
+ if (address.deliveryType == VCard.DeliveryType.DomesticDelivery) {
+ adrElement.addNode(new XMLElement("DOM"));
+ }
+ if (address.deliveryType == VCard.DeliveryType.InternationalDelivery) {
+ adrElement.addNode(new XMLElement("INTL"));
+ }
+ if (address.isPreferred) {
+ adrElement.addNode(new XMLElement("PREF"));
+ }
+ queryElement.addNode(adrElement);
+ }
+
+ if (vcard.getAddressLabels() != null) for (final VCard.AddressLabel addressLabel : vcard.getAddressLabels()) {
+ XMLElement labelElement = new XMLElement("LABEL");
+
+ for (final String line : addressLabel.lines) {
+ labelElement.addNode(new XMLElement("LINE", "", line));
+ }
+
+ if (addressLabel.isHome) {
+ labelElement.addNode(new XMLElement("HOME"));
+ }
+ if (addressLabel.isWork) {
+ labelElement.addNode(new XMLElement("WORK"));
+ }
+ if (addressLabel.isPostal) {
+ labelElement.addNode(new XMLElement("POSTAL"));
+ }
+ if (addressLabel.isParcel) {
+ labelElement.addNode(new XMLElement("PARCEL"));
+ }
+ if (addressLabel.deliveryType == VCard.DeliveryType.DomesticDelivery) {
+ labelElement.addNode(new XMLElement("DOM"));
+ }
+ if (addressLabel.deliveryType == VCard.DeliveryType.InternationalDelivery) {
+ labelElement.addNode(new XMLElement("INTL"));
+ }
+ if (addressLabel.isPreferred) {
+ labelElement.addNode(new XMLElement("PREF"));
+ }
+ queryElement.addNode(labelElement);
+ }
+
+ if (vcard.getJIDs() != null) for (final JID jid : vcard.getJIDs()) {
+ queryElement.addNode(new XMLElement("JID", "", jid.toString()));
+ }
+
+ if (vcard.getDescription() != null && !vcard.getDescription().isEmpty()) {
+ queryElement.addNode(new XMLElement("DESC", "", vcard.getDescription()));
+ }
+
+ if (vcard.getOrganizations() != null) for (final VCard.Organization org : vcard.getOrganizations()) {
+ XMLElement orgElement = new XMLElement("ORG");
+ if (!org.name.isEmpty()) {
+ orgElement.addNode(new XMLElement("ORGNAME", "", org.name));
+ }
+ if (!org.units.isEmpty()) {
+ for (final String unit : org.units) {
+ orgElement.addNode(new XMLElement("ORGUNIT", "", unit));
+ }
+ }
+ queryElement.addNode(orgElement);
+ }
+
+ if (vcard.getTitles() != null) for (final String title : vcard.getTitles()) {
+ queryElement.addNode(new XMLElement("TITLE", "", title));
+ }
+
+ if (vcard.getRoles() != null) for (final String role : vcard.getRoles()) {
+ queryElement.addNode(new XMLElement("ROLE", "", role));
+ }
+
+ if (vcard.getURLs() != null) for (final String url : vcard.getURLs()) {
+ queryElement.addNode(new XMLElement("URL", "", url));
+ }
+
+ if (vcard.getUnknownContent() != null && !vcard.getUnknownContent().isEmpty()) {
+ queryElement.addNode(new XMLRawTextNode(vcard.getUnknownContent()));
+ }
+ return queryElement.serialize();
+ }
+
+}
diff --git a/src/com/isode/stroke/serializer/xml/XMLTextNode.java b/src/com/isode/stroke/serializer/xml/XMLTextNode.java
index fa1881e..da1b03f 100644
--- a/src/com/isode/stroke/serializer/xml/XMLTextNode.java
+++ b/src/com/isode/stroke/serializer/xml/XMLTextNode.java
@@ -1,9 +1,5 @@
/*
- * Copyright (c) 2010-2012, Isode Limited, London, England.
- * All rights reserved.
- */
-/*
- * Copyright (c) 2010, Remko Tronçon.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.serializer.xml;
@@ -13,7 +9,7 @@ public class XMLTextNode implements XMLNode {
private String text_;
public XMLTextNode(String text) {
- text_ = text;
+ text_ = text != null ? text : "";
text_ = text_.replaceAll("&", "&amp;"); // Should come first
text_ = text_.replaceAll("<", "&lt;");
text_ = text_.replaceAll(">", "&gt;");
diff --git a/src/com/isode/stroke/signals/Signal7.java b/src/com/isode/stroke/signals/Signal7.java
new file mode 100644
index 0000000..296cc02
--- /dev/null
+++ b/src/com/isode/stroke/signals/Signal7.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2012-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+
+package com.isode.stroke.signals;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.isode.stroke.signals.SignalConnection;
+import com.isode.stroke.signals.Slot;
+
+
+/**
+ * An approximation of the boost::signals system with 4 parameters
+ * @param <T1> Type 1
+ * @param <T2> Type 2
+ * @param <T3> Type 3
+ * @param <T4> Type 4
+ */
+public class Signal7<T1, T2, T3, T4, T5, T6, T7> {
+ private final Map<SignalConnection, Slot7<T1, T2, T3, T4, T5, T6, T7> > binds_ = Collections.synchronizedMap(
+ new HashMap<SignalConnection, Slot7<T1, T2, T3, T4, T5, T6, T7> >());
+
+ /**
+ * Add a slot which will be notified
+ * @param bind slot, not null
+ * @return signal connection
+ */
+ public SignalConnection connect(Slot7<T1, T2, T3, T4, T5, T6, T7> bind) {
+ final SignalConnection connection = new SignalConnection();
+ binds_.put(connection, bind);
+ connection.onDestroyed.connect(new Slot() {
+ public void call() {
+ binds_.remove(connection);
+ }
+ });
+ return connection;
+ }
+
+ /**
+ * Notify all slots(listeners)
+ * @param p1 parameter value 1
+ * @param p2 parameter value 2
+ * @param p3 parameter value 3
+ * @param p4 parameter value 4
+ */
+ public void emit(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7) {
+ List<Slot7<T1, T2, T3, T4, T5, T6, T7>> binds = new ArrayList<Slot7<T1, T2, T3, T4, T5, T6, T7>>();
+ binds.addAll(binds_.values());
+ for (Slot7<T1, T2, T3, T4, T5, T6, T7> bind : binds) {
+ bind.call(p1, p2, p3, p4, p5, p6, p7);
+ }
+ }
+
+ /**
+ * Remove all slots(listeners)
+ */
+ public void disconnectAll() {
+ binds_.clear();
+ }
+}
diff --git a/src/com/isode/stroke/signals/Slot7.java b/src/com/isode/stroke/signals/Slot7.java
new file mode 100644
index 0000000..7df6973
--- /dev/null
+++ b/src/com/isode/stroke/signals/Slot7.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+
+package com.isode.stroke.signals;
+
+/**
+ * Bind class for connecting to a signal with 7 parameters.
+ * @param <T1> Type 1
+ * @param <T2> Type 2
+ * @param <T3> Type 3
+ * @param <T4> Type 4
+ * @param <T5> Type 5
+ * @param <T6> Type 6
+ * @param <T7> Type 7
+ */
+public interface Slot7<T1, T2, T3, T4, T5, T6, T7> {
+ /**
+ * This method will be called on notification from a signal
+ * @param p1 parameter value 1
+ * @param p2 parameter value 2
+ * @param p3 parameter value 3
+ * @param p4 parameter value 4
+ * @param p5 parameter value 5
+ * @param p6 parameter value 6
+ * @param p7 parameter value 7
+ */
+ void call(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7);
+}
diff --git a/src/com/isode/stroke/stringcodecs/Base64.java b/src/com/isode/stroke/stringcodecs/Base64.java
index 9837993..e19bb5d 100644
--- a/src/com/isode/stroke/stringcodecs/Base64.java
+++ b/src/com/isode/stroke/stringcodecs/Base64.java
@@ -1,10 +1,5 @@
/*
- * Copyright (c) 2010 Remko Tronçon
- * Licensed under the GNU General Public License v3.
- * See Documentation/Licenses/GPLv3.txt for more information.
- */
-/*
- * Copyright (c) 2010, Isode Limited, London, England.
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
* All rights reserved.
*/
package com.isode.stroke.stringcodecs;
@@ -20,4 +15,8 @@ public class Base64 {
public static String encode(ByteArray input) {
return Base64BSD.encodeToString(input.getData(), false);
}
+
+ public static String encode(byte[] input) {
+ return Base64BSD.encodeToString(input, false);
+ }
}
diff --git a/src/com/isode/stroke/vcards/GetVCardRequest.java b/src/com/isode/stroke/vcards/GetVCardRequest.java
new file mode 100644
index 0000000..e563106
--- /dev/null
+++ b/src/com/isode/stroke/vcards/GetVCardRequest.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.vcards;
+
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+
+public class GetVCardRequest extends GenericRequest<VCard> {
+ public static GetVCardRequest create(final JID jid, IQRouter router) {
+ return new GetVCardRequest(jid, router);
+ }
+
+ private GetVCardRequest(final JID jid, IQRouter router) {
+ super(IQ.Type.Get, jid, new VCard(), router);
+ }
+
+}
diff --git a/src/com/isode/stroke/vcards/SetVCardRequest.java b/src/com/isode/stroke/vcards/SetVCardRequest.java
new file mode 100644
index 0000000..85b908f
--- /dev/null
+++ b/src/com/isode/stroke/vcards/SetVCardRequest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.vcards;
+
+import com.isode.stroke.elements.IQ;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.GenericRequest;
+import com.isode.stroke.queries.IQRouter;
+
+public class SetVCardRequest extends GenericRequest<VCard> {
+
+ public static SetVCardRequest create(VCard vcard, IQRouter router) {
+ return new SetVCardRequest(vcard, router);
+ }
+
+ private SetVCardRequest(VCard vcard, IQRouter router) {
+ super(IQ.Type.Set, new JID(), vcard, router);
+ }
+
+}
diff --git a/src/com/isode/stroke/vcards/VCardManager.java b/src/com/isode/stroke/vcards/VCardManager.java
new file mode 100644
index 0000000..593d244
--- /dev/null
+++ b/src/com/isode/stroke/vcards/VCardManager.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.vcards;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.isode.stroke.elements.ErrorPayload;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.queries.IQRouter;
+import com.isode.stroke.signals.Signal1;
+import com.isode.stroke.signals.Signal2;
+import com.isode.stroke.signals.Slot2;
+
+public class VCardManager {
+ private final JID ownJID;
+ private final IQRouter iqRouter;
+ private final VCardStorage storage;
+ private final Set<JID> requestedVCards = new HashSet<JID>();
+
+ /**
+ * The JID will always be bare.
+ */
+ public final Signal2<JID, VCard> onVCardChanged = new Signal2<JID, VCard>();
+
+ /**
+ * Emitted when our own vcard changes.
+ *
+ * onVCardChanged will also be emitted.
+ */
+ public final Signal1<VCard> onOwnVCardChanged = new Signal1<VCard>();
+
+ public VCardManager(final JID ownJID, IQRouter iqRouter, VCardStorage vcardStorage) {
+ this.ownJID = ownJID;
+ this.iqRouter = iqRouter;
+ this.storage = vcardStorage;
+ }
+
+ void delete() {
+ }
+
+ public VCard getVCard(final JID jid) {
+ return storage.getVCard(jid);
+ }
+
+ public VCard getVCardAndRequestWhenNeeded(final JID jid) {
+ VCard vcard = storage.getVCard(jid);
+ if (vcard == null) {
+ requestVCard(jid);
+ }
+ return vcard;
+ }
+
+ void requestVCard(final JID requestedJID) {
+ final JID jid = requestedJID.compare(ownJID, JID.CompareType.WithoutResource) == 0 ? new JID() : requestedJID;
+ if (requestedVCards.contains(jid)) {
+ return;
+ }
+ GetVCardRequest request = GetVCardRequest.create(jid, iqRouter);
+ request.onResponse.connect(new Slot2<VCard, ErrorPayload>() {
+ @Override
+ public void call(VCard p1, ErrorPayload p2) {
+ handleVCardReceived(jid, p1, p2);
+ }
+ });
+ request.send();
+ requestedVCards.add(jid);
+ }
+
+ public void requestOwnVCard() {
+ requestVCard(new JID());
+ }
+
+
+ void handleVCardReceived(final JID actualJID, VCard vcard, ErrorPayload error) {
+ if (error != null || vcard == null) {
+ vcard = new VCard();
+ }
+ requestedVCards.remove(actualJID);
+ JID jid = actualJID.isValid() ? actualJID : ownJID.toBare();
+ setVCard(jid, vcard);
+ }
+
+ SetVCardRequest createSetVCardRequest(final VCard vcard) {
+ SetVCardRequest request = SetVCardRequest.create(vcard, iqRouter);
+ request.onResponse.connect(new Slot2<VCard, ErrorPayload>() {
+ @Override
+ public void call(VCard p1, ErrorPayload p2) {
+ handleSetVCardResponse(vcard, p2);
+ }
+ });
+ return request;
+ }
+
+ void handleSetVCardResponse(VCard vcard, ErrorPayload error) {
+ if (error == null) {
+ setVCard(ownJID.toBare(), vcard);
+ }
+ }
+
+ void setVCard(final JID jid, VCard vcard) {
+ storage.setVCard(jid, vcard);
+ onVCardChanged.emit(jid, vcard);
+ if (jid.compare(ownJID, JID.CompareType.WithoutResource) == 0) {
+ onOwnVCardChanged.emit(vcard);
+ }
+ }
+
+// String getPhotoHash(final JID jid) {
+// return storage.getPhotoHash(jid);
+// }
+}
diff --git a/src/com/isode/stroke/vcards/VCardMemoryStorage.java b/src/com/isode/stroke/vcards/VCardMemoryStorage.java
new file mode 100644
index 0000000..d666d98
--- /dev/null
+++ b/src/com/isode/stroke/vcards/VCardMemoryStorage.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.vcards;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+
+public class VCardMemoryStorage extends VCardStorage {
+ public VCardMemoryStorage(CryptoProvider crypto) {
+ super(crypto);
+ }
+
+ private Map<JID, VCard> vcards = new HashMap<JID, VCard>();
+
+ @Override
+ public VCard getVCard(JID jid) {
+ return vcards.get(jid);
+ }
+
+ @Override
+ public void setVCard(JID jid, VCard vcard) {
+ vcards.put(jid, vcard);
+ }
+
+}
diff --git a/src/com/isode/stroke/vcards/VCardStorage.java b/src/com/isode/stroke/vcards/VCardStorage.java
new file mode 100644
index 0000000..9021599
--- /dev/null
+++ b/src/com/isode/stroke/vcards/VCardStorage.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010-2015, Isode Limited, London, England.
+ * All rights reserved.
+ */
+package com.isode.stroke.vcards;
+
+import com.isode.stroke.crypto.CryptoProvider;
+import com.isode.stroke.elements.VCard;
+import com.isode.stroke.jid.JID;
+import com.isode.stroke.stringcodecs.Hexify;
+
+public abstract class VCardStorage {
+ private CryptoProvider crypto;
+
+ public abstract VCard getVCard(JID jid);
+ public abstract void setVCard(JID jid, VCard vcard);
+
+ public VCardStorage(CryptoProvider crypto) {
+ this.crypto = crypto;
+ }
+
+ public void delete() {};
+
+ public String getPhotoHash(final JID jid) {
+ VCard vCard = getVCard(jid);
+ if (vCard != null && vCard.getPhoto().getSize() != 0) {
+ return Hexify.hexify(crypto.getSHA1Hash(vCard.getPhoto()));
+ }
+ else {
+ return "";
+ }
+ }
+}