You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/doc/tutorials/seruser/apps.xml

809 lines
28 KiB

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<section id="application_writing" xmlns:xi="http://www.w3.org/2001/XInclude">
<sectioninfo>
<revhistory>
<revision>
<revnumber>$Revision$</revnumber>
<date>$Date$</date>
</revision>
</revhistory>
</sectioninfo>
<title>Application Writing</title>
<para>
<application>ser</application> offers several
ways to couple its functionality with applications. The coupling
is bidirectional: <application>ser</application>
can utilize external applications and external applications can
utilize <application>ser</application>.
An example of the former direction would be an external program
determining a least-cost route for a called destination using
a pricing table. An example of the latter case
is a web application for server provisioning.
Such an application may want to send instant
messages, query all current user's locations and monitor server
health. An existing web interface to <application>ser</application>,
<application>serweb</application>, actually
does all of it.
</para>
<para>
The easiest, language-independent way of using external logic
from <application>ser</application> is provided
by exec module. exec module allows <application>ser</application>
to start external programs on receipt of a request. The
programs can execute arbitrary logic and/or affect routing of SIP
requests. A great benefit of this programming method is it
is language-independent. Programmers may use programming languages
that are effective or with which they are best familiar.
<xref linkend="usingexec"/> gives additional examples illustrating
use of the exec module.
</para>
<para>
Another method for extending <application>ser</application>
capabilities is to write new modules in C. This method takes
deeper understanding of <application>ser</application>
internals but gains the highest flexibility. Modules can implement
arbitrary brand-new commands upon which <application>ser</application>
scripts can rely on. Guidelines on module programming can be
found in <application>ser</application>
programmer's handbook available from iptel.org website.
</para>
<para>
To address needs of applications wishing to leverage
<application>ser</application>,
<application>ser</application> exports
parts of its functionality via its built-in
"Application FIFO server". This is a simple textual
interface that allows any external applications
to communicate with the server. It can be used to
send instant messages, manipulate user contacts,
watch server health, etc. Programs written in any
language (PHP, shell scripts, Perl, C, etc.) can
utilize this feature. How to use it is shown in
<xref linkend="fifoserver"/>.
</para>
<section id="usingexec">
<title>Using exec Module</title>
<para>
The easiest way is to couple <application>ser</application>
with external applications via the <emphasis>exec</emphasis>
module. This module allows execution of logic and URI manipulation
by external applications on request receipt. While very
simple, many useful services can be
implemented this way. External applications can be written in
any programming language and do not be aware of SIP at all.
<application>ser</application> interacts with
the application via standard input/output and environment
variables.
</para>
<para>
For example, an external shell script
may send an email whenever a request for a user arrives.
</para>
<example>
<title>Using exec: Step 1</title>
<programlisting>
# send email if a request for user "jiri" arrives
if (uri=~"^sip:jiri@") {
exec_msg("echo 'body: call arrived'|mail -s 'call for you' jiri");
}
</programlisting>
</example> <!-- step 1 -->
<para>
In this example, the <command>exec_msg</command>
action starts an external shell. It passes a received SIP request
to shell's input. In the shell, <command>mail</command> command
is called to send a notification by e-mail.
The script however features several simplifications:
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para>
The email notification does not tell who was calling.
</para>
</listitem>
<listitem>
<para>
The logic is not general: it only supports one well-known user (jiri).
</para>
</listitem>
<listitem>
<para>
The logic is stateless. It will be executed on
every retransmission.
</para>
</listitem>
<listitem>
<para>
It is a script fragment not explaining the
context. This particular example may be for
example used to report on missed calls.
</para>
</listitem>
</orderedlist>
All of these simplifications are addressed step-by-step
in the following examples.
</para>
<example> <!-- step 2: who called me -->
<title>Using exec: Step 2, Who Called Me</title>
<para>
This example shows how to display caller's address
in email notification. The trick is easy: process
request received on shell program's input
and grep From header field.
</para>
<programlisting>
<xi:include href="../../examples/exec_s2.cfg" parse="text"/>
</programlisting>
<para>
The following two figures show an example SIP request and
email notification generated on its receipt.
<screen>
<![CDATA[
INVITE sip:jiri@iptel.org SIP/2.0
Via: SIP/2.0/UDP 195.37.77.100:5040
Max-Forwards: 10
From: "alice" <sip:alice@iptel.org>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
To: <sip:jiri@iptel.org>
Call-ID: d10815e0-bf17-4afa-8412-d9130a793d96@213.20.128.35
CSeq: 2 INVITE
Contact: <sip:123.20.128.35:9315>
Content-Type: application/sdp
Content-Length: 451
--- SDP payload snipped ---
]]>
</screen>
email received:
<screen>
<![CDATA[
Date: Thu, 12 Dec 2002 14:25:02 +0100
From: root <root@cat.iptel.org>
To: jiri@cat.iptel.org
Subject: request for you
From: "alice" <sip:alice@iptel.org>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
request received
]]>
</screen>
</para>
<para>
There is another way to learn values of request
header fields, simpler than use of <command>grep</command>.
<application>ser</application>
parses header fields and passes their values in
environment variables. Their names correspond
to header field names prefixed with "SIP_HF_".
<programlisting>
# send email if a request for "jiri" arrives
if (uri=~"^sip:jiri@") {
exec_msg("echo request received from $SIP_HF_FROM | mail -s 'request for you' jiri");
};
</programlisting>
Moreover, several other values are passed in environment
variables. <varname>SIP_TID</varname> is a token uniquely identifying
transaction, to which the request belongs. <varname>SIP_DID</varname>
includes to-tag, and is empty in requests creating a dialog.
<varname>SIP_SRCIP</varname> includes IP address, from which the
request was sent. <varname>SIP_RURI</varname> and <varname>SIP_ORURI</varname>
include current request-uri and original request-uri respectively,
<varname>SIP_USER</varname> and <varname>SIP_OUSER</varname> username
parts of these. The following listing shows environment variables
passed to a shell script on receipt of the previous message:
<programlisting>
<![CDATA[
SIP_HF_MAX_FORWARDS=10
SIP_HF_VIA=SIP/2.0/UDP 195.37.77.100:5040
SIP_HF_CSEQ=2 INVITE
SIP_HF_FROM="alice" <sip:alice@iptel.org>;tag=76ff7a07-c091-4192-84a0-d56e91fe104f
SIP_ORUI=sip:jiri@iptel.org
SIP_HF_CONTENT_LENGTH=451
SIP_TID=3b6b8295db0835815847b1f35f3b29b8
SIP_DID=
SIP_RURI=iptel.org
SIP_HF_TO=<sip:jiri@iptel.org>
SIP_OUSER=jiri
SIP_HF_CALLID=d10815e0-bf17-4afa-8412-d9130a793d96@213.20.128.35
SIP_SRCIP=195.37.77.100
SIP_HF_CONTENT_TYPE=application/sdp
SIP_HF_CONTACT=<sip:123.20.128.35:9315>
]]>
</programlisting>
</para>
</example> <!-- step 2, who called me -->
<example> <!-- step 3, make the script work for anyone -->
<title>Using exec: step 3, Make The Script Work For Anyone</title>
<para>
A drawback of the previous example is it works only
for one well-known user: request URI is matched against
his SIP address and notification is sent to his hard-wired email
address. In real scenarios, one would like
to enable such a service for all users without enumerating
their addresses in the script. The missing piece
is translation of user's SIP name to his email address.
This information is maintained in subscriber profiles,
stored in MySQL by <application>ser</application>.
To translate the username to email address, the executed script
needs to query the MySQL database. That is what this example
shows. First, an SQL query is constructed which looks up
email address of user, for whom a request arrived. If the
query does not return a valid email address, the script
returns with an error status and <application>ser</application>
script replies with "user does not exist". Otherwise
an email notification is sent.
<programlisting>
<xi:include href="../../examples/exec_s3.cfg" parse="text"/>
</programlisting>
</para>
</example> <!-- step 3 make the script work for anyone -->
<example> <!-- step 4, stateful processing -->
<title>Adding Stateful Processing</title>
<para>
The previously improved example still features a shortcoming.
When a message retransmission arrives due to a network
mistake such as lost reply, the email notification is
executed again and again. That happens because the script
is stateless, i.e., no track of current transactions is
kept. The script does not know whether a request is
a new or a retransmitted one. Transaction management may
be introduced by use of tm module as described in
<xref linkend="statefulua"/>. In the script,
<command>t_newtran</command> is first
called to absorb requests retransmission -- if they
occur, script does not continue. Then, as in the previous
example, an exec module action is called. Eventually,
a reply is sent statefully.
<note>
<para>
Note carefully: it is important that the stateful
reply processing (<command>t_reply</command>)
is used as opposed to using stateless replies
(<command>sl_send_reply</command>).
Otherwise, the outgoing reply would not affect
transactional context and would not be resent on
receipt of a request retransmission.
</para>
</note>
<programlisting>
<xi:include href="../../examples/exec_s4.cfg" parse="text"/>
</programlisting>
</para>
</example> <!-- step 4, stateful processing -->
<example> <!-- step 5, full exec use -->
<title>Full Example of exec Use</title>
<para>
The last example iteration shows how to integrate the
email notification on missed calls with the default
<application>ser</application> script
(see <xref linkend="defaultscript"/>). It generates an
email for every call invitation to an off-line user.
<programlisting>
<xi:include href="../../examples/exec_s5.cfg" parse="text"/>
</programlisting>
</para>
<para>
Production "missed calls" services may want to
report on calls missed for other reasons than
being off-line too. Particularly, users may wish to be
reported calls missed due to call cancellation,
busy status or a downstream failure. Such missed
calls can be easily reported to syslog or mysql
using the acc module (see <xref linkend="missedcalls"/>).
The other, more general way, is to return to request
processing on receipt of a negative reply.
(see <xref linkend="replyprocessingsection"/>). Before
a request is forwarded, it is labeled to be
re-processed in a <command>failure_route</command>
on receipt of a negative reply -- this is what
<command>t_on_failure</command> action
is used for. It does not matter what caused the transaction
to fail -- it may be unresponsive downstream server,
server responding with 6xx, or server sending a 487
reply, because an INVITE was canceled. When any such
circumstances occur (i.e., transaction does not complete
with a 2xx status code), <command>failure_route</command>
is entered.
</para>
<para>
The following <application>ser</application>
script reports missed calls in all possible cases.
It reports them when a user is off-line as well as when
a user is on-line, but INVITE transaction does not complete
successfully.
<programlisting>
<xi:include href="../../examples/exec_s5b.cfg" parse="text"/>
</programlisting>
</para>
</example> <!-- step 5, full exec use -->
</section> <!-- using exec -->
<section id="fifoserver">
<title>Application FIFO Server</title>
<para>
Application FIFO server is a very powerful method to program
SIP services. The most valuable benefit
is it works with SIP-unaware applications
written in any programming language. Textual nature of the
FIFO interface allows for easy integration with a lot of
existing programs. Today, <application>ser</application>'s
complementary web-interface, <application>serweb</application>,
written in PHP, leverages the FIFO interface when displaying
and changing user location records stored in server's memory.
It uses this interface to send instant messages too, without
any knowledge of underlying <acronym>SIP</acronym> stack.
Another application relying on the FIFO interface is
<application>serctl</application>, <application>ser</application>
management utility. The command-line utility can browse
server's in-memory user-location database, display
running processes and operational statistics.
</para>
<para>
The way the FIFO server works is similar to how
<filename>/proc</filename> filesystem works
on some operating systems. It provides a human-readable way
to access <application>ser</application>'s
internals. Applications dump their requests into the FIFO
server and receive a status report when request processing
completes. <application>ser</application>
exports a lot of its functionality located in both the
core and external modules through the FIFO server.
</para>
<para>
FIFO requests are formed easily. They begin with a command
enclosed in colons and followed by name of file or pipe (relative
to <filename>/tmp/</filename> path), to which
a reply should be printed. The first request line may be
followed by additional lines with command-specific
parameters. For example, the <command>t_uac_dlg</command>
FIFO command for initiating a transaction allows
to pass additional header fields and message body to
a newly created transaction. Each request is terminated by
an empty line. Whole requests must be sent by applications
atomically in a single batch to avoid mixing with
requests from other applications. Requests are sent to
pipe at which <application>ser</application>
listens (filename configured by the <varname>fifo</varname> config
file option).
</para>
<para>
An easy way to use the FIFO interface is via the
<application>serctl</application>
command-line tool. When called along with "fifo",
FIFO command name, and optional parameters, the tool
generates a FIFO request and prints request result.
The following example shows use of this tool with
the <command>uptime</command> and
<command>which</command> commands.
<command>uptime</command> returns
server's running time, <command>which</command>
returns list of available FIFO commands. Note that only
the built-in FIFO command set is displayed as no modules
were loaded in this example.
<example>
<title>Use of <application>serctl</application>
to Access FIFO Server</title>
<programlisting>
[jiri@cat test]$ serctl fifo uptime
Now: Fri Dec 6 17:56:10 2002
Up Since: Fri Dec 6 17:56:07 2002
Up time: 3 [sec]
[jiri@cat test]$ serctl fifo which
ps
which
version
uptime
print
</programlisting>
</example>
The request which the <application>serctl</application>
command-line tool sent to FIFO server looked like this:
<example>
<title><command>uptime</command> FIFO Request</title>
<programlisting>
:uptime:ser_receiver_1114
</programlisting>
</example>
This request contains no parameters and consists only of
command name enclosed in colons and name of file, to which
a reply should be printed. FIFO replies consist of a status
line followed by optional parameters. The status line consists,
similarly to <acronym>SIP</acronym> reply status, of
a three-digit status code and a reason phrase. Status codes
with leading digit 2 (200..299) are considered positive,
any other values indicate an error. For example, FIFO server
returns "500" if execution of a non-existing FIFO command is
requested.
<example>
<title>FIFO Errors</title>
<programlisting>
[jiri@cat sip_router]$ serctl fifo foobar
500 command 'foobar' not available
</programlisting>
</example>
<example>
<title>Showing User Contacts Using serctl</title>
<para>
Another example of use of FIFO is accessing server's
in-memory user location database. That's a very powerful
feature: web applications and other tools can use it
to gain users access to the database. They can add new
contacts (like permanent gateway destinations), remove
and review users' whereabouts. The example here utilizes
FIFO command <command>ul_show_contact</command> to
retrieve current whereabouts of user "jiri".
<programlisting>
<![CDATA[
[jiri@fox ser]$ serctl fifo ul_show_contact location jiri
<sip:195.37.78.160:14519>;q=0.00;expires=1012
]]>
</programlisting>
</para>
</example>
</para>
<para>
The user location example demonstrates an essential feature
of the FIFO server: extensibility. It is able to export new
commands implemented in new modules.
Currently, usrloc module exports FIFO
commands for maintaining in-memory user location
database and tm module exports FIFO commands for
management of SIP transactions. See the
example in
<filename>examples/web_im/send_im.php</filename>
for how to initiate a SIP transaction
(instant message)
from a PHP script via the FIFO server. This example
uses FIFO command
<command>t_uac_dlg</command>. The command
is followed by parameters: header fields and
message body. The same FIFO command can be used from
other environments to send instant messages too. The
following example shows how to send instant messages
from a shell script.
<example>
<title>Sending IM From Shell Script</title>
<programlisting>
#!/bin/sh
#
# call this script to send an instant message; script parameters
# will be displayed in message body
#
# parameters mean: message type, request-URI, outbound server is
# left blank ("."), required header fields From and To follow,
# then optional header fields terminated by dot and optional
# dot-terminated body
cat &gt; /tmp/ser_fifo &lt;&lt;EOF
:t_uac_dlg:hh
NOTIFY
sip:receiver@127.0.0.1
.
From: sip:originator@foo.bar
To: sip:receiver@127.0.0.1
foo: bar_special_header
x: y
p_header: p_value
Contact: &lt;sip:devnull@192.168.0.100:9&gt;
Content-Type: text/plain; charset=UTF-8
.
Hello world!!!! $@
.
EOF
</programlisting>
</example>
</para>
<example>
<title>Manipulation of User Contacts</title>
<para>
The following example shows use of FIFO server to change
user's contacts. This may be very practical, if for example
a user wishes to set up his cell phone number as his temporary
contact. The cell phone, which is behind a PSTN gateway, cannot
register automatically using SIP. The user needs to set
forwarding manually through some convenient web interface.
The web interface needs to have the ability to upload new user's
contacts to <application>ser</application>.
This is what the <command>ul_add</command> FIFO
command is good for. Parameterized by user's name, table name,
expiration time and weight, it allows external applications to
introduce new contacts to server's in-memory user location table.
</para>
<para>
The example is borrowed from <application>serweb</application>,
<application>ser</application>'s web
PHP-written interface.
It consists of a short "stub" function which carries out
all mechanics of FIFO communication and of forming the FIFO
request.
</para>
<programlisting>
<![CDATA[
/* construct and send a FIFO command; the command parameters $sip_address,
$expires are PHP variables originating from an HTML form
*/
$fifo_cmd=":ul_add:".$config->reply_fifo_filename."\n".
$config->ul_table."\n". //table
$user_id."\n". //username
$sip_address."\n". //contact
$expires."\n". //expires
$config->ul_priority."\n\n"; //priority
$message=write2fifo($fifo_cmd, $errors, $status);
/* .......... snip .................. */
/* this is the stub function for communicating with FIFO server.
it dumps a request to FIFO server, opens a reply FIFO and
reads server's reply from it
*/
function write2fifo($fifo_cmd, &$errors, &$status){
global $config;
/* open fifo now */
$fifo_handle=fopen( $config->fifo_server, "w" );
if (!$fifo_handle) {
$errors[]="sorry -- cannot open fifo"; return;
}
/* create fifo for replies */
@system("mkfifo -m 666 ".$config->reply_fifo_path );
/* add command separator */
$fifo_cmd=$fifo_cmd."\n";
/* write fifo command */
if (fwrite( $fifo_handle, $fifo_cmd)==-1) {
@unlink($config->reply_fifo_path);
@fclose($fifo_handle);
$errors[]="sorry -- fifo writing error"; return;
}
@fclose($fifo_handle);
/* read output now */
@$fp = fopen( $config->reply_fifo_path, "r");
if (!$fp) {
@unlink($config->reply_fifo_path);
$errors[]="sorry -- fifo reading error"; return;
}
$status=fgetS($fp,256);
if (!$status) {
@unlink($config->reply_fifo_path);
$errors[]="sorry -- fifo reading error"; return;
}
$rd=fread($fp,8192);
@unlink($config->reply_fifo_path);
return $rd;
}
]]>
</programlisting>
</example>
<para>
See
<xref linkend="fiforeference"/> for a complete listing
of FIFO commands available with current
<application>ser</application>
distribution.
</para>
<section>
<title>Advanced Example: Click-To-Dial</title>
<para>
A very useful SIP application is phonebook with
"click-to-dial" feature. It allows users to keep their
phonebooks on the web and dial by clicking on an entry.
The great advantage is that you can use the phonebook
alone with any phone you have. If you temporarily use
another phone, upgrade it permanently with another make,
or use multiple phones in parallel, your phonebook will
stay with you on the web. You just need to click an entry
to initiate a call. Other scenario using "click-to-dial"
feature includes "click to be connected with our
sales representative".
</para>
<para>
There are basically two ways how to build such a feature:
distributed and centralized. We prefer the distributed
approach since it is very robust and light-weighted.
The "click-to-dial" application just needs to instruct
the calling user to call a destination and that's it.
(That's done using "REFER" method.)
Then, the calling user takes over whereas the initiating
application disappears from signaling and
is no longer involved in subsequent communication. Which
is good because such a simple design scales well.
</para>
<para>
The other design alternative is use of a B2BUA
<footnote>
<para>
See <filename>
draft-ietf-sipping-3pcc-02.txt
</filename> for more details.
</para>
</footnote>
which acts as a "middleman" involved in signaling during the
whole session. It is complex: ringing needs to be achieved
using a media server, it introduces session state,
mangling of SIP payloads, complexity when QoS reservation
is used and possibly other threats which result from
e2e-unfriendly design. The only benefit
is it works even for poor phones which do not support
REFER -- which should not matter because you do not wish
to buy such.
</para>
<para>
So how does "distributed click-to-dial" application
work? It is simple. The core piece is sending a REFER
request to the calling party. REFER method is typically
used for call transfer and it means "set up a call
to someone else".
</para>
<para>
There is an issue -- most phones
don't accept unsolicited REFER. If a malicious
user made your phone to call thirty destinations without
your agreement, you would certainly not appreciate it.
The workaround is that first of all the click-to-dial
application gives you a "wrapper call". If you accept it,
the application will send a REFER which will be considered
by the phone as a part of approved communication and
granted. Be aware that without cryptography,
security is still weak. Anyone who saw an INVITE can
generate an acceptable REFER.
<note>
<para>
The wrapper INVITE may or may not be used
in future. The Internet draft
draft-ietf-sipping-service-examples
mentions the click-to-dial application
without use of the dummy INVITE. As of
today, most telephones do need it.
</para>
</note>
<example>
<title>Call-Flow for Click-To-Dial Using REFER</title>
<programlisting>
CTD Caller Callee
#1 INVITE
-----------------&gt;
...
caller answers
#2 200
&lt;-----------------
#3 ACK
-----------------&gt;
#4 REFER
-----------------&gt;
#5 202
&lt;-----------------
#6 BYE
-----------------&gt;
#7 200
&lt;-----------------
#8 INVITE
------------------&gt;
#9 180 ringing
&lt;------------------
#1 click-to-dial (CTD) is started and the "wrapper call" is initiated
INVITE caller
From: controller
To: caller
SDP: on hold
#2 calling user answes
200 OK
From: controller
To: caller
#3 CTD acknowledges
ACK caller
From controller
To: caller
#4 CTD initiates a transfer
REFER caller
From: controller
To: caller
Refer-To: callee
Refered-By: controller
#5 caller confirms delivery of REFER
202 Accepted
From: controller
To: caller
#6 CTD terminates the wrapper call -- it is no longer needed
BYE caller
From: controller
To: caller
#7 BYE is confirmed
200 Ok
From: controller
To: caller
#8 caller initates transaction solicited through REFER
INVITE callee
From: caller
To: callee
Referred-By: controller
#9 that's it -- it is now up to callee to answer the INVITE
180 ringing
From: caller
To: callee
</programlisting>
</example>
</para>
<para>
Implementation of this scenario is quite
straight-forward: you initiate INVITE, BYE and
REFER transaction.
Source code of the example written in Bourne shell
is available in source distrubtion, in
<filename>examples/ctd.sh</filename>.
A PHP implementation exists as well as a part of
<application>serweb</application>.
</para>
<example>
<title>Running the CTD Example</title>
<programlisting>
[jiri@cat examples]$ ./ctd.sh
destination unspecified -- taking default value sip:23@192.168.2.16
caller unspecified -- taking default value sip:113311@192.168.2.16
invitation succeeded
refer succeeded
bye succeeded
</programlisting>
</example>
</section> <!-- click-to-dial -->
<!-- for some reason, this does not work :-(
<example>
<title>Initiating a SIP Transaction from PHP via FIFO</title>
<programlisting>
<textobject>
<textdata fileref="../../examples/web_im/send_im.php">
</textobject>
</programlisting>
</example>
-->
</section> <!-- FIFO server -->
</section>