1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-12-28 13:38:00 +01:00
Commit graph

195 commits

Author SHA1 Message Date
Florian Schmaus
6fec813ec0 Rework DiscoverInfo
Make Feature and Identity immutable and allow fast lookup of those two
in DiscoverInfo.
2014-10-31 23:44:04 +01:00
Florian Schmaus
b60b20e312 Remove unnecessary multiplication in DiscoverInfo.hashCode() 2014-10-31 20:02:50 +01:00
Florian Schmaus
5fb17dc85f Use ServiceDiscoveryManager.supportsFeature() in AMPManager 2014-10-31 20:02:44 +01:00
Florian Schmaus
1de2fc2a81 Refactor Bind and Roster IQ parsing code into providers 2014-10-30 13:21:36 +01:00
Florian Schmaus
f94f7590a6 Store packet extensions in a HashMap
allowing O(1) lookups for PacketExtensions

The one EntityCapsManagerTest becomes obsolete with this change, as
duplicate extension elements (RFC 6120 § 8.4) are now no longer possible
after the stanza has been parsed (they still may be received on the
wire, but only the last duplicate will be added).
2014-10-28 15:11:43 +01:00
Florian Schmaus
51d84647f3 s/connection()/connection/ in PingManager 2014-10-24 12:59:41 +02:00
Florian Schmaus
403ecff2b2 Add SCRAM-SHA1 support
Thanks to Stefan Karlsson for helping with the implementation.

Also add SASLMechanism.checkIfSuccessfulOrThrow(), to increase the
security by verifying the mechanisms state at the end of SASL
authentication.

SASLMechanism now has a SASLPrep StringTransformer.

Refactor SHA1 functions out of StringUtils into SHA1 utility class.

Add MAC utility class.

Make DummyConnection getSentpacket() methods use generics to make unit
testing SCRAM-SHA1 easier.

Fixes SMACK-398
2014-10-21 15:03:48 +02:00
Florian Schmaus
6a2bc0c02d Fix MUC invitationPacketListener ClassCastException
Make sure that the Packets are of type Message. To prevent:

E/AbstractXMPPConnection﹕ Exception in packet listener
    java.lang.ClassCastException: org.jivesoftware.smack.packet.Presence cannot be cast to org.jivesoftware.smack.packet.Message
            at org.jivesoftware.smackx.muc.MultiUserChatManager$2.processPacket(MultiUserChatM anager.java:124)
            at org.jivesoftware.smack.AbstractXMPPConnection.notifiyReceivedListeners(Abstract XMPPConnection.java:854)
            at org.jivesoftware.smack.AbstractXMPPConnection$ListenerNotification.run(Abstract XMPPConnection.java:876)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)
            at java.util.concurrent.FutureTask.run(FutureTask.java:234)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201 (ScheduledThreadPoolExecutor.java:153)
            at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Schedu ledThreadPoolExecutor.java:267)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
            at java.lang.Thread.run(Thread.java:856)
2014-10-21 10:38:01 +02:00
Florian Schmaus
47e327f2e6 Fix NPE in MultiUserChatManager.getMultiUserchat()
Since the HashMap has WeakReferences as values, there are two levels at
which values could be null.
2014-10-20 21:12:51 +02:00
Florian Schmaus
5d963f7f9b Fix NPE in MUCItem if role is null 2014-10-20 21:12:33 +02:00
Florian Schmaus
17bb738e9e Make PubSubManager.getNode(String) more robust
I've got reports from users that in some cases there can be multiple
identities. Not just one, and in this case, the node type may not be the
first identity. We now iterate over all identities until we either found
one of type "leaf" or "collection".

For example one user reports an ejabberd with PEP case, where the first
identity is of type "pep", the second of type "leaf" and a third one
with category "account" and type "registered".

Also extend DiscoverInfo API with hasIdentity(String, String) and
getIdentities(String, String).
2014-10-17 14:26:06 +02:00
Florian Schmaus
888de7e1c1 Remove nullOrEmpty check for setSessionID()
in AdHocCommandData.

