Presence Tutorial
This tutorial describes how to setup OpenSIPS Presence Agent together with OpenXCAP Presence Policy server and Blink SIP client. The interactions with Blink client are described here:
https://icanblink.com/help/manual-mac/presence-mac/
OpenXCAP setup
Database setup
Both OpenXCAP backends (Database and OpenSIPS) depend on a database engine to store service subscribers and XCAP resources. The database creation scripts are found in the scripts/ directory, but it's tipically created using the _opensipsdbctl_ tool provided by OpenSIPS.
Create Database
If you use OpenSIPS backend, you do not need to create any tables and configure OpenXCAP to use the same database as OpenSIPS.
If you want to setup OpenXCAP to use its own database, create the database:
mysqladmin create openxcap
Add MySQL user
Use the following sql as template, edit it first and run it against on your database:
scripts/mysql-create-user.sql
Create tables
If you run it in a venv and you have the requirements installed, alembic can create the database, for debian you can go to /usr/share/doc/openxcap and run the command:
alembic upgrade heads
Or you can use the script:
scripts/mysql-create-tables.sql
This sql creates two tables:
subscriber, which is used to authenticate XCAP requests
xcap, where the XCAP documents are actually stored
The subscriber table is a subset of the subscriber table from OpenSIPS, xcap table is the same as the one from OpenSIPS.
Server configuration
For the Debian package edit /etc/openxcap/config.ini. For other Linux OS copy config.ini.sample from the tar archive to the same directory. Edit config.ini with your settings.
In a venv a config.ini can be read from the same location as the openxcap script.
The specific settings for an installation must be set from the configuration file, which is split in several configuration sections.
The [Server]
section contains global settings: the IP address and port where
OpenXCAP listens for client requests.
The XCAP root is the context that contains all the documents across all
applications and users that are managed by the server. Only the client requests
that address the root defined here are accepted. If the root URI has the
"https" scheme and a certificate and key file are configured, the server will
listen for requests in TLS mode. The X509 certificate and private key that will
identify the server are loaded using the values in the [TLS]
section.
OpenXCAP supports multiple, interchangeable backend modules. Each backend
knows where and how to authorize and authenticate XCAP users and where to
store the XCAP documents. Currently, supported values are "Database" and
"OpenSIPS", the specific settings will be taken the corresponding sections,
[Database]
or [OpenSIPS]
.
An XCAP request must be authenticated before it's handled, and the various
settings are found in the [Authentication]
section.
A trusted peer IP list can be defined, requests matching this list will be accepted without authentication.
Client requests must be authenticated in the context of a realm that is the same as the SIP domain. This realm is derived in real time for each request using the following logic:
if the user section of the XCAP URI (the section following the "users" path segment) is in the form of username@domain, the realm is taken from the domain part
some XCAP clients (e.g. CounterPath's Eyebeam), only put the username in the XCAP URI, so there is the need for a convention to determine the realm: it must be included in the XCAP root URI on the client side. For example, if the XCAP root of the server is http://example.com/xcap-root, the client should be provisioned with http://example.com/xcap-root@domain/
if the above logic does not provide the realm, the realm will be taken from the default_realm setting of [Authentication] There are separate configuration settings for each backend. The current supported back-ends are Database and OpenSIPS.
The Database section contains the database connection URI to the database where the service subscribers are kept (authentication_db_uri) and the database connection URI to the database where XCAP documents are stored. Currently, only MySQL database engine has been implemented.
The OpenSIPS section contains all the settings for OpenSIPS.
When using TLS you must generate an X.509 certificate and the corresponding key. Consult Internet resources for how to do this. The procedure is the same as for any other TLS server like Apache web server.
Running
For non Debian systems copy the service file from the debian directory to /etc/systemd/system/ edit it to match your system.
The reload systemd:
sudo systemctl daemon-reload
Start OpenXCAP server:
sudo systemctl start openxcap
You can also start OpenXCAP in the foreground, which is useful to debug the configuration and requests in real time. The server will log its messages in the console where it was started:
~ ./openxcap --no-fork 5.2s Mon Mar 24 15:55:18 2025 INFO Started server process [99259] INFO Waiting for application startup. INFO Context impl SQLiteImpl. INFO Will assume non-transactional DDL. INFO Database initialized and migrations applied. INFO OpenXCAP app is running... INFO Application startup complete. INFO Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
OpenXCAP logs its messages to /var/log/openxcap/ and to the system log.
Logging
OpenXCAP logs its start, stop and error messages to /var/log/syslog
or the system journal. Client
access requests are logged in /var/log/openxcap/access.log
. You can
configure the logging of the headers and bodies of client requests and responses
in the [Logging]
section of the configuration file.
Adding Accounts
The accounts used for authentication of XCAP requests are stored in OpenSIPS subscriber table. You can add subscribers by using your favorite OpenSIPS subscriber management tool (OpenSIPS provides the opensipsctl command line script with the default installation). The following script that can also be used to manually add accounts to opensips subscriber table:
#!/usr/bin/env python """This is an example that shows how a new user could be added to `subscriber' table. It does NOT actually create a new record in the database. """ import sys from hashlib import md5 print __doc__ try: username, domain, password = sys.argv[1:] except ValueError: sys.exit('USAGE: %s username domain password' % sys.argv[0]) hash = md5(":".join([username, domain, password])).hexdigest() query = """INSERT INTO subscriber (username, domain, password, ha1) VALUES ("%(username)s", "%(domain)s", "%(password)s", "%(hash)s");""" % locals() print query
After completing this chapter, go to Test suite section.
JSON API
A RESTful API is available to manage in an easy way an addressbook, which handles the modifications of XCAP documents. The API can show/update/delete contacts, groups and policies.
A full description of the API can be accessed using a web browser at the '/docs' or '/redoc' urls. At this URL one can also test all API functions.
A working example is deployed with sip2sip.info SIP service at:
and
OpenSIPS setup
The following OpenSIPS configuration example enables OpenSIPS to act like a dedicated SIP Presence agent that accepts messages from a trusted SIP Proxy with XCAP authorization enabled using OpenXCAP as policy server. The SIP Proxies defined as trusted peers must be configured to authenticate and authorize the PUBLISH and SUBSCRIBE methods and deal with NAT traversal.
Is advisable to consult https://opensips.org web site, documentation section for up to date settings documentation.
Settings (save as settings.m4):
divert(-1) define(`SERVER_IP',`127.0.0.1') define(`UDP_PORT', `5060') define(`TCP_PORT', `5060') define(`TLS_PORT', `5061') define(`XMLRPC_PORT', `8000') define(`ENABLE_TCP', `no') # OpenSIPS will listen on TCP for SIP requests define(`ENABLE_TLS', `no') # OpenSIPS will listen on TLS for SIP requests define(`TRUSTED_PEERS_GROUP', `42') # ID of the group containing the trusted peers define(`DB_URL', `mysql://opensips:opensipsrw@localhost/opensips') # Database address # Helpers define(`PRESENCE_SERVER_ADDRESS', `sip:presence@SERVER_IP:UDP_PORT') define(`RLS_SERVER_ADDRESS', `sip:rls@SERVER_IP:UDP_PORT') divert
OpenSIPS configuration (save as opensips.m4):
# # OpenSIPS Presence Agent script # by AG Projects <support@ag-projects.com> # # This OpenSIPS configuration example enables OpenSIPS to act like a dedicated SIP Presence agent that accepts messages # from a trusted SIP Proxy with XCAP authorization enabled using OpenXCAP as policy server. The SIP Proxies defined as trusted # peers must be configured to authenticate and authorize the PUBLISH and SUBSCRIBE methods and deal with NAT traversal. # # You can enable / disable more features / functionalities by # re-generating the scenario with different options. # # Please refer to the Core CookBook at: # http://www.opensips.org/Resources/DocsCookbooks # for a explanation of possible statements, functions and parameters. # ####### Global Parameters ######### debug=3 log_stderror=no log_facility=LOG_LOCAL0 fork=yes children=4 /* uncomment the following lines to enable debugging */ #debug=6 #fork=no #log_stderror=yes /* uncomment the next line to enable the auto temporary blacklisting of not available destinations (default disabled) */ #disable_dns_blacklist=no /* uncomment the next line to enable IPv6 lookup after IPv4 dns lookup failures (default disabled) */ #dns_try_ipv6=yes /* comment the next line to enable the auto discovery of local aliases based on revers DNS on IPs */ auto_aliases=no listen=udp:SERVER_IP:UDP_PORT ifelse(ENABLE_TCP, `yes', `disable_tcp=no listen=tcp:SERVER_IP:TCP_PORT' , ` ifelse(ENABLE_TLS,`yes',`disable_tcp=no ',`disable_tcp=yes')') ifelse(ENABLE_TLS,`yes',`disable_tls=no listen=tls:SERVER_IP:TLS_PORT tls_verify_server=1 tls_verify_client = 1 tls_require_client_certificate = 0 tls_method = TLSv1 tls_certificate = "/usr/local/etc/opensips/tls/user/user-cert.pem" tls_private_key = "/usr/local/etc/opensips/tls/user/user-privkey.pem" tls_ca_list = "/usr/local/etc/opensips/tls/user/user-calist.pem" ', `disable_tls=yes') ####### Modules Section ######## #set module path mpath="/usr/local/lib/opensips/modules/" #### SIGNALING module loadmodule "signaling.so" #### StateLess module loadmodule "sl.so" #### Transaction Module loadmodule "tm.so" #### Record Route Module loadmodule "rr.so" #### MAX ForWarD module loadmodule "maxfwd.so" #### FIFO Management Interface loadmodule "mi_fifo.so" modparam("mi_fifo", "fifo_name", "/tmp/opensips_presence_fifo") modparam("mi_fifo", "fifo_mode", 0666) #### XMLRPC Management Interface loadmodule "mi_xmlrpc.so" modparam("mi_xmlrpc", "port", XMLRPC_PORT) #### URI module loadmodule "uri.so" #### DB MySQL module loadmodule "db_mysql.so" #### DOMAIN module loadmodule "domain.so" modparam("domain", "db_url", "DB_URL") modparam("domain", "db_mode", 1) modparam("usrloc|uri", "use_domain", 1) #### PERMISSIONS module loadmodule "permissions.so" modparam("permissions", "db_url", "DB_URL") #### PRESENCE modules loadmodule "presence.so" loadmodule "xcap.so" loadmodule "presence_xml.so" loadmodule "rls.so" modparam("presence|pua|xcap", "db_url", "DB_URL") modparam("presence", "server_address", "PRESENCE_SERVER_ADDRESS") modparam("presence", "fallback2db", 1) modparam("presence", "notify_offline_body", 0) modparam("presence_xml", "pres_rules_auid", "org.openmobilealliance.pres-rules") modparam("presence_xml", "force_active", 0) modparam("presence_xml", "pidf_manipulation", 1) modparam("presence_xml", "generate_offline_body", 0) modparam("xcap", "integrated_xcap_server", 1) modparam("rls", "server_address", "RLS_SERVER_ADDRESS") modparam("rls", "to_presence_code", 5) modparam("rls", "waitn_time", 10) ####### Routing Logic ######## # main request routing logic route{ if (!mf_process_maxfwd_header("10")) { sl_send_reply("483","Too Many Hops"); exit; } if (!(check_source_address("TRUSTED_PEERS_GROUP")) || (uri==myself && !is_present_hf("Self-Forwarded"))) { sl_send_reply("403", "Forbidden"); exit; } if(!is_method("PUBLISH|SUBSCRIBE|NOTIFY")) { sl_send_reply("503", "Service Unavailable"); exit; } if (is_method("SUBSCRIBE")) { if (has_totag()) { if (uri=="PRESENCE_SERVER_ADDRESS" || uri=="RLS_SERVER_ADDRESS" || !loose_route()) { if (t_newtran()) { rls_handle_subscribe(); if ($retcode==5) { handle_subscribe(); } } else { sl_reply_error(); } } else { sl_send_reply("503", "Service Unavailable"); exit; } } else { if (loose_route()) { xlog("Rejecting incorrectly formatted request\n"); sl_send_reply("400", "Bad request"); exit; } # only process presence for local users if (!is_uri_host_local()) { send_reply("403","Forbidden"); exit; } rls_handle_subscribe(); switch ($retcode) { case 5: # RLS indicated that message should be processed by presence if (db_does_uri_exist()) { handle_subscribe(); } else { t_reply("404", "User not found"); } exit; default: exit; } } } if (is_method("PUBLISH")) { if (!is_from_local()) { sl_send_reply("403", "PUBLISH forbidden for outside domains"); exit; } if (t_newtran()) { handle_publish(); } else { sl_reply_error(); } exit; } if (is_method("NOTIFY")) { if (!has_totag()) { sl_send_reply("405", "Method Not Allowed"); exit; } if (!loose_route()) { if (!t_newtran()) { sl_reply_error(); exit; } rls_handle_notify(); switch ($retcode) { case 1: # Notify processed by rls xlog("$rm processed by RLS\n"); exit; case -1: # Error xlog("$rm processed by RLS but has error\n"); t_reply("500", "Server error while processing RLS NOTIFY"); exit; default: if (uri == "RLS_SERVER_ADDRESS") { xlog("$rm should be processed by RLS but was not recognized, dropping\n"); t_reply("481", "Server error while processing RLS NOTIFY"); } exit; } } } } local_route { if (is_method("SUBSCRIBE")) { xlog("Locally generated SUBSCRIBE due to RLS to $tu\n"); # Send it back to ourselves, so that we can do handle_subscribe on append_hf("Self-Forwarded: Yes\r\n"); $du = "sip:SERVER_IP:UDP_PORT"; } }
In order to build a working opensips.cfg file adjust settings.m4 to your needs and run the following command:
m4 settings.m4 opensips.m4 > opensips.cfg
Blink Client setup
The only setting required by Blink is the XCAP Root, it must match the XCAP root from OpenXCAP server configuration.
Alternatively, the owner of the SIP domain can add the following record into the DNS:
xcap.example.com. 3600 IN TXT "https://xcap.example.net/xcap-root/"
Replace example.com with your domain and the xcap root accordingly. If this DNS TXT record exists, Blink will discover the XCAP root in the DNS response.