18

i'm using smack 3.1.0, and when i add a roster,i can't get subscription "both". who can help me? below is my code:

Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.accept_all);
Roster roster = connection.getRoster();
roster.createEntry("buddy@test.com","me",null)

after the code execution, i observed in openfire the subscription is "to"

mdelolmo
  • 6,417
  • 3
  • 40
  • 58
snowway
  • 191
  • 1
  • 1
  • 7

5 Answers5

42

Rewriting @mschonaker's answer to be a little more clear.

Both users need to subscribe to each other and accept the subscription request they received. Let's call them Alice and Bob. Alice sends a subscription request to Bob:

Presence subscribe = new Presence(Presence.Type.subscribe);
subscribe.setTo('bob@example.com');
connection.sendPacket(subscribe);

When Bob receives the request, he approves it:

Presence subscribed = new Presence(Presence.Type.subscribed);
subscribed.setTo('alice@example.com');
connection.sendPacket(subscribed);

Bob may also be interested in Alice's presence, so he subscribes to her:

Presence subscribe = new Presence(Presence.Type.subscribe);
subscribe.setTo('alice@example.com');
connection.sendPacket(subscribe);

And Alice needs to approve Bob's request:

Presence subscribed = new Presence(Presence.Type.subscribed);
subscribed.setTo('bob@example.com');
connection.sendPacket(subscribed);

Section 3.1 of RFC6121 is the current best reference for how this works.

Flow
  • 23,572
  • 15
  • 99
  • 156
Joe Hildebrand
  • 10,354
  • 2
  • 38
  • 48
  • 1
    Hello. but where to write this two code for subscribe both side – Niranj Patel May 22 '12 at 06:45
  • if i use Subscribed what i have to use JID here subscribed.setTo(jid); i am able to add entry to my account, but i am not getting presence of that entry.tell me what i to do for that. – RajaReddy PolamReddy Oct 25 '12 at 12:53
  • @JoeHildebrand I am doning the things in same manner but not getting any subscription notification. Please have look http://stackoverflow.com/questions/16815531/android-smack-library-subscriptionnot-showing-inbond-or-outbond-notifications – Ricky Khatri May 30 '13 at 04:45
  • Why the bob again the send the request to alice after bob subscribed to alice.... can we implement in simple way like facebook request. if alice sends the request to bob. bob will accept it and then both are becoming friends @Joe Hildebrand – Roster Jul 25 '13 at 06:20
  • @sravani: This is just the way XMPP works. You can hide this in your user interface; many clients do. The easiest way is if you get a 'subscribe' request from a contact where the roster subscription state is 'to', your client automatically responds with 'subscribed'. – Joe Hildebrand Jul 25 '13 at 17:14
  • Ya. i need to Handle in UI. But how can i detect the sybscription type is to or from or both...i have added this but it is throwing null pointer exception. connection.getRoster().getEntry(presence.getFrom()).getType());PLease suggest how can i handle that alice already sent the request to that bob and when bob got subscribe packet he is subscribed and he sent subscribe packet to alice again then this time alice should check whether alice already sent the request to bob or not if already sent then he should added bob as friend. – Roster Jul 26 '13 at 11:03
  • Instead of `getType()`, you probably have a `getSubscription()` method, or something similar. – Joe Hildebrand Jul 27 '13 at 09:16
  • 1
    excellent example @JoeHildebrand really appreciated answer. And i want to ask one question my scenario is that when user-A subscribe user-B then when user-B subscribed user-A then automatically user-B subscribe user a are u getting what i am trying to say. – Ali Ashiq Sep 10 '14 at 08:24
  • @AliAshiq I think you're asking if subscriptions are always bi-directional. The answer is no, mostly. Some servers, clients, or libraries might mask that somewhat by automatically subscribing in response to subscription requests. – Joe Hildebrand Jun 13 '15 at 06:23
  • What would happen, if User2(Bob) does not exist on the server? So, now Alice sends a request to Bob, but bob, being unregistered, didnt receive the request. Now after 2 days, Bob registers himself. Now, how does pushes a subscription request to all those users who added them at first places? – Praful Bagai Feb 27 '17 at 12:17
  • Years later, API have changed but the flow itself is classic! – Stefano Mtangoo Apr 26 '20 at 16:11
10

