# OpenSIPS configuration This is a configuration example for handling push notifications for Linphone and Sylk Mobile applications. The token data generated by the device is sent as parameters to the Contact header. Later, when an INVITE comes in, if a token is found for the called party, a push notification is sent to each mobile device which will wake up and register while the call is in progress. These late registrations will cause OpenSIPS to fork the initial request to the newly registered contacts. ## SQL storage The token is saved into a MySQL table. ``` CREATE TABLE `push_tokens` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` char(64) NOT NULL DEFAULT '', `domain` char(64) NOT NULL DEFAULT '', `platform` char(64) NOT NULL DEFAULT '', `app` char(255) NOT NULL DEFAULT '', `token` char(255) NOT NULL DEFAULT '', `sip_instance` char(255) NOT NULL DEFAULT '', `user_agent` char(255) NOT NULL DEFAULT '', `last_modified` datetime NOT NULL DEFAULT current_timestamp(), `silent` char(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `token_idx` (`username`,`domain`,`sip_instance`,`app`) ); ``` ## Loaded modules ``` loadmodule "event_routing.so" ``` ## Handle REGISTER route ``` route[REGISTER] { ... # parse the Contact header paramaters or the Contact uri parameters of # the register and save the push token to the database $avp(sip_instance) = $(ct.fields(params){param.value,+sip.instance}); if (!$avp(sip_instance)) { $avp(sip_instance) = $(ct.fields(uri){uri.param,pn-device}); } if ($avp(sip_instance)) { # try to find if the device is mobile $avp(pn_type) = $(ct.fields(uri){uri.param,pn-type}); if (!$avp(pn_type)) { $avp(pn_type) = $(ct.fields(uri){uri.param,pn-type}); } $avp(pn_app) = $(ct.fields(uri){uri.param,app-id}); if (!$avp(pn_app)) { $avp(pn_app) = $(ct.fields(uri){uri.param,pn-app}); } $avp(pn_token) = $(ct.fields(uri){uri.param,pn-tok}); if (!$avp(pn_token)) { $avp(pn_token) = $(ct.fields(uri){uri.param,pn-tok}); } $avp(pn_silent) = $(ct.fields(uri){uri.param,pn-silent}); if (!$avp(pn_silent)) { $avp(pn_silent) = $(ct.fields(uri){uri.param,pn-silent}); } if (!$avp(pn_silent)) { $avp(pn_silent) = "0"; } } if (save("location")) { # save data required later for push notifications if ($avp(pn_type) and $avp(pn_token) and $avp(pn_app)) { $avp(query) = "SELECT token from push_tokens where username = '" + $(tU{s.escape.common}) + "' and domain = '" + $(td{s.escape.common}) + "' and app xlog("L_DBG", "[CONFIG] REGISTER push SQL query: $avp(query)\n"); avp_db_query($avp(query), "$avp(old_pn_token)"); if (not $avp(old_pn_token)) { $avp(query) = "INSERT into push_tokens (username, domain, platform, app, token, sip_instance, user_agent, silent) values ('" + $(tU{s.escape.comm xlog("L_DBG", "[CONFIG] REGISTER push SQL query: $avp(query)\n"); avp_db_query($avp(query)); } else { $avp(query) = "UPDATE push_tokens set silent = '" + $(avp(pn_silent){s.escape.common}) + "', last_modified = NOW(), token = '" + $(avp(pn_token){ xlog("L_DBG", "[CONFIG] REGISTER push SQL query: $avp(query)\n"); avp_db_query($avp(query)); } } } } ``` ## Handle INVITE route ``` route[INVITE] { ... $avp(sip_application_type) = "audio"; if (has_body("application/sdp")) { if (search_body("m=audio")) { $avp(sip_application_type) = "audio"; } if (search_body("m=video")) { if (is_avp_set("$avp(sip_application_type)")) { pv_printf($avp(sip_application_type), "$avp(sip_application_type), video"); } else { $avp(sip_application_type) = "video"; } } if (search_body("(m=message).*?(MSRP)")) { if (search_body("a=file-selector")) { if (is_avp_set("$avp(sip_application_type)")) { pv_printf($avp(sip_application_type), "$avp(sip_application_type), file-transfer"); } else { $avp(sip_application_type) = "file-transfer"; } } else { if (is_avp_set("$avp(sip_application_type)")) { pv_printf($avp(sip_application_type), "$avp(sip_application_type), chat"); } else { $avp(sip_application_type) = "chat"; } } } } # load push notifications tokens saved during register and wake up the devices $avp(query) = "SELECT token, app, platform, sip_instance from push_tokens WHERE username='" + $(var(user){s.escape.common}) + "' AND domain='" + $(var(domain){s.escape.common}) + "'"; xlog("L_DBG", "[CONFIG] $avp(query)\n"); $var(i) = 0; store_dlg_value("late_forking","0"); if (avp_db_query($avp(query), "$avp(pn_token);$avp(pn_app);$avp(pn_platform);$avp(sip_instance)")) { $var(pn_event) = 'incoming_session'; $var(from) = $fU + "@" + $fd; $var(to) = $tU + "@" + $td; $var(ci) = $ci; for ($var(pn_token) in $(avp(pn_token)[*])) { $var(pn_app) = $(avp(pn_app)[$var(i)]); $var(pn_platform) = $(avp(pn_platform)[$var(i)]); $var(sip_instance) = $(avp(sip_instance)[$var(i)]); $var(i) = $var(i) + 1; $avp(late_forking) = 1; store_dlg_value("late_forking","1"); store_dlg_value("sip_application_type",$avp(sip_application_type)); $var(sip_application_type) = $avp(sip_application_type); # Launch method # launch is a fire and forget mechanism, the script does not block or wait, it just continues $var(push_command) = "/usr/local/bin/sylk-pushclient --url \"http://81.23.228.160:8400/push\" --platform=\"" + $var(pn_platform) + "\" --appid=\"" + $var(pn_app) + "\" --from_name=\"" + $(fn{s.escape.common}) + "\" --mediatype=\"" + $var(sip_application_type) + "\" --event=\"" + $var(pn_event) + "\" --token=\"" + $var(pn_token) + "\" --deviceid=\"" + $var(sip_instance) +"\" --callid=\"" + $ci + "\" --from=\"" + $var(from) + "\" --to=\"" + $var(to) + "\""; xlog("L_INFO", "[CONFIG] Push notification command: $var(push_command)\n"); launch(exec("$var(push_command)",, $var(rc)), "PN_RESULT"); } } if (not t_newtran()) { sl_reply_error(); exit; } if ($avp(late_forking)) { sl_send_reply(110, "Push sent"); t_wait_for_new_branches(); $avp(filter) = "aor="+$rU+"@"+$rd; notify_on_event("E_UL_CONTACT_INSERT",$avp(filter), "LATE_FORKING", 40); } if (lookup("location")) { xlog("L_INFO", "[CONFIG] $rU@$rd has online devices for $rm ($ci)\n"); } else { if ($avp(late_forking)) { xlog("L_INFO", "[CONFIG] $ru is not yet online ($ci)\n"); } else { sl_send_reply(480, "User not online"); } } } ``` ## Handle CANCEL route ``` route[CANCEL] { ... if ($dlg_val(late_forking) == "1") { $var(user) = $rU; $var(domain) = $rd; $var(sip_application_type) = $dlg_val(sip_application_type); $avp(query) = "SELECT token, app, platform, sip_instance from push_tokens WHERE username='" + $(var(user){s.escape.common}) + "' AND domain='" + $(var(domain){s.escape.common}) + "'"; xlog("L_DBG", "[CONFIG] $avp(query)\n"); if (avp_db_query($avp(query), "$avp(pn_token);$avp(pn_app);$avp(pn_platform);$avp(sip_instance)")) { $var(i) = 0; $var(pn_event) = 'cancel'; $var(from) = $fU + "@" + $fd; $var(to) = $tU + "@" + $td; $var(ci) = $ci; for ($var(pn_token) in $(avp(pn_token)[*])) { $var(pn_app) = $(avp(pn_app)[$var(i)]); $var(pn_platform) = $(avp(pn_platform)[$var(i)]); $var(sip_instance) = $(avp(sip_instance)[$var(i)]); $var(i) = $var(i) + 1; # Launch method # launch is a fire and forget mechanism, the script does not block or wait, it just continues $var(push_command) = "/etc/opensips/config/siteconfig/pusher.py --url \"http://81.23.228.160:8400/push\" --platform=\"" + $var(pn_platform) + "\" --appid=\"" + $var(pn_app) + "\" --from_name=\"" + $(fn{s.escape.common}) + "\" --mediatype=\"" + $var(sip_application_type) + "\" --event=\"" + $var(pn_event) + "\" --token=\"" + $var(pn_token) + "\" --deviceid=\"" + $var(sip_instance) +"\" --callid=\"" + $ci + "\" --from=\"" + $var(from) + "\" --to=\"" + $var(to) + "\""; xlog("L_INFO", "[CONFIG] Push notification command: $var(push_command)\n"); launch(exec("$var(push_command)",, $var(rc)), "PN_RESULT"); } } } } ``` ## Handle late forking route ``` route[LATE_FORKING] { # handle incoming calls for mobile devices woken up by push notifications INFO("$avp(aor) registered contact $avp(uri) while receiving an incoming call"); # take the contact described by the E_UL_CONTACT_INSERT # event and inject it as a new branch into the original # transaction t_inject_branches("event"); } ``` ## Handle push result route ``` route[PN_RESULT] { if ($var(retcode) == "1") { xlog("L_INFO", "[CONFIG] $var(pn_event) push notification for $var(to) on device $var(sip_instance) ($var(ci)) suceeded\n"); } else { xlog("L_INFO", "[CONFIG] $var(pn_event) push notification for $var(to) on device $var(sip_instance) ($var(ci)) failed\n"); } } ```