2
0
Эх сурвалжийг харах

sms(s): move to common modules directory

Henning Westerholt 15 жил өмнө
parent
commit
56ccad10bb

+ 15 - 0
modules/sms/Makefile

@@ -0,0 +1,15 @@
+# $Id$
+#
+# example module makefile
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=sms.so
+LIBS=
+
+DEFS+=-DSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 413 - 0
modules/sms/README

@@ -0,0 +1,413 @@
+
+SMS Module
+
+Bogdan Iancu
+
+   FhG FOKUS
+
+Edited by
+
+Bogdan Iancu
+
+   Copyright � 2003 FhG FOKUS
+     _________________________________________________________
+
+   Table of Contents
+   1. User's Guide
+
+        1.1. Overview
+
+              1.1.1. New Features
+              1.1.2. Hardware Requirements
+              1.1.3. Numbering Plan
+              1.1.4. Address Mapping
+
+        1.2. Dependencies
+
+              1.2.1. SER Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported Parameters
+
+              1.3.1. modems (string)
+              1.3.2. networks (string)
+              1.3.3. links (string)
+              1.3.4. default_net (string)
+              1.3.5. max_sms_parts (integer)
+              1.3.6. domain_str (string)
+              1.3.7. use_contact (integer)
+              1.3.8. sms_report_type (integer)
+
+        1.4. Exported Functions
+
+              1.4.1. sms_send_msg_to_net(network_name)
+              1.4.2. sms_send_msg()
+
+   2. Developer's Guide
+   3. Frequently Asked Questions
+
+   List of Examples
+   1-1. Set modems parameter
+   1-2. Set networks parameter
+   1-3. Set links parameter
+   1-4. Set default_net parameter
+   1-5. Set max_sms_parts parameter
+   1-6. Set domain_str parameter
+   1-7. Set use_contact parameter
+   1-8. Set sms_report_type parameter
+   1-9. sms_send_msg_to_net usage
+   1-10. sms_send_msg usage
+     _________________________________________________________
+
+Chapter 1. User's Guide
+
+1.1. Overview
+
+   This module provides a way of communication between SIP
+   network (via SIP MESSAGE) and GSM networks (via
+   ShortMessageService). Communication is possible from SIP to
+   SMS and vice versa. The module provides facilities like SMS
+   confirmation--the gateway can confirm to the SIP user if his
+   message really reached its destination as a SMS--or multi-part
+   messages--if a SIP messages is too long it will be split and
+   sent as multiple SMS.
+
+   Errors occurred because of an invalid number or a too long
+   message or because of an internal modem malfunction are
+   reported back to the SIP user via a SIP message containing
+   explanations regarding the error.
+     _________________________________________________________
+
+1.1.1. New Features
+
+   -) July 2007 
+      The SMS module may no extract the URI from SMS body,
+      configuring the URI in the new modems parameters 's' and
+      't'.
+     _________________________________________________________
+
+1.1.2. Hardware Requirements
+
+   The SMS module needs a GSM modem to be able to send/receive
+   the SMS messages. Usually, this kind of modems are externals,
+   linked to the machine via serial cable. The modem can be a
+   dedicated one (as the ones provided by FALCOM) or can be a GSM
+   telephone that has an internal modem (as the latest mobile
+   phones from NOKIA and ERICSSON).
+     _________________________________________________________
+
+1.1.3. Numbering Plan
+
+   The gateway accepts and advertises phone numbers in
+   international format, more specific like: +(international
+   code)(area code)(number). Ex: Germany, D1 = +49 170 5678181
+   Romania, Connex = +40 722 123456 A number in this format is
+   expected to be placed as username into RURI or in the To
+   header. If RURI misses the username, the To header will be
+   consider. Also, the gateway will advertise in this format the
+   username in Contact headers (in SIP replies and requests) and
+   in From headers (in SIP requests).
+     _________________________________________________________
+
+1.1.4. Address Mapping
+
+   To identify the destination number of the SMS, the gateway
+   expects to have a mobile number in username of the SIP
+   destination address (for example sip:[email protected]).
+   For the reverse direction, because the gateway has only one
+   GSM number, the destination SIP address has to be encapsulated
+   into the SMS body. The gateway expects to find a SIP address
+   at the beginning of the SMS body in "sip:user.host" format.
+   Everything before the SIP address will be discarded, the
+   useful text begins exactly after the address (for example
+   SMS="For sip:user@host hello world!!" -> SIP="hello world"), 
+   view configuration for disable this behavior (modems parameters
+   's' y 't'). In order to facilitate replying, the gateway sends 
+   all the SMS messages with a header containing the source SIP 
+   address in the following format: "From sip:user@host (if you 
+   reply DONOT remove it)<new_line>". When an SMS-reply is received 
+   having this header (all of it or truncated at the end), the header
+   will be left out (it will not be in the SIP message). 
+
+     _________________________________________________________
+
+1.2. Dependencies
+
+1.2.1. SER Modules
+
+   The following modules must be loaded before this module:
+
+     * tm - Transaction Manager.
+     _________________________________________________________
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed
+   before running SER with this module loaded:
+
+     * None.
+     _________________________________________________________
+
+1.3. Exported Parameters
+
+1.3.1. modems (string)
+
+   Define and configure one or more GSM modems.
+modems_value     = modem_definition *( ";" modem_definition )
+modem_definition = modem_name "[" list_of_params "]"
+list_of_params   = modem_param *( ";" modem_param )
+modem_param      = name "=" value
+
+   The following parameters can be used:
+
+     * d=device (mandatory) - Device associated with modem
+       (/dev/ttyS0, /dev/modem, etc.).
+     * p=pin (optional) - SIM PIN - default is NULL.
+     * m=mode (optional) - Modem working mode
+       ("ASCII","OLD","DIGICOM","NEW"). Default value is "NEW".
+     * c=SMS_Center (optional) - SMS center number for that
+       modem. Default is the SMS center set on the SIM card.
+     * b=baudrate (optional) - Default is 19600.
+     * r=retry (optional) - How many times to try to re-send a
+       SMS that reported error. Default is twice.
+     * l=looping (optional) - Time for modem to wait before
+       performing a new check for incomimg/outgoing SMS/SIP_MSG.
+       Default is 20.
+     * t=to (optional) - uri for sip header TO - default is NULL. 
+     * s=scan (optional) - 0: NOT SCAN uri from body sms, use URI
+			      in t=to.
+			   1: SCAN uri from body sms (normal mode,
+			      default mode, clasic mode)
+			   2: SCAN MIX (both modes), First SCAN
+
+   No default value, the parameter is mandatory. 
+
+   Example 1-1. Set modems parameter
+...
+modparam("sms", "modems", "Nokia[d=/dev/ttyS1;s=0;t=sip:[email protected]]")
+modparam("sms", "modems", "Nokia[d=/dev/ttyS1;b=9600;m=new;l=30]")
+modparam("sms", "modems", "Nokia[d=/dev/ttyS1];Siemens[d=/dev/ttyS2]")
+...
+     _________________________________________________________
+
+1.3.2. networks (string)
+
+   Define and configure used GSM networks.
+networks_value = net_definition *( ";" net_definition )
+net_definition = net_name "[" list_of_params "]"
+list_of_params = set_param *( ";" set_param )
+set_param      = name "=" value
+
+   The following parameters can be used:
+
+     * m=msx_sms_per_call (optional) - Maximum number of SMS send
+       / received from that net in one modem loop. Default is 10.
+       This parameter was introduced to avoid starvation.
+       Example of the starvation--a modem can send SMS for more
+       than 1 networks. If you have a huge number of SMS for the
+       first network and the number of incoming SIP messages is
+       equal to the sent SMS per same unit of time, the modem
+       will never get to send SMS for the next networks.
+
+   No default value, the parameter is mandatory. 
+
+   Example 1-2. Set networks parameter
+...
+modparam("sms", "networks", "D1 [m=10] ;d2[ m=20]")
+...
+     _________________________________________________________
+
+1.3.3. links (string)
+
+   Define from which network each modem should send SMS.
+links_value = modem_assoc *( ";" modem_assoc )
+modem_assoc = modem_name "[" list_of_networks "]"
+list_of_networks = network *( ";" network )
+
+   No default value, the parameter is mandatory. 
+
+   Example 1-3. Set links parameter
+...
+modparam("sms", "links", "NOKIA[D1;d2]")
+...
+
+   The modem NOKIA will send SMS from D1 and D2 net (in this
+   order !). if in a net queue are more then max_sms_per_call SMS
+   the modem will not sleep before starting the next loop !
+   Shortly, if messages are waiting to be sent, the modem will
+   not go in sleep.
+     _________________________________________________________
+
+1.3.4. default_net (string)
+
+   The default network to use. If no one specified, the first
+   defined network is used. This parameter is useful only if the
+   the "sms_send_msg" exported function is used (see Section
+   1.4).
+
+   Example 1-4. Set default_net parameter
+...
+modparam("sms", "default_net", "D1")
+...
+     _________________________________________________________
+
+1.3.5. max_sms_parts (integer)
+
+   Shows in how many parts (SMS messages) a SIP message can be
+   split. If exceeded, the SIP message will be sent truncated and
+   the SIP user will get back another message containing the
+   unsent part.
+
+   Default value is 4. 
+
+   Example 1-5. Set max_sms_parts parameter
+...
+modparam("sms", "max_sms_parts", 10)
+...
+     _________________________________________________________
+
+1.3.6. domain_str (string)
+
+   Specify a fake domain name to be used by the gateway. The
+   Contact headers and the From header from request will be
+   construct based on this fake domain name. It's useful when the
+   gateway is transparently hidden behind a proxy/register
+   (located on different machines).
+
+   Default is the name of the machine the gateway is running on. 
+
+   Example 1-6. Set domain_str parameter
+...
+modparam("sms", "domain_str", "foo.bar")
+...
+     _________________________________________________________
+
+1.3.7. use_contact (integer)
+
+   If a contact header should be added to the outgoing SIP
+   messages. Even if the SIP draft forbids this, some UAS require
+   it.
+
+   Default is 0 (no). 
+
+   Example 1-7. Set use_contact parameter
+...
+modparam("sms", "use_contact", 1)
+...
+     _________________________________________________________
+
+1.3.8. sms_report_type (integer)
+
+   If the modem should ask for SMS confirmation from the SMS
+   Center. If the SMSC reply with an error code, the gateway will
+   send back to SIP user a SIP message containing the text (or
+   part of it) that couldn't be send. Two report mechanisms are
+   implemented:
+
+     * 1 - the reports are delivered by the GSM device as SMS
+       reports (so far supported only by Nokia modems);
+     * 2 - the reports are delivered as async. CDS responses
+       (supported by almost all modems, except Ericsson).
+
+   Default is 0 (no report). 
+
+   Example 1-8. Set sms_report_type parameter
+...
+modparam("sms", "sms_report_type", 1)
+...
+     _________________________________________________________
+
+1.4. Exported Functions
+
+1.4.1. sms_send_msg_to_net(network_name)
+
+   Put the SIP msg in the specified network queue. The function
+   return error if the number encapsulated into SIP message is
+   malformed, if the content_type is incorrect or because of some
+   internal failures.
+
+   Meaning of the parameters is as follows:
+
+     * network_name - Name of network.
+
+   Example 1-9. sms_send_msg_to_net usage
+...
+if (sms_send_msg_to_net("D1"))
+{
+    if (!t_reply("202", "yes sir, SMS sent over"))
+    {
+        # if replying failed, retry statelessly
+        sl_reply_error();
+    };
+} else {
+    if (!t_reply("502", "Bad gateway - SMS error"))
+    {
+        # if replying failed, retry statelessly
+        sl_reply_error();
+    };
+    break;
+};
+...
+     _________________________________________________________
+
+1.4.2. sms_send_msg()
+
+   The same as the previous one, but use the default network
+   queue.
+
+   Example 1-10. sms_send_msg usage
+...
+if (sms_send_msg_to_net())
+{
+    if (!t_reply("202", "yes sir, SMS sent over"))
+    {
+        # if replying failed, retry statelessly
+        sl_reply_error();
+    };
+} else {
+    if (!t_reply("502", "Bad gateway - SMS error"))
+    {
+        # if replying failed, retry statelessly
+        sl_reply_error();
+    };
+    break;
+};
+...
+     _________________________________________________________
+
+Chapter 2. Developer's Guide
+
+   Each modem forks its own process for sending /fetching SMS.
+   Communication and queuing between ser working processes and
+   modem processes is done with pipes.
+     _________________________________________________________
+
+Chapter 3. Frequently Asked Questions
+
+   3.1. Where can I find more about SER?
+   3.2. Where can I post a question about this module?
+   3.3. How can I report a bug?
+
+   3.1. Where can I find more about SER?
+
+   Take a look at http://iptel.org/ser.
+
+   3.2. Where can I post a question about this module?
+
+   First at all check if your question was already answered on
+   one of our mailing lists:
+
+     * http://mail.iptel.org/mailman/listinfo/serusers
+     * http://mail.iptel.org/mailman/listinfo/serdev
+
+   E-mails regarding any stable version should be sent to
+   <[email protected]> and e-mail regarding development versions
+   or CVS snapshots should be send to <[email protected]>.
+
+   If you want to keep the mail private, send it to
+   <[email protected]>.
+
+   3.3. How can I report a bug?
+
+   Please follow the guidelines provided at:
+   http://iptel.org/ser/bugs

+ 4 - 0
modules/sms/doc/Makefile

@@ -0,0 +1,4 @@
+docs = sms.xml
+
+docbook_dir=../../../docbook
+include $(docbook_dir)/Makefile.module

+ 88 - 0
modules/sms/doc/functions.xml

@@ -0,0 +1,88 @@
+<?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="sms.functions" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </sectioninfo>
+
+    <title>Functions</title>
+
+    <section id="sms_send_msg_to_net">
+	<title>
+	    <function>sms_send_msg_to_net(network_name)</function>
+	</title>
+	<para>
+	    Put the SIP msg in the specified network queue. The function return
+	    error if the number encapsulated into SIP message is malformed, if
+	    the content_type is incorrect or because of some internal failures.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para>
+		    <emphasis>network_name</emphasis> - Name of network.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>sms_send_msg_to_net</function> usage</title>
+	    <programlisting>
+...
+if (sms_send_msg_to_net("D1"))
+{
+    if (!t_reply("202", "yes sir, SMS sent over"))
+    {
+        # if replying failed, retry statelessly
+        sl_reply_error();
+    };
+} else {
+    if (!t_reply("502", "Bad gateway - SMS error"))
+    {
+        # if replying failed, retry statelessly
+        sl_reply_error();
+    };
+    break;
+};
+...
+	    </programlisting>
+	</example>
+    </section>
+    
+    <section id="sms_send_msg">
+	<title>
+	    <function>sms_send_msg()</function>
+	</title>
+	<para>
+	    The same as the previous one, but use the default network queue.
+	</para>
+	<example>
+	    <title><function>sms_send_msg</function> usage</title>
+	    <programlisting>
+...
+if (sms_send_msg_to_net())
+{
+    if (!t_reply("202", "yes sir, SMS sent over"))
+    {
+        # if replying failed, retry statelessly
+        sl_reply_error();
+    };
+} else {
+    if (!t_reply("502", "Bad gateway - SMS error"))
+    {
+        # if replying failed, retry statelessly
+        sl_reply_error();
+    };
+    break;
+};
+...
+	    </programlisting>
+	</example>
+    </section>
+</section>

+ 304 - 0
modules/sms/doc/params.xml

