Archive

Posts Tagged ‘“ejabberd_service” bot’

How to handle presence stanzas using EXMPP

August 10, 2010 Leave a comment

I have written many posts this year about EXMPP and XMPP; we discussed about message stanzas and IQ stanzas therefore I would like to tell you something about presence handling today, so that you would be able to start some implementation by yourselves.

Presence in XMPP is used to communicate to other users our status (e.g “online”, “do not disturb”, “away”, etc etc); let’s see how we can improve our component by exploiting this kind of stanzas.

In a previous post we saw how to handle user subscription, anyhow we didn’t take in consideration the fact that many times one user can be connected to the server with a status different than “online”…we don’t want our bot to become a spamming machine, users should receive notifications only if they are using an “online” status.

How can we achieve this?

Ok, first of all our bot should subscribe to user’s presence during the registration process, this way we could understand whether to send the notifications or not; we can do this by using the following functions:

subscribe_to_presence(JID) ->
    CompAddr = ?XMLCDATA(<>),
    Presence = ?XMLEL4(?NS_USER_NICKNAME, 'nick', [], [CompAddr]),
    exmpp_xml:append_child(make_presence(JID, <<"subscribe">>), Presence).

make_presence(JID, Type) ->
    From = ?XMLATTR('from', ?COMPONENT_ADDRESS),
    PresenceType = ?XMLATTR('type', Type),
    Presence = ?XMLEL4(?NS_COMPONENT_ACCEPT, 'presence', [PresenceType, From], []),
    exmpp_stanza:set_recipient(Presence, JID).

so that we can send to the user the following stanzas:

<presence from="mod_xxxxx.localhost" 
  type="subscribe" to="user@localhost" 
  id="Component-985548963" >
<nick xmlns="http://jabber.org/protocol/nick">mod_xxxxx.localhost</nick>
</presence>

If you are using PSI, you will receive this:

Component asks for presence subscription

Component asks for presence subscription

 

In the previous code, ?COMPONENT_ADDRESS is nothing more than the address of our component (e.g. mod_xxxxx.domain), it is just the address to which the user should send his status updates; at this point if the user sends back a presence stanza with the attribute type set to “subscribed” the process is successful, and in you PSI your user will have a new Agents/Transports contact in his roster.

Component in now in user's roster

Component in now in user's roster

 

Now one can say: “Hey! Wait a second! What are ?XMLCDATA, ?XMLEL4, ?XMLATTR and ?NS_USER_NICKNAME?”, well let’s say that in previous posts I didn’t tell you all the truth: if you include in your module the library exmpp_xml.hrl with:

-include_lib("exmpp/include/exmpp_xml.hrl").

you will be able to build xml elements and xmlcdata with this easier sintax 🙂 , I suggest you to take a look at that file, so that you will see all the other functions provided.

Another smart thing to do is to include the library exmpp_nss.hrl:

-include_lib("exmpp/include/exmpp_nss.hrl").

this way you will be able to use a set of macros for the different namespaces, instead of declaring them by yourselves.

Now what? Well, we should process the presence stanzas as we did for messages and IQs; as I told you before I want to send notifications only to user whose status is set to “online”, thus we should upgrade our mnesia user_table whenever a presence stanza is received, we can do it using the following code:

process_received_presence(_Session,
	                  #received_packet{packet_type=presence,
                          type_attr=_Type, raw_packet=Presence}) ->
    Status = exmpp_presence:get_show(Presence),
    From = exmpp_jid:parse(exmpp_stanza:get_sender(Presence)),
    BareFrom = binary_to_list(exmpp_jid:prep_bare_to_binary(From)),

    case get_subscription(BareFrom) of
	{subscribed, UId, Pwd} ->
	    mnesia:dirty_write({user_table, BareFrom, Status, UId, Pwd});
	_ ->
	    nothing
    end.

And this is done! Now we can select all the users whose status in online using:

mnesia:dirty_index_read(user_table, online, presence).

We can use this function because we set the field presence as a secondary index while creating the Mnesia scheme.

And for today this is all!

Advertisements

How to provide In-Band Registration to users

June 15, 2010 Leave a comment

As you should already know, I am currently working (when I have some free time) to an External Component for Jabber servers which is supposed to be a sort of interface to some Twitter functionalities that I’m not going to explain within this post.

We saw a couple of posts ago, that some information are mandatory in order to exploits Twitter APIs, so we implemented by using Mnesia and Erlang two functions for storing Twitter username and password of our customers; those data should be provided to our component by the user using In-Band Registration, the XEP-0077 of XMPP Protocol).

In few lines we can say that XEP-0077 is a method for in-band registration with instant messaging servers and associated services. In-band registration allows an entity to register with a host, cancel an existing registration with a host, or change a password with a host where the host can be either a server or service.

I strongly suggest you to read the extension’s specs on the web page; I’m not going to write them here since brevity is the soul of wit, and tediousness the limbs and outward flourishes”.

Even though I’m not writing the specs, I will post here the code I wrote for my robot (or at least a snippet of it) 😀

First of all when we receive an IQ with the namespace jabber:iq:register we reply one of the following ways: a form with the parameters to be filled in with the user’s data or the IQ containing the element <registered/>, followed by the data already provided in the previous registration.

Here is a code example:

handle_form_request(Session, IQ, NS) ->
    From = exmpp_jid:parse(exmpp_stanza:get_sender(IQ)),
    BareFrom = binary_to_list(exmpp_jid:prep_bare_to_binary(From)),

    case get_subscribtion(BareFrom) of
	notsubscribed ->
	    send_registration_fields(Session, IQ, NS);
	{subscribed, UId, _Pwd} ->
	    send_registration_form(UId, Session, IQ)
    end.

Where:

send_registration_form(UId, Session, IQ) ->
    Registered = exmpp_xml:element(?NS_INBAND_REGISTER, 'registered', [], []),
    Username = exmpp_xml:element(?NS_INBAND_REGISTER, 'username', [],
				 [{xmlcdata, list_to_binary(UId)}]),
    Password = exmpp_xml:element(?NS_INBAND_REGISTER, 'password', [],
				 [{xmlcdata, "empty due to security"}]),
    Result = exmpp_iq:result(IQ, exmpp_xml:element(?NS_INBAND_REGISTER,
                                         'query', [],
                                         [Registered, Username, Password])),
    exmpp_component:send_packet(Session, Result).

And:

send_registration_fields(Session, IQ, NS) ->
    Instructions = exmpp_xml:element(NS, 'instructions', [], [{xmlcdata,
                <<"Choose a username and password for use with this service.">>}]),
    Pwd = exmpp_xml:element(NS, 'password', [], []),
    User = exmpp_xml:element(NS, 'username', [], []),
    Result = exmpp_iq:result(IQ, exmpp_xml:element(NS, 'query', [],
                                        [Instructions, User, Pwd])),
    exmpp_component:send_packet(Session, Result).

Here is a screenshot of the service working:

In-Band registration process

In-Band registration process


All the other functionalities provided by In-Band Registration can be implemented with really little effort.
If you have any kind of problem or if you want to point out something feel free to contact me!