This check was added in 3a37b71e19, but a
session ID can be null, if it's the first invocation. This prevented
RemoteCommand.execute() from executing, since RemoteCommand's sessionID
member will be null at the first invocation.
2014-10-16 13:02:06 +02:00
Florian Schmaus
9edf06e1bb Fix typos and closing javadoc tag in RemoteCommand 2014-10-16 12:55:17 +02:00
Florian Schmaus
b5328d38fa MUC: Establish callbacks before sending the presence 2014-10-14 09:41:48 +02:00
Florian Schmaus
ce203e76b5 Use AbstractNodeInformationProvider where possible 2014-10-13 22:35:45 +02:00
Florian Schmaus
b54d133b36 Add MultiUserChatManager
apply the Manager pattern to 'muc'. This prevents the user creating
multiple MultiUserChat instances for the same MUC.

Move the static method from MultiUserChat to MultiUserChatManager.

Also add AbstractNodeInformationProvider.
2014-10-13 22:32:34 +02:00
Florian Schmaus
c674808a4a Improve MultiUserChat
Use CopyOnWriteArraySet for listeners, remove the old reflection based
listener invocation approach.

Remove unnecessary casts.

Return List instead of Collection where possible.

sendMessage(Message) now set's the MUC as 'to' and the message type to
groupchat.
2014-10-13 16:39:17 +02:00
Florian Schmaus
365c71db79 Rework MultiUserChat
remove PacketMultiplexListener, RoomListenerMultiplexor and
ConnectionDetachedPacketCollector(Test), which was a bunch of
(in same cases redundant) code that formed a complex construct that
presumably tried to make MultiUserChat instances easily garbage
collect-able.

Now, MultiUserChat should be eligible for gc if the userHashLeft() is
invoked before the reference to the instance is dropped, which should be
the case in the most scenarios. Otherwise the connection may references
the MultiUserChat instance over Packet(Listener|Interceptor)s preventing
the gc.
2014-10-13 11:10:30 +02:00
Florian Schmaus
98a3c46e9a Add XMPPConnection.createPacketcollectorAndSend(PacketFilter, Packet)
Using

createPacketCollector(filter);
sendPacket(packet);

was error prone, i.e. the PacketCollector could leak if sendPacket()
would throw an exception and the user forgot to call
PacketCollector.cancel(). For cases where
createPacketCollectorAndSend(IQ) is not sufficient (because we don't
send IQs), createPacketCollectorAndSend(PacketFilter, Packet) is now
used, which does take care that the PacketCollector does not leak if
sendPacket() throws an Exception.
2014-10-13 10:45:00 +02:00
Florian Schmaus
8ce474b0df Improve MUC Presence interceptors
Use PresenceListener as intercepting callback interface.

Remove the for-each loops in MultiUserChat by using the interception
facilities of the XMPPConnection.

Add ToFilter.
2014-10-12 19:34:14 +02:00
Florian Schmaus
55d7c57a4f Use PacketTypeFilter.PRESENCE in EntityCapsManager 2014-10-12 19:21:49 +02:00
Florian Schmaus
63d52f6eaa Replace PacketInterceptor with PacketListener
both serve the same purpose: As callback for Packets. There is no need
to have both, so remace PacketInterceptor and let PacketListener take
its place. Some classes like ChatStateManager can now use
MessageListener as interceptor callback, which is more convenient.
2014-10-12 19:19:44 +02:00
Florian Schmaus
fd7b747987 Remove init() in MultiUserChat
and mark more members 'final'

Also use CopyOnWriteArraySet for presenceInterceptors.
2014-10-12 18:53:19 +02:00
Florian Schmaus
e835df5641 Improve MUC message and presence listeners
instead of using a PacketListener, which means that the user has to
downcast the Packet to Message, we now use a Listener which callback
parameter is already Message/Presence.

It is necessary to introduce MessageListener and PresenceListener, which
are interfaces that have a callback for Message/Presence instead of
Packet. The 'old' MessageListener is renamed to ChatMessageListener.