@@ -0,0 +1,304 @@
+<?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="sms.parameters" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </sectioninfo>
+
+    <title>Parameters</title>
+
+    <section id="modems">
+	<title><varname>modems</varname> (string)</title>
+	<para>
+	    Define and configure one or more <acronym>GSM</acronym> modems.
+	</para>
+	<programlisting>
+modems_value     = modem_definition *( ";" modem_definition )
+modem_definition = modem_name "[" list_of_params "]"
+list_of_params   = modem_param *( ";" modem_param )
+modem_param      = name "=" value
+	</programlisting>
+	<para>
+	    The following parameters can be used:
+	</para>
+	<itemizedlist>
+	    <listitem>
+		<para>
+		    d=device (mandatory) - Device associated with modem (/dev/ttyS0, /dev/modem,
+		    etc.).
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    p=pin (optional) - <acronym>SIM</acronym> <acronym>PIN</acronym> - default
+		    is NULL.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    m=mode (optional) - Modem working mode
+		    ("ASCII","OLD","DIGICOM","NEW"). Default value is "NEW".
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    c=SMS_Center (optional) - <acronym>SMS</acronym> center
+		    number for that modem. Default is the
+		    <acronym>SMS</acronym> center set on the
+		    <acronym>SIM</acronym> card.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    b=baudrate (optional) - Default is 19600.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    r=retry (optional) - How many times to try to re-send a
+		    <acronym>SMS</acronym> that reported error. Default is twice.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    l=looping (optional) - Time for modem to wait before performing a new check
+		    for incomimg/outgoing <acronym>SMS</acronym>/SIP_MSG. Default is 20.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		   t=to (optional) - uri for sip header TO.  Default is NULL.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    s=scan (optional) - Values: 
+		        	0: NOT SCAN uri from body sms, use URI in t=to.
+			1: SCAN uri from body sms (normal mode,
+			default mode, clasic mode)
+			2: SCAN MIX (both modes), First SCAN 
+			Default is 1 (SCAN).
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<note>
+	    <para>
+		No default value, the parameter is mandatory.		
+	    </para>
+	</note>
+	<example>
+	    <title>Set <varname>modems</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sms", "modems", "Nokia[d=/dev/ttyS1;s=0;t=sip:[email protected]]") 
+modparam("sms", "modems", "Nokia [d=/dev/ttyS1;b=9600;m=new;l=30] ")
+modparam("sms", "modems", "Nokia[d=/dev/ttyS1];Siemens[d=/dev/ttyS2]")
+...
+	    </programlisting>
+	</example>
+    </section>
+    
+    <section id="networks">
+	<title><varname>networks</varname> (string)</title>
+	<para>
+	    Define and configure used <acronym>GSM</acronym> networks.
+	</para>
+	<programlisting>
+networks_value = net_definition *( ";" net_definition )
+net_definition = net_name "[" list_of_params "]"
+list_of_params = set_param *( ";" set_param )
+set_param      = name "=" value
+	</programlisting>
+	<para>
+	    The following parameters can be used:
+	</para>
+	<itemizedlist>
+	    <listitem>
+		<para>
+		    m=msx_sms_per_call (optional) - Maximum number of
+		    <acronym>SMS</acronym> send / received from that net in one
+		    modem loop. Default is 10. This parameter was introduced to
+		    avoid starvation.
+		</para>
+		<para>
+		    Example of the starvation--a modem can send
+		    <acronym>SMS</acronym> for more than 1 networks. If you
+		    have a huge number of <acronym>SMS</acronym> for the first
+		    network and the number of incoming SIP messages is equal
+		    to the sent <acronym>SMS</acronym> per same unit of time,
+		    the modem will never get to send <acronym>SMS</acronym> for
+		    the next networks.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<note>
+	    <para>
+		No default value, the parameter is mandatory.		
+	    </para>
+	</note>
+	<example>
+	    <title>Set <varname>networks</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sms", "networks", "D1 [m=10] ;d2[ m=20]")
+...
+	    </programlisting>
+	</example>
+    </section>
+    
+    <section id="links">
+	<title><varname>links</varname> (string)</title>
+	<para>
+	    Define from which network each modem should send
+	    <acronym>SMS</acronym>.
+	</para>
+	<programlisting>
+links_value = modem_assoc *( ";" modem_assoc )
+modem_assoc = modem_name "[" list_of_networks "]"
+list_of_networks = network *( ";" network )
+	</programlisting>
+	<note>
+	    <para>
+		No default value, the parameter is mandatory.		
+	    </para>
+	</note>
+	<example>
+	    <title>Set <varname>links</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sms", "links", "NOKIA[D1;d2]")
+...
+	    </programlisting>
+	</example>
+	<para>
+	    The modem NOKIA will send <acronym>SMS</acronym> from D1 and D2 net
+	    (in this order !). if in a net queue are more then max_sms_per_call
+	    <acronym>SMS</acronym> the modem will <emphasis>not
+	    sleep</emphasis> before starting the next loop ! Shortly, if
+	    messages are waiting to be sent, the modem will not go in sleep.
+	</para>
+    </section>
+    
+    <section id="default_net">
+	<title><varname>default_net</varname> (string)</title>
+	<para>
+	    The default network to use. If no one specified, the first defined
+	    network is used. This parameter is useful only if the
+	    "sms_send_msg" exported function is used (see <xref
+	    linkend="sms.functions"/>).
+	</para>
+	<example>
+	    <title>Set <varname>default_net</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sms", "default_net", "D1")
+...
+	    </programlisting>
+	</example>
+    </section>
+    
+    <section id="max_sms_parts">
+	<title><varname>max_sms_parts</varname> (integer)</title>
+	<para>
+	    Shows in how many parts (<acronym>SMS</acronym> messages) a SIP message can be
+	    split. If exceeded, the SIP message will be sent truncated and the SIP user will
+	    get back another message containing the unsent part.
+	</para>
+	<para>
+	    Default value is 4.
+	</para>
+	<example>
+	    <title>Set <varname>max_sms_parts</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sms", "max_sms_parts", 10)
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="domain_str">
+	<title><varname>domain_str</varname> (string)</title>
+	<para>
+	    Specify a fake domain name to be used by the gateway. The Contact
+	    headers and the From header from request will be construct based on
+	    this fake domain name. It's useful when the gateway is
+	    transparently hidden behind a proxy/register (located on different
+	    machines).
+	</para>
+	<para>
+	    Default is the name of the machine the gateway is running on.
+	</para>
+	<example>
+	    <title>Set <varname>domain_str</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sms", "domain_str", "foo.bar")
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="sms.use_contact">
+	<title><varname>use_contact</varname> (integer)</title>
+	<para>
+	    If a contact header should be added to the outgoing SIP messages. Even if the
+	    SIP draft forbids this, some UAS require it. 
+	</para>
+	<para>
+	    Default is 0 (no).
+	</para>
+	<example>
+	    <title>Set <varname>use_contact</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sms", "use_contact", 1)
+...
+	    </programlisting>
+	</example>
+    </section>
+    
+    <section id="sms_report_type">
+	<title><varname>sms_report_type</varname> (integer)</title>
+	<para>
+	    If the modem should ask for <acronym>SMS</acronym> confirmation
+	    from the <acronym>SMS</acronym> Center. If the
+	    <acronym>SMSC</acronym> reply with an error code, the gateway will
+	    send back to SIP user a SIP message containing the text (or
+	    part of it) that couldn't be send. Two report mechanisms are
+	    implemented:
+	</para>
+	<itemizedlist>
+	    <listitem>
+		<para>
+		    1 - the reports are delivered by the <acronym>GSM</acronym> device as
+		    <acronym>SMS</acronym> reports (so far supported only by Nokia modems);
+		</para>
+	    </listitem>
+	    <listitem>
+		<para>
+		    2 - the reports are delivered as async. <acronym>CDS</acronym> responses
+		    (supported by almost all modems, except Ericsson).
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<para>
+	    Default is 0 (no report).
+	</para>
+	<example>
+	    <title>Set <varname>sms_report_type</varname> parameter</title>
+	    <programlisting>
+...
+modparam("sms", "sms_report_type", 1)
+...
+	    </programlisting>
+	</example>
+    </section>
+</section>

+ 125 - 0
modules/sms/doc/sms.xml

@@ -0,0 +1,125 @@
+<?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="sms" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<authorgroup>
+	    <author>
+		<firstname>Bogdan</firstname>
+		<surname>Iancu</surname>
+		<affiliation><orgname>FhG FOKUS</orgname></affiliation>
+		<address>
+		    <email>[email protected]</email>
+		</address>
+	    </author>
+	</authorgroup>
+	<copyright>
+	    <year>2003</year>
+	    <holder>FhG FOKUS</holder>
+	</copyright>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </sectioninfo>
+
+    <title>SMS Module</title>
+
+    <section id="sms.overview">
+	<title>Overview</title>
+	<para>
+	    This module provides a way of communication between SIP network
+	    (via SIP MESSAGE) and <acronym>GSM</acronym> networks (via
+	    ShortMessageService). Communication is possible from SIP to
+	    <acronym>SMS</acronym> and vice versa. The module provides
+	    facilities like <acronym>SMS</acronym> confirmation--the gateway
+	    can confirm to the SIP user if his message really reached its
+	    destination as a <acronym>SMS</acronym>--or multi-part messages--if
+	    a SIP messages is too long it will be split and sent as multiple
+	    <acronym>SMS</acronym>.
+	</para>
+	<para>
+	    Errors occurred because of an invalid number or a too long message
+	    or because of an internal modem malfunction are reported back to
+	    the SIP user via a SIP message containing explanations
+	    regarding the error.
+	</para>
+
+	<section id="hw_requirements">
+	    <title>Hardware Requirements</title>
+	    <para>
+		The <acronym>SMS</acronym> module needs a
+		<acronym>GSM</acronym> modem to be able to send/receive the
+		<acronym>SMS</acronym> messages.  Usually, this kind of modems
+		are externals, linked to the machine via serial cable. The
+		modem can be a dedicated one (as the ones provided by FALCOM)
+		or can be a <acronym>GSM</acronym> telephone that has an
+		internal modem (as the latest mobile phones from NOKIA and
+		ERICSSON).
+	    </para>
+	</section>
+
+	<section id="numbering_plan">
+	    <title>Numbering Plan</title>
+	    <para>
+		The gateway accepts and advertises phone numbers in
+		international format, more specific like: +(international
+		code)(area code)(number). Ex: Germany, D1 = +49 170 5678181
+		Romania, Connex = +40 722 123456 A number in this format is
+		expected to be placed as username into <acronym>RURI</acronym>
+		or in the To header. If <acronym>RURI</acronym> misses the
+		username, the To header will be consider. Also, the gateway
+		will advertise in this format the username in Contact headers
+		(in SIP replies and requests) and in From headers (in SIP
+		requests).
+	    </para>
+	</section>
+
+	<section id="address_mapping">
+	    <title>Address Mapping</title>
+	    <para>
+		To identify the destination number of the
+		<acronym>SMS</acronym>, the gateway expects to have a mobile
+		number in username of the SIP destination address (for example
+		sip:[email protected]). For the reverse direction,
+		because the gateway has only one <acronym>GSM</acronym> number,
+		the destination SIP address has to be encapsulated into the
+		<acronym>SMS</acronym> body. The gateway expects to find a SIP
+		address at the beginning of the <acronym>SMS</acronym> body in
+		"sip:user.host" format. Everything before the SIP address will
+		be discarded, the useful text begins exactly after the address
+		(for example SMS="For sip:user@host hello world!!" ->
+		SIP="hello world"), view configuration for disable this behavior (modems parameters
+		's' y 't'). In order to facilitate replying, the gateway
+		sends all the <acronym>SMS</acronym> messages with a header
+		containing the source SIP address in the following format:
+		"From sip:user@host (if you reply DONOT remove
+		it)&lt;new_line&gt;". When an <acronym>SMS</acronym>-reply is
+		received having this header (all of it or truncated at the
+		end), the header will be left out (it will not be in the SIP
+		message).
+	    </para>
+	</section>
+    </section>
+
+    <section id="sms.dep">
+	<title>Dependencies</title>
+	<para>
+	    The following modules must be loaded before this module:
+	    <itemizedlist>
+		<listitem>
+		    <para>
+			<emphasis>tm</emphasis> - Transaction Manager.
+		    </para>
+		</listitem>
+	    </itemizedlist>
+	</para>
+    </section>
+
+    <xi:include href="params.xml"/>
+    <xi:include href="functions.xml"/>
+
+</section>

+ 88 - 0
modules/sms/etc/ser.cfg

@@ -0,0 +1,88 @@
+#
+# $Id$
+#
+# simple quick-start config script
+#
+
+# ----------- global configuration parameters ------------------------
+
+debug=9         # debug level (cmd line: -dddddddddd)
+#fork=yes
+#log_stderror=no	# (cmd line: -E)
+
+/* Uncomment these lines to enter debugging mode */
+fork=no
+log_stderror=yes
+/**/
+
+#listen=192.168.80.64
+#port=5060
+#children=4
+
+dns=no           # (cmd. line: -r)
+rev_dns=no      # (cmd. line: -R)
+
+#check_via=no	# (cmd. line: -v)
+
+# ------------------ module loading ----------------------------------
+
+loadmodule "/usr/local/lib/ser/modules/sl.so"		
+loadmodule "/usr/local/lib/ser/modules/tm.so"		
+loadmodule "/usr/local/lib/ser/modules/maxfwd.so"	
+
+loadmodule "/usr/local/lib/ser/modules/sms.so"
+
+# ----------------- setting module-specific parameters ---------------
+
+# -- sms params --
+
+modparam("sms", "modems", "Nokia[d=/dev/ttyS1;s=0;t=sip:[email protected];c=+543411234567]")
+modparam("sms", "networks", "D1 [m=10]")
+modparam("sms", "links", "NOKIA[D1]")
+#modparam("sms", "domain_str", "cidra.com.ar")
+
+# -------------------------  request routing logic -------------------
+
+# main routing logic
+
+route{
+
+	# initial sanity checks -- messages with
+	# max_forwards==0, or excessively long requests
+	if (!mf_process_maxfwd_header("10")) {
+		sl_send_reply("483","Too Many Hops");
+		break;
+	};
+
+	if (msg:len >=  2048 ) {
+		sl_send_reply("513", "Message too big");
+		break;
+	};
+
+	# El GW sms2sip solo procesa MESSAGEs
+	if (!method=="MESSAGE") {
+		sl_send_reply("502","Bad gateway - SMS error");
+		break;
+	};
+
+	# El GW sms2sip solo procesa paquetes dirigidos a el
+	if (!uri==myself) {
+		sl_send_reply("502","Bad gateway - SMS error");
+		break;
+	};
+
+	# Put the SIP msg in the specified network queue.
+	if (sms_send_msg_to_net("D1")) {
+		if (!t_reply("202", "yes sir, SMS sent over")) {
+			# if replying failed, retry statelessly
+			sl_reply_error();
+		};
+	} 
+	else {
+		if (!t_reply("502", "Bad gateway - SMS error"))	{
+			# if replying failed, retry statelessly
+			sl_reply_error();
+		};
+		break;
+	};
+}

+ 60 - 0
modules/sms/libsms_charset.c

@@ -0,0 +1,60 @@
+/*
+SMS Server Tools
+Copyright (C) 2000 Stefan Frings
+
+This program is free software unless you got it under another license directly
+from the author. You can redistribute it and/or modify it under the terms of 
+the GNU General Public License as published by the Free Software Foundation.
+Either version 2 of the License, or (at your option) any later version.
+
+http://www.isis.de/members/~s.frings
+mailto:[email protected]
+ */
+
+
+#include "libsms_charset.h"
+
+#define noc 183 
+// non existent character
+
+// iso 8859-1
+unsigned char charset[128] ={
+		     '@' , 163 , '$' , 165 , 232 , 233 , 249 , 236 ,
+		     242 , 199 ,  10 , 216 , 248 ,  13 , 197 , 229 ,
+		     noc , '_' , noc , noc , noc , noc , noc , noc ,
+		     noc , noc , noc , noc , 198 , 230 , 223 , 201 ,
+		     ' ' , '!' ,  34 , '#' , '$' , '%' , '&' ,  39 ,
+		     '(' , ')' , '*' , '+' , ',' , '-' , '.' , '/' ,
+		     '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' ,
+		     '8' , '9' , ':' , ';' , '<' , '=' , '>' , '?' ,
+		     161 , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' ,
+		     'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N' , 'O' ,
+		     'P' , 'Q' , 'R' , 'S' , 'T' , 'U' , 'V' , 'W' ,
+		     'X' , 'Y' , 'Z' , 196 , 214 , 209 , 220 , 167 ,
+		     191 , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' ,
+		     'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' ,
+		     'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' ,
+		     'x' , 'y' , 'z' , 228 , 246 , 241 , 252 , 224 };
+		      
+char ascii2sms(const char c)
+{
+	char found='*'; // replacement for nonexistent characters
+	int i;
+
+	for (i=0; i<128 ; i++)
+		if (c==charset[i])
+		{
+			found=i;
+			break;
+		}
+
+	return found;
+}
+
+
+
+
+char sms2ascii(const char c)
+{
+	return charset[(int)c];
+}

+ 22 - 0
modules/sms/libsms_charset.h

@@ -0,0 +1,22 @@
+/*
+SMS Server Tools
+Copyright (C) 2000 Stefan Frings
+
+This program is free software unless you got it under another license directly
+from the author. You can redistribute it and/or modify it under the terms of 
+the GNU General Public License as published by the Free Software Foundation.
+Either version 2 of the License, or (at your option) any later version.
+
+http://www.isis.de/members/~s.frings
+mailto:[email protected]
+ */
+
+
+#ifndef CHARSET_H
+#define CHARSET_H
+
+char ascii2sms(const char c);
+
+char sms2ascii(const char c);
+
+#endif

+ 545 - 0
modules/sms/libsms_getsms.c

