Archive

Posts Tagged ‘EXMPP’

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!

Suffering from amnesia? Use Mnesia!

May 31, 2010 Leave a comment

As you should already know, I’m currently working with Erlang and exmpp to build some XMPP gateways: some of them require only the Jid (jabber id) of the user, while on the other hand some require more specific information as the credentials needed to retrieve user’s personal information from the internet (e.g. Twitter ID and password).

Actually I could use OAuth and ask the user only for his id, but I thought to keep it simple and to go for the full credentials, so the point now is: how and where should I store these data? Well, MySQL  is definitely among the best solutions if you have to store a wide amount of data, but since in my case this is not true, I decided to focus my attention on Mnesia, a distributed DataBase Management System used in Erlang applications, which require continuous operation and exhibit soft real-time properties.

The first thing to do is to create a schema, a sort of description of your databas (e.g. are the data stored on RAM memory? Disk memory? Both?).

When you create a new Mnesia schema, you just save an empty schema table that you will populate further.

To create the schema for you application, start your distributed Erlang nodes and connect them. If you don’t want to distribute Mnesia over all the nodes, just start a non-distributed Erlang node (be careful! It is important that no old schemas exist, as well as ensuring that Mnesia is NOT started).
Ok let’s see same code!

Let’s define the record user_info as:

-record(user_info, {jid, presence, uid, pwd}).

In the init([]) function we add the following code:

case mnesia:create_schema([node()]) of
  ok ->
    ok = mnesia:start(),
    {atomic,ok}=mnesia:create_table(user_table,
                                    [{disc_copies, [node()]},
 				     {type, set},
 				     {attributes, record_info(fields, user_info)}]),
    mnesia:add_table_index(user_table, presence);
  _ ->
    mnesia:start()
end,

As you can see we try to create a new schema by calling the function  mnesia:create_schema(Nodes) that has to be executed on one of the connected nodes, if you want to set up the schema only in the local node just let the command as is it in the previous example, otherwise put as argument of the function:  [node()|nodes()].

If the creation was successful, we start the application by calling mnesia:start() or application:start(mnesia) (note that to stop Mnesia you call either application:stop(mnesia) or mnesia:stop()).

After the creation of the schema, we create the first table (user_table). The first parameter of the function is of course the name, followed by a list of characteristics for the table. First of all we provide the list of nodes where we want disc and RAM replicas of the table, then we state that the table is a set (other possible values are bag and ordered_set), in the end we specify the fields of the table, by saying that they are the ones of the record user_info we declared above.