Use Generics in ConnectionDetachedPacketCollector.
2014-10-12 18:11:02 +02:00
Florian Schmaus
38582eed84 Make getServiceNames() return List
instead of Collection. Return types should always be as specific as
possible. Because e.g. Collection does not allow 'get(0)', but List
does.
2014-10-12 16:54:21 +02:00
Florian Schmaus
fe74fc23dc Rework smackx.address (XEP-0033: Extended Stanza Addressing)
- Make MultipleAddress.Type a enum
- Change the signature of the methods to use Collection instead of List
- Use for-each loops instead of iterators
- Switch Provider to new provider pattern (using switch-case)
- Use XmlStringBuilder (extend the API by two new methods)
2014-10-11 17:11:38 +02:00
Florian Schmaus
36744ea0d1 Remove MUC DeafOccupantIntercepter
as it's a old (likely unused) Jive (Wildfire) proprietary extension.
2014-10-09 23:12:22 +02:00
Florian Schmaus
452b2aca0f Add 'PacketParserUtils.addPacketextension()' 2014-10-08 16:01:19 +02:00
Florian Schmaus
6980c8e63d Rework Smack Provider design
this is the first stop towards fixing "SMACK-65: parsing should look for
depth", by providing the initial parsing depth to the provider. Some
methods (.e.g parseMessage) now use the depth as abort condition,
instead of a unclean String equals check.

parseIQ() and parseExtension() where both renamed to parse.

This also restricts the Exceptions thrown by the parse method, to just
XmlPullParserException, IOException and SmackException (not really a big
victory, but nevertheless a slight improvement).

StreamFeatureProvider is now gone, we simply use PacketExtensionProvider
for stream features.
2014-10-08 16:01:14 +02:00
Florian Schmaus
387be5bded Add DeliveryReceiptRequest.addTo(Message) 2014-10-07 21:50:32 +02:00
Florian Schmaus
19ee9f016b Use CopyOnWriteArraySet in DeliveryReceiptManager 2014-10-05 16:27:52 +02:00
Florian Schmaus
8bf74c6e92 Add Packet to onReceiptReceived parameters
in ReceiptReceivedListener. SMACK-582
2014-10-05 12:56:32 +02:00
Florian Schmaus
1e803ab29d Use from(Packet) instead of getFrom(Packet) in DeliveryReceiptManager 2014-10-05 12:52:19 +02:00
Florian Schmaus
5f06234149 Use CopyOnWriteArraySet in InvitationsMonitor 2014-10-05 12:46:51 +02:00
Florian Schmaus
f7517ab6cc Apply Manager pattern to InvitationsMonitor
Don't remove listeners on disconnect (SMACK-571).
2014-10-05 12:04:46 +02:00
Florian Schmaus
a0fe337bcd Use Collections.emptyList() in MultiUserChat 2014-10-05 12:04:46 +02:00
Florian Schmaus
8529d1463f Use from(Packet) instead of getFrom(Packet) in MultiUserChat 2014-10-05 12:04:45 +02:00
Florian Schmaus
414db1ebbe Add PingManager.pingMyServer(boolean,long) 2014-09-30 13:09:09 +02:00
Florian Schmaus
7521ef951a Ensure that connection is authenticated in PingManager 2014-09-29 17:04:18 +02:00
Florian Schmaus
50e068b6a7 Merge branch '4.0'
Conflicts:
	build.gradle
2014-09-29 14:00:48 +02:00
Florian Schmaus
039fac7ab7 Fix PingManager.ping(String,long)
ignoring timeout. SMACK-609
2014-09-29 13:52:13 +02:00
Abmar Barros
ae385e6b44 Enabling additional and returned extensions in getItems() 2014-09-26 16:56:08 +02:00
Florian Schmaus
13f4f36995 Use non-deprecated method in DelayInformationManager 2014-09-24 19:23:49 +02:00
Florian Schmaus
b945e591ff Add MessageWithBodies and DelayedStanza Filter 2014-09-24 19:23:49 +02:00
Florian Schmaus
d8c77de785 Remove duplicate code in pubusb LeafNode
Make fields of GetItemsRequest and NodeExtension final and use
XmlStringBuilder.
2014-09-21 20:49:11 +02:00
Florian Schmaus
bd0fb973c8 Add new RSMSet constructors
and fix typo in RSMSetManager
2014-09-20 16:54:36 +02:00
Florian Schmaus
58e430af42 PacketExtension.from(Packet), deprecate getFrom()
add 'from(Packet)' to DataForm
2014-09-16 22:22:25 +02:00
Florian Schmaus
f0651bae2d Remove unnecessary check for non-null in PubSubProvider
addExtension() does that already.
2014-09-13 12:33:51 +02:00
Florian Schmaus
2dc93d7639 Add support for XEP-0059: Result Set Management
SMACK-581
2014-09-13 12:28:18 +02:00
Florian Schmaus
f2718c2d76 Remove smack.util.Cache
and use the Cache from jxmpp-util-cache instead.
2014-09-13 00:38:11 +02:00
Florian Schmaus
9016120d71 Add support for (custom) data form extensions
Fixes SMACK-605
2014-09-12 13:43:04 +02:00
Florian Schmaus
3a37b71e19 Use XmlStringBuilder in AdHocCommandData
also remove 'lang' attribute, since it belongs into IQ, see XEP-50 3.7:

