sms(s): move to common modules directory
authorHenning Westerholt <henning.westerholt@1und1.de>
Fri, 13 Aug 2010 16:03:31 +0000 (18:03 +0200)
committerHenning Westerholt <henning.westerholt@1und1.de>
Fri, 13 Aug 2010 16:03:31 +0000 (18:03 +0200)
19 files changed:
modules/sms/Makefile [new file with mode: 0644]
modules/sms/README [new file with mode: 0644]
modules/sms/doc/Makefile [new file with mode: 0644]
modules/sms/doc/functions.xml [new file with mode: 0644]
modules/sms/doc/params.xml [new file with mode: 0644]
modules/sms/doc/sms.xml [new file with mode: 0644]
modules/sms/etc/ser.cfg [new file with mode: 0644]
modules/sms/libsms_charset.c [new file with mode: 0644]
modules/sms/libsms_charset.h [new file with mode: 0644]
modules/sms/libsms_getsms.c [new file with mode: 0644]
modules/sms/libsms_modem.c [new file with mode: 0644]
modules/sms/libsms_modem.h [new file with mode: 0644]
modules/sms/libsms_putsms.c [new file with mode: 0644]
modules/sms/libsms_sms.h [new file with mode: 0644]
modules/sms/sms.c [new file with mode: 0644]
modules/sms/sms_funcs.c [new file with mode: 0644]
modules/sms/sms_funcs.h [new file with mode: 0644]
modules/sms/sms_report.c [new file with mode: 0644]
modules/sms/sms_report.h [new file with mode: 0644]

diff --git a/modules/sms/Makefile b/modules/sms/Makefile
new file mode 100644 (file)
index 0000000..775e73c
--- /dev/null
@@ -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
diff --git a/modules/sms/README b/modules/sms/README
new file mode 100644 (file)
index 0000000..9696644
--- /dev/null
@@ -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:+401704678811@iptel.org).
+   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:p-cscf@cidra.com.ar]")
+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
+   <serusers@iptel.org> and e-mail regarding development versions
+   or CVS snapshots should be send to <serdev@iptel.org>.
+
+   If you want to keep the mail private, send it to
+   <serhelp@iptel.org>.
+
+   3.3. How can I report a bug?
+
+   Please follow the guidelines provided at:
+   http://iptel.org/ser/bugs
diff --git a/modules/sms/doc/Makefile b/modules/sms/doc/Makefile
new file mode 100644 (file)
index 0000000..54de097
--- /dev/null
@@ -0,0 +1,4 @@
+docs = sms.xml
+
+docbook_dir=../../../docbook
+include $(docbook_dir)/Makefile.module
diff --git a/modules/sms/doc/functions.xml b/modules/sms/doc/functions.xml
new file mode 100644 (file)
index 0000000..d53bfff
--- /dev/null
@@ -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>
diff --git a/modules/sms/doc/params.xml b/modules/sms/doc/params.xml
new file mode 100644 (file)
index 0000000..f5d86cf
--- /dev/null
@@ -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:p-cscf@cidra.com.ar]") 
+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>
diff --git a/modules/sms/doc/sms.xml b/modules/sms/doc/sms.xml
new file mode 100644 (file)
index 0000000..f48b3c4
--- /dev/null
@@ -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>iancu@fokus.fraunhofer.de</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:+401704678811@iptel.org). 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>
diff --git a/modules/sms/etc/ser.cfg b/modules/sms/etc/ser.cfg
new file mode 100644 (file)
index 0000000..0401b1b
--- /dev/null
@@ -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:p-cscf@cidra.com.ar;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;
+       };
+}
diff --git a/modules/sms/libsms_charset.c b/modules/sms/libsms_charset.c
new file mode 100644 (file)
index 0000000..7a59e93
--- /dev/null
@@ -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:s.frings@mail.isis.de
+ */
+
+
+#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];
+}
diff --git a/modules/sms/libsms_charset.h b/modules/sms/libsms_charset.h
new file mode 100644 (file)
index 0000000..e39a4d8
--- /dev/null
@@ -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:s.frings@mail.isis.de
+ */
+
+
+#ifndef CHARSET_H
+#define CHARSET_H
+
+char ascii2sms(const char c);
+
+char sms2ascii(const char c);
+
+#endif
diff --git a/modules/sms/libsms_getsms.c b/modules/sms/libsms_getsms.c
new file mode 100644 (file)
index 0000000..c4969eb
--- /dev/null
@@ -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:s.frings@mail.isis.de
+ */
+
+
+
+#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;
+}
+
diff --git a/modules/sms/libsms_modem.c b/modules/sms/libsms_modem.c
new file mode 100644 (file)
index 0000000..d0f5b33
--- /dev/null
@@ -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:s.frings@mail.isis.de
+ */
+
+
+#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;
+}
+
diff --git a/modules/sms/libsms_modem.h b/modules/sms/libsms_modem.h
new file mode 100644 (file)
index 0000000..feeb6ac
--- /dev/null
@@ -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:s.frings@mail.isis.de
+ */
+
+
+#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
diff --git a/modules/sms/libsms_putsms.c b/modules/sms/libsms_putsms.c
new file mode 100644 (file)
index 0000000..8223f0d
--- /dev/null
@@ -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:s.frings@mail.isis.de
+ */
+
+
+#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));
+}
+
diff --git a/modules/sms/libsms_sms.h b/modules/sms/libsms_sms.h
new file mode 100644 (file)
index 0000000..3553e03
--- /dev/null
@@ -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:
+ *    info@iptel.org
+ *
+ * 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
+
diff --git a/modules/sms/sms.c b/modules/sms/sms.c
new file mode 100644 (file)
index 0000000..bfd916c
--- /dev/null
@@ -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:
+ *    info@iptel.org
+ *
+ * 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);
+}
+
diff --git a/modules/sms/sms_funcs.c b/modules/sms/sms_funcs.c
new file mode 100644 (file)
index 0000000..9a4afa6
--- /dev/null
@@ -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:
+ *    info@iptel.org
+ *
+ * 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*/
+}
+
+
+
diff --git a/modules/sms/sms_funcs.h b/modules/sms/sms_funcs.h
new file mode 100644 (file)
index 0000000..7b9c2d5
--- /dev/null
@@ -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:
+ *    info@iptel.org
+ *
+ * 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
+
diff --git a/modules/sms/sms_report.c b/modules/sms/sms_report.c
new file mode 100644 (file)
index 0000000..930b561
--- /dev/null
@@ -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:
+ *    info@iptel.org
+ *
+ * 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:
+ *    info@iptel.org
+ *
+ * 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;
+}
diff --git a/modules/sms/sms_report.h b/modules/sms/sms_report.h
new file mode 100644 (file)
index 0000000..0d2b77f
--- /dev/null
@@ -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:
+ *    info@iptel.org
+ *
+ * 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