After this, we add another index to the table for our inspections to the table (remember that the first attribute is always an index.

In worths to notice when the init function starts, if the schema is already there, we just start Mnesia.

One of the coolest feature of Mnesia are transactions:  a way to prevent race conditions while touching the database, anyhow in this post I’m not gonna talk about transactions, since I’m going to discuss about dirty operations, that are not good for preventing race  conditions, but are 10 times faster.

Here there are two functions, let’s analyze them:

set_subscribtion(BareFrom, UId, Pwd) ->
    mnesia:dirty_write({user_table, BareFrom, "online", UId, Pwd}).

set_subscription takes as input 3 values: a Jid, a User id and a Password, dirty_write is used to write into the specified tablea (user_table) these values.

get_subscribtion(BareFrom) ->
   case mnesia:dirty_read({user_table, BareFrom}) of
       [] ->
	   notsubscribed;
       [{user_table, BareFrom, _Status, UId, Pwd}] ->
	   {subscribed, UId, Pwd}
   end.

get_subscription takes as input only the Jid and checks whether the user identified by it is registered to our service, in fact if the dirty_read is an empty list it means that the user in not subscribed, otherwise the user data will be returned. Note that to use dirty read you must have the relative attribute as index (in this case we used jid, but we can use also presence).

Sending and receiving stanzas using EXMPP

May 10, 2010 4 comments

A couple of posts ago, I introduced EXMPP, the Erlang library developed by Process One for XMPP.

In that post we used EXMPP to connect an external component to eJabberd server by using the XEP-0114.

This time I will show you how to process streams directed to our component, analyze them and reply to the sender.
First of all I used a gen_server behaviour and put all the code regarding the handshake inside the init([]) method:

init([]) ->
    exmpp:start(),
    Session = exmpp_component:start_link(),
    exmpp_component:auth(Session, ?COMPONENT, ?SECRET),
    _StreamId = exmpp_component:connect(Session, ?SERVER_HOST, ?SERVER_PORT),
    ok = exmpp_component:handshake(Session),
    {ok, #state{session = Session}}

You may notice that I used a set of Erlang macros to identify the variables needed for the connection,
those macros were previously defined in the code as:

-define(COMPONENT, get_conf_value(component)).
-define(SECRET, get_conf_value(secret)).
-define(SERVER_HOST, get_conf_value(server_host)).
-define(SERVER_PORT, get_conf_value(server_port)).

Where get_conf_value(Value) is simply a function I implemented to parse a configuration file.

EXMPP provides to developers a set of records (a record may be thought as a complex data structure and I will write about
them in the future); among these records we need in our code the one representing a received_packet.
It is defined as following:

-record(received_packet,
                   {
                   packet_type, % message, iq, presence
                   type_attr,   % depend on packet. Example: set, get, subscribe, etc
                   from,        % JID
                   id,          % Packet ID
                   queryns,     % IQ only: Namespace of the query
                   raw_packet   % raw exmpp record
}).

After the handshake we can send any kind of stanzas defined in XMPP realm towards the server we are connected to as long as
we have a reference to the session we established before.

A really cool feature of EXMPP is that two different processes spawend by our gen_server can send a stanza at the same time over the same session and the stanzas will not be mixed during their “trip” to the XMPP server.

About incoming stanzas, you must know that all of them will be processed by the gen_server as normal Erlang messages.

I told you before that all the stanzas can be handled by processes spawned by the gen_server, so it seems sensible to spawn
a new process any time a packet is received; this new process will elaborate the stanza and if required will send a reply stanza, then it
terminates.

To do that we add the gen_server something like this:

handle_info(#received_packet{} = Packet, #state{session = Session} = State) ->
            spawn(fun() ->
                          process_received_packet(Session, Packet)
                  end),
{noreply, State};

In practice whenever we receive a Packet we spawn (without linking!) a new process which takes as input the current session and the packet itself.
This process is represented by the following function:

process_received_packet(Session, Packet) ->
       Packet_Type = Packet#received_packet.packet_type,
       case Packet_Type of
          iq ->
               process_received_iq(Session, Packet);
          message ->
               process_received_message(Session, Packet);
          presence ->
               process_received_presence(Session, Packet);
       end.

The previous function is pretty simple: basically it checks the type of the packet and then based on it a different function is called.

Let’s implement process_received_message(Session, Packet) so that whenever a message stanza is received our component sends back the same message:

process_received_message(Session,
                      #received_packet{packet_type=message, raw_packet=Packet}) ->
    %% retrieve recipient jid
    From = exmpp_xml:get_attribute(Packet, from, <<"unknown">>),
    %% retrieve sender jid
    To = exmpp_xml:get_attribute(Packet, to, <<"unknown">>),
    %% set recipient jid
    Pkt1 = exmpp_xml:set_attribute(Packet, from, To),
    %% set sender jid
    Pkt2 = exmpp_xml:set_attribute(Pkt1, to, From),
    %% remove old id
    NewPacket = exmpp_xml:remove_attribute(Pkt2, id),
    %% send new packet
    exmpp_session:send_packet(Session, NewPacket).

Here is the result (captured by using PSI):

Message reply

Let’s  implement process_received_iq(Session, Packet) that sends back a simple message if an iq stanza is received:

process_received_iq(Session,
            #received_packet{packet_type=iq, type_attr=Type, raw_packet=IQ}) ->
    %% get namespace of IQ stanza
    NS = exmpp_xml:get_ns_as_atom(exmpp_iq:get_payload(IQ)),
    %% set text value of IQ stanza
    Reply = exmpp_xml:element(NS, 'response', [],
                                      [{xmlcdata,<<"your iq has been received">>}]);
    %% build result packet
    Result = exmpp_iq:result(IQ, exmpp_xml:element(NS, 'query', [], [Reply]));
    %% sends new packet
    exmpp_component:send_packet(Session, Result).

Ok! For now it is enough…try to read EXMPP specifications and see how you can change slightly this code!

Categories: English, Erlang Tags: , ,

Connecting an XMPP external component via Erlang and EXMPP

February 23, 2010 10 comments

Here I am again. Sorry for the time it took me to write a new blog entry, but I was preparing the speech for my bachelor thesis..so as you could understand that took all of my free time.
My thesis explained how social networking could be integrated in an XMPP environment, and I have to say that during the time it took me to design and write the code I became an XMPP nuts. XMPP is a protocol aimed to real-time messaging and presence, but it allows developers to use many extensions to implement different kind of services.
For my thesis I used as XMPP server the software Tigase and since it is developed in Java, I wrote all of my code in the same language, but I was aware of the fact that a XMPP server developed in Erlang and called eJabberd exists so I decided to port all of my code in Erlang.
In this post I will teach you how enable an external component in eJabberd and how to code it using Erlang and EXMPP a library provided by Process One. First of all we have to enable the external component in the eJabberd configuration file; by default you can find it in /etc/ejabberd, so:
bellerofonte@pegaso:~$ cd /etc/ejabberd/
belleforonte@pegaso:/etc/ejabberd$ ls
ejabberd.cfg  ejabberdctl.cfg  inetrc

At this point let’s edit ejabberd.cfg (you may be asked to do it with the sudo option).

bellerofonte@pegaso:~$ sudo nano ejabberd.cfg

Now you have to find the part of the configuration file that starts with:

%% ejabberd_service: Interact with external components (transports...)

And add the following lines to add two different external components:

  {8888, ejabberd_service, [
                          {access, all},
                          {shaper_rule, fast},
                          {ip, {127, 0, 0, 1}},
                          {hosts, ["weather.localhost", "weather.trentino.com"],
                           [{password, "weather"}]
                          }
                         ]},

  {8888, ejabberd_service, [
                          {access, all},
                          {shaper_rule, fast},
                          {ip, {127, 0, 0, 1}},
                          {hosts, ["news.localhost", "news.trentino.com"],
                           [{password, "news"}]
                          }
                         ]},

If you don’t know about XMPP and external component I can just tell you that the names I specified in the list after hosts represent the names that will be used to identify this component in the XMPP network, while the value of password is just the password used by the component to perform the handshake with the server. For more info you can read this.

At this point save the configuration file and restart eJabberd.

Now let’s use EXMPP to connect from the Erlang shell to eJabberd as if we were one of the two components.

bellerofonte@pegaso:/etc/ejabberd$ erl
Erlang R13B03 (erts-5.7.4) 

Eshell V5.7.4  (abort with ^G)
1> exmpp:start().
ok
2> Session = exmpp_component:start_link().
<0.59.0>
3> exmpp_component:auth(Session, "weather", "weather").
ok
4> _StreamId = exmpp_component:connect(Session, "localhost", 8888).
"1627139564"
5>  ok = exmpp_component:handshake(Session).
ok

In the code above, I first started  the exmpp application, then I created a Session for the XMPP component and made the authorization stream by passing to the function exmpp_component the session, the name used by the component and the password needed  for the handshake.

Then I connected it to the eJabberd server by using the function connect, which needs the session, the name of the server and the port on which it is listening to.

After that just connect the component by using the function handshake.

Now, you can log in to the server with one test account of yours and perform a service discovery, the result should be something like:

PSI: Service Discovery

Ok, I know this post was kinda short, but I promise that next one will be much better, and in it I will teach you how to receive and send XMPP stanzas from our component!

Categories: English, Erlang Tags: , ,