@@ -0,0 +1,545 @@
+/*
+SMS Server Tools
+Copyright (C) 2000-2002 Stefan Frings
+
+This program is free software unless you got it under another license directly
+from the author. You can redistribute it and/or modify it under the terms of
+the GNU General Public License as published by the Free Software Foundation.
+Either version 2 of the License, or (at your option) any later version.
+
+http://www.isis.de/members/~s.frings
+mailto:[email protected]
+ */
+
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include "../../ut.h"
+#include "libsms_charset.h"
+#include "libsms_modem.h"
+#include "libsms_sms.h"
+#include "sms_funcs.h"
+
+
+
+#define set_date(_date,_Pointer) {\
+	(_date)[0] = (_Pointer)[3];\
+	(_date)[1] = (_Pointer)[2];\
+	(_date)[2] = '-';\
+	(_date)[3] = (_Pointer)[5];\
+	(_date)[4] = (_Pointer)[4];\
+	(_date)[5] = '-';\
+	(_date)[6] = (_Pointer)[1];\
+	(_date)[7] = (_Pointer)[0];}
+
+#define set_time( _time , _Pointer) {\
+	(_time)[0] = (_Pointer)[1];\
+	(_time)[1] = (_Pointer)[0];\
+	(_time)[2] = ':';\
+	(_time)[3] = (_Pointer)[3];\
+	(_time)[4] = (_Pointer)[2];\
+	(_time)[5] = ':';\
+	(_time)[6] = (_Pointer)[5];\
+	(_time)[7] = (_Pointer)[4];}
+
+
+
+
+/* converts an octet to a 8-Bit value */
+static inline int octet2bin(char* octet)
+{
+	int result=0;
+
+	if (octet[0]>57)
+		result=octet[0]-55;
+	else
+		result=octet[0]-48;
+	result=result<<4;
+	if (octet[1]>57)
+		result+=octet[1]-55;
+	else
+		result+=octet[1]-48;
+	return result;
+}
+
+
+
+
+/* converts a PDU-String to Ascii; the first octet is the length
+   return the length of ascii */
+static int pdu2ascii(char* pdu, char* ascii)
+{
+	int bitposition=0;
+	int byteposition;
+	int byteoffset;
+	int charcounter;
+	int bitcounter;
+	int count;
+	int octetcounter;
+	char c;
+	char binary[500];
+
+	/* First convert all octets to bytes */
+	count=octet2bin(pdu);
+	for (octetcounter=0; octetcounter<count; octetcounter++)
+		binary[octetcounter]=octet2bin(pdu+(octetcounter<<1)+2);
+
+	/* Then convert from 8-Bit to 7-Bit encapsulated in 8 bit */
+	for (charcounter=0; charcounter<count; charcounter++) {
+		c=0;
+		for (bitcounter=0; bitcounter<7; bitcounter++) {
+			byteposition=bitposition/8;
+			byteoffset=bitposition%8;
+			if (binary[byteposition]&(1<<byteoffset))
+				c=c|128;
+			bitposition++;
+			c=(c>>1)&127; /* The shift fills with 1, but I want 0 */
+		}
+		if (/*cs_convert*/1)
+			ascii[charcounter]=sms2ascii(c);
+		else if (c==0)
+			ascii[charcounter]=183;
+		else
+			ascii[charcounter]=c;
+	}
+	ascii[count]=0;
+	return count;
+}
+
+
+
+
+static int pdu2binary(char* pdu, char* binary)
+{
+	int count;
+	int octetcounter;
+
+	count=octet2bin(pdu);
+	for (octetcounter=0; octetcounter<count; octetcounter++)
+		binary[octetcounter]=octet2bin(pdu+(octetcounter<<1)+2);
+	binary[count]=0;
+	return count;
+}
+
+
+
+
+/* reads a SMS from the SIM-memory 1-10 */
+/* returns number of SIM memory if successful */
+/* on digicom the return value can be != sim */
+static int fetchsms(struct modem *mdm, int sim, char* pdu)
+{
+	char command[16];
+	char answer[512];
+	char* position;
+	char* beginning;
+	char* end;
+	int  foo,err;
+	int  clen;
+
+	// Digicom reports date+time only with AT+CMGL
+	if (mdm->mode==MODE_DIGICOM) {
+		put_command(mdm,"AT+CMGL=\"ALL\"\r",14,answer,
+			sizeof(answer),200,0);
+		/* search for beginning of the answer */
+		position=strstr(answer,"+CMGL: ");
+		if (position) {
+			end=position+7;
+			while (*end<'9' && *end>'0') end++;
+			if (end==position+7) {
+				foo = str2s(position+7,end-position-7,&err);
+				if (!err) {
+					LM_DBG("Found a message at memory %i\n",foo);
+					sim=foo;
+				}
+				position = 0;
+			}
+			position = 0;
+		}
+	} else {
+		LM_DBG("Trying to get stored message %i\n",sim);
+		clen=sprintf(command,"AT+CMGR=%i\r",sim);
+		put_command(mdm,command,clen,answer,sizeof(answer),50,0);
+		/* search for beginning of the answer */
+		position=strstr(answer,"+CMGR:");
+	}
+
+	/* keine SMS empfangen, weil Modem nicht mit +CMGR 
+	oder +CMGL geantwortet hat */
+	if (position==0)
+		return 0;
+	beginning=position+7;
+	/* keine SMS, weil Modem mit +CMGR: 0,,0 geantwortet hat */
+	if (strstr(answer,",,0\r"))
+		return 0;
+
+	/* After that we have the PDU or ASCII string */
+	for( end=beginning ; *end && *end!='\r' ; end++ );
+	if ( !*end || end-beginning<4)
+		return 0;
+	for( end=end+1 ; *end && *end!='\r' ; end++ );
+	if ( !*end || end-beginning<4)
+		return 0;
+	/* Now we have the end of the PDU or ASCII string */
+	*end=0;
+	strcpy(pdu,beginning);
+
+	return sim;
+}
+
+
+
+
+/* deletes the selected sms from the sim card */
+static void deletesms(struct modem *mdm, int sim) {
+	char command[32];
+	char answer[128];
+	int  clen;
+
+	LM_DBG("Deleting message %i !\n",sim);
+	clen = sprintf(command,"AT+CMGD=%i\r",sim);
+	put_command(mdm, command, clen, answer, sizeof(answer), 50, 0);
+}
+
+
+
+
+// checks the size of the SIM memory
+int check_memory(struct modem *mdm, int flag)
+{
+	char  answer[500];
+	char* posi;
+	int   laenge;
+	int   err,foo;
+	int   j, out;
+
+	for(out=0,j=0;!out && j<10; j++) 
+	{
+		if (put_command(mdm,"AT+CPMS?\r",9,answer,sizeof(answer),50,0)
+		&& (posi=strstr(answer,"+CPMS:"))!=0 )
+		{
+			// Modem supports CPMS command. Read memory size
+			if ( (posi=strchr(posi,','))!=0 ) {
+				posi++;
+				if ( (laenge=strcspn(posi,",\r"))!=0 ) {
+					if (flag==USED_MEM ) {
+						foo = str2s(posi,laenge,&err);
+						if (err) {
+							LM_ERR("unable to convert into integer used_memory from CPMS"
+								" response\n");
+						} else {
+							return foo;
+						}
+					}
+					posi+=laenge+1;
+					if ( (laenge=strcspn(posi,",\r"))!=0 ) {
+						foo = str2s(posi,laenge,&err);
+						if (err) {
+							LM_ERR("unable to convert into integer max_memory from CPMS"
+								" response\n");
+						} else {
+							return foo;
+						}
+					}
+				}
+			} /* if(strstr) */
+		} /* if(put_command) */
+		/* if we are here ->  some error happend */
+		if (checkmodem(mdm)!=0) {
+			LM_WARN("something happend with the modem -> was reinit -> let's retry\n");
+		} else {
+			LM_ERR("modem seems to be ok, but we had an error? I give up!\n");
+			out = 1;
+		}
+	} /* for */
+
+	if (out==0)
+		LM_ERR("modem does not respond after 10 retries, give up!\n");
+
+	return -1;
+}
+
+
+
+
+/* splits an ASCII string into the parts */
+/* returns length of ascii */
+static int splitascii(struct modem *mdm, char *source, struct incame_sms *sms)
+{
+	char* start;
+	char* end;
+
+	/* the text is after the \r */
+	for( start=source ; *start && *start!='\r' ; start++ );
+	if (!*start)
+		return 1;
+	start++;
+	strcpy(sms->ascii,start);
+	/* get the senders MSISDN */
+	start=strstr(source,"\",\"");
+	if (start==0) {
+		sms->userdatalength=strlen(sms->ascii);
+		return 1;
+	}
+	start+=3;
+	end=strstr(start,"\",");
+	if (end==0) {
+		sms->userdatalength=strlen(sms->ascii);
+		return 1;
+	}
+	*end=0;
+	strcpy(sms->sender,start);
+	/* Siemens M20 inserts the senders name between MSISDN and date */
+	start=end+3;
+	// Workaround for Thomas Stoeckel //
+	if (start[0]=='\"')
+		start++;
+	if (start[2]!='/')  { // if next is not a date is must be the name
+		end=strstr(start,"\",");
+		if (end==0) {
+			sms->userdatalength=strlen(sms->ascii);
+			return 1;
+		}
+		*end=0;
+		strcpy(sms->name,start);
+	}
+	/* Get the date */
+	start=end+3;
+	sprintf(sms->date,"%c%c-%c%c-%c%c",start[3],start[4],start[0],start[1],
+		start[6],start[7]);
+	/* Get the time */
+	start+=9;
+	sprintf(sms->time,"%c%c:%c%c:%c%c",start[0],start[1],start[3],start[4],
+		start[7],start[7]);
+	sms->userdatalength=strlen(sms->ascii);
+	return 1;
+}
+
+
+
+
+/* Subroutine for splitpdu() for messages type 0 (SMS-Deliver)
+   Returns the length of the ascii string
+   In binary mode ascii contains the binary SMS */
+static int split_type_0( char* Pointer,struct incame_sms *sms)
+{
+	int Length;
+	int padding;
+	int is_binary;
+
+	Length=octet2bin(Pointer);
+	padding=Length%2;
+	Pointer+=4;
+	memcpy(sms->sender,Pointer,Length+padding);
+	swapchars(sms->sender,Length+padding);
+	/* remove Padding characters after swapping */
+	sms->sender[Length]=0;
+	Pointer=Pointer+Length+padding+3;
+	is_binary = ((Pointer[0] & 4)==4);
+	Pointer++;
+	set_date(sms->date,Pointer);
+	Pointer=Pointer+6;
+	set_time(sms->time,Pointer);
+	Pointer=Pointer+8;
+	if (is_binary)
+		sms->userdatalength = pdu2binary(Pointer,sms->ascii);
+	else
+		sms->userdatalength = pdu2ascii(Pointer,sms->ascii);
+	return 1;
+}
+
+
+
+
+/* Subroutine for splitpdu() for messages type 2 (Staus Report)
+   Returns the length of the ascii string. In binary mode ascii 
+   contains the binary SMS */
+static int split_type_2( char* position, struct incame_sms *sms)
+{
+	int  length;
+	int  padding;
+	char *p;
+
+	/* get from report the sms id */
+	sms->sms_id = octet2bin(position);
+	position+=2;
+	/* get recipient address */
+	length=octet2bin(position);
+	padding=length%2;
+	position+=4;
+	memcpy(sms->sender,position,length+padding);
+	sms->sender[length]=0;
+	swapchars(sms->sender,length);
+	// get SMSC timestamp
+	position+=length+padding;
+	set_date(sms->date,position);
+	set_time(sms->time,position+6);
+	// get Discharge timestamp
+	position+=14;
+	p = sms->ascii + 2;
+	set_date(p,position);
+	*(p+DATE_LEN) = ' ';
+	set_time(p+DATE_LEN+1,position+6);
+	// get Status
+	position+=14;
+	sms->ascii[0] = (unsigned char)octet2bin(position);
+	sms->ascii[1] = ' ';
+	sms->ascii[2+DATE_LEN+1+TIME_LEN] = 0;
+
+	sms->userdatalength=2+DATE_LEN+1+TIME_LEN;
+	return 1;
+}
+
+
+
+
+/* Splits a PDU string into the parts */
+/* Returns the length of the ascii string. In binary mode ascii contains the binary SMS */
+static int splitpdu(struct modem *mdm, char* pdu, struct incame_sms *sms)
+{
+	int Length;
+	int Type;
+	char* Pointer;
+	char* start;
+	char* end;
+
+	/* Get the senders Name if given. Depends on the modem. */
+	start=strstr(pdu,"\",\"");
+	if (start!=0) {
+		start+=3;
+		end=strstr(start,"\",");
+		if (end!=0) {
+			memcpy(sms->name,start,end-start);
+			sms->name[end-start]=0;
+		}
+	} else
+		end=pdu;
+
+	/* the pdu is after the first \r */
+	for( start=end+1 ; *start && *start!='\r' ; start++ );
+	if (!*start)
+		return 0;
+	pdu=++start;
+	/* removes unwanted ctrl chars at the beginning */
+	while ( *pdu && (*pdu<=' '))
+		pdu++;
+	Pointer=pdu;
+	if (mdm->mode!=MODE_OLD) {
+		/* get senders smsc */
+		Length=octet2bin(pdu)*2-2;
+		if (Length>0) {
+			Pointer=pdu+4;
+			memcpy(sms->smsc,Pointer,Length);
+			swapchars(sms->smsc,Length);
+			/* remove Padding characters after swapping */
+			if (sms->smsc[Length-1]=='F')
+				sms->smsc[Length-1]=0;
+			else
+				sms->smsc[Length] = 0;
+		}
+		Pointer=pdu+Length+4;
+	}
+	/* is UDH bit set? udh=(octet2bin(Pointer)&4) */
+	Type=octet2bin(Pointer) & 3;
+	Pointer+=2;
+	if (Type==0) {
+		sms->is_statusreport = 0; /*SMS Deliver*/
+		return split_type_0( Pointer, sms);
+	} else if (Type==2) {
+		sms->is_statusreport = 1; /*Status Report*/
+		return split_type_2( Pointer, sms);
+	}
+	/*Unsupported type*/
+	return -1;
+}
+
+
+
+
+static inline int decode_pdu( struct modem *mdm, char *pdu, struct incame_sms *sms)
+{
+	int ret;
+
+	memset( sms, 0, sizeof(struct incame_sms) );
+	/* Ok, now we split the PDU string into parts and show it */
+	if (mdm->mode==MODE_ASCII || mdm->mode==MODE_DIGICOM)
+		ret = splitascii(mdm, pdu, sms);
+	else
+		ret = splitpdu(mdm, pdu, sms);
+
+	if (ret==-1) {
+		LM_ERR("unable split pdu/ascii!\n");
+		return -1;
+	}
+	return 1;
+}
+
+
+
+
+int getsms( struct incame_sms *sms, struct modem *mdm, int sim)
+{
+	char   pdu[512];
+	int    found;
+	int    ret;
+
+	found = fetchsms(mdm,sim,pdu);
+	if ( !found ) {
+		LM_ERR("unable to fetch sms %d!\n",sim);
+		return -1;
+	}
+
+	/* decode the pdu */
+	ret = decode_pdu(mdm,pdu,sms);
+
+	/* delete the sms*/
+	deletesms(mdm,found);
+
+	return ret;
+}
+
+
+
+
+int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len)
+{
+	char *data;
+	char *ptr;
+	char tmp;
+	int  n;
+
+	/* pdu starts after 2 "\r\n" */
+	ptr = s;
+	for ( n=0 ; n<2 && (ptr=strstr(ptr,"\r\n")) ; n++,ptr+=2 );
+	if (n<2) {
+		LM_ERR("failed to find pdu begining in CDS!\n");
+		goto error;
+	}
+	data = ptr;
+
+	/* pdu end with "\r\n" */
+	if (!(ptr=strstr(data,"\r\n"))) {
+		LM_ERR("failed to find pdu end in CDS!\n");
+		goto error;
+		}
+	tmp = ptr[0];
+	ptr[0] = 0;
+
+	/* decode the pdu */
+	n = decode_pdu(mdm,data-3,sms);
+	ptr[0] = tmp;
+	if (n==-1)
+		goto error;
+
+	return 1;
+error:
+	return -1;
+}
+

+ 413 - 0
modules/sms/libsms_modem.c

