This page describes mod_xmlrpc, which is useful only in old ejabberd 2 releases. If you are using newer ejabberd, you can use ejabberd_xmlrpc which is included in ejabberd, and call any command that is provided by ejabberd. You can find the list of commands and their arguments running "ejabberdctl help" in the shell. Or you can refer to ejabberd documentation: Managing ejabberd.
Description
mod_xmlrpc is a module for ejabberd, a XMPP/Jabber server written in Erlang. It starts a XML-RPC server and waits for external requests. Implemented calls include statistics and user administration. This allows external programs written in any language like websites or administrative tools to communicate with ejabberd to get information or to make changes without the need to know ejabberd internals.
One example usage is a corporate site in PHP that creates a Jabber user every time a new user is created on the website.
Some benefits of interfacing with the Jabber server by XML-RPC instead of modifying directly the database are:
- external programs are more simple and easy to develop and debug
- can communicate with a server in a different machine, and even on Internet
Here is a document (in French) showing example calls in XML (raw XMLRPC) instead of Erlang formalism: PDF|doc/download/attachments/636/french_document_xmlrpc_xml_examples.pdf?version=1&modificationDate=1244121737000\ MSWord|doc/download/attachments/636/french_document_xmlrpc_xml_examples.doc?version=1&modificationDate=1244121737000\ It should be merged with this document. |
Table of contents
- Description
- Table of contents
- Installation
- Configuration
- Usage
- Call target
- Available methods
- Debug methods
- Users administration
- create_account
- delete_account
- rename_account
- change_password
- set_nickname
- set_rosternick
- add_rosteritem
- delete_rosteritem
- link_contacts
- unlink_contacts
- add_contacts
- remove_contacts
- check_users_registration
- get_roster
- get_roster_with_presence
- get_presence
- get_resources
- send_chat
- send_message
- send_stanza
- Note on return codes
- Erlang XML-RPC formalism
- Tests
Installation
mod_xmlrpc has been integrated with ejabberd in the latest installer version. This means that the XMLRPC server is already installed and preconfigured.
Configuration
To activate the XMLRPC module, simply uncomment the following line from the 'module' section of _ejabberd.cfg_ file:
{mod_xmlrpc,[{port, 4560},{timeout,5000}]},
With this line the XMLRPC server will be listening on port 4560. Simply change the value here to match your requirements. The timeout value is optional and is expressed in milliseconds. Default timeout value is 5 seconds (5000 milliseconds). If you want the server to keep the connection open, you can set the timeout value to infinity.
Note if you are updating your mod_xmlrpc calls from ejabberd 2.0.x or older to ejabberd 2.1.0 or newer: Since ejabberd 2.1.0, the code in mod_xmlrpc has been reorganized: mod_xmlrpc provides only a few test XML-RPC calls and access to ejabberd commands. All the calls that were implemented in the old mod_xmlrpc has been moved to a new module: mod_admin_p1. So, in ejabberd 2.1.0 and newer it is required to enable mod_admin_p1 which provides all the calls. Also note that most calls have changed the arguments and/or result. So please update the XML-RPC calls according to this updated documentation. |
To uncomment a line in ejabberd config file, simply remove the leading percent '%' characters. |
Note that it's not yet possible to configure the IP address to listen to, so the XML-RPC server listens on ALL of them. |
Usage
Call target
All XMLRPC are send to the following URL: http://host:4560/.
host is the hostname of your ejabberd server. 4560 should match the XMLRPC server port you have defined in your ejabberd.cfg file.
Here is the list of currently implemented methods:
Available methods
Debug methods
echothis
Simple echo implementation, using the old argument and result formatting.
String echothis(String echostring)
echothisnew
Simple echo implementation, using the new argument and result formatting.
Struct(String repeated) echothisnew(Struct(String sentence))
multhis
Send the result of the multiplication operation, using the old argument and result formatting.
int multhis(int FirstNumber, int SecondNumber)
multhisnew
Send the result of the multiplication operation, using the new argument and result formatting.
Struct(int mu) multhisnew(Struct(int FirstNumber, int SecondNumber))
Users administration
create_account
Create an ejabberd user account.
Struct(Integer res) create_account(Struct(String user, String server, String password))
delete_account
Remove an account from the server.
Struct(Integer res) delete_account(Struct(String user, String server))
rename_account
Change an account name.
Struct(Integer res) rename_account(Struct(String user, String server, String newuser, String newserver))
This function creates a new account, and reproduce the roster of the old one.
Offline messages and private storage are lost.
change_password
Change the password on behalf of the given user.
Struct(int res) change_password(Struct(String user, String server, String newpass))
set_nickname
Define user nickname.
Struct(int res) set_nickname(Struct(String user, String server, String nick))
The nickname is set/updated in the user Vcard. Other informations are unchanged.
set_rosternick
Define user nick in all its roster entries.
Struct(int res) set_nickname(Struct(String user, String server, String nick))
The nick is set/updated in the roster, and all roster changes are pushed to user's contacts. Other informations are unchanged.
add_rosteritem
Add an entry in a user's roster.
Struct(Integer res) add_rosteritem(Struct(String user, String server, String jid, String group, String nick, String subs))
jid is the JabberID of the user you would like to add in user roster on the server.
subs is the state of the roster item subscription. It can be either both, to, from or none. nonemeans that presence packets are not send between parties. both means that presence packets are send in both direction. to means that the user see the presence of the given JID. from means that the JID specified sees the user presence.
Do not forget that roster items should be kept symmetric: when adding a roster item for a user, you have to do the symmetric roster item addition.
The group feature is not yet supported, group must be set as empty list.
delete_rosteritem
Remove an entry for a user roster.
Struct(Integer res) delete_rosteritem(Struct(String user, String server, String jid))
Do not forget that roster items should be kept symmetric: when removing a roster item for a user, you have to do the symmetric roster item removal.
This mechanism bypass the standard roster approval addition mechanism and should only be used for server administration or server integration purpose.
link_contacts
Add a symmetrical entry in two users roster.
Struct(Integer res) link_contacts(Struct(String jid1, String nick1, String jid1, String nick2))
jid1 is the JabberID of the user1 you would like to add in user2 roster on the server.
nick1 is the nick of user1
jid2 is the JabberID of the user2 you would like to add in user1 roster on the server.
nick2 is the nick of user2
This mechanism bypass the standard roster approval addition mechanism and should only be userd for server administration or server integration purpose.
unlink_contacts
Remove a symmetrical entry in two users roster.
Struct(Integer res) unlink_contacts(Struct(String jid1, Stringjid2))
jid1 is the JabberID of the user1
jid2 is the JabberID of the user2
This mechanism bypass the standard roster approval addition mechanism and should only be userd for server administration or server integration purpose.
add_contacts
Call add_rosteritem with subscription "both" for a given list of contacts
Struct(Integer res) add_contacts(Struct(String user, String server, Array of Struct(Array of Struct(String jid, String group, String nick contact) contacts))
remove_contacts
Call del_rosteritem for a given list of contacts
Struct(Integer res) remove_contacts(Struct(String user, Stringserver, Array of Struct(String jid) contacts))
check_users_registration
List registration status for a given list of users
Struct(Array of Struct(Array of Struct(String user, String server, Integer status) auser) users) check_users_registration(Struct(Array of Struct(Array of Struct(String user, String server) auser) users))
get_roster
Retrieve the roster for a given user.
Struct(Array of Struct(Array of Struct(String jid, String group, String nick, String subscription, String pending) contact) contacts) get_roster(Struct(String user, String server))
The function returns a list of the contacts in a user roster.
The function also returns the state of the contact subscription. Subscription can be either "none", "from", "to", "both". Pending can be "in", "out" or "none".
get_roster_with_presence
Retrieve the roster for a given user. The retrieved roster includes presence information.
Struct(Array of Struct(Array of Struct(String jid, String resource, String group, String nick, String subscription, String pending, String show, String status) contact) contacts) get_roster_with_presence(Struct(String user, String server))
The 'show' value contains the user presence flag. It can take limited values:
- available
- chat (Free for chat)
- away
- dnd (Do not disturb)
- xa (Not available, extended away)
- unavailable (Not connected)
The 'status' is a free text defined by the user client.
The function also returns the state of the contact subscription. Subscription can be either "none", "from", "to", "both". Pending can be "in", "out" or "none".
Note: If user is connected several times, only keep the resource with the highest non-negative priority
get_presence
Retrieve the resource with highest priority, and it's presence (show and status message) for a given user.
Struct(Array of Struct(String jid, String show, String status) presence) get_presence(Struct(String user, String server))
The 'jid' value contains the user jid with resource.
The 'show' value contains the user presence flag. It can take limited values:
- available
- chat (Free for chat)
- away
- dnd (Do not disturb)
- xa (Not available, extended away)
- unavailable (Not connected)
The 'status' is a free text defined by the user client.
get_resources
Retrieve all available resources for a given user.
Struct(Array of Struct(String resource) resources) get_resources(Struct(String user, String server))
send_chat
Send chat message to a given user
Struct(Integer res) send_chat(Struct(String from, String to, String body))
send_message
Send normal message to a given user
Struct(Integer res) send_message(Struct(String from, String to, String subject, String body))
send_stanza
Send stanza to a given user
Struct(Integer res) send_stanza(Struct(String from, String to, String stanza))
If Stanza contains a "from" field, then it overrides the passed from argument.
If Stanza contains a "to" field, then it overrides the passed to argument.
Note on return codes
Response code integer contains either 0 or the response code of ejabberd in case of error (Example: 409 returned when creating a user means "conflict").
Error code mapping is defined in JEP-0086
Erlang XML-RPC formalism
For developers willing to test or implement the XML-RPC call in an Erlang client, here is the Erlang XML-RPC structures to use:
Debug methods
Call |
Arguments |
Returns |
---|---|---|
echothis |
String |
String |
multhis |
struct[{a, Integer}, {b, Integer}] |
Integer |
Users administration
Call |
Arguments |
Returns |
---|---|---|
create_account |
struct[{user, String}, {server, String}, {password, String}] |
struct[{res, Integer}] |
delete_account |
struct[{user, String}, {server, String}] |
struct[{res, Integer}] |
change_password |
struct[{user, String}, {server, String}, {newpass, String}] |
struct[{res, Integer}] |
check_users_registration |
struct[{users, array[{struct, [{auser, array[{struct, [{user, String}, {server, String}]}]}]}]}] |
struct[{users, array[{struct, [{auser, array[{struct, [{user, String}, {server, String}, {status, Integer}]}]}]}]}] |
rename_account |
struct[{user, String}, {server, String}, {newuser, String}, {newserver, String}] |
struct[{res, Integer}] |
set_nickname |
struct[{user, String}, {server, String}, {nick, String}] |
struct[{res, Integer}] |
set_rosternick |
struct[{user, String}, {server, String}, {nick, String}] |
struct[{res, Integer}] |
add_rosteritem |
struct[{user, String}, {server, String}, {jid, String}, {group, String}, {nick, String}, {subs, String}] |
struct[{res, Integer}] |
delete_rosteritem |
struct[{user, String}, {server, String}, {jid, String}] |
struct[{res, Integer}] |
add_contacts |
struct[{user, String}, {server, String}, {contacts, {array, [{struct[{contact, {array, [{struct, [{jid, JID}, {group, Group}, {nick, Nick}]}]}}]}]}}] |
struct[{res, Integer}] |
remove_contacts |
struct[{user, String}, {server, String}, {contacts, {array, [{struct[{jid, String}]}]}}] |
struct[{res, Integer}] |
link_contacts |
struct[{jid1, String}, {nick1, String}, {jid2, String}, {nick2, String}] |
struct[{res, Integer}] |
unlink_contacts |
struct[{jid1, String}, {jid2, String}] |
struct[{res, Integer}] |
get_roster |
struct[{user, String}, {server, String}] |
struct[{contacts, {array, struct[{contact, {array, [{struct [{jid, String}, {group, String}, {nick, String}, {subscription, String}, {pending, String}]}]}}]}}] |
get_roster_with_presence |
struct[{user, String}, {server, String}] |
struct[{contacts, {array, struct[{contact, {array, [{struct [{jid, String}, {resource, String}, {group, String}, {nick,String}, {subscription, String}, {pending, String}, {show, String},{status, String}]}]}}]}}] |
get_presence |
struct[{user, String}, {server, String}] |
struct[{jid, String}, {show, String}, {status, String}] |
get_resources |
struct[{user, String}, {server, String}] |
struct[{resources, {array, [{struct, [{resource, String}]}]}=C2=A0 }] |
send_chat |
struct[{from, String}, {to, String}, {body, String}] |
struct[{res, Integer}] |
send_message |
struct[{from, String}, {to, String}, {subject, String}, {body, String}] |
struct[{res, Integer}] |
send_stanza |
struct[{from, String}, {to, String}, {stanza, String}] |
struct[{res, Integer}] |
Response code integer contains either 0 or the response code of ejabberd in case of error (Example: 409 returned when creating a user means "conflict").
Error code mapping is defined in JEP-0086
Tests
Common examples
If you are familiar with Erlang, you can easily try the XML-RPC server starting a new Erlang Virtual Machine and making calls to ejabberd's XML-RPC. Otherwise, try issuing the following calls using your favorite programming language.
1. Start Erlang with the following command:
$ bin/erl
2. Now on the Erlang console, write the following commands and check theresults:
xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, ["blot cloc 557.889 kg"]}). {ok,{response,["blot cloc 557.889 kg"]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothisnew, [{struct, [{sentence, "this is a test."}]}]}). {ok,{response,[{struct,[{repeated,"this is a test."}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, multhis, [{struct,[{a, 83}, {b, 689}]}]}). {ok,{response,[57187]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, multhisnew, [{struct,[{a, 83}, {b, 689}]}]}). {ok,{response,[{struct,[{mu,57187}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, create_account,[{struct, [{user, "test"}, {server, "localhost"}, {password, "pass"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, create_account,[{struct, [{user, "test"}, {server, "localhost"}, {password, "pass"}]}]}). {ok,{response,[{struct,[{res,409}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, change_password, [{struct, [{user, "test"}, {server, "localhost"}, {newpass, "newpass"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, set_nickname, [{struct, [{user, "test"}, {server, "localhost"}, {nick, "nick"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, set_nickname, [{struct, [{user, "test"}, {server, "localhost"}, {nick, "nick2"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, set_rosternick, [{struct, [{user, "test"}, {server, "localhost"}, {nick, "nick2"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, create_account,[{struct, [{user, "test2"}, {server, "localhost"}, {password, "pass"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, add_rosteritem,[{struct, [{user, "test"}, {server, "localhost"}, {jid, "test2@localhost"}, {nick, "test2nick"}, {group, "Friends"}, {subs, "both"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, add_rosteritem,[{struct, [{user, "test2"}, {server, "localhost"}, {jid, "test@localhost"}, {nick, "MyFriend"}, {group, "Coworkers"}, {subs, "both"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, create_account,[{struct, [{user, "test3"}, {server, "localhost"}, {password, "pass"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, delete_account,[{struct, [{user, "test3"}, {server, "localhost"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, delete_rosteritem,[{struct, [{user, "test2"}, {server, "localhost"}, {jid, "test@localhost"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, get_roster,[{struct, [{user, "test"}, {server, "localhost"}]}]}). {ok,{response,[{struct,[{contacts,{array,[{struct,[{contact,{array,[{struct,[{jid,"test2@localhost"}]}, {struct,[{group,[]}]}, {struct,[{nick,"test2nick"}]}, {struct,[{subscription,[...]}]}, {struct,[{pending,...}]}]}}]}]}}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, get_roster,[{struct, [{user, "test2"}, {server, "localhost"}]}]}). {ok,{response,[{struct,[{contacts,{array,[]}}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, get_roster_with_presence,[{struct, [{user, "test"}, {server, "localhost"}]}]}). {ok,{response,[{struct,[{contacts,{array,[{struct,[{contact,{array,[{struct,[{jid,"test2@localhost"}]}, {struct,[{resource,[]}]}, {struct,[{group,[]}]}, {struct,[{nick,[...]}]}, {struct,[{subscription,...}]}, {struct,[{...}]}, {struct,[...]}, {struct,...}]}}]}]}}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, get_roster_with_presence,[{struct, [{user, "test"}, {server, "localhost"}]}]}). {ok,{response,[{struct,[{contacts,{array,[{struct,[{contact,{array,[{struct,[{jid,"test2@localhost"}]}, {struct,[{resource,[]}]}, {struct,[{group,[]}]}, {struct,[{nick,[...]}]}, {struct,[{subscription,...}]}, {struct,[{...}]}, {struct,[...]}, {struct,...}]}}]}]}}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, get_presence,[{struct, [{user, "test"},{server, "localhost"}]}]}). {ok,{response,[{struct,[{presence,{array,[{struct,[{jid,"test@localhost/Home"}]}, {struct,[{show,"dnd"}]}, {struct,[{status,"you'rewelcome to talk ! it's demo time..."}]}]}}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, send_chat,[{struct,[{from, "admin@localhost"},{to,"test@localhost"},{body,"Hello"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, send_message,[{struct, [{from, "admin@localhost"},{to, "test@localhost"},{subject,"Test"},{body,"Hello"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} Stanza="<message from='admin@localhost' type='chat' to='test@localhost'><body>test</body></message>". xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, send_stanza,[{struct, [{from, "admin@localhost"},{to, "test@localhost"},{stanza,Stanza}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, get_resources,[{struct, [{user, "test"},{server, "localhost"}]}]}). {ok,{response,[{struct,[{resources,{array,[{struct,[{resource,"Home"}]}]}}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, link_contacts,[{struct, [{jid1, "user1@localhost"},{nick1,"user1"},{jid2, "user2@localhost"},{nick2,"user2"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, unlink_contacts,[{struct, [{jid1, "user1@localhost"},{jid2, "user2@localhost"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, add_contacts, [{struct, [{user, "badlop"}, {server, "localhost"}, {contacts, {array, [{struct, [{contact, {array, [{struct, [{group, "Friends"}, {jid, "tom@localhost"}, {nick, "Tom"}]}]}}]}]}}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, rename_account,[{struct,[{user,"user3"},{server,"localhost"},{newuser,"user4"},{newserver,"localhost"}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, check_users_registration, [{struct, [{users, {array, [{struct, [{auser, {array, [{struct, [{user, "test"}, {server, "localhost"}]}]}}, {auser, {array, [{struct, [{user, "test99"}, {server, "localhost"}]}]}}]}]}}]}]}). {ok,{response,[{struct,[{users,{array,[{struct,[{auser,{array,[{struct,[{user,"test"}]}, {struct,[{server,"localhost"}]}, {struct,[{status,1}]}]}}]}, {struct,[{auser,{array,[{struct,[{user,"test99"}]}, {struct,[{server,"localhost"}]}, {struct,[{status,0}]}]}}]}]}}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, add_contacts, [{struct, [{user, "test"}, {server, "localhost"}, {contacts,{array, [{struct, [{contact, {array, [{struct, [{group, "Friends"}, {jid, "test2@localhost"}, {nick, "Test2"}]}]}}, {contact, {array, [{struct, [{group, "Friends"}, {jid, "test3@localhost"}, {nick, "Test3"}]}]}}]}]}}]}]}). {ok,{response,[{struct,[{res,0}]}]}} xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, remove_contacts, [{struct, [{user, "test"}, {server, "localhost"}, {contacts, {array, [{struct, [{jid, "test3@localhost"}]}]}}]}]}). {ok,{response,[{struct,[{res,0}]}]}}
Error cases
Here are some possible XML-RPC server error messages:
- "Connection refused" can mean several different problems: wrong IP, wrong port, the server is not started, etc.:
1> xmlrpc:call({127, 0, 0, 1}, 44444, "/", {call,echothis, [800]}). {error,econnrefused}
- "Bad value" messages are often triggered on type error. Inthe following example, a800 is a string, so it must be put into quotes (""):
2> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, echothis, [a800]}). {error,{bad_value,a800}}
- an "unknown call" is returned when you try to call a method that the server does not implement:
3> xmlrpc:call({127, 0, 0, 1}, 4560, "/", {call, bububu, [800]}). {ok,{response,{fault,-1,"Unknown call:{call,bububu,[800]}"}}}