Both users need to subscribe to each other. By sending a presence subscription stanza. In Smack:

    Presence presence = new Presence(Presence.Type.subscribe);
    presence.setTo(jid);
    connection.sendPacket(presence);

Section 3.1 of the RFC6121 will give you the semantic details.

Flow
  • 23,572
  • 15
  • 99
  • 156
Martín Schonaker
  • 7,273
  • 4
  • 32
  • 55
  • If user added from any other source, listen for packet listener and send Subscribe to jid, this is how it works, and you can see subscription as "both" in roster list. If type subscribed is used, then subscription will be from (roster sending request) and to(roster to whom request is sent) – Zoombie Mar 12 '12 at 13:15
  • Hello. but where to write this two code for subscribe both side – Niranj Patel May 22 '12 at 08:41
  • RFC3921 is obsoleted by RFC6121. Also, your code doesn't make much sense, since you are subscribing, not unsubscribing, and "presence" isn't a variable currently in scope. – Joe Hildebrand Oct 29 '12 at 21:54
  • excellent example really appreciated answer. And i want to ask one question my scenario is that when user-A subscribe user-B then when user-B subscribed user-A then automatically user-B subscribe user a are u getting what i am trying to say. – Ali Ashiq Sep 10 '14 at 07:53
  • I'm away from XMPP by the moment. You could ask another question in SO, I think. – Martín Schonaker Sep 10 '14 at 14:01
  • Each request and each response is a separate, non-automatic action, requiring an explicit choice by the user or by the software that they're running. Often, clients will auto-accept subscription requests from people you are subscribed to. – Joe Hildebrand Sep 10 '14 at 15:36
2

Okay, I toiled hard at this for a couple of days and finally got things working. Thank you @Joe Hildebrand, your answer put me on the right track to solve this. I have implemented it with a manual subscription mode (ie. user needs to accept another user's request manually).

The server keeps pushing subscribe request to the user (upon re-login) if the user hasn't sent a subscribed or unsubscribed back. So what you can do is save the incoming subscribe requests locally in a list and display that as a "friend request list" for manual accept/reject. If your application gets restarted (and hence re-connects to server), the server will push subscribe requests again.

This is how it works:

  • User1 sends subscribe presence to User2.
  • Roster entry gets automatically created in User1's roster (but not in User2's roster).
  • User2 receives subscribe request from User1.
  • User2 sends back a subscribed presence to User2 (User2 > User1 subscription complete).
  • User2 checks if User1 is in User2's roster. User1 is not in User2's roster. User2 sends back a subscribe presence to User1.
  • Roster entry gets automatically created in User2's roster.
  • User1 receives subscribe presence from User2.
  • User1 checks if User2 is in User1's roster. User2 is in User1's roster. User1 sends back a subscribed presence to User2 (User2 > User1 subscription complete).

            final Presence newPresence = (Presence) packet;
            final Presence.Type presenceType = newPresence.getType();
            final String fromId = newPresence.getFrom();
            final RosterEntry newEntry = getRoster().getEntry(fromId);
    
            if (presenceType == Presence.Type.subscribe)
            {
                //from new user
                if (newEntry == null)
                {
                    //save request locally for later accept/reject
                    //later accept will send back a subscribe & subscribed presence to user with fromId
                    //or accept immediately by sending back subscribe and unsubscribed right now
                }
                //from a user that previously accepted your request
                else
                {
                    //send back subscribed presence to user with fromId
                }
            }
    
Bam
  • 1,183
  • 1
  • 9
  • 13
1

If you are using open fire server you can also use User Service plugin which will create roster with subscription both...

Ram
  • 845
  • 1
  • 13
  • 26
1

Same problem I was face but I got the solution how subscribe set 'both'

Below is sending subscription to user, when you added the user

 Presence pres = new Presence(Presence.Type.subscribed);
        pres.setPriority(24);
        pres.setMode(Presence.Mode.available);
        pres.setTo(friendJid);

        RoosterConnection.getConnection().sendStanza(pres);

and Receiving end put below method in connection class and presenceChanged is default method of RosterListener.

 @Override
public void presenceChanged(Presence presence) {
    mBus.post(presence);
    try {
        Presence pres = new Presence(Presence.Type.subscribed);
        pres.setTo(presence.getFrom());
        RoosterConnection.getConnection().sendStanza(pres);
    } catch (SmackException.NotConnectedException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
Vinod Makode
  • 953
  • 7
  • 7