@@ -0,0 +1,413 @@
+/*
+SMS Server Tools
+Copyright (C) 2000 Stefan Frings
+
+This program is free software unless you got it under another license directly
+from the author. You can redistribute it and/or modify it under the terms of
+the GNU General Public License as published by the Free Software Foundation.
+Either version 2 of the License, or (at your option) any later version.
+
+http://www.isis.de/members/~s.frings
+mailto:[email protected]
+ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <termios.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#ifdef __sun
+#include <sys/filio.h>
+#endif
+#include "libsms_modem.h"
+#include "../../dprint.h"
+
+#define  MAX_BUF        2048
+#define  CDS_HDR        "\r\n+CDS:"
+#define  CDS_HDR_LEN    (strlen(CDS_HDR))
+#define optz(_n,_l)     (buf+buf_len-(((_n)+(_l)>buf_len)?buf_len:(_n)+(_l)))
+
+/* global variables */
+int         sms_report_type;
+cds_report  cds_report_func;
+
+
+
+int put_command( struct modem *mdm, char* cmd, int cmd_len, char* answer,
+											int max, int timeout,char* exp_end)
+{
+	static char buf[MAX_BUF];
+	static int  buf_len = 0;
+	char* pos;
+	char* foo;
+	char* ptr;
+	char* to_move;
+	char* answer_s;
+	char* answer_e;
+	int   timeoutcounter;
+	int   available;
+	int   status;
+	int   exp_end_len;
+	int   n;
+
+	/* check if fd is "clean" for reading */
+	timeoutcounter = 0;
+	ioctl(mdm->fd,TIOCMGET,&status);
+	while (!(status & TIOCM_CTS))
+	{
+		usleep( READ_SLEEP );
+		timeoutcounter++;
+		ioctl(mdm->fd,TIOCMGET,&status);
+		if (timeoutcounter>=timeout) {
+			LM_INFO("Modem is not clear to send\n");
+			return 0;
+		}
+	}
+#ifdef SHOW_SMS_MODEM_COMMAND
+	LM_DBG("-<%d>-->[%.*s] \n",cmd_len,cmd_len,cmd);
+#endif
+	/* send the command to the modem */
+	write(mdm->fd,cmd,cmd_len);
+	tcdrain(mdm->fd);
+
+	/* read from the modem */
+	exp_end_len = exp_end?strlen(exp_end):0;
+	answer_s = buf;
+	answer_e = 0;
+	to_move = 0;
+	do
+	{
+		/* try to read some bytes */
+		ioctl(mdm->fd,FIONREAD,&available);
+		/* how many bytes are available to read? */
+		if (available<1)  /* if 0 then wait a little bit and retry */
+		{
+			usleep( READ_SLEEP );
+			timeoutcounter++;
+			ioctl(mdm->fd,FIONREAD,&available);
+		}
+		if (available>0)
+		{
+			/* How many bytes do I want to read maximum?
+			Not more than buffer size. And how many bytes are available? */
+			n = (available>MAX_BUF-buf_len-1)?MAX_BUF-buf_len-1:available;
+			/* read data */
+			n = read( mdm->fd, buf+buf_len, n);
+			if (n<0) {
+				LM_ERR("error reading from modem: %s\n", strerror(errno));
+				goto error;
+			}
+			if (n) {
+				buf_len += n;
+				buf[buf_len] = 0;
+				//LM_DBG("read = [%s]\n",buf+buf_len-n);
+				foo = pos = 0;
+				if ( (!exp_end && ((pos=strstr(optz(n,4),"OK\r\n"))
+				|| (foo=strstr(optz(n,5),"ERROR"))))
+				|| (exp_end && (pos=strstr(optz(n,exp_end_len),exp_end)) )) {
+					/* we found the end */
+					//LM_DBG("end found = %s\n",
+					//	(foo?"ERROR":(exp_end?exp_end:"OK")));
+					/* for ERROR we still have to read EOL */
+					if (!foo || (foo=strstr(foo+5,"\r\n"))) {
+						answer_e = foo?foo+2:(pos+(exp_end?exp_end_len:4));
+						timeoutcounter = timeout;
+					}
+				}
+			}
+		}
+	/* repeat until timout */
+	}while (timeoutcounter<timeout);
+
+	if (!answer_e)
+		answer_e = buf+buf_len;
+
+	/* CDS report is activ? */
+	if (sms_report_type==CDS_REPORT) {
+		to_move = 0;
+		ptr = buf;
+		/* do we have a CDS reply inside? */
+		while ( (pos=strstr(ptr,CDS_HDR)) ) {
+			if (pos!=ptr) {  /* here we have the command response */
+				answer_s = ptr;
+			}
+			/* look for the end of CDS response */
+			ptr = pos+CDS_HDR_LEN;
+			for( n=0 ; n<2&&(foo=strstr(ptr,"\r\n")) ; ptr=foo+2,n++ );
+			if (n<2) { /* we haven't read the entire CDS response */
+				LM_DBG("CDS end not found!\n");
+				to_move = pos;
+				ptr = buf + buf_len;
+			}else{
+				/* process the CDS */
+				LM_DBG("CDS=[%.*s]\n",(int)(ptr-pos),pos);
+				cds_report_func(mdm,pos,ptr-pos);
+			}
+		}
+		if ((*ptr)) {
+			answer_s = ptr;
+			ptr = answer_e;
+		}
+		if (ptr!=buf+buf_len)
+			to_move = ptr;
+	}
+
+	/* copy the response in answer buffer - as much as fits */
+	if (answer && max) {
+		n = max-1<answer_e-answer_s?max-1:answer_e-answer_s;
+		memcpy(answer,answer_s,n);
+		answer[n] = 0;
+	}
+	/* shift left the remaining data into the buffer - if needs */
+	if (sms_report_type==CDS_REPORT && to_move) {
+		buf_len = buf_len - (to_move-buf);
+		memcpy(buf,to_move,buf_len);
+		buf[buf_len] = 0;
+		LM_DBG("buffer shifted left=[%d][%s]\n",buf_len,buf);
+	} else {
+		buf_len = 0;
+	}
+
+#ifdef SHOW_SMS_MODEM_COMMAND
+	LM_DBG("<-[%s] \n",answer);
+#endif
+	return answer_e-answer_s;
+
+error:
+	return 0;
+}
+
+
+
+
+/* setup serial port */
+int setmodemparams( struct modem *mdm )
+{
+	struct termios newtio;
+
+	bzero(&newtio, sizeof(newtio));
+	newtio.c_cflag = mdm->baudrate | CRTSCTS | CS8 | CLOCAL | CREAD | O_NDELAY;
+	//uncomment next line to disable hardware handshake
+	//newtio.c_cflag &= ~CRTSCTS;
+	newtio.c_iflag = IGNPAR;
+	newtio.c_oflag = 0;
+	newtio.c_lflag = 0;
+	newtio.c_cc[VTIME]    = 1;
+	newtio.c_cc[VMIN]     = 0;
+	tcflush(mdm->fd, TCIOFLUSH);
+	tcsetattr(mdm->fd,TCSANOW,&newtio);
+	return 0;
+}
+
+
+
+
+int initmodem(struct modem *mdm, cds_report cds_report_f)
+{
+	char command[100];
+	char answer[100];
+	int retries=0;
+	int success=0;
+	int clen=0;
+	int n;
+
+	LM_INFO("init modem %s on %s.\n",mdm->name,mdm->device);
+
+	if (mdm->pin[0]) {
+		/* Checking if modem needs PIN */
+		put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0);
+		if (strstr(answer,"+CPIN: SIM PIN")) {
+			LM_INFO("Modem needs PIN, entering PIN...\n");
+			clen=sprintf(command,"AT+CPIN=\"%s\"\r",mdm->pin);
+			put_command(mdm,command,clen,answer,sizeof(answer),100,0);
+			put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0);
+			if (!strstr(answer,"+CPIN: READY")) {
+				if (strstr(answer,"+CPIN: SIM PIN")) {
+					LM_ERR("Modem did not accept this PIN\n");
+					goto error;
+				} else if (strstr(answer,"+CPIN: SIM PUK")) {
+					LM_ERR("YourPIN is locked! Unlock it manually!\n");
+					goto error;
+				} else {
+					goto error;
+				}
+			}
+			LM_INFO("PIN Ready!\n");
+			sleep(5);
+		}
+	}
+
+	if (mdm->mode==MODE_DIGICOM)
+		success=1;
+	else {
+		LM_INFO("Checking if Modem is registered to the network\n");
+		success=0;
+		retries=0;
+		do
+		{
+			retries++;
+			put_command(mdm,"AT+CREG?\r",9,answer,sizeof(answer),100,0);
+			if (strchr(answer,'1') )
+			{
+				LM_INFO("Modem is registered to the network\n");
+				success=1;
+			} else if (strchr(answer,'2')) {
+				// added by bogdan
+				LM_WARN("Modems seems to try to reach the network!"
+						" Let's wait a little bit\n");
+				retries--;
+				sleep(2);
+			} else if (strchr(answer,'5')) {
+				// added by Thomas Stoeckel
+				LM_INFO("Modem is registered to a roaming partner network\n");
+				success=1;
+			} else if (strstr(answer,"ERROR")) {
+				LM_WARN("Ignoring that modem does not support +CREG command.\n");
+				success=1;
+			} else {
+				LM_NOTICE("Waiting 2 sec. before retrying\n");
+				sleep(2);
+			}
+		}while ((success==0)&&(retries<20));
+	}
+
+	if (success==0) {
+		LM_ERR("Modem is not registered to the network\n");
+		goto error;
+	}
+
+	for( n=0 ; n<2+2*(sms_report_type==CDS_REPORT) ; n++) {
+		/* build the command */
+		switch (n) {
+			case 0:
+				strcpy(command,"AT+CMGF=0\r");
+				command[8]+=(mdm->mode==MODE_ASCII || mdm->mode==MODE_DIGICOM);
+				clen = 10;
+				break;
+			case 1:
+				strcpy(command,"AT S7=45 S0=0 L1 V1 X4 &c1 E1 Q0\r");
+				clen = 33;
+				break;
+			case 2:
+				strcpy(command,"AT+CSMP=49,167,0,241\r");
+				clen = 21;
+				break;
+			case 3:
+				strcpy(command,"AT+CNMI=1,1,0,1,0\r");
+				clen = 18;
+				break;
+		}
+		/* send it to modem */
+		retries=0;
+		success=0;
+		do {
+			retries++;
+			/*querying the modem*/
+			put_command(mdm,command,clen,answer,sizeof(answer),100,0);
+			/*dealing with the answer*/
+			if (strstr(answer,"ERROR")) {
+				LM_NOTICE("Waiting 1 sec. before to retrying\n");
+				sleep(1);
+			} else
+				success=1;
+		}while ((success==0)&&(retries<3));
+		/* have we succeeded? */
+		if (success==0) {
+			LM_ERR("cmd [%.*s] returned ERROR\n", clen-1,command);
+			goto error;
+		}
+	} /* end for */
+
+	if ( sms_report_type==CDS_REPORT && !cds_report_f) {
+		LM_ERR("no CDS_REPORT function given\n");
+		goto error;
+	}
+	cds_report_func = cds_report_f;
+
+	if (mdm->smsc[0]) {
+		LM_INFO("Changing SMSC to \"%s\"\n",mdm->smsc);
+		setsmsc(mdm,mdm->smsc);
+	}
+
+
+
+	return 0;
+error:
+	return -1;
+}
+
+
+
+
+int checkmodem(struct modem *mdm)
+{
+	char answer[500];
+
+	/* Checking if modem needs PIN */
+	put_command(mdm,"AT+CPIN?\r",9,answer,sizeof(answer),50,0);
+	if (!strstr(answer,"+CPIN: READY")) {
+		LM_WARN("modem wants the PIN again!\n");
+		goto reinit;
+	}
+
+	if (mdm->mode!=MODE_DIGICOM) {
+		put_command(mdm,"AT+CREG?\r",9,answer,sizeof(answer),100,0);
+		if (!strchr(answer,'1') ) {
+			LM_WARN("Modem is not registered to the network\n");
+			goto reinit;
+		}
+	}
+
+	return 1;
+reinit:
+	LM_WARN("re -init the modem!!\n");
+	initmodem(mdm,cds_report_func);
+	return -1;
+}
+
+
+
+
+int setsmsc(struct modem *mdm, char *smsc)
+{
+	char command[100];
+	char answer[50];
+	int  clen;
+
+	if (smsc && smsc[0]) {
+		clen=sprintf(command,"AT+CSCA=\"+%s\"\r",smsc);
+		put_command(mdm,command,clen,answer,sizeof(answer),50,0);
+	}
+	return 0;
+}
+
+
+
+
+int openmodem( struct modem *mdm)
+{
+	mdm->fd = open(mdm->device, O_RDWR | O_NOCTTY );
+	if (mdm->fd <0)
+		return -1;
+
+	tcgetattr(mdm->fd,&(mdm->oldtio));
+	return 0;
+}
+
+
+
+
+int closemodem(struct modem *mdm)
+{
+	tcsetattr(mdm->fd,TCSANOW,&(mdm->oldtio));
+	close(mdm->fd);
+	return 0;
+}
+

+ 55 - 0
modules/sms/libsms_modem.h

@@ -0,0 +1,55 @@
+/*
+SMS Server Tools
+Copyright (C) 2000 Stefan Frings
+
+This program is free software unless you got it under another license directly
+from the author. You can redistribute it and/or modify it under the terms of 
+the GNU General Public License as published by the Free Software Foundation.
+Either version 2 of the License, or (at your option) any later version.
+
+http://www.isis.de/members/~s.frings
+mailto:[email protected]
+ */
+
+
+#ifndef _LIBSMS_MODEM_H
+#define _LIBSMS_MODEM_H
+
+#include <termios.h>
+#include "sms_funcs.h"
+
+
+#define MODE_OLD      1
+#define MODE_DIGICOM  2
+#define MODE_ASCII    3
+#define MODE_NEW      4
+
+#define READ_SLEEP   10000
+#define READ_TIMEOUT  10
+
+typedef int(*cds_report)( struct modem* , char* , int );
+
+
+/* put_command
+   Sends a command to the modem and waits max timout*0.1 seconds for an answer.
+   The function returns the length of the answer.
+   The answer can be Ok, ERROR or expect.
+   The command may be empty or NULL  */
+
+int put_command( struct modem *mdm, char* command, int clen, char* answer,
+											int max, int timeout,char* expect);
+
+int setmodemparams( struct modem *mdm);
+
+int checkmodem(struct modem *mdm);
+
+int initmodem(struct modem *mdm, cds_report cds_report_f);
+
+int setsmsc(struct modem *mdm, char *smsc);
+
+int openmodem(struct modem *mdm);
+
+int closemodem(struct modem *mdm);
+
+
+#endif

+ 250 - 0
modules/sms/libsms_putsms.c

@@ -0,0 +1,250 @@
+/*
+SMS Server Tools
+Copyright (C) 2000-2002 Stefan Frings
+
+This program is free software unless you got it under another license directly
+from the author. You can redistribute it and/or modify it under the terms of
+the GNU General Public License as published by the Free Software Foundation.
+Either version 2 of the License, or (at your option) any later version.
+
+http://www.isis.de/members/~s.frings
+mailto:[email protected]
+ */
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include "sms_funcs.h"
+#include "libsms_charset.h"
+#include "libsms_modem.h"
+
+
+
+static char hexa[16] = {
+	'0','1','2','3','4','5','6','7',
+	'8','9','A','B','C','D','E','F'
+	};
+
+
+void swapchars(char* string, int len) /* Swaps every second character */
+{
+	int position;
+	char c;
+
+	for (position=0; position<len-1; position+=2)
+	{
+		c=string[position];
+		string[position]=string[position+1];
+		string[position+1]=c;
+	}
+}
+
+
+
+
+/* Work with the complex bit building to generate a 7 bit PDU string
+   encapsulated in 8 bit */
+int ascii2pdu(char* ascii, int asciiLength, char* pdu, int cs_convert)
+{
+	static char tmp[500];
+	int pdubitposition=0;
+	int pdubyteposition=0;
+	int character;
+	int bit;
+	int pdubitnr;
+	char converted;
+	unsigned char foo;
+
+	memset(tmp,0,asciiLength);
+	for (character=0;character<asciiLength;character++)
+	{
+		if (cs_convert)
+			converted=ascii2sms(ascii[character]);
+		else
+			converted=ascii[character];
+		for (bit=0;bit<7;bit++)
+		{
+			pdubitnr=7*character+bit;
+			pdubyteposition=pdubitnr/8;
+			pdubitposition=pdubitnr%8;
+			if (converted & (1<<bit))
+				tmp[pdubyteposition]=tmp[pdubyteposition]|(1<<pdubitposition);
+			else
+				tmp[pdubyteposition]=tmp[pdubyteposition]&~(1<<pdubitposition);
+		}
+	}
+	tmp[pdubyteposition+1]=0;
+	for (character=0;character<=pdubyteposition; character++)
+	{
+		foo = tmp[character] ;
+		pdu[2*character  ] = hexa[foo>>4];
+		pdu[2*character+1] = hexa[foo&0x0f];
+	}
+	pdu[2*(pdubyteposition+1)]=0;
+	return 2*(pdubyteposition+1);
+}
+
+
+
+
+/* Create a HEX Dump */
+int binary2pdu(char* binary, int length, char* pdu)
+{
+	int character;
+	unsigned char foo;
+
+	for (character=0;character<length; character++)
+	{
+		foo = binary[character];
+		pdu[2*character  ] = hexa[foo>>4];
+		pdu[2*character+1] = hexa[foo&0x0f];
+	}
+	pdu[2*length]=0;
+	return 2*length;
+}
+
+
+
+
+/* make the PDU string. The destination variable pdu has to be big enough. */
+int make_pdu(struct sms_msg *msg, struct modem *mdm, char* pdu)
+{
+	int  coding;
+	int  flags;
+	char tmp[500];
+	int  pdu_len=0;
+	int  foo;
+
+	memcpy(tmp,msg->to.s,msg->to.len);
+	foo = msg->to.len;
+	tmp[foo] = 0;
+	// terminate the number with F if the length is odd
+	if ( foo%2 ) {
+		tmp[foo]='F';
+		tmp[++foo] = 0;
+	}
+	// Swap every second character
+	swapchars(tmp,foo);
+	flags = 0x01;   /* SMS-Submit MS to SMSC */
+	if (sms_report_type!=NO_REPORT)
+		flags |= 0x20 ; /* status report request */
+	coding=240+1; // Dummy + Class 1
+	if (mdm->mode!=MODE_OLD)
+		flags+=16; // Validity field
+	/* concatenate the first part of the PDU string */
+	if (mdm->mode==MODE_OLD)
+		pdu_len += sprintf(pdu,"%02X00%02X91%s00%02X%02X",flags,
+			msg->to.len,tmp,coding,msg->text.len);
+	else
+		pdu_len += sprintf(pdu,"00%02X00%02X91%s00%02XA7%02X",flags,
+			msg->to.len,tmp,coding,msg->text.len);
+	/* Create the PDU string of the message */
+	/* pdu_len += binary2pdu(msg->text.s,msg->text.len,pdu+pdu_len); */
+	pdu_len += ascii2pdu(msg->text.s,msg->text.len,pdu+pdu_len,1/*convert*/);
+	/* concatenate the text to the PDU string */
+	return pdu_len;
+}
+
+
+
+
+/* search into modem reply for the sms id */
+inline int fetch_sms_id(char *answer)
+{
+	char *p;
+	int  id;
+
+	p = strstr(answer,"+CMGS:");
+	if (!p)
+		goto error;
+	p += 6;
+	/* parse to the first digit */
+	while(p && *p && (*p==' ' || *p=='\r' || *p=='\n'))
+		p++;
+	if (*p<'0' || *p>'9')
+		goto error;
+	/* convert the number*/
+	id = 0;
+	while (p && *p>='0' && *p<='9')
+		id = id*10 + *(p++)-'0';
+
+	return id;
+error:
+	return -1;
+}
+
+
+
+
+/* send sms */
+int putsms( struct sms_msg *sms_messg, struct modem *mdm)
+{
+	char command[500];
+	char command2[500];
+	char answer[500];
+	char pdu[500];
+	int clen,clen2;
+	int retries;
+	int err_code;
+	int pdu_len;
+	int sms_id;
+
+	pdu_len = make_pdu(sms_messg, mdm, pdu);
+	if (mdm->mode==MODE_OLD)
+		clen = sprintf(command,"AT+CMGS=%i\r",pdu_len/2);
+	else if (mdm->mode==MODE_ASCII)
+		clen = sprintf(command,"AT+CMGS=\"+%.*s\"\r",sms_messg->to.len,
+			sms_messg->to.s);
+	else
+		clen = sprintf(command,"AT+CMGS=%i\r",pdu_len/2-1);
+
+	if (mdm->mode==MODE_ASCII)
+		clen2=sprintf(command2,"%.*s\x1A",sms_messg->text.len,
+		sms_messg->text.s);
+	else
+		clen2=sprintf(command2,"%.*s\x1A",pdu_len,pdu);
+
+	sms_id = 0;
+	for(err_code=0,retries=0;err_code<2 && retries<mdm->retry; retries++)
+	{
+		if (put_command(mdm,command,clen,answer,sizeof(answer),50,"\r\n> ")
+		&& put_command(mdm,command2,clen2,answer,sizeof(answer),1000,0)
+		&& strstr(answer,"OK") )
+		{
+			/* no error during sending and the modem said OK */
+			err_code = 2;
+			/* if reports were request, we have to fetch the sms id from
+			the modem reply to keep trace of the status reports */
+			if (sms_report_type!=NO_REPORT) {
+				sms_id = fetch_sms_id(answer);
+				if (sms_id==-1)
+					err_code = 1;
+			}
+		} else {
+			/* we have an error */
+			if (checkmodem(mdm)==-1) {
+				err_code = 0;
+				LM_WARN("resending last sms! \n");
+			} else if (err_code==0) {
+				LM_WARN("possible corrupted sms. Let's try again!\n");
+				err_code = 1;
+			}else {
+				LM_ERR("We have a FUBAR sms!! drop it!\n");
+				err_code = 3;
+			}
+		}
+	}
+
+	if (err_code==0)
+		LM_WARN("something spooky is going on with the modem!"
+			" Re-inited and re-tried for %d times without success!\n",
+			mdm->retry);
+	return (err_code==0?-2:(err_code==2?sms_id:-1));
+}
+

