Archive

Posts Tagged ‘bot’

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).