"The requester SHOULD provide its locale information using the "xml:lang"
attribute on either the <iq/> (RECOMMENDED) or <command/> element."
2014-09-12 11:22:49 +02:00
Florian Schmaus
46a4402a69 Add StreamFeatureProvider
so that AccountManager and the CapsExtension can be moved to
smack-extensions, where they belong.
2014-09-11 23:58:07 +02:00
Florian Schmaus
fc51f3df48 Add support for XEP-0198: Stream Management
- De-duplicate code by moving it into AbstractXMPPConnection
- Introduce TopLevelStreamElement as superclass for all XMPP stream elements.
- Add SynchronizationPoint, ParserUtils
- Add ParserUtils

Fixes SMACK-333 and SMACK-521
2014-09-11 09:49:16 +02:00
Florian Schmaus
d8db43e4c5 Improve FileTransfer.writeToStream()
let's use the standard idiom for Input- to OutputStream transfers. This
also avoids an initial no-op on the first write, when the count is '0'.

Also fixes a bug when the size of file/stream transferred is '0' (which
is perfectly fine and possible).
2014-09-09 21:06:12 +02:00
Florian Schmaus
090f7cfc49 Add PingManager.pingServerIfNecessary()
which is basically the body of the pingServerRunnable available as
public part of the API. The intention is to allow 3rd party
components (e.g. Android's AlarmManager) to trigger the code.
2014-09-08 10:09:05 +02:00
Florian Schmaus
184604bba2 Init Base64 test encoder for unit-tests
The previous approach with

testCompile project("smack-java7")

was flawed. Instead we setup a Base64 encoder especially for the unit
tests now.
2014-09-07 17:22:55 +02:00
Florian Schmaus
34bc13bec7 Merge branch '4.0'
Conflicts:
	build.gradle
	smack-core/src/main/java/org/jivesoftware/smack/PacketCollector.java
	smack-core/src/main/java/org/jivesoftware/smack/packet/XMPPError.java
	smack-core/src/main/java/org/jivesoftware/smack/util/PacketParserUtils.java
	smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
	smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/provider/ItemProvider.java
2014-09-05 22:11:35 +02:00
Florian Schmaus
28c3554085 Use switch/case in MUCUserProvider 2014-09-05 11:46:44 +02:00
Florian Schmaus
afd7c67bf9 Add support for multiple status codes to MUCUser
Fixes SMACK-604
2014-09-05 11:45:06 +02:00
Florian Schmaus
5d4aa76d19 Create smack.util.stringencoder for Base64, Base32,…
Use Android's Base64 implementation when on Android, otherwise, when on
Java7, use the existing one.
2014-09-04 11:07:55 +02:00
Florian Schmaus
b5b134f569 Process all XML namespaces in PubSub ItemProvider
Fixes SMACK-601
2014-08-29 14:59:54 +02:00
Florian Schmaus
e8471baccb Add ChatState.getChatState() 2014-08-28 19:00:01 +02:00
Florian Schmaus
218dc66cf7 Filetransfer API should not wrap IOException
into SmackException.
2014-08-26 15:26:37 +02:00
Florian Schmaus
6af5d5e462 SDM: Follow guidelines, perform put() where get() is performed 2014-08-26 13:50:47 +02:00
Florian Schmaus
5d159c4be2 Make ServiceDiscoveryManager use constant PacketFilters 2014-08-26 13:50:47 +02:00
Florian Schmaus
191c4429da Fix FileTransferManager invoking request listeners
those need to be called asynchronously.

Also add a small API for that.
2014-08-26 13:50:42 +02:00
Florian Schmaus
856b4a6090 Make FaultTolerantNegotiator respect packetReplyTimeout 2014-08-26 12:04:52 +02:00
Florian Schmaus
f282323eec Extend RoomInfo to contain more meta-data
found in the extended disco info result form. See also
http://xmpp.org/extensions/xep-0045.html#disco-roominfo

Fixes SMACK-542

Also change getShortDescription() to getName()
2014-08-25 11:39:57 +02:00
Florian Schmaus
6a43fc7c36 Add shortDescription to RoomInfo
which is obtained from the disco#info result's identity. Fixes
SMACK-600.

Also make fields in RoomInfo final.
2014-08-24 20:00:35 +02:00
Florian Schmaus
e19cfa4d83 Introduce MUCRole and MUCAffiliation enums
Also rework MUCItem and parseItem to follow current coding guidelines.
2014-08-24 18:30:18 +02:00
Florian Schmaus
754bdada18 MultiUserChat grant/revoke Admin must use #admin namepsace
SMACK-371
2014-08-24 17:49:08 +02:00
Florian Schmaus
229e526f6d Make AbstractDelayInformationProvider handle empty string as reason 2014-08-21 19:47:31 +02:00
Florian Schmaus
76f8895ae3 Remove Pong class, add NAMESPACE to Ping
The Pong class was harmful, as people could try to use it with
PacketTypeFilter, which wouldn't work, a Pong is just a plain IQ result
without child XML.
2014-08-21 10:55:33 +02:00
Florian Schmaus
bbf89c65bf Fix typo in XmlStringBuilder, it's not an Angel
s/rightAngelBracket/rightAngleBracket/
2014-08-20 21:25:14 +02:00
Florian Schmaus
eeea0e34ca Add '::1' to the loopbackAddresses 2014-08-20 18:12:48 +02:00
Florian Schmaus
b9b43ad5d7 Make the next server ping code easier to read 2014-08-20 10:44:02 +02:00
Florian Schmaus
e16997eb9d Fix PingServerRunnable scheduling
should be nextPingIn and not pingInterval.
2014-08-20 10:35:45 +02:00
Florian Schmaus
64e49cb934 Add getLastStanzaReceived() to XMPPConnection
also remove faulty PongFilter from PingManager. It never matched any
stanzas, since a Pong is just a plain result IQ that is not qualified by
any XMPP Ping namespace.

Fixes SMACK-597
2014-08-20 10:35:17 +02:00
Florian Schmaus
62893376dd Cleanup Bytestream and Streamhost
remove unnecessary setPort() setter.
2014-08-19 19:24:57 +02:00
Florian Schmaus
b468a29881 Improve Socks5 Bytestreams
- determine all local IPv4 and IPv6 addresses
- prevent loopback addresses from appearing as streamhost

Some unit tests where changed because they assumed that a host only has
one local address. But nowadays hosts often have more, at least because
they are IPv4 and IPv6 multi-homed.
2014-08-19 19:00:43 +02:00
Florian Schmaus
650da55b23 Improve XmlStringBuilder usage in Bytestream 2014-08-19 12:43:21 +02:00
Florian Schmaus
6021f8a8ce Fix Bytestream.getChildElementXML returning invalid XML 2014-08-19 12:42:23 +02:00
Florian Schmaus
fe9606adb2 Wait for Socks5TestProxy initialization
to prevent

org.jivesoftware.smackx.bytestreams.socks5.Socks5ClientForInitiatorTest
  > shouldSuccessfullyEstablishConnectionAndActivateSocks5Proxy
    FAILED
    java.lang.NullPointerException at Socks5ClientForInitiatorTest.java:292
2014-08-16 08:51:13 +02:00
Florian Schmaus
397eb88468 Merge branch '4.0'
Conflicts:
	build.gradle
	documentation/connections.html
	documentation/debugging.html
	documentation/extensions/filetransfer.html
	documentation/extensions/rosterexchange.html
	documentation/gettingstarted.html
	documentation/index.html
	documentation/legacy/messageevents.html
	smack-extensions/src/main/java/org/jivesoftware/smackx/address/MultipleRecipientManager.java
2014-08-16 00:30:28 +02:00
Florian Schmaus
18a958e442 Fix faulty string comparison in SubscriptionProvider
'!=' tests for reference inequality, must use '!String.equals(String)'
instead.
2014-08-15 23:22:59 +02:00
Florian Schmaus
7277eb553a Enable more 'checkstyle' checks
AvoidStarImport
IllegalImport
RedundantImport
UpperEll
ArrayTypeStyle
GenericWhitespace
EmptyStatement
PackageDelcaration
2014-08-15 23:17:45 +02:00
Vyacheslav Blinov
522d0f30ff Generify Packet class returned by PackageCollector everywhere
This will help to get rid of repetitive class casts, and make
PacketCollector api more inline with itself (since some methods
are already generic return methods).
2014-08-15 12:14:19 +02:00
Florian Schmaus
450015bf40 Make Bookmarks.toXML() return valid XML again
Fixes SMACK-594
2014-08-14 22:25:52 +02:00
Florian Schmaus
0d890623c7 Make getMultipleRecipientServiceAddress more robust
to avoid

feature-not-implemented
	at org.jivesoftware.smack.PacketCollector.nextResultOrThrow(PacketCollector.java:182)
	at org.jivesoftware.smack.PacketCollector.nextResultOrThrow(PacketCollector.java:161)
	at org.jivesoftware.smackx.disco.ServiceDiscoveryManager.discoverItems(ServiceDiscoveryManager.java:573)
	at org.jivesoftware.smackx.address.MultipleRecipientManager.getMultipleRecipienServiceAddress(MultipleRecipientManager.java:310)
 	at org.jivesoftware.smackx.address.MultipleRecipientManager.send$7a184d34(MultipleRecipientManager.java:82)

for e.g. Google Apps XMPP services.

This is not to be merged in master/4.1 branch as it already contains a
similar fix.
2014-08-10 17:22:00 +02:00
Florian Schmaus
a5eebf3840 Make ServiceDiscoveryManager.findServices() more robust
the method should not throw an exception if the discovery of the service
fails.
2014-08-08 16:23:13 +02:00
Florian Schmaus
c9dd1cdc40 Improved iqVersion code
including parsing and the version class.
2014-08-08 16:22:56 +02:00
Florian Schmaus
92a6d01507 Merge branch '4.0'
Also slightly improve OfflineMessageManager.getMessages()

Conflicts:
	build.gradle
	smack-extensions/src/main/java/org/jivesoftware/smackx/offline/OfflineMessageManager.java
2014-08-07 16:51:03 +02:00
Florian Schmaus
4e588f7908 Throw IAE if invalid port number is given for Socks5Proxy 2014-08-05 09:33:20 +02:00
Florian Schmaus
5c2f051c1c Make Socks5Proxy try other ports if 7777 is already in use
by setting the default value to '-7777' (negative values mean that the
next port number will be tried if the current one is in use).
2014-08-05 09:32:13 +02:00
Florian Schmaus
d10ff92d44 Use createPacketCollectorAndSend in FileTransferNegoiator
also fixes a potential PacketCollector leak.
2014-08-04 20:04:39 +02:00
Florian Schmaus
d65d239550 Make getMessages() set up collector *before* sending the request
Similar flaw was also fixed in 2c7f1efe80.

Also ensure that OfflineMessagesManager doesn't leak collector by using
a try/finally block.

Fixes SMACK-592
2014-08-04 12:21:37 +02:00
Florian Schmaus
66da4dfa91 Fix Typo: s/isSubscibe/isSubscribe/ in ConfigureForm
Fixes SMACK-588
2014-08-01 23:15:46 +02:00
Florian Schmaus
0b6069d75f Fix FormField.Option.toXML() to use correct element
Fixes SMACK-589
2014-07-30 21:58:10 +02:00
Florian Schmaus
7b3331dda0 Add API to find services for a given feature
It's a common pattern to look for all services hosted under the users
service that provide a given feature. Let's provide an API for that and
use that API in the methods that benefit from it.
2014-07-26 07:14:42 +02:00