+ 49 - 0
modules/sms/libsms_sms.h

@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _LIBSMS_SMS_H
+#define _LIBSMS_SMS_H
+
+#include "sms_funcs.h"
+
+#define MAX_MEM  0
+#define USED_MEM 1
+
+
+int putsms( struct sms_msg *sms_messg, struct modem *mdm);
+
+int getsms( struct incame_sms *sms, struct modem *mdm, int sim);
+
+int check_memory( struct modem *mdm, int flag);
+
+void swapchars(char* string, int len);
+
+int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len);
+
+#endif
+

+ 702 - 0
modules/sms/sms.c

@@ -0,0 +1,702 @@
+/*
+ * $Id$
+ *
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/*
+ * History:
+ * --------
+ *  2003-03-11  updated to the new module exports interface (andrei)
+ *  2003-03-16  flags export parameter added (janakj)
+ *  2003-03-19  all mallocs/frees replaced w/ pkg_malloc/pkg_free (andrei)
+ *  2003-04-02  port_no_str does not contain a leading ':' anymore (andrei)
+ *  2003-04-06  Only child 1 will execute child init (janakj)
+ *  2003-10-24  updated to the new socket_info lists (andrei)
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "../../sr_module.h"
+#include "../../error.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../globals.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../socket_info.h"
+#include "../../cfg/cfg_struct.h"
+#include "../../modules/tm/tm_load.h"
+#include "sms_funcs.h"
+#include "sms_report.h"
+#include "libsms_modem.h"
+
+
+MODULE_VERSION
+
+
+static int sms_init(void);
+static int sms_exit(void);
+static int sms_child_init(int);
+static int w_sms_send_msg(struct sip_msg*, char*, char* );
+static int w_sms_send_msg_to_net(struct sip_msg*, char*, char*);
+static int fixup_sms_send_msg_to_net(void** param, int param_no);
+
+
+
+/* parameters */
+char *networks_config = 0;
+char *modems_config   = 0;
+char *links_config    = 0;
+char *default_net_str = 0;
+char *domain_str      = 0;
+
+/*global variables*/
+int    default_net    = 0;
+int    max_sms_parts  = MAX_SMS_PARTS;
+str    domain;
+int    *queued_msgs    = 0;
+int    use_contact     = 0;
+int    sms_report_type = NO_REPORT;
+
+
+static cmd_export_t cmds[]={
+	{"sms_send_msg_to_net", w_sms_send_msg_to_net, 1,
+	     fixup_sms_send_msg_to_net, REQUEST_ROUTE},
+	{"sms_send_msg",        w_sms_send_msg,        0,
+	     0,                         REQUEST_ROUTE},
+	{0,0,0,0,0}
+};
+
+
+static param_export_t params[]={
+	{"networks",        STR_PARAM, &networks_config },
+	{"modems",          STR_PARAM, &modems_config   },
+	{"links",           STR_PARAM, &links_config    },
+	{"default_net",     STR_PARAM, &default_net_str },
+	{"max_sms_parts",   INT_PARAM, &max_sms_parts   },
+	{"domain",          STR_PARAM, &domain_str      },
+	{"use_contact",     INT_PARAM, &use_contact     },
+	{"sms_report_type", INT_PARAM, &sms_report_type },
+	{0,0,0}
+};
+
+
+struct module_exports exports= {
+	"sms",
+	cmds,
+	0,        /* RPC methods */
+	params,
+
+	sms_init,   /* module initialization function */
+	(response_function) 0,
+	(destroy_function) sms_exit,   /* module exit function */
+	0,
+	(child_init_function) sms_child_init  /* per-child init function */
+};
+
+
+
+
+static int fixup_sms_send_msg_to_net(void** param, int param_no)
+{
+	long net_nr,i;
+
+	if (param_no==1) {
+		for(net_nr=-1,i=0;i<nr_of_networks&&net_nr==-1;i++)
+			if (!strcasecmp(networks[i].name,*param))
+				net_nr = i;
+		if (net_nr==-1) {
+			LM_ERR("network \"%s\" not found in net list!\n",(char*)*param);
+			return E_UNSPEC;
+		} else {
+			pkg_free(*param);
+			*param=(void*)net_nr;
+			return 0;
+		}
+	}
+	return 0;
+}
+
+
+
+
+
+#define eat_spaces(_p) \
+	while( *(_p)==' ' || *(_p)=='\t' ){\
+	(_p)++;}
+
+
+
+
+int set_modem_arg(struct modem *mdm, char *arg, char *arg_end)
+{
+	int err, foo;
+
+	if (*(arg+1)!='=') {
+		LM_ERR("invalid parameter syntax near [=]\n");
+		goto error;
+	}
+	switch (*arg)
+	{
+		case 'd':  /* device */
+			memcpy(mdm->device,arg+2,arg_end-arg-2);
+			mdm->device[arg_end-arg-2] = 0;
+			break;
+		case 'p':  /* pin */
+			memcpy(mdm->pin,arg+2,arg_end-arg-2);
+			mdm->pin[arg_end-arg-2] = 0;
+			break;
+		case 'm':  /* mode */
+			if (!strncasecmp(arg+2,"OLD",3)
+			&& arg_end-arg-2==3) {
+				mdm->mode = MODE_OLD;
+			} else if (!strncasecmp(arg+2,"DIGICOM",7)
+			&& arg_end-arg-2==7) {
+				mdm->mode = MODE_DIGICOM;
+			} else if (!strncasecmp(arg+2,"ASCII",5)
+			&& arg_end-arg-2==5) {
+				mdm->mode = MODE_ASCII;
+			} else if (!strncasecmp(arg+2,"NEW",3)
+			&& arg_end-arg-2==3) {
+				mdm->mode = MODE_NEW;
+			} else {
+				LM_ERR("invalid value \"%.*s\" for param [m]\n",
+					(int)(arg_end-arg-2),arg+2);
+				goto error;
+			}
+			break;
+		case 'c':  /* sms center number */
+			memcpy(mdm->smsc,arg+2,arg_end-arg-2);
+			mdm->smsc[arg_end-arg-2] = 0;
+			break;
+		case 'r':  /* retry time */
+			foo=str2s(arg+2,arg_end-arg-2,&err);
+			if (err) {
+				LM_ERR("failed to convert [r] arg to integer!\n");
+				goto error;
+			}
+			mdm->retry = foo;
+			break;
+		case 'l':  /* looping interval */
+			foo=str2s(arg+2,arg_end-arg-2,&err);
+			if (err) {
+				LM_ERR("failed to convert [l] arg to integer!\n");
+				goto error;
+			}
+			mdm->looping_interval = foo;
+			break;
+		case 'b':  /* baudrate */
+			foo=str2s(arg+2,arg_end-arg-2,&err);
+			if (err) {
+				LM_ERR("failed to convert [b] arg to integer!\n");
+				goto error;
+			}
+			switch (foo) {
+				case   300: foo=B300; break;
+				case  1200: foo=B1200; break;
+				case  2400: foo=B2400; break;
+				case  9600: foo=B9600; break;
+				case 19200: foo=B19200; break;
+				case 38400: foo=B38400; break;
+				case 57600: foo=B57600; break;
+				default:
+					LM_ERR("unsupported value %d for [b] arg!\n",foo);
+					goto error;
+			}
+			mdm->baudrate = foo;
+			break;
+		case 's':  /* scan */
+			foo=str2s(arg+2,arg_end-arg-2,&err);
+			if (err) {
+				LM_WARN("cannot convert [s] arg to integer!, assume default mode s=%d (SCAN)\n", 
+					SMS_BODY_SCAN);
+				foo = SMS_BODY_SCAN;
+			}
+			switch (foo) {
+				case   SMS_BODY_SCAN: 
+				case   SMS_BODY_SCAN_NO: 
+				case   SMS_BODY_SCAN_MIX: 
+					break;
+				default:
+					LM_WARN("unsupported value s=%d for [s] arg!, assume default mode s=%d (SCAN)\n",
+						  foo,SMS_BODY_SCAN);
+					foo = SMS_BODY_SCAN;
+			}
+			mdm->scan = foo;
+			break;
+		case 't':  /* to */
+			memcpy(mdm->to,arg+2,arg_end-arg-2);
+			mdm->to[arg_end-arg-2] = 0;
+			break;
+		default:
+			LM_ERR("unknown param name [%c]\n",*arg);
+			goto error;
+	}
+
+	return 1;
+error:
+	return -1;
+}
+
+
+
+
+int set_network_arg(struct network *net, char *arg, char *arg_end)
+{
+	int err,foo;
+
+	if (*(arg+1)!='=') {
+		LM_ERR("invalid parameter syntax near [=]\n");
+		goto error;
+	}
+	switch (*arg)
+	{
+		case 'm':  /* maximum sms per one call */
+			foo=str2s(arg+2,arg_end-arg-2,&err);
+			if (err) {
+				LM_ERR("cannot convert [m] arg to integer!\n");
+				goto error;
+			}
+			net->max_sms_per_call = foo;
+			break;
+		default:
+			LM_ERR("unknown param name [%c]\n",*arg);
+			goto error;
+	}
+
+	return 1;
+error:
+	return -1;
+}
+
+
+
+
+int parse_config_lines(void)
+{
+	char *p,*start;
+	int  i, k, step;
+	int  mdm_nr, net_nr;
+
+	nr_of_networks = 0;
+	nr_of_modems = 0;
+
+	step = 1;
+	/* parsing modems configuration string */
+	if ( (p = modems_config)==0) {
+		LM_ERR("param \"modems\" not found\n");
+		goto error;
+	}
+	while (*p)
+	{
+		eat_spaces(p);
+		/*get modem's name*/
+		start = p;
+		while (*p!=' ' && *p!='\t' && *p!='[' && *p!=0)
+			p++;
+		if ( p==start || *p==0 )
+			goto parse_error;
+		memcpy(modems[nr_of_modems].name, start, p-start);
+		modems[nr_of_modems].name[p-start] = 0;
+		modems[nr_of_modems].smsc[0] = 0;
+		modems[nr_of_modems].device[0] = 0;
+		modems[nr_of_modems].pin[0] = 0;
+		modems[nr_of_modems].mode = MODE_NEW;
+		modems[nr_of_modems].retry = 4;
+		modems[nr_of_modems].looping_interval = 20;
+		modems[nr_of_modems].baudrate = B9600;
+		modems[nr_of_modems].scan = SMS_BODY_SCAN;
+		modems[nr_of_modems].to[0] = 0;
+		memset(modems[nr_of_modems].net_list,0XFF,
+			sizeof(modems[nr_of_modems].net_list) );
+		/*get modem parameters*/
+		eat_spaces(p);
+		if (*p!='[')
+			goto parse_error;
+		p++;
+		while (*p!=']')
+		{
+			eat_spaces(p);
+			start = p;
+			while(*p!=' ' && *p!='\t' && *p!=']' && *p!=';' && *p!=0)
+				p++;
+			if ( p==start || *p==0 )
+				goto parse_error;
+			if (set_modem_arg( &(modems[nr_of_modems]), start, p)==-1)
+				goto error;
+			eat_spaces(p);
+			if (*p==';') {
+				p++;
+				eat_spaces(p);
+			}
+		}
+		if (*p!=']')
+			goto parse_error;
+		p++;
+		/* end of element */
+		if (modems[nr_of_modems].device[0]==0) {
+			LM_ERR("modem %s has no device associated\n",
+					modems[nr_of_modems].name);
+			goto error;
+		}
+		if (modems[nr_of_modems].smsc[0]==0) {
+			LM_WARN("modem %s has no sms center associated -> using"
+				" the default one from modem\n",modems[nr_of_modems].name);
+		}
+		nr_of_modems++;
+		eat_spaces(p);
+		if (*p==';') {
+			p++;
+			eat_spaces(p);
+		}
+	}
+	if (nr_of_modems==0)
+	{
+		LM_ERR("failed to parse config modems - no modem found!\n");
+		goto error;
+	}
+
+	step++;
+	/* parsing networks configuration string */
+	if ( (p = networks_config)==0) {
+		LM_ERR("param \"networks\" not found\n");
+		goto error;
+	}
+	while (*p)
+	{
+		eat_spaces(p);
+		/*get network name*/
+		start = p;
+		while (*p!=' ' && *p!='\t' && *p!='[' && *p!=0)
+			p++;
+		if ( p==start || *p==0 )
+			goto parse_error;
+		memcpy(networks[nr_of_networks].name, start, p-start);
+		networks[nr_of_networks].name[p-start] = 0;
+		networks[nr_of_networks].max_sms_per_call = 10;
+		/*get network parameters*/
+		eat_spaces(p);
+		if (*p!='[')
+			goto parse_error;
+		p++;
+		while (*p!=']')
+		{
+			eat_spaces(p);
+			start = p;
+			while(*p!=' ' && *p!='\t' && *p!=']' && *p!=';' && *p!=0)
+				p++;
+			if ( p==start || *p==0 )
+				goto parse_error;
+			if (set_network_arg( &(networks[nr_of_networks]), start, p)==-1)
+				goto error;
+			eat_spaces(p);
+			if (*p==';') {
+				p++;
+				eat_spaces(p);
+			}
+		}
+		if (*p!=']')
+			goto parse_error;
+		p++;
+		/* end of element */
+		nr_of_networks++;
+		eat_spaces(p);
+		if (*p==';')
+			p++;
+		eat_spaces(p);
+	}
+	if (nr_of_networks==0)
+	{
+		LM_ERR("no network found!\n");
+		goto error;
+	}
+
+	step++;
+	/* parsing links configuration string */
+	if ( (p = links_config)==0) {
+		LM_ERR("param \"links\" not found\n");
+		goto error;
+	}
+	while (*p)
+	{
+		eat_spaces(p);
+		/*get modem's device*/
+		start = p;
+		while (*p!=' ' && *p!='\t' && *p!='[' && *p!=0)
+			p++;
+		if ( p==start || *p==0 )
+			goto parse_error;
+		/*looks for modem index*/
+		for(mdm_nr=-1,i=0;i<nr_of_modems && mdm_nr==-1;i++)
+			if (!strncasecmp(modems[i].name,start,p-start)&&
+			modems[i].name[p-start]==0)
+				mdm_nr = i;
+		if (mdm_nr==-1) {
+			LM_ERR("unknown modem %.*s \n,",(int)(p-start), start);
+			goto error;
+		}
+		/*get associated networks list*/
+		eat_spaces(p);
+		if (*p!='[')
+			goto parse_error;
+		p++;
+		k=0;
+		while (*p!=']')
+		{
+			eat_spaces(p);
+			start = p;
+			while(*p!=' ' && *p!='\t' && *p!=']' && *p!=';' && *p!=0)
+				p++;
+			if ( p==start || *p==0 )
+				goto parse_error;
+			/* lookup for the network -> get its index */
+			for(net_nr=-1,i=0;i<nr_of_networks&&net_nr==-1;i++)
+				if (!strncasecmp(networks[i].name,start,p-start)
+				&& networks[i].name[p-start]==0)
+					net_nr = i;
+			if (net_nr==-1) {
+				LM_ERR("associated net <%.*s> not found in net list\n",
+					(int)(p-start), start);
+				goto error;
+			}
+			LM_DBG("linking net \"%s\" to modem \"%s\" on pos %d.\n",
+					networks[net_nr].name,modems[mdm_nr].name,k);
+			modems[mdm_nr].net_list[k++]=net_nr;
+			eat_spaces(p);
+			if (*p==';') {
+				p++;
+				eat_spaces(p);
+			}
+		}
+		if (*p!=']')
+			goto parse_error;
+		p++;
+		/* end of element */
+		eat_spaces(p);
+		if (*p==';') {
+			p++;
+			eat_spaces(p);
+		}
+	}
+
+	/* resolving default network name - if any*/
+	if (default_net_str) {
+		for(net_nr=-1,i=0;i<nr_of_networks&&net_nr==-1;i++)
+			if (!strcasecmp(networks[i].name,default_net_str))
+				net_nr = i;
+		if (net_nr==-1) {
+			LM_ERR("network \"%s\" not found in net list!\n",default_net_str);
+			goto error;
+		}
+		default_net = net_nr;
+	}
+
+	return 0;
+parse_error:
+	LM_ERR("SMS %s config: parse error before  chr %d [%.*s]\n",
+		(step==1)?"modems":(step==2?"networks":"links"),
+		(int)(p - ((step==1)?modems_config:
+				   (step==2?networks_config:links_config))),
+		(*p==0)?4:1,(*p==0)?"NULL":p );
+error:
+	return -1;
+}
+
+
+
+
+int global_init(void)
+{
+	load_tm_f  load_tm;
+	int        i, net_pipe[2], foo;
+	char       *p;
+	struct socket_info* si;
+
+	/* import the TM auto-loading function */
+	if ( !(load_tm=(load_tm_f)find_export("load_tm", NO_SCRIPT, 0))) {
+		LM_ERR("cannot import load_tm\n");
+		goto error;
+	}
+	/* let the auto-loading function load all TM stuff */
+	if (load_tm( &tmb )==-1)
+		goto error;
+
+	/*fix domain length*/
+	if (domain_str) {
+		domain.s = domain_str;
+		domain.len = strlen(domain_str);
+	} else {
+		si=get_first_socket();
+		if (si==0){
+			LM_CRIT("null listen socket list\n");
+			goto error;
+		}
+		/*do I have to add port?*/
+		i = (si->port_no_str.len && si->port_no!=5060);
+		domain.len = si->name.len + i*(si->port_no_str.len+1);
+		domain.s = (char*)pkg_malloc(domain.len);
+		if (!domain.s) {
+			LM_ERR("no free pkg memory!\n");
+			goto error;
+		}
+		p = domain.s;
+		memcpy(p,si->name.s,si->name.len);
+		p += si->name.len;
+		if (i) {
+			*p=':'; p++;
+			memcpy(p,si->port_no_str.s, si->port_no_str.len);
+			p += si->port_no_str.len;
+		}
+	}
+
+	/* creates pipes for networks */
+	for(i=0;i<nr_of_networks;i++)
+	{
+		/* create the pipe*/
+		if (pipe(net_pipe)==-1) {
+			LM_ERR("failed to create pipe!\n");
+			goto error;
+		}
+		networks[i].pipe_out = net_pipe[0];
+		net_pipes_in[i] = net_pipe[1];
+		/* sets reading from pipe to non blocking */
+		if ((foo=fcntl(net_pipe[0],F_GETFL,0))<0) {
+			LM_ERR("failed to get flag for pipe - fcntl\n");
+			goto error;
+		}
+		foo |= O_NONBLOCK;
+		if (fcntl(net_pipe[0],F_SETFL,foo)<0) {
+			LM_ERR("failed to set flag for pipe - fcntl\n");
+			goto error;
+		}
+	}
+
+	/* if report will be used, init the report queue */
+	if (sms_report_type!=NO_REPORT && !init_report_queue()) {
+		LM_ERR("cannot get shm memory!\n");
+		goto error;
+	}
+
+	/* alloc in shm for queued_msgs */
+	queued_msgs = (int*)shm_malloc(sizeof(int));
+	if (!queued_msgs) {
+		LM_ERR("cannot get shm memory!\n");
+		goto error;
+	}
+	*queued_msgs = 0;
+	
+	/* register nr_of_modems number of child processes that will
+	 * update their local configuration */
+	cfg_register_child(nr_of_modems);
+
+	return 1;
+error:
+	return -1;
+}
+
+
+
+
+int sms_child_init(int rank)
+{
+	int  i, foo;
+
+	/* only the child 1 will execute this */
+	if (rank != 1) goto done;
+
+	/* creates processes for each modem */
+	for(i=0;i<nr_of_modems;i++)
+	{
+		if ( (foo=fork())<0 ) {
+			LM_ERR("cannot fork \n");
+			goto error;
+		}
+		if (!foo) {
+			/* initialize the config framework */
+			if (cfg_child_init()) goto error;
+
+			modem_process(&(modems[i]));
+			goto done;
+		}
+	}
+
+done:
+	return 0;
+error:
+	return-1;
+}
+
+
+
+
+static int sms_init(void)
+{
+	LM_INFO("SMS - initializing\n");
+
+	if (parse_config_lines()==-1)
+		return -1;
+	if (global_init()==-1)
+		return -1;
+	return 0;
+}
+
+
+
+
+static int sms_exit(void)
+{
+	if ((!domain_str) && (domain.s))
+		pkg_free(domain.s);
+
+	if (queued_msgs)
+		shm_free(queued_msgs);
+
+	if (sms_report_type!=NO_REPORT)
+		destroy_report_queue();
+
+	return 0;
+}
+
+
+
+
+static int w_sms_send_msg(struct sip_msg *msg, char *foo, char *bar)
+{
+	return push_on_network(msg, default_net);
+}
+
+
+
+
+static int w_sms_send_msg_to_net(struct sip_msg *msg, char *net_nr, char *foo)
+{
+	return push_on_network(msg,(unsigned int)(unsigned long)net_nr);
+}
+

