summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Young <consult.awy@gmail.com>2014-11-13 06:42:37 (GMT)
committerAlan Young <consult.awy@gmail.com>2015-04-10 06:50:58 (GMT)
commit7d2101b93b6253c3ea15b663f7f3dc385cb21364 (patch)
treed81338baf0d117e83cdc07f882cbedd9471e834d /src/com/isode
parenta20ca7ba40d837abe228462be0aba5d32d6831e3 (diff)
downloadstroke-7d2101b93b6253c3ea15b663f7f3dc385cb21364.zip
stroke-7d2101b93b6253c3ea15b663f7f3dc385cb21364.tar.bz2
Checkpoint - A bunch of initial stuff for Android
MemoryStorages, Storages NickManager, NickResolver CryptoProvider, Hash, SafeByteArray, JavaCryptoProvider CapsInfoGenerator, CapsManager, CapsMemoryStorage, CapsProvider, CapsStorage, CapsInfo CapsInfoSerializer, CapsInfoParser ClientDiscoManager, DiscoInfoResponder, EntityCapsManager, EntityCapsProvider GetDiscoInfoRequest ChatState, Idle Presence, PayloadAddingPresenceSender, PresenceOracle, SubscriptionManager StatusSerializer, StatusShowSerializer, StatusParser, StatusShowParser, Replace, ReplaceParser, ReplaceSerializer SecurityLabel, SecurityLabelsCatalog, GetSecurityLabelsCatalogRequest VCard, GetVCardRequest, SetVCardRequest, VCardManager, VCardMemoryStorage, VCardStorage RosterMemoryStorage, RosterPushResponder, RosterStorage, SetRosterRequest XMPPRoster, XMPPRosterController, XMPPRosterImpl, XMPPRosterItem GetRosterRequest, SetResponder Add parsers and serializers for Idle, VCard, PrivateStorage & Stroage. Add parser for Subject. Add impromptu flag to MUCInvitation. Update copyrights. Change-Id: I9949f506b70e60b3a64f1dadde8f9b235b322e1d
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 "";
+ }
+ }
+}