Skip to content

PRESENCE_CALLINFO module

This module provides OpenSIPS support for shared call appearances (SCA) as defined by BroadWorks SIP Access Side Extensions Interface specifications. The SCA mechanism is a fundamental building block for a variety of enhanced telephony services. Features like attendant console, line extensions, and key system emulation cannot be delivered without some mechanism for sharing call appearances across access devices. Although SIP (RFC 3261) by itself offers no inherent semantics for supporting SCA features, when coupled with an appropriate instantiation of the “SIP Specific Event Notification” framework (RFC 3265), these services can be deployed quite easily in a distributed network.

A shared line is an address of record managed by central controlling element, such as an application server. The application server allows multiple endpoints to register locations against the address of record. The application server is responsible for policing who can register and who cannot register against the shared line.

The module enables the handling of “call-info” and “line-seize” events inside the presence module. It is used with the general event handling module: presence and it constructs and adds “Call-Info” headers to notification events.

The module provides shared call appearance support based on the BroadWorks Shared Call Appearance Interface Specification, Release 23.0.

The module can be used in two ways (depending on who is doing the publishing of the “call-info” data:

  • external publishing - the “call-info” data is received from a third party via SIP PUBLISH requests. In this mode, the modules simply distributes the SCA info, it is not producing any of it - a third-party application must publish “call-info” events to the presence server.
  • internal publishing - the “call-info” data is internally generated by the module, based on the information received from the dialog module - what calls are using what line/index, what is the state of the call, etc. There is no SIP PUBLISH in this case and there is no need for a third-party - the module is self-sufficient and stand alone as functionality.

The used mode can be controlled via the module parameter “disable_dialog_support_for_sca” - see below in the parameter’s section.

The module does not currently implement any authorization rules. It assumes that publish requests are only issued by a third-party application and subscribe requests only by subscriber to call-info and line-seize events. Authorization can thus be easily done by OpenSIPS configuration file before calling handle_publish() and handle_subscribe() functions.

To get better understanding on how the module works please take a look at the following figure:

caller proxy & callee watcher publisher
alice@example presence bob@example watcher@example
server
| | | | |
| |<-----SUBSCRIBE bob----| |
| |------200 OK---------->| |
| |------NOTIFY---------->| |
| |<-----200 OK-----------| |
| | | | |
|--INV bob--->| | | |
| |--INV bob->| | |
| |<-100------| | |
| |<-----PUBLISH(alerting)---------------|
| |------200 OK------------------------->|
| |------NOTIFY---------->| |
| |<-----200 OK-----------| |
| | | | |
| |<-180 ring-| | |
|<--180 ring--| | | |
| | | | |
| | | | |
| |<-200 OK---| | |
|<--200 OK----| | | |
| |<-----PUBLISH(active)-----------------|
| |------200 OK------------------------->|
| |------NOTIFY---------->| |
| |<-----200 OK-----------| |
| | | | |
  • The watcher subscribes to Bob’s “call-info” event.
  • Alice calls Bob.
  • The publisher is publishing the “alerting” state for Bob.
  • PUBLISH is received and handled by presence module. Presence module updates the “presentity”. Presence module checks for active watchers of the presentity. The active watcher is notified via a NOTIFY SIP request.
  • Bob answers the call.
  • The publisher is publishing the “active” state for Bob.
  • PUBLISH is received and handled by presence module. Presence module updates the “presentity”. Presence module checks for active watchers of the presentity. The active watcher is notified via a NOTIFY SIP request.

Line seizures are tracked independently for each appearance index, as permitted by section 6.1.6 of the BroadWorks Shared Call Appearance Interface Specification. Different appearances on the same shared line may be seized concurrently. A competing subscription for an index with a live seizure is rejected with 480, as is a seizure of an index in progressing, alerting, active, or held state. Refreshing or releasing a seizure affects only its owning subscription’s index.

In this mode, the module requires the “dialog” module to be loaded into OpenSIPS. All the publishing will be automatically done (the modules will exchange data directly via C API).

From presence perspective, the OpenSIPS script must be configured to handle the SUBSCRIBE requests only (there is no need for PUBLISH handling as there is no SIP publishing in this mode). So be sure to use the “handle_subscribe()” function (from presence module) in the script.

To trigger the internal publishing (from the dialog module) for a certain call, use the “sca_engage()” function from the script when handling the initial INVITE. It creates the dialog and publishes the state of the caller’s line, of the callee’s line, or of both. For outbound calls, “sca_validate_invite_out()” checks for a recorded seizure on the caller’s requested appearance index. Per branch, “sca_set_called_line()” changes the callee’s line and “sca_mute_branch()” stops publishing it - see the below documentation.

LIMITATIONS : in this mode, the module does not really check if the line exists or not (like defined) - it blindly trust the traffic; also there is no check on how many indexes are for each line. Such information (lines and indexes) are not provisioned into the module: a line becomes known with the first “line-seize” SUBSCRIBE for it, and no “call-info” is published for a line that is not known.

The following routing fragment handles initial INVITEs to a shared line. Call route(sca_inbound) after authentication and access control, before resolving the called line to its registered contacts. It assumes that the line is already known from a previous line-seize subscription, and that the configuration loads the registrar/usrloc, rr, sl, and signaling modules in addition to the dependencies below. Handle SUBSCRIBE requests with handle_subscribe() and route in-dialog requests with loose_route() in the main request route.

Inbound SCA with per-branch publishing
route[sca_inbound] {
# Save the shared line before lookup rewrites the R-URI to a contact.
$var(sca_line) = $ru;
if (!sca_engage("B")) {
send_reply(500, "Cannot Enable SCA");
exit;
}
record_route();
t_on_branch("sca_publish");
if (!lookup("location")) {
send_reply(404, "No Registered Contact");
exit;
}
if (!t_relay()) {
sl_reply_error();
}
exit;
}
branch_route[sca_publish] {
# Example policy: do not publish the callee on a voicemail branch.
if ($rU == "voicemail") {
sca_mute_branch("B");
} else {
sca_set_called_line($var(sca_line));
}
}

Only the callee’s line is published here. For an outbound call from a shared line, use sca_engage("A"); use sca_engage("AB") when both parties are shared lines. Both forms include caller-side validation, so handle a negative return value before relaying the INVITE. A separate sca_validate_invite_out() call is only needed if the script needs to check the caller before engaging publishing.

The following modules must be loaded before this module:

  • presence.
  • tm.
  • dialog - required only for internal publishing (disable_dialog_support_for_sca=0).

None.

Enables or disables call_info event timeout notifications.

Default value is “1” (enabled).

Set call_info_timeout_notification parameter
...
modparam("presence_callinfo", "call_info_timeout_notification", 0)
...

Enables or disables line_seize event timeout notifications.

Default value is “0” (disabled).

Set line_seize_timeout_notification parameter
...
modparam("presence_callinfo", "line_seize_timeout_notification", 1)
...

Disables the internal publishing of the “call-info” events (generated by the dialog module). The publishing is expected to be done via SIP PUBLISH from a third-party. See the working mode described in the beginning of this document.

Default value is “0” (not disabled).

Set disable_dialog_support_for_sca parameter
...
modparam("presence_callinfo", "disable_dialog_support_for_sca", 1)
...

Allows you to control the size of the internal hash table used for storing the information about the lines and indexes (in the internal publishing mode).

The value must be a power of 2. You may consider increasing the value if using a large set of lines (>1000).

Default value is “64”.

Set line_hash_size parameter
...
modparam("presence_callinfo", "line_hash_size", 128)
...

A variable holding the caller’s line, as a SIP URI. When the variable has a string value, “sca_engage()” and “sca_validate_invite_out()” use it instead of the From URI of the INVITE. An unset or non-string value falls back to the From URI; an empty or malformed string is not a valid override and causes an error.

Default value is “NULL” (the From URI is used).

Set caller_spec_param parameter
...
modparam("presence_callinfo", "caller_spec_param", "$var(sca_caller)")
...

A variable holding the callee’s line, as a SIP URI. When the variable has a string value, “sca_engage()” uses it instead of the R-URI of the INVITE. An unset or non-string value falls back to the R-URI; an empty or malformed string is not a valid override and causes an error. The value is captured when “sca_engage()” first registers the publishing callbacks; use “sca_set_called_line()” for subsequent per-branch changes.

Default value is “NULL” (the R-URI is used).

Set callee_spec_param parameter
...
modparam("presence_callinfo", "callee_spec_param", "$var(sca_callee)")
...

The log level of the module’s SCA trace messages. It can be changed at runtime with the presence_callinfo:set_log_level MI command.

Default value is “-3” (L_ALERT).

These messages are subject to the core log level. For example, setting this parameter to 4 (L_DBG) only prints SCA trace messages when the core log level also permits debug messages. Other module errors and debug messages keep their own log levels.

Set log_level parameter
...
modparam("presence_callinfo", "log_level", 4)
...

The function (to be used only in internal publishing mode) checks an outbound call from a shared line: the INVITE must carry a Call-Info header with a nonzero “appearance-index”, and the caller’s line (the From URI, or “caller_spec_param”) must have a recorded seizure for that index.

This checks recorded seizure information, not whether its expiry is still in the future or whether the INVITE sender owns the line-seize subscription. It does not replace authentication or access control in the routing script.

It returns 1 when the check succeeds, or for a non-INVITE request without performing the check. Negative return codes are:

  • -1 - internal publishing is disabled, the caller override cannot be parsed, or an internal error occurs;
  • -2 - the line is not known to the module;
  • -3 - no seizure is recorded for the requested appearance index;
  • -5 - missing or bogus Call-Info header;
  • -6 - no index in the Call-Info header.

Return code -4 (a seizure on a different index) is no longer used: seizures on other indexes are independent and do not satisfy the check for this index.

This function can be used from REQUEST_ROUTE.

sca_validate_invite_out() usage
...
if (is_method("INVITE") and !has_totag()) {
if (!sca_validate_invite_out()) {
send_reply(403, "Line Not Seized");
exit;
}
}
...

The function (to be used only in internal publishing mode) enables the internal publishing for the current new call (initial INVITE). It creates the dialog, if there is none yet, and publishes the state of the call on the lines given by parties:

  • A - the caller’s line: the From URI, or “caller_spec_param”;
  • B - the callee’s line: the R-URI, or “callee_spec_param”, or the line set for the branch by “sca_set_called_line()”.

The flags are uppercase and may be combined as AB. If the parameter is missing or empty, both lines are selected. Unsupported characters are logged and ignored; if no recognized flag remains, both lines are selected.

Whenever A is selected, including by default, the caller is checked as by “sca_validate_invite_out()”. Use B for an inbound call whose caller does not have a shared line and a recorded seizure.

The function returns 1 on success, or for a non-INVITE request without enabling publishing. It returns -1 if internal publishing is disabled or setup fails, and propagates the caller-validation error codes when A is selected.

This function can be used from REQUEST_ROUTE.

sca_engage() usage
...
if (is_method("INVITE") and !has_totag()) {
# a call to a shared line - publish the callee's line only
sca_engage("B");
}
...

The function (to be used only in internal publishing mode) sets the callee’s line for the current branch, overriding the callee identity captured by “sca_engage()”. The value must be a SIP URI; variables are accepted. An empty string removes a previous branch override and restores the captured identity: “callee_spec_param”, if it had a string value, otherwise the R-URI at the time the publishing callbacks were registered. It does not re-read the branch’s current R-URI.

The dialog must already exist, so call “sca_engage()” first.

The function returns 1 on success, or for a non-INVITE request without changing the line. It returns -1 if internal publishing is disabled, no dialog exists, the line cannot be parsed, or an internal error occurs.

This function can be used from REQUEST_ROUTE and BRANCH_ROUTE.

sca_set_called_line() usage
...
branch_route[sca] {
sca_set_called_line("sip:line@example.com");
}
...

The function (to be used only in internal publishing mode) stops the publishing for the callee’s line on the current branch. Only B has an effect; A is accepted, but the caller’s line is still published.

The flags use the same uppercase A, B, and AB syntax as “sca_engage()”. An empty string, or a string with no recognized flags, selects both parties; currently this still only mutes the callee. To disable caller publishing, select only B when calling “sca_engage()”.

The dialog must already exist, so call “sca_engage()” first.

This function can be used from BRANCH_ROUTE.

It returns 1 when the branch mute settings are stored, or -1 if no dialog exists or the settings cannot be stored.

sca_mute_branch() usage
...
branch_route[sca] {
sca_mute_branch("B");
}
...

Replaces obsolete MI command: sca_list_lines.

Lists the SCA lines and indexes currently tracked by the module.

Seizure information is returned inside each entry of indexes, rather than on the line object. seize_state is the index number when a seizure is recorded, or zero after release. seize_expires is the remaining lifetime in seconds (negative for an expired seizure), and seize_expires_ts is the corresponding Unix timestamp. Both expiry fields are zero after release.

Parameters:

  • sca_line (optional) - only list information for this SCA line.

MI FIFO Command Format:

Terminal window
opensips-cli -x mi presence_callinfo:list_lines
opensips-cli -x mi presence_callinfo:list_lines sca_line=sip:line@example.com

Replaces obsolete MI command: sca_release_line.

Releases a tracked SCA line, a specific index from a line, or all lines.

Parameters:

  • sca_line - the SCA line to release; use ALL to release all lines.
  • sca_index (optional) - release only this index from the selected line.

MI FIFO Command Format:

Terminal window
opensips-cli -x mi presence_callinfo:release_line sca_line=sip:line@example.com
opensips-cli -x mi presence_callinfo:release_line sca_line=sip:line@example.com sca_index=1
opensips-cli -x mi presence_callinfo:release_line sca_line=ALL

Replaces obsolete MI command: sca_set_log_level.

Gets or sets the SCA-specific log level used by this module.

Parameters:

  • log_level (optional) - the new log level. If missing, the command returns the current level.

MI FIFO Command Format:

Terminal window
opensips-cli -x mi presence_callinfo:set_log_level
opensips-cli -x mi presence_callinfo:set_log_level log_level=3

Top contributors by DevScore(1), authored commits(2) and lines added/removed(3)

#NameDevScoreCommitsLines++Lines—
1.Liviu Chircu (@liviuchircu)38161670410
2.Bogdan-Andrei Iancu (@bogdan-iancu)2781605256
3.Razvan Crainea (@razvancrainea)2413209488
4.Ovidiu Sas (@ovidiusas)1265777
5.Stefan Darius (@dariusstefan)10439078
6.Vlad Patrascu (@rvlad-patrascu)744567
7.Maksym Sobolyev (@sobomax)6456
8.Denys Ivonenko6316851
9.Dragos Oancea31301
10.Walter Doekes (@wdoekes)3122

All remaining contributors: Alexandra Titoc, Peter Lemenkov (@lemenkov).

(1) DevScore = author_commits + author_lines_added / (project_lines_added / project_commits) + author_lines_deleted / (project_lines_deleted / project_commits)

(2) including any documentation-related commits, excluding merge commits

(3) ignoring whitespace edits, renamed files and auto-generated files

#NameCommit Activity
1.Liviu Chircu (@liviuchircu)Mar 2014 - Nov 2026
2.Dragos OanceaNov 2026 - Nov 2026
3.Denys IvonenkoSep 2026 - Oct 2026
4.Razvan Crainea (@razvancrainea)Oct 2011 - Sep 2026
5.Stefan Darius (@dariusstefan)Jun 2026 - Jul 2026
6.Alexandra TitocSep 2024 - Sep 2024
7.Bogdan-Andrei Iancu (@bogdan-iancu)Jan 2013 - Apr 2024
8.Vlad Patrascu (@rvlad-patrascu)May 2017 - Mar 2023
9.Maksym Sobolyev (@sobomax)Jan 2021 - Feb 2023
10.Peter Lemenkov (@lemenkov)Jun 2018 - Jun 2018

All remaining contributors: Walter Doekes (@wdoekes), Ovidiu Sas (@ovidiusas).

(1) including any documentation-related commits, excluding merge commits

Last edited by: Razvan Crainea (@razvancrainea), Vlad Patrascu (@rvlad-patrascu), Peter Lemenkov (@lemenkov), Liviu Chircu (@liviuchircu), Bogdan-Andrei Iancu (@bogdan-iancu), Ovidiu Sas (@ovidiusas).

All documentation files (i.e. .md extension) are licensed under the Creative Common License 4.0