+ 937 - 0
modules/sms/sms_funcs.c

@@ -0,0 +1,937 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * History:
+ * --------
+ * 2003-01-23: switched from t_uac to t_uac_dlg, adapted to new way of
+ *              parsing for Content-Type (bogdan)
+ * 2003-02-28: protocolization of t_uac_dlg completed (jiri)
+ * 2003-08-05: adapted to the new parse_content_type_hdr function (bogdan)
+ * 2003-09-11: updated to new build_lump_rpl() interface (bogdan)
+ * 2003-09-11: force parsing to hdr when extracting destination user (bogdan)
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../config.h"
+#include "../../globals.h"
+#include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/parse_content.h"
+#include "../../parser/parse_from.h"
+#include "../../data_lump_rpl.h"
+#include "../../cfg/cfg_struct.h"
+#include "../../modules/tm/t_hooks.h"
+#include "../../modules/tm/uac.h"
+#include "sms_funcs.h"
+#include "sms_report.h"
+#include "libsms_modem.h"
+#include "libsms_sms.h"
+
+
+
+
+struct modem modems[MAX_MODEMS];
+struct network networks[MAX_NETWORKS];
+int net_pipes_in[MAX_NETWORKS];
+int nr_of_networks;
+int nr_of_modems;
+int max_sms_parts;
+int *queued_msgs;
+int use_contact;
+struct tm_binds tmb;
+
+
+#define ERR_NUMBER_TEXT " is an invalid number! Please resend your SMS "\
+	"using a number in +(country code)(area code)(local number) format. Thanks"\
+	" for using our service!"
+#define ERR_NUMBER_TEXT_LEN (sizeof(ERR_NUMBER_TEXT)-1)
+
+#define ERR_TRUNCATE_TEXT "We are sorry, but your message exceeded our "\
+	"maximum allowed length. The following part of the message wasn't sent"\
+	" : "
+#define ERR_TRUNCATE_TEXT_LEN (sizeof(ERR_TRUNCATE_TEXT)-1)
+
+#define ERR_MODEM_TEXT "Due to our modem temporary indisponibility, "\
+	"the following message couldn't be sent : "
+#define ERR_MODEM_TEXT_LEN (sizeof(ERR_MODEM_TEXT)-1)
+
+#define STORED_NOTE "NOTE: Your SMS received provisional confirmation"\
+	" 48 \"Delivery is not yet possible\". The SMS was store on the "\
+	"SMSCenter for further delivery. Our gateway cannot guarantee "\
+	"further information regarding your SMS delivery! Your message was: "
+#define STORED_NOTE_LEN  (sizeof(STORED_NOTE)-1)
+
+#define OK_MSG "Your SMS was finally successfully delivered!"\
+	" Your message was: "
+#define OK_MSG_LEN  (sizeof(OK_MSG)-1)
+
+#define CONTENT_TYPE_HDR     "Content-Type: text/plain"
+#define CONTENT_TYPE_HDR_LEN (sizeof(CONTENT_TYPE_HDR)-1)
+
+#define is_in_sip_addr(_p) \
+	((_p)!=' ' && (_p)!='\t' && (_p)!='(' && (_p)!='[' && (_p)!='<' \
+	&& (_p)!='>' && (_p)!=']' && (_p)!=')' && (_p)!='?' && (_p)!='!' \
+	&& (_p)!=';' && (_p)!=',' && (_p)!='\n' && (_p)!='\r' && (_p)!='=')
+
+#define no_sip_addr_begin(_p) \
+	( (_p)!=' ' && (_p)!='\t' && (_p)!='-' && (_p)!='=' && (_p)!='\r'\
+	&& (_p)!='\n' && (_p)!=';' && (_p)!=',' && (_p)!='.' && (_p)!=':')
+
+
+
+#if 0
+inline int add_contact(struct sip_msg* msg , str* user)
+{
+	struct lump_rpl *lump;
+	char *buf, *p;
+	int len;
+
+	len = 9 /*"Contact: "*/ + user->len/*user*/ + 1 /*"@"*/
+		+ domain.len/*host*/ + 6/*"<sip:>"*/ + CRLF_LEN;
+
+	buf = pkg_malloc( len );
+	if(!buf) {
+		LM_ERR("out of memory! \n");
+		return -1;
+	}
+
+	p = buf;
+	append_str( p, "Contact: " , 9);
+	append_str( p, "<sip:" , 5);
+	append_str( p, user->s, user->len);
+	*(p++) = '@';
+	append_str( p, domain.s, domain.len);
+	*(p++) = '>';
+	append_str( p, CRLF, CRLF_LEN);
+
+	lump = build_lump_rpl( buf , len , LUMP_RPL_HDR);
+	if(!lump) {
+		LM_ERR("unable to build lump_rpl! \n");
+		pkg_free( buf );
+		return -1;
+	}
+	add_lump_rpl( msg , lump );
+
+	pkg_free(buf);
+	return 1;
+}
+#endif
+
+
+
+int push_on_network(struct sip_msg *msg, int net)
+{
+	str    body;
+	struct sip_uri  uri;
+	struct sms_msg  *sms_messg;
+	struct to_body  *from;
+	char   *p;
+	int    len;
+	int    mime;
+
+	/* get the message's body
+	 * anyhow we have to call this function, so let's do it at the beginning
+	 * to force the parsing of all the headers - like this we avoid separate
+	 * calls of parse_headers function for FROM, CONTENT_LENGTH, TO hdrs  */
+	body.s = get_body( msg );
+	if (body.s==0) {
+		LM_ERR("failed to extract body from msg!\n");
+		goto error;
+	}
+
+	/* content-length (if present) must be already parsed */
+	if (!msg->content_length) {
+		LM_ERR("no Content-Length header found!\n");
+		goto error;
+	}
+	body.len = get_content_length( msg );
+
+	/* parse the content-type header */
+	if ( (mime=parse_content_type_hdr(msg))<1 ) {
+		LM_ERR("failed to parse Content-Type header\n");
+		goto error;
+	}
+
+	/* check the content-type value */
+	if ( mime!=(TYPE_TEXT<<16)+SUBTYPE_PLAIN
+	&& mime!=(TYPE_MESSAGE<<16)+SUBTYPE_CPIM ) {
+		LM_ERR("invalid content-type for a message request! type found=%d\n",
+				mime);
+		goto error;
+	}
+
+	/* we try to get the user name (phone number) first from the RURI 
+	   (in our case means from new_uri or from first_line.u.request.uri);
+	   if it's missing there (like in requests generated by MSN MESSENGER),
+	   we go for "to" header
+	*/
+	LM_DBG("string to get user from new_uri\n");
+	if ( !msg->new_uri.s||parse_uri( msg->new_uri.s,msg->new_uri.len,&uri)
+	|| !uri.user.len )
+	{
+		LM_DBG("string to get user from R_uri\n");
+		if ( parse_uri( msg->first_line.u.request.uri.s,
+		msg->first_line.u.request.uri.len ,&uri)||!uri.user.len )
+		{
+			LM_DBG("string to get user from To\n");
+			if ( (!msg->to&&((parse_headers(msg,HDR_TO_F,0)==-1) ||
+					!msg->to)) ||
+				parse_uri( get_to(msg)->uri.s, get_to(msg)->uri.len, &uri)==-1
+			|| !uri.user.len)
+			{
+				LM_ERR("unable to extract user name from RURI and To header!\n");
+				goto error;
+			}
+		}
+	}
+	/* check the uri.user format = '+(inter code)(number)' */
+	if (uri.user.len<2 || uri.user.s[0]!='+' || uri.user.s[1]<'1'
+	|| uri.user.s[1]>'9') {
+		LM_ERR("user tel number [%.*s] does not respect international format\n"
+				,uri.user.len,uri.user.s);
+		goto error;
+	}
+
+	/* parsing from header */
+	if ( parse_from_header( msg )==-1 ) {
+		LM_ERR("failed to get FROM header\n");
+		goto error;
+	}
+	from = (struct to_body*)msg->from->parsed;
+
+#if 0
+	/* adds contact header into reply */
+	if (add_contact(msg,&(uri.user))==-1) {
+		LM_ERR("can't build contact for reply\n");
+		goto error;
+	}
+#endif
+
+	/*-------------BUILD AND FILL THE SMS_MSG STRUCTURE --------------------*/
+	/* computes the amount of memory needed */
+	len = SMS_HDR_BF_ADDR_LEN + from->uri.len
+		+ SMS_HDR_AF_ADDR_LEN + body.len + SMS_FOOTER_LEN /*text to send*/
+		+ from->uri.len /* from */
+		+ uri.user.len-1 /* to user (without '+') */
+		+ sizeof(struct sms_msg) ; /* the sms_msg structure */
+	/* allocs a new sms_msg structure in shared memory */
+	sms_messg = (struct sms_msg*)shm_malloc(len);
+	if (!sms_messg) {
+		LM_ERR("failed to get shm memory!\n");
+		goto error;
+	}
+	p = (char*)sms_messg + sizeof(struct sms_msg);
+
+	/* copy "from" into sms struct */
+	sms_messg->from.len = from->uri.len;
+	sms_messg->from.s = p;
+	append_str(p,from->uri.s,from->uri.len);
+
+	/* copy "to.user" - we have to strip out the '+' */
+	sms_messg->to.len = uri.user.len-1;
+	sms_messg->to.s = p;
+	append_str(p,uri.user.s+1,sms_messg->to.len);
+
+	/* copy (and composing) sms body */
+	sms_messg->text.len = SMS_HDR_BF_ADDR_LEN + sms_messg->from.len
+		+ SMS_HDR_AF_ADDR_LEN + body.len+SMS_FOOTER_LEN;
+	sms_messg->text.s = p;
+	append_str(p, SMS_HDR_BF_ADDR, SMS_HDR_BF_ADDR_LEN);
+	append_str(p, sms_messg->from.s, sms_messg->from.len);
+	append_str(p, SMS_HDR_AF_ADDR, SMS_HDR_AF_ADDR_LEN);
+	append_str(p, body.s, body.len);
+	append_str(p, SMS_FOOTER, SMS_FOOTER_LEN);
+
+	if (*queued_msgs>MAX_QUEUED_MESSAGES)
+		goto error;
+	(*queued_msgs)++;
+
+	if (write(net_pipes_in[net], &sms_messg, sizeof(sms_messg))!=
+	sizeof(sms_messg) )
+	{
+		LM_ERR("error when writing for net %d to pipe [%d] : %s\n",
+				net,net_pipes_in[net],strerror(errno) );
+		shm_free(sms_messg);
+		(*queued_msgs)--;
+		goto error;
+	}
+
+	return 1;
+ error:
+	return -1;
+}
+
+
+
+
+
+int send_sip_msg_request(str *to, str *from_user, str *body)
+{
+	str msg_type = STR_STATIC_INIT("MESSAGE");
+	str from;
+	str hdrs;
+	int foo;
+	char *p;
+	uac_req_t uac_r;
+
+	from.s = hdrs.s = 0;
+	from.len = hdrs.len = 0;
+
+	/* From header */
+	from.len = 6 /*"<sip:+"*/ +  from_user->len/*user*/ + 1/*"@"*/
+		+ domain.len /*host*/ + 1 /*">"*/ ;
+	from.s = (char*)pkg_malloc(from.len);
+	if (!from.s)
+		goto error;
+	p=from.s;
+	append_str(p,"<sip:+",6);
+	append_str(p,from_user->s,from_user->len);
+	*(p++)='@';
+	append_str(p,domain.s,domain.len);
+	*(p++)='>';
+
+	/* hdrs = Contact header + Content-type */
+	/* length */
+	hdrs.len = CONTENT_TYPE_HDR_LEN + CRLF_LEN;
+	if (use_contact)
+		hdrs.len += 15 /*"Contact: <sip:+"*/ + from_user->len/*user*/ +
+			1/*"@"*/ + domain.len/*host*/ + 1 /*">"*/ + CRLF_LEN;
+	hdrs.s = (char*)pkg_malloc(hdrs.len);
+	if (!hdrs.s)
+		goto error;
+	p=hdrs.s;
+	append_str(p,CONTENT_TYPE_HDR,CONTENT_TYPE_HDR_LEN);
+	append_str(p,CRLF,CRLF_LEN);
+	if (use_contact) {
+		append_str(p,"Contact: <sip:+",15);
+		append_str(p,from_user->s,from_user->len);
+		*(p++)='@';
+		append_str(p,domain.s,domain.len);
+		append_str(p,">"CRLF,1+CRLF_LEN);
+	}
+
+	/* sending the request */
+	set_uac_req(&uac_r,
+			&msg_type,	/* request type */
+			&hdrs,		/* Additional headers including CRLF */
+			body,		/* Message body */
+			0,		/* dialog structure */
+			0,		/* callback flags */
+			0,		/* Callback function */
+			0		/* Callback parameter */
+		);
+	
+	foo = tmb.t_request(&uac_r,
+			0,	/* Request-URI */
+			to,	/* To */
+			&from,	/* From */
+			0	/* next hop */
+		);
+
+	if (from.s) pkg_free(from.s);
+	if (hdrs.s) pkg_free(hdrs.s);
+	return foo;
+error:
+	LM_ERR("no free pkg memory!\n");
+	if (from.s) pkg_free(from.s);
+	if (hdrs.s) pkg_free(hdrs.s);
+	return -1;
+}
+
+
+
+
+inline int send_error(struct sms_msg *sms_messg, char *msg1_s, int msg1_len,
+													char *msg2_s, int msg2_len)
+{
+	str  body;
+	char *p;
+	int  foo;
+
+	/* body */
+	body.len = msg1_len + msg2_len;
+	body.s = (char*)pkg_malloc(body.len);
+	if (!body.s)
+		goto error;
+	p=body.s;
+	append_str(p, msg1_s, msg1_len );
+	append_str(p, msg2_s, msg2_len);
+
+	/* sending */
+	foo = send_sip_msg_request( &(sms_messg->from), &(sms_messg->to), &body);
+	pkg_free( body.s );
+	return foo;
+error:
+	LM_ERR("no free pkg memory!\n");
+	return -1;
+
+}
+
+
+
+inline unsigned int split_text(str *text, unsigned char *lens,int nice)
+{
+	int  nr_chunks;
+	int  k,k1,len;
+	char c;
+
+	nr_chunks = 0;
+	len = 0;
+
+	do{
+		k = MAX_SMS_LENGTH-(nice&&nr_chunks?SMS_EDGE_PART_LEN:0);
+		if ( len+k<text->len ) {
+			/* is not the last piece :-( */
+			if (nice && !nr_chunks) k -= SMS_EDGE_PART_LEN;
+			if (text->len-len-k<=SMS_FOOTER_LEN+4)
+				k = (text->len-len)/2;
+			/* ->looks for a point to split */
+			k1 = k;
+			while( k>0 && (c=text->s[len+k-1])!='.' && c!=' ' && c!=';'
+			&& c!='\r' && c!='\n' && c!='-' && c!='!' && c!='?' && c!='+'
+			&& c!='=' && c!='\t' && c!='\'')
+				k--;
+			if (k<k1/2)
+				/* wast of space !!!!*/
+				k=k1;
+			len += k;
+			lens[nr_chunks] = k;
+		}else {
+			/*last chunk*/
+			lens[nr_chunks] = text->len-len;
+			len = text->len;
+		}
+		nr_chunks++;
+	}while (len<text->len);
+
+	return nr_chunks;
+}
+
+
+
+
+int send_as_sms(struct sms_msg *sms_messg, struct modem *mdm)
+{
+	static char   buf[MAX_SMS_LENGTH];
+	unsigned int  buf_len;
+	unsigned char len_array_1[256], len_array_2[256], *len_array;
+	unsigned int  nr_chunks_1,  nr_chunks_2, nr_chunks;
+	unsigned int  use_nice;
+	str  text;
+	char *p, *q;
+	int  ret_code;
+	int  i;
+
+	text.s   = sms_messg->text.s;
+	text.len = sms_messg->text.len;
+
+	nr_chunks_1 = split_text( &text, len_array_1, 0);
+	nr_chunks_2 = split_text( &text, len_array_2, 1);
+	if (nr_chunks_1==nr_chunks_2) {
+		len_array = len_array_2;
+		nr_chunks = nr_chunks_2;
+		use_nice = 1;
+	} else {
+		len_array = len_array_1;
+		nr_chunks = nr_chunks_1;
+		use_nice = 0;
+	}
+
+	sms_messg->ref = 1;
+	for(i=0,p=text.s ; i<nr_chunks&&i<max_sms_parts ; p+=len_array[i++]) {
+		if (use_nice) {
+			q = buf;
+			if (nr_chunks>1 && i)  {
+				append_str(q,SMS_EDGE_PART,SMS_EDGE_PART_LEN);
+				*(q-2)=nr_chunks+'0';
+				*(q-4)=i+1+'0';
+			}
+			append_str(q,p,len_array[i]);
+			if (nr_chunks>1 && !i)  {
+				append_str(q,SMS_EDGE_PART,SMS_EDGE_PART_LEN);
+				*(q-2)=nr_chunks+'0';
+				*(q-4)=i+1+'0';
+			}
+			buf_len = q-buf;
+		} else {
+			q = buf;
+			append_str(q,p,len_array[i]);
+			buf_len = len_array[i];
+		}
+		if (i+1==max_sms_parts && i+1<nr_chunks) {
+			/* simply override the end of the last allowed part */
+			buf_len += SMS_TRUNCATED_LEN+SMS_FOOTER_LEN;
+			if (buf_len>MAX_SMS_LENGTH) buf_len = MAX_SMS_LENGTH;
+			q = buf + (buf_len-SMS_TRUNCATED_LEN-SMS_FOOTER_LEN);
+			append_str(q,SMS_TRUNCATED,SMS_TRUNCATED_LEN);
+			append_str(q,SMS_FOOTER,SMS_FOOTER_LEN);
+			p += buf_len-SMS_TRUNCATED_LEN-SMS_FOOTER_LEN-SMS_EDGE_PART_LEN;
+			send_error(sms_messg, ERR_TRUNCATE_TEXT, ERR_TRUNCATE_TEXT_LEN,
+				p, text.len-(p-text.s)-SMS_FOOTER_LEN);
+		}
+		LM_DBG("---%d--<%d><%d>--\n|%.*s|\n", i, len_array[i], buf_len,
+										(int)buf_len, buf);
+		sms_messg->text.s   = buf;
+		sms_messg->text.len = buf_len;
+		if ( (ret_code=putsms(sms_messg,mdm))<0)
+			goto error;
+		if (sms_report_type!=NO_REPORT)
+			add_sms_into_report_queue(ret_code,sms_messg,
+				p-use_nice*(nr_chunks>1)*SMS_EDGE_PART_LEN,len_array[i]);
+	}
+
+	sms_messg->ref--;
+	/* put back the pointer to the beginning of the message*/
+	sms_messg->text.s = text.s;
+	sms_messg->text.len = text.len;
+	/* remove the sms if nobody points to it */
+	if (!sms_messg->ref){
+		shm_free(sms_messg);
+	}
+	return 1;
+error:
+	if (ret_code==-1)
+		/* bad number */
+		send_error(sms_messg, sms_messg->to.s, sms_messg->to.len,
+			ERR_NUMBER_TEXT, ERR_NUMBER_TEXT_LEN);
+	else if (ret_code==-2)
+		/* bad modem */
+		send_error(sms_messg, ERR_MODEM_TEXT, ERR_MODEM_TEXT_LEN,
+			text.s+SMS_HDR_BF_ADDR_LEN+sms_messg->from.len+SMS_HDR_AF_ADDR_LEN,
+			text.len-SMS_FOOTER_LEN-SMS_HDR_BF_ADDR_LEN-sms_messg->from.len-
+			SMS_HDR_AF_ADDR_LEN );
+
+	if (!(--(sms_messg->ref)))
+		shm_free(sms_messg);
+	return -1;
+}
+
+
+
+
+int send_sms_as_sip( struct incame_sms *sms )
+{
+	str  sip_addr;
+	str  sip_body;
+	str  sip_from;
+	int  is_pattern;
+	int  in_address;
+	int  k;
+	char *p;
+
+	/* first we have to parse the body to try to get out
+	   the sip destination address;
+	   The sms body can to be in the following two formats:
+	   1. The entire or part of the sent header still exists - we will
+	      pars it and consider the start of the sip message the first
+	      character that doesn't match the header!
+	   2. The sms body is totally different of the send sms -> search for a
+	      sip address inside; everything before it is ignored, only the
+	      part following the address being send as sip
+	*/
+	in_address = 0;
+	sip_addr.len = 0;
+	sip_body.len = 0;
+	p = sms->ascii;
+
+	/* is our logo (or a part of it) still there? */
+	if (*p==SMS_HDR_BF_ADDR[0]) {
+		is_pattern = 1;
+		/* try to match SMS_HDR_BF_ADDR */
+		k=0;
+		while( is_pattern && p<sms->ascii+sms->userdatalength
+		&& k<SMS_HDR_BF_ADDR_LEN)
+			if (*(p++)!=SMS_HDR_BF_ADDR[k++])
+				is_pattern = 0;
+		if (!is_pattern) {
+			/* first header part is broken -> let's give it a chance
+			   and parse for the first word delimiter */
+			while(p<sms->ascii+sms->userdatalength && no_sip_addr_begin(*p))
+				p++;
+			p++;
+			if (p+9>=sms->ascii+sms->userdatalength) {
+				LM_ERR("unable to find sip_address start in sms body [%s]!\n",
+						sms->ascii);
+				goto error;
+			}
+			
+		}
+		/* lets get the address */
+		if (p[0]!='s' || p[1]!='i' || p[2]!='p' || p[3]!=':') {
+			LM_ERR("wrong sip address format in sms body [%s]!\n",sms->ascii);
+			goto error;
+		}
+		sip_addr.s = p;
+		/* goes to the end of the address */
+		while(p<sms->ascii+sms->userdatalength && is_in_sip_addr(*p) )
+			p++;
+		if (p>=sms->ascii+sms->userdatalength) {
+			LM_ERR("failed to find sip address end in sms body [%s]!\n",
+					sms->ascii);
+		}
+		sip_addr.len = p-sip_addr.s;
+		LM_DBG("sip address found [%.*s]\n",
+			sip_addr.len,sip_addr.s);
+		/* try to match SMS_HDR_AF_ADDR */
+		k=0;
+		while( is_pattern && p<sms->ascii+sms->userdatalength
+		&& k<SMS_HDR_AF_ADDR_LEN)
+			if (*(p++)!=SMS_HDR_AF_ADDR[k++])
+				is_pattern = 0;
+	} else {
+		/* no trace of the pattern sent along with the orig sms*/
+		do {
+			if ((p[0]=='s'||p[0]=='S') && (p[1]=='i'||p[1]=='I')
+			&& (p[2]=='p'||p[2]=='P') && p[3]==':') {
+				/* we got the address beginning */
+				sip_addr.s = p;
+				/* goes to the end of the address */
+				while(p<sms->ascii+sms->userdatalength && is_in_sip_addr(*p) )
+					p++;
+				if (p==sms->ascii+sms->userdatalength) {
+					LM_ERR("failed to find sip address end in sms body [%s]!\n",
+							sms->ascii);
+					goto error;
+				}
+				sip_addr.len = p-sip_addr.s;
+			} else {
+				/* parse to the next word */
+				/*LM_DBG("*** Skipping word len=%d\n",sms->userdatalength);*/
+				while(p<sms->ascii+sms->userdatalength&&no_sip_addr_begin(*p)){
+					p++;
+				}
+				p++;
+				if (p+9>=sms->ascii+sms->userdatalength) {
+				LM_ERR("unable to find sip address start in sms body [%s]!\n",
+						sms->ascii);
+					goto error;
+				}
+				/*LM_DBG("*** Done\n");*/
+			}
+		}while (!sip_addr.len);
+	}
+
+	/* the rest of the sms (if any ;-)) is the body! */
+	sip_body.s = p;
+	sip_body.len = sms->ascii + sms->userdatalength - p;
+	/* let's trim out all \n an \r from begining */
+	while ( sip_body.len && sip_body.s
+	&& (sip_body.s[0]=='\n' || sip_body.s[0]=='\r') ) {
+		sip_body.s++;
+		sip_body.len--;
+	}
+	if (sip_body.len==0) {
+		LM_WARN("empty body for sms [%s]", sms->ascii);
+		goto error;
+	}
+	LM_DBG("extracted body is: [%.*s]\n",sip_body.len, sip_body.s);
+
+	/* finally, let's send it as sip message */
+	sip_from.s = sms->sender;
+	sip_from.len = strlen(sms->sender);
+	/* patch the body with date and time */
+	if (sms->userdatalength + CRLF_LEN + 1 /*'('*/ + DATE_LEN
+	+ 1 /*','*/ + TIME_LEN + 1 /*')'*/< sizeof(sms->ascii)) {
+		p = sip_body.s + sip_body.len;
+		append_str( p, CRLF, CRLF_LEN);
+		*(p++) = '(';
+		append_str( p, sms->date, DATE_LEN);
+		*(p++) = ',';
+		append_str( p, sms->time, TIME_LEN);
+		*(p++) = ')';
+		sip_body.len += CRLF_LEN + DATE_LEN + TIME_LEN + 3;
+	}
+	return send_sip_msg_request(&sip_addr, &sip_from, &sip_body);
+
+error:
+	return -1;
+}
+
+
+
+
+int send_sms_as_sip_scan_no(struct incame_sms *sms, char *to) 
+{	
+	str  sip_from;
+	str  sip_to;
+	str  sip_body;
+	char *p;
+
+	/* charge from header */
+	sip_from.s = sms->sender;
+	sip_from.len = strlen(sms->sender);
+
+	/* charge to header */
+	sip_to.len = strlen(to);
+	sip_to.s   = to;
+
+	/* charge body */
+	sip_body.len = sms->ascii + sms->userdatalength - sms->ascii;
+	sip_body.s = sms->ascii;	
+
+	/* let's trim out all \n an \r from begining */
+	while (sip_body.len && sip_body.s && 
+	      (sip_body.s[0] == '\n' || sip_body.s[0] == '\r')) {
+	       sip_body.s++;
+	       sip_body.len--;
+	}
+
+	if (sip_body.len == 0) {
+		LM_WARN("SMS empty body for sms [%s]\n",sms->ascii);
+		goto error;
+	}
+
+	/* patch the body with date and time */
+	if (sms->userdatalength + CRLF_LEN + 1 /*'('*/ + DATE_LEN
+	+ 1 /*','*/ + TIME_LEN + 1 /*')'*/< sizeof(sms->ascii)) {
+		p = sip_body.s + sip_body.len;
+		append_str( p, CRLF, CRLF_LEN);
+		*(p++) = '(';
+		append_str( p, sms->date, DATE_LEN);
+		*(p++) = ',';
+		append_str( p, sms->time, TIME_LEN);
+		*(p++) = ')';
+		sip_body.len += CRLF_LEN + DATE_LEN + TIME_LEN + 3;
+	}
+
+	LM_DBG("SMS from: [%.*s], to: [%.*s], body: [%.*s]\n",
+		sip_from.len, sip_from.s, sip_to.len, sip_to.s, sip_body.len, sip_body.s);
+
+	/* finally, let's send it as sip message */
+	return send_sip_msg_request(&sip_to, &sip_from, &sip_body);
+
+error:
+	return -1;
+}
+
+
+
+
+int _send_sms_as_sip(struct incame_sms *sms, struct modem *mdm) 
+{
+	switch(mdm->scan) 
+	{
+		case SMS_BODY_SCAN:
+			return send_sms_as_sip(sms);	
+	
+		case SMS_BODY_SCAN_MIX:
+			if(send_sms_as_sip(sms) == 1)
+				return 1;
+
+		case SMS_BODY_SCAN_NO:
+			return send_sms_as_sip_scan_no(sms, mdm->to);
+
+		default:
+			break;
+	}
+	
+	/* CASE IMPOSIBLE!!!!, scan assume default value SMS_BODY_SCAN */
+	LM_ERR("SMS bad config param scan: %d for modem: %s\n",
+		mdm->scan, mdm->name);
+
+	return -1;
+}
+
+
+
+int check_sms_report( struct incame_sms *sms )
+{
+	struct sms_msg *sms_messg;
+	str *s1, *s2;
+	int old;
+	int res;
+
+	LM_DBG("Report for sms number %d.\n",sms->sms_id);
+	res=relay_report_to_queue( sms->sms_id, sms->sender, sms->ascii[0], &old);
+	if (res==3) { /* error */
+		/* the sms was confirmed with an error code -> we have to send a
+		message to the SIP user */
+		s1 = get_error_str(sms->ascii[0]);
+		s2 = get_text_from_report_queue(sms->sms_id);
+		sms_messg = get_sms_from_report_queue(sms->sms_id);
+		send_error( sms_messg, s1->s, s1->len, s2->s, s2->len);
+	} else if (res==1 && sms->ascii[0]==48 && old!=48) { /* provisional 48 */
+		/* the sms was provisional confirmed with a 48 code -> was stored
+		by SMSC -> no further real-time tracing possible */
+		s2 = get_text_from_report_queue(sms->sms_id);
+		sms_messg = get_sms_from_report_queue(sms->sms_id);
+		send_error( sms_messg, STORED_NOTE, STORED_NOTE_LEN, s2->s, s2->len);
+	} else if (res==2 && old==48) {
+		/* we received OK for a SMS that had received prev. an 48 code.
+		The note that we send for 48 has to be now clarify */
+		s2 = get_text_from_report_queue(sms->sms_id);
+		sms_messg = get_sms_from_report_queue(sms->sms_id);
+		send_error( sms_messg, OK_MSG, OK_MSG_LEN, s2->s, s2->len);
+	}
+	if (res>1) /* final response */
+		remove_sms_from_report_queue(sms->sms_id);
+
+	return 1;
+}
+
+
+
+
+int check_cds_report( struct modem *mdm, char *cds, int cds_len)
+{
+	struct incame_sms sms;
+
+	if (cds2sms( &sms, mdm, cds, cds_len)==-1)
+		return -1;
+	check_sms_report( &sms );
+	return 1;
+}
+
+
+
+
+void modem_process(struct modem *mdm)
+{
+	struct sms_msg    *sms_messg;
+	struct incame_sms sms;
+	struct network *net;
+	int i,k,len;
+	int counter;
+	int dont_wait;
+	int empty_pipe;
+	int cpms_unsuported;
+	int max_mem=0, used_mem=0;
+
+	sms_messg = 0;
+	cpms_unsuported = 0;
+
+	/* let's open/init the modem */
+	LM_DBG("opening modem\n");
+	if (openmodem(mdm)==-1) {
+		LM_ERR("failed to open modem %s! %s \n",
+			mdm->name,strerror(errno));
+		return;
+	}
+
+	setmodemparams(mdm);
+	initmodem(mdm,check_cds_report);
+
+	if ( (max_mem=check_memory(mdm,MAX_MEM))==-1 ) {
+		LM_WARN("CPMS command unsuported! using default values (10,10)\n");
+		used_mem = max_mem = 10;
+		cpms_unsuported = 1;
+	}
+	LM_DBG("modem maximum memory is %d\n",max_mem);
+
+	set_gettime_function();
+
+	while(1)
+	{
+		/* update the local config */
+		cfg_update();
+
+		dont_wait = 0;
+		for (i=0;i<nr_of_networks && mdm->net_list[i]!=-1;i++)
+		{
+			counter = 0;
+			empty_pipe = 0;
+			net = &(networks[mdm->net_list[i]]);
+			/*getting msgs from pipe*/
+			while( counter<net->max_sms_per_call && !empty_pipe )
+			{
+				/* let's read a sms from pipe */
+				len = read(net->pipe_out, &sms_messg,
+					sizeof(sms_messg));
+				if (len!=sizeof(sms_messg)) {
+					if (len>=0)
+						LM_ERR("truncated message read from pipe!"
+								" -> discarded\n");
+					else if (errno==EAGAIN)
+						empty_pipe = 1;
+					else
+						LM_ERR("pipe reading failed: %s\n",strerror(errno));
+					sleep(1);
+					counter++;
+					continue;
+				}
+				(*queued_msgs)--;
+
+				/* compute and send the sms */
+				LM_DBG("%s processing sms for net %s:"
+					" \n\tTo:[%.*s]\n\tBody=<%d>[%.*s]\n",
+					mdm->device, net->name,
+					sms_messg->to.len,sms_messg->to.s,
+					sms_messg->text.len,sms_messg->text.len,sms_messg->text.s);
+				send_as_sms( sms_messg , mdm);
+
+				counter++;
+				/* if I reached the limit -> set not to wait */
+				if (counter==net->max_sms_per_call)
+					dont_wait = 1;
+			}/*while*/
+		}/*for*/
+
+		/* let's see if we have incoming sms */
+		if ( !cpms_unsuported )
+			if ((used_mem = check_memory(mdm,USED_MEM))==-1) {
+				LM_ERR("CPMS command failed! cannot get used mem -> using 10\n");
+				used_mem = 10;
+			}
+
+		/* if any, let's get them */
+		if (used_mem)
+			LM_DBG("%d new SMS on modem\n",used_mem);
+			for(i=1,k=1;k<=used_mem && i<=max_mem;i++) {
+				if (getsms(&sms,mdm,i)!=-1) {
+					k++;
+					LM_DBG("SMS Get from location %d\n",i);
+					/*for test ;-) ->  to be remove*/
+					LM_DBG("SMS RECEIVED:\n\rFrom: %s %s\n\r%.*s %.*s"
+						"\n\r\"%.*s\"\n\r",sms.sender,sms.name,
+						DATE_LEN,sms.date,TIME_LEN,sms.time,
+						sms.userdatalength,sms.ascii);
+					if (!sms.is_statusreport)
+						_send_sms_as_sip(&sms, mdm);
+					else 
+						check_sms_report(&sms);
+				}
+			}
+
+		/* if reports are used, checks for expired records in report queue */
+		if (sms_report_type!=NO_REPORT)
+			check_timeout_in_report_queue();
+
+		/* sleep -> if it's needed */
+		if (!dont_wait) {
+				sleep(mdm->looping_interval);
+		}
+	}/*while*/
+}
+
+
+

+ 128 - 0
modules/sms/sms_funcs.h

@@ -0,0 +1,128 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _SMS_FUNCS_H
+#define  _SMS_FUNCS_H
+
+#include "../../parser/msg_parser.h"
+#include "../../str.h"
+#include <termios.h>
+#include "../../modules/tm/tm_load.h"
+
+
+#define MAX_MODEMS    5       /* max number of modems */
+#define MAX_NETWORKS  5       /* max number of networks */
+
+#define MAX_CHAR_BUF 128        /* max length of character buffer */
+#define MAX_CONFIG_PARAM 1024   /* max length of a config parameter */
+#define MAX_SMS_LENGTH   160
+#define MAX_SMS_PARTS    4      /* maximum number of parts for a sms */
+#define MAX_QUEUED_MESSAGES 100 /* maximum nr of messages waiting to send */
+
+#define SMS_HDR_BF_ADDR      "From "
+#define SMS_HDR_BF_ADDR_LEN  (sizeof(SMS_HDR_BF_ADDR)-1)
+#define SMS_HDR_AF_ADDR      " (if you reply DO NOT remove it)\r\n\r\n"
+#define SMS_HDR_AF_ADDR_LEN  (sizeof(SMS_HDR_AF_ADDR)-1)
+#define SMS_FOOTER           "\r\n\r\n[IPTEL.ORG]"
+#define SMS_FOOTER_LEN       (sizeof(SMS_FOOTER)-1)
+#define SMS_EDGE_PART        "( / )"
+#define SMS_EDGE_PART_LEN    (sizeof(SMS_EDGE_PART)-1)
+#define SMS_TRUNCATED        "(truncated)"
+#define SMS_TRUNCATED_LEN    (sizeof(SMS_TRUNCATED)-1)
+
+#define TIME_LEN   8          /* xx-xx-xx */
+#define DATE_LEN   TIME_LEN
+
+#define NO_REPORT  0
+#define SMS_REPORT 1
+#define CDS_REPORT 2
+
+#define SMS_BODY_SCAN_NO	0
+#define SMS_BODY_SCAN		1
+#define SMS_BODY_SCAN_MIX	2
+
+struct network {
+	char name[MAX_CHAR_BUF+1];
+	int  max_sms_per_call;
+	int  pipe_out;
+};
+
+struct modem {
+	char name[MAX_CHAR_BUF+1];
+	char device[MAX_CHAR_BUF+1];
+	char pin[MAX_CHAR_BUF+1];
+	char smsc[MAX_CHAR_BUF+1];
+	int  net_list[MAX_NETWORKS];
+	struct termios oldtio;
+	int  mode;
+	int  retry;
+	int  looping_interval;
+	int  fd;
+	int  baudrate;
+	int  scan;
+	char to[MAX_CHAR_BUF+1];
+};
+
+struct sms_msg {
+	str  text;
+	str  to;
+	str  from;
+	int  ref;
+};
+
+struct incame_sms {
+	char sender[31];
+	char name[64];
+	char date[DATE_LEN];
+	char time[TIME_LEN];
+	char ascii[500];
+	char smsc[31];
+	int  userdatalength;
+	int  is_statusreport;
+	int  sms_id;
+};
+
+
+extern struct modem modems[MAX_MODEMS];
+extern struct network networks[MAX_NETWORKS];
+extern int    net_pipes_in[MAX_NETWORKS];
+extern int    nr_of_networks;
+extern int    nr_of_modems;
+extern int    max_sms_parts;
+extern str    domain;
+extern int    *queued_msgs;
+extern int    use_contact;
+extern int    sms_report_type;
+extern struct tm_binds tmb;
+
+void modem_process(struct modem*);
+int  push_on_network(struct sip_msg*, int);
+
+
+#endif
+

+ 382 - 0
modules/sms/sms_report.c

@@ -0,0 +1,382 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <time.h>
+#include <unistd.h>
+#include "../../mem/shm_mem.h"
+#include "../../timer.h"
+#include "sms_report.h"
+#include "sms_funcs.h"
+
+#define REPORT_TIMEOUT     1*60*60   // one hour
+#define START_ERR_MSG      "Your message (or part of it) couldn't be "\
+                           "delivered. The SMS Center said: "
+#define START_ERR_MSG_LEN  (strlen(START_ERR_MSG))
+#define END_ERR_MSG        ". The message was: "
+#define END_ERR_MSG_LEN    (strlen( END_ERR_MSG))
+
+struct report_cell {
+	int             status;
+	time_t          timeout;
+	char            *text;
+	unsigned int    text_len;
+	struct sms_msg  *sms;
+};
+
+struct report_cell *report_queue=0;
+typedef time_t (get_time_func)(void);
+get_time_func  *get_time;
+
+
+
+
+/*-------------- Function to set time - from ser or system ------------------*/
+/*
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* gets the time from ser */
+static time_t get_time_ser(void)
+{
+	return  get_ticks();
+}
+/* gets the time from system */
+static time_t get_time_sys(void)
+{
+	return time(0);
+}
+/* detects if the ser time function get_ticks works, and depending of that
+   sets the correct time function to be used */
+void set_gettime_function(void)
+{
+	unsigned int t1,t2;
+
+	t1 = get_ticks();
+	sleep(2);
+	t2 = get_ticks();
+	if (!t1 && !t2) {
+		get_time = get_time_sys;
+		LM_INFO("using system time func.\n");
+	} else {
+		get_time = get_time_ser;
+		LM_INFO("using ser time func.\n");
+	}
+}
+
+
+
+
+inline void free_report_cell(struct report_cell *cell)
+{
+	if (!cell)
+		return;
+	if (cell->sms && !(--(cell->sms->ref)))
+		shm_free(cell->sms);
+	cell->sms = 0;
+	cell->status = 0;
+	cell->timeout = 0;
+	cell->text = 0;
+	cell->text_len = 0;
+}
+
+
+
+
+int init_report_queue(void)
+{
+	report_queue = (struct report_cell*)
+		shm_malloc(NR_CELLS*sizeof(struct report_cell));
+	if (!report_queue) {
+		LM_ERR("no more free pkg_mem!\n");
+		return -1;
+	}
+	memset( report_queue , 0 , NR_CELLS*sizeof(struct report_cell) );
+	return 1;
+}
+
+
+
+
+void destroy_report_queue(void)
+{
+	int i;
+
+	if (report_queue){
+		for(i=0;i<NR_CELLS;i++)
+			if (report_queue[i].sms)
+				free_report_cell(&(report_queue[i]));
+		shm_free(report_queue);
+		report_queue = 0;
+	}
+}
+
+
+
+
+void add_sms_into_report_queue(int id, struct sms_msg *sms, char *p, int l)
+{
+	if (report_queue[id].sms){
+		LM_INFO("old message still waiting for report at location %d"
+				" -> discarding\n",id);
+		free_report_cell(&(report_queue[id]));
+	}
+
+	sms->ref++;
+	report_queue[id].status = -1;
+	report_queue[id].sms = sms;
+	report_queue[id].text = p;
+	report_queue[id].text_len = l;
+	report_queue[id].timeout = get_time() + REPORT_TIMEOUT;
+}
+
+
+
+
+int  relay_report_to_queue(int id, char *phone, int status, int *old_status)
+{
+	struct report_cell *cell;
+	int    ret_code;
+
+	cell = &(report_queue[id]);
+	ret_code = 0;
+
+	/* first, do we have a match into the sms queue? */
+	if (!cell->sms) {
+		LM_INFO("report received for cell %d,"
+				" but the sms was already trashed from queue!\n",id);
+		goto done;
+	}
+	if (strlen(phone)!=cell->sms->to.len ||
+	strncmp(phone,cell->sms->to.s,cell->sms->to.len)) {
+		LM_INFO("report received for cell %d, but the phone nr is different"
+				"->old report->ignored\n",id);
+		goto done;
+	}
+
+	if (old_status)
+		*old_status = cell->status;
+	cell->status = status;
+	if (status>=0 && status<32) {
+		LM_DBG("sms %d confirmed with code %d\n", id, status);
+		ret_code = 2; /* success */
+	} else if (status<64) {
+		/* provisional report */
+		LM_DBG("sms %d received prov. report with"
+			" code %d\n",id, status);
+		ret_code = 1; /* provisional */
+	} else {
+		LM_DBG("sms %d received error report with code %d\n",id, status);
+		ret_code = 3; /* error */
+	}
+
+done:
+	return ret_code;
+}
+
+
+
+
+void check_timeout_in_report_queue(void)
+{
+	int i;
+	time_t current_time;
+
+	current_time = get_time();
+	for(i=0;i<NR_CELLS;i++)
+		if (report_queue[i].sms && report_queue[i].timeout<=current_time) {
+			LM_INFO("[%lu,%lu] record %d is discarded (timeout), having status"
+				" %d\n", (long unsigned int)current_time,
+				(long unsigned int)report_queue[i].timeout,
+				i,report_queue[i].status);
+			free_report_cell(&(report_queue[i]));
+		}
+}
+
+
+
+
+void remove_sms_from_report_queue(int id)
+{
+	free_report_cell(&(report_queue[id]));
+}
+
+
+
+
+str* get_text_from_report_queue(int id)
+{
+	static str text;
+
+	text.s = report_queue[id].text;
+	text.len = report_queue[id].text_len;
+	return &text;
+}
+
+
+
+
+struct sms_msg* get_sms_from_report_queue(int id)
+{
+	return ( report_queue[id].sms);
+}
+
+
+
+
+str* get_error_str(int status)
+{
+	static str err_str;
+
+	switch (status) {
+		/*
+		case 0: strcat(sms->ascii,"Ok,short message received by the SME");
+			break;
+		case 1: strcat(sms->ascii,"Ok,short message forwarded by the SC to"
+			" the SME but the SC is unable to confirm delivery");
+			break;
+		case 2: strcat(sms->ascii,"Ok,short message replaced by the SC");
+			break;
+		case 32: strcat(sms->ascii,"Still trying,congestion");
+			break;
+		case 33: strcat(sms->ascii,"Still trying,SME busy");
+			break;
+		case 34: strcat(sms->ascii,"Still trying,no response from SME");
+			break;
+		case 35: strcat(sms->ascii,"Still trying,service rejected");
+			break;
+		case 36: strcat(sms->ascii,"Still trying,quality of service not"
+			" available");
+			break;
+		case 37: strcat(sms->ascii,"Still trying,error in SME");
+			break;
+		case 48:
+			err_str.s =
+			START_ERR_MSG"Delivery is not possible"END_ERR_MSG;
+			err_str.len = 24 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		*/
+		case 64:
+			err_str.s =
+			START_ERR_MSG"Error, remote procedure error"END_ERR_MSG;
+			err_str.len = 29 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 65: 
+			err_str.s =
+			START_ERR_MSG"Error,incompatible destination"END_ERR_MSG;
+			err_str.len = 30 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 66: 
+			err_str.s =
+			START_ERR_MSG"Error,connection rejected by SME"END_ERR_MSG;
+			err_str.len = 32 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 67:
+			err_str.s = START_ERR_MSG"Error,not obtainable"END_ERR_MSG;
+			err_str.len = 20 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 68:
+			err_str.s =
+			START_ERR_MSG"Error,quality of service not available"END_ERR_MSG;
+			err_str.len = 38 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 69:
+			err_str.s =
+			START_ERR_MSG"Error,no interworking available"END_ERR_MSG;
+			err_str.len = 31 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 70:
+			err_str.s =
+			START_ERR_MSG"Error,SM validity period expired"END_ERR_MSG;
+			err_str.len = 32 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 71:
+			err_str.s =
+			START_ERR_MSG"Error,SM deleted by originating SME"END_ERR_MSG;
+			err_str.len = 35 + START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 72:
+			err_str.s =
+			START_ERR_MSG"Error,SM deleted by SC administration"END_ERR_MSG;
+			err_str.len = 37+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 73:
+			err_str.s = START_ERR_MSG"Error,SM does not exist"END_ERR_MSG;
+			err_str.len = 29+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 96:
+			err_str.s = START_ERR_MSG"Error,congestion"END_ERR_MSG;
+			err_str.len = 23+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 97:
+			err_str.s = START_ERR_MSG"Error,SME busy"END_ERR_MSG;
+			err_str.len = 14+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 98:
+			err_str.s = START_ERR_MSG"Error,no response from SME"END_ERR_MSG;
+			err_str.len = 26+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 99:
+			err_str.s = START_ERR_MSG"Error,service rejected"END_ERR_MSG;
+			err_str.len = 22+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 100:
+			err_str.s =
+			START_ERR_MSG"Error,quality of service not available"END_ERR_MSG;
+			err_str.len = 38+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		case 101:
+			err_str.s = START_ERR_MSG"Error,error in SME"END_ERR_MSG;
+			err_str.len = 18+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+			break;
+		default:
+			err_str.s = START_ERR_MSG"Unknown error code"END_ERR_MSG;
+			err_str.len = 18+ START_ERR_MSG_LEN + END_ERR_MSG_LEN;
+	}
+	return &err_str;
+}

+ 49 - 0
modules/sms/sms_report.h

@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _H_SMS_REPORT_DEF
+#define _H_SMS_REPORT_DEF
+
+#include "../../str.h"
+#include "sms_funcs.h"
+
+#define NR_CELLS  256
+
+
+int    init_report_queue(void);
+void   destroy_report_queue(void);
+void   add_sms_into_report_queue(int id, struct sms_msg *sms, char *, int );
+int    relay_report_to_queue(int id, char *phone, int status, int *old_status);
+void   check_timeout_in_report_queue(void);
+str*   get_error_str(int status);
+void   remove_sms_from_report_queue(int id);
+str*   get_text_from_report_queue(int id);
+struct sms_msg* get_sms_from_report_queue(int id);
+void   set_gettime_function(void);
+
+
+#endif