Fri Aug 17 00:17:23 2018

Asterisk developer's documentation


app_sms.c File Reference

SMS application - ETSI ES 201 912 protocol 1 implementation. More...

#include "asterisk.h"
#include <dirent.h>
#include <ctype.h>
#include <sys/stat.h>
#include "asterisk/paths.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/alaw.h"
#include "asterisk/callerid.h"
#include "asterisk/utils.h"
#include "asterisk/app.h"

Go to the source code of this file.

Data Structures

struct  sms_s

Defines

#define __OUT_FMT   AST_FORMAT_SLINEAR
#define DIR_RX   1
#define DIR_TX   2
#define DLL2_ACK(h)   ((h->framenumber & 1) ? DLL2_SMS_ACK1: DLL2_SMS_ACK1)
#define is16bit(dcs)   ( ((dcs) & 0xC0) ? 0 : (((dcs) & 0xc) == 8) )
#define is7bit(dcs)   ( ((dcs) & 0xC0) ? (!((dcs) & 4) ) : (((dcs) & 0xc) == 0) )
#define is8bit(dcs)   ( ((dcs) & 0xC0) ? ( ((dcs) & 4) ) : (((dcs) & 0xc) == 4) )
#define MAX_DEBUG_LEN   300
#define MAXSAMPLES   (800)
#define OSYNC_BITS   80
#define SMSLEN   160
#define SMSLEN_8   140

Typedefs

typedef signed short output_t
typedef struct sms_s sms_t

Enumerations

enum  message_types {
  DLL_SMS_MASK = 0x7f, DLL1_SMS_DATA = 0x11, DLL1_SMS_ERROR = 0x12, DLL1_SMS_EST = 0x13,
  DLL1_SMS_REL = 0x14, DLL1_SMS_ACK = 0x15, DLL1_SMS_NACK = 0x16, DLL1_SMS_COMPLETE = 0x80,
  DLL1_SMS_MORE = 0x00, DLL2_SMS_EST = 0x7f, DLL2_SMS_INFO_MO = 0x10, DLL2_SMS_INFO_MT = 0x11,
  DLL2_SMS_INFO_STA = 0x12, DLL2_SMS_NACK = 0x13, DLL2_SMS_ACK0 = 0x14, DLL2_SMS_ACK1 = 0x15,
  DLL2_SMS_ENQ = 0x16, DLL2_SMS_REL = 0x17, DLL2_SMS_COMPLETE = 0x00, DLL2_SMS_MORE = 0x80
}
enum  sms_flags {
  OPTION_BE_SMSC = (1 << 0), OPTION_ANSWER = (1 << 1), OPTION_TWO = (1 << 2), OPTION_PAUSE = (1 << 3),
  OPTION_SRR = (1 << 4), OPTION_DCS = (1 << 5)
}
enum  sms_opt_args { OPTION_ARG_PAUSE = 0, OPTION_ARG_ARRAY_SIZE }

Functions

static void adddata_proto2 (sms_t *h, unsigned char msg, char *data, int size)
 AST_APP_OPTIONS (sms_options,{AST_APP_OPTION('s', OPTION_BE_SMSC), AST_APP_OPTION('a', OPTION_ANSWER), AST_APP_OPTION('t', OPTION_TWO), AST_APP_OPTION('r', OPTION_SRR), AST_APP_OPTION('o', OPTION_DCS), AST_APP_OPTION_ARG('p', OPTION_PAUSE, OPTION_ARG_PAUSE),})
 AST_MODULE_INFO_STANDARD (ASTERISK_GPL_KEY,"SMS/PSTN handler")
static char * isodate (time_t t, char *buf, int len)
 static, return a date/time in ISO format
static int load_module (void)
static void numcpy (char *d, char *s)
 copy number, skipping non digits apart from leading +
static unsigned char packaddress (unsigned char *o, char *i)
 store an address at o, and return number of bytes used
static void packdate (unsigned char *o, time_t w)
 pack a date and return
static int packsms (unsigned char dcs, unsigned char *base, unsigned int udhl, unsigned char *udh, int udl, unsigned short *ud)
 general pack, with length and data, returns number of bytes of target used
static int packsms16 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
 takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using 16 bit UCS-2 character codes The return value is the number of bytes packed in to o, which is internally limited to 140 o can be null, in which case this is used to validate or count only if the input contains invalid characters then the return value is -1
static int packsms7 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
 takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using SMS 7 bit character codes
static int packsms8 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
 takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using 8 bit character codes. The return value is the number of bytes packed in to o, which is internally limited to 140. o can be null, in which case this is used to validate or count only. if the input contains invalid characters then the return value is -1
static void putdummydata_proto2 (sms_t *h)
static struct dirent * readdirqueue (DIR *d, char *queue)
 read dir skipping dot files...
static void * sms_alloc (struct ast_channel *chan, void *sms_t_ptr)
static void sms_compose1 (sms_t *h, int more)
 compose a message for protocol 1
static void sms_compose2 (sms_t *h, int more)
static void sms_debug (int dir, sms_t *h)
static int sms_exec (struct ast_channel *chan, const char *data)
static int sms_generate (struct ast_channel *chan, void *data, int len, int samples)
static unsigned char sms_handleincoming (sms_t *h)
 handle the incoming message
static int sms_handleincoming_proto2 (sms_t *h)
 sms_handleincoming_proto2: handle the incoming message
static char * sms_hexdump (unsigned char buf[], int size, char *s)
static void sms_log (sms_t *h, char status)
 Log the output, and remove file.
static void sms_messagerx (sms_t *h)
static void sms_messagerx2 (sms_t *h)
static void sms_messagetx (sms_t *h)
static void sms_nextoutgoing (sms_t *h)
 find and fill in next message, or send a REL if none waiting
static void sms_process (sms_t *h, int samples, signed short *data)
static void sms_readfile (sms_t *h, char *fn)
 parse and delete a file
static void sms_release (struct ast_channel *chan, void *data)
static void sms_writefile (sms_t *h)
 white a received text message to a file
static int unload_module (void)
static unsigned char unpackaddress (char *o, unsigned char *i)
 unpack an address from i, return byte length, unpack to o
static struct timeval unpackdate (unsigned char *i)
 unpack a date and return
static int unpacksms (unsigned char dcs, unsigned char *i, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
 general unpack - starts with length byte (octet or septet) and returns number of bytes used, inc length
static void unpacksms16 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
 unpacks bytes (16 bit encoding) at i, len l septets, and places in udh and ud setting udhl and udl. udh not used if udhi not set
static void unpacksms7 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
 unpacks bytes (7 bit encoding) at i, len l septets, and places in udh and ud setting udhl and udl. udh not used if udhi not set
static void unpacksms8 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
 unpacks bytes (8 bit encoding) at i, len l septets, and places in udh and ud setting udhl and udl. udh not used if udhi not set.
static long utf8decode (unsigned char **pp)
 Reads next UCS character from NUL terminated UTF-8 string and advance pointer.

Variables

static char * app = "SMS"
static const unsigned short defaultalphabet []
static const unsigned short escapes []
static char log_file [255]
static volatile unsigned char message_ref
static volatile unsigned int seq
static struct ast_generator smsgen
static const signed short wave []
static const output_twave_out = wave

Detailed Description

SMS application - ETSI ES 201 912 protocol 1 implementation.

Development notes
Note:
The ETSI standards are available free of charge from ETSI at http://pda.etsi.org/pda/queryform.asp Among the relevant documents here we have:

ES 201 912 SMS for PSTN/ISDN TS 123 040 Technical realization of SMS

Author:
Adrian Kennard (for the original protocol 1 code)
Filippo Grassilli (Hyppo) - protocol 2 support Not fully tested, under development

Definition in file app_sms.c.


Define Documentation

#define __OUT_FMT   AST_FORMAT_SLINEAR

Definition at line 147 of file app_sms.c.

Referenced by sms_generate().

#define DIR_RX   1

Definition at line 1475 of file app_sms.c.

Referenced by sms_debug(), and sms_messagerx().

#define DIR_TX   2

Definition at line 1476 of file app_sms.c.

Referenced by sms_messagetx().

#define DLL2_ACK (  )     ((h->framenumber & 1) ? DLL2_SMS_ACK1: DLL2_SMS_ACK1)

Referenced by sms_messagerx2().

#define is16bit ( dcs   )     ( ((dcs) & 0xC0) ? 0 : (((dcs) & 0xc) == 8) )

Definition at line 280 of file app_sms.c.

Referenced by sms_readfile().

#define is7bit ( dcs   )     ( ((dcs) & 0xC0) ? (!((dcs) & 4) ) : (((dcs) & 0xc) == 0) )

Definition at line 278 of file app_sms.c.

Referenced by packsms(), sms_readfile(), and unpacksms().

#define is8bit ( dcs   )     ( ((dcs) & 0xC0) ? ( ((dcs) & 4) ) : (((dcs) & 0xc) == 4) )

Definition at line 279 of file app_sms.c.

Referenced by packsms(), sms_readfile(), and unpacksms().

#define MAX_DEBUG_LEN   300

Definition at line 1240 of file app_sms.c.

Referenced by sms_handleincoming_proto2(), and sms_hexdump().

#define MAXSAMPLES   (800)

Referenced by sms_generate().

#define OSYNC_BITS   80

Definition at line 150 of file app_sms.c.

Referenced by sms_messagetx().

#define SMSLEN   160

max SMS length

Definition at line 213 of file app_sms.c.

Referenced by packsms7(), and sms_readfile().

#define SMSLEN_8   140

max SMS length for 8-bit char

Definition at line 214 of file app_sms.c.

Referenced by packsms16(), and packsms8().


Typedef Documentation

typedef signed short output_t

Definition at line 145 of file app_sms.c.

typedef struct sms_s sms_t

Enumeration Type Documentation

The SMS spec ETSI ES 201 912 defines two protocols with different message types. Also note that the high bit is used to indicate whether the message is complete or not, but in two opposite ways: for Protocol 1, 0x80 means that the message is complete; for Protocol 2, 0x00 means that the message is complete;

Enumerator:
DLL_SMS_MASK 
DLL1_SMS_DATA 
DLL1_SMS_ERROR 
DLL1_SMS_EST 
DLL1_SMS_REL 
DLL1_SMS_ACK 
DLL1_SMS_NACK 
DLL1_SMS_COMPLETE 
DLL1_SMS_MORE 
DLL2_SMS_EST 
DLL2_SMS_INFO_MO 
DLL2_SMS_INFO_MT 
DLL2_SMS_INFO_STA 
DLL2_SMS_NACK 
DLL2_SMS_ACK0 
DLL2_SMS_ACK1 
DLL2_SMS_ENQ 
DLL2_SMS_REL 
DLL2_SMS_COMPLETE 
DLL2_SMS_MORE 

Definition at line 159 of file app_sms.c.

00159                    {
00160    DLL_SMS_MASK        = 0x7f,             /* mask for the valid bits */
00161 
00162    /* Protocol 1 values */
00163    DLL1_SMS_DATA       = 0x11,             /* data packet */
00164    DLL1_SMS_ERROR      = 0x12,
00165    DLL1_SMS_EST        = 0x13,             /* start the connection */
00166    DLL1_SMS_REL        = 0x14,             /* end the connection */
00167    DLL1_SMS_ACK        = 0x15,
00168    DLL1_SMS_NACK       = 0x16,
00169 
00170    DLL1_SMS_COMPLETE   = 0x80,             /* packet is complete */
00171    DLL1_SMS_MORE       = 0x00,             /* more data to follow */
00172 
00173    /* Protocol 2 values */
00174    DLL2_SMS_EST        = 0x7f,             /* magic number. No message body */
00175    DLL2_SMS_INFO_MO    = 0x10,
00176    DLL2_SMS_INFO_MT    = 0x11,
00177    DLL2_SMS_INFO_STA   = 0x12,
00178    DLL2_SMS_NACK       = 0x13,
00179    DLL2_SMS_ACK0       = 0x14,             /* ack even-numbered frame */
00180    DLL2_SMS_ACK1       = 0x15,             /* ack odd-numbered frame */
00181    DLL2_SMS_ENQ        = 0x16,
00182    DLL2_SMS_REL        = 0x17,             /* end the connection */
00183 
00184    DLL2_SMS_COMPLETE   = 0x00,             /* packet is complete */
00185    DLL2_SMS_MORE       = 0x80,             /* more data to follow */
00186 };

enum sms_flags
Enumerator:
OPTION_BE_SMSC 
OPTION_ANSWER 
OPTION_TWO 
OPTION_PAUSE 
OPTION_SRR 
OPTION_DCS 

Definition at line 1846 of file app_sms.c.

01846                {
01847    OPTION_BE_SMSC = (1 << 0),             /* act as sms center */
01848    OPTION_ANSWER  = (1 << 1),             /* answer on incoming calls */
01849    OPTION_TWO  = (1 << 2),                 /* Use Protocol Two */
01850    OPTION_PAUSE   = (1 << 3),             /* pause before sending data, in ms */
01851    OPTION_SRR  = (1 << 4),                 /* set srr */
01852    OPTION_DCS  = (1 << 5),                 /* set dcs */
01853 };

Enumerator:
OPTION_ARG_PAUSE 
OPTION_ARG_ARRAY_SIZE 

Definition at line 1855 of file app_sms.c.

01855                   {
01856    OPTION_ARG_PAUSE = 0,
01857    OPTION_ARG_ARRAY_SIZE
01858 };


Function Documentation

static void adddata_proto2 ( sms_t h,
unsigned char  msg,
char *  data,
int  size 
) [static]

Add data to a protocol 2 message. Use the length field (h->omsg[1]) as a pointer to the next free position.

Definition at line 1180 of file app_sms.c.

References sms_s::omsg.

Referenced by sms_compose2().

01181 {
01182    int x = h->omsg[1] + 2;                 /* Get current position */
01183    if (x == 2) {
01184       x += 2;                             /* First: skip Payload length (set later) */
01185    }
01186    h->omsg[x++] = msg;                     /* Message code */
01187    h->omsg[x++] = (unsigned char)size;     /* Data size Low */
01188    h->omsg[x++] = 0;                       /* Data size Hi */
01189    for (; size > 0 ; size--) {
01190       h->omsg[x++] = *data++;
01191    }
01192    h->omsg[1] = x - 2;                     /* Frame size */
01193    h->omsg[2] = x - 4;                     /* Payload length (Lo) */
01194    h->omsg[3] = 0;                         /* Payload length (Hi) */
01195 }

AST_APP_OPTIONS ( sms_options   ) 
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY  ,
"SMS/PSTN handler"   
)
static char* isodate ( time_t  t,
char *  buf,
int  len 
) [static]

static, return a date/time in ISO format

Definition at line 300 of file app_sms.c.

References ast_localtime(), and ast_strftime().

Referenced by sms_log(), and sms_writefile().

00301 {
00302    struct ast_tm tm;
00303    struct timeval local = { t, 0 };
00304    ast_localtime(&local, &tm, NULL);
00305    ast_strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm);
00306    return buf;
00307 }

static int load_module ( void   )  [static]

Definition at line 2070 of file app_sms.c.

References ast_config_AST_LOG_DIR, AST_LIN2A, ast_register_application_xml, and sms_exec().

02071 {
02072 #ifdef OUTALAW
02073    int p;
02074    for (p = 0; p < 80; p++) {
02075       wavea[p] = AST_LIN2A(wave[p]);
02076    }
02077 #endif
02078    snprintf(log_file, sizeof(log_file), "%s/sms", ast_config_AST_LOG_DIR);
02079    return ast_register_application_xml(app, sms_exec);
02080 }

static void numcpy ( char *  d,
char *  s 
) [static]

copy number, skipping non digits apart from leading +

Definition at line 285 of file app_sms.c.

Referenced by sms_readfile().

00286 {
00287    if (*s == '+') {
00288       *d++ = *s++;
00289    }
00290    while (*s) {
00291       if (isdigit(*s)) {
00292          *d++ = *s;
00293       }
00294       s++;
00295    }
00296    *d = 0;
00297 }

static unsigned char packaddress ( unsigned char *  o,
char *  i 
) [static]

store an address at o, and return number of bytes used

Definition at line 743 of file app_sms.c.

Referenced by sms_compose1().

00744 {
00745    unsigned char p = 2;
00746    o[0] = 0;                               /* number of bytes */
00747    if (*i == '+') {                        /* record as bit 0 in byte 1 */
00748       i++;
00749       o[1] = 0x91;
00750    } else {
00751       o[1] = 0x81;
00752    }
00753    for ( ; *i ; i++) {
00754       if (!isdigit(*i)) {                 /* ignore non-digits */
00755          continue;
00756       }
00757       if (o[0] & 1) {
00758          o[p++] |= ((*i & 0xF) << 4);
00759       } else {
00760          o[p] = (*i & 0xF);
00761       }
00762       o[0]++;
00763    }
00764    if (o[0] & 1) {
00765       o[p++] |= 0xF0;                     /* pad */
00766    }
00767    return p;
00768 }

static void packdate ( unsigned char *  o,
time_t  w 
) [static]

pack a date and return

Definition at line 550 of file app_sms.c.

References ast_localtime(), ast_tm::tm_gmtoff, ast_tm::tm_hour, ast_tm::tm_mday, ast_tm::tm_min, ast_tm::tm_mon, ast_tm::tm_sec, and ast_tm::tm_year.

Referenced by sms_compose1().

00551 {
00552    struct ast_tm t;
00553    struct timeval topack = { w, 0 };
00554    int z;
00555 
00556    ast_localtime(&topack, &t, NULL);
00557 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__) || defined(__CYGWIN__)
00558    z = -t.tm_gmtoff / 60 / 15;
00559 #else
00560    z = timezone / 60 / 15;
00561 #endif
00562    *o++ = ((t.tm_year % 10) << 4) + (t.tm_year % 100) / 10;
00563    *o++ = (((t.tm_mon + 1) % 10) << 4) + (t.tm_mon + 1) / 10;
00564    *o++ = ((t.tm_mday % 10) << 4) + t.tm_mday / 10;
00565    *o++ = ((t.tm_hour % 10) << 4) + t.tm_hour / 10;
00566    *o++ = ((t.tm_min % 10) << 4) + t.tm_min / 10;
00567    *o++ = ((t.tm_sec % 10) << 4) + t.tm_sec / 10;
00568    if (z < 0) {
00569       *o++ = (((-z) % 10) << 4) + (-z) / 10 + 0x08;
00570    } else {
00571       *o++ = ((z % 10) << 4) + z / 10;
00572    }
00573 }

static int packsms ( unsigned char  dcs,
unsigned char *  base,
unsigned int  udhl,
unsigned char *  udh,
int  udl,
unsigned short *  ud 
) [static]

general pack, with length and data, returns number of bytes of target used

Definition at line 517 of file app_sms.c.

References is7bit, is8bit, packsms16(), packsms7(), and packsms8().

Referenced by sms_compose1().

00518 {
00519    unsigned char *p = base;
00520    if (udl == 0) {
00521       *p++ = 0;                           /* no user data */
00522    } else {
00523       
00524       int l = 0;
00525       if (is7bit(dcs)) {                  /* 7 bit */
00526          if ((l = packsms7(p + 1, udhl, udh, udl, ud)) < 0) {
00527             l = 0;
00528          }
00529          *p++ = l;
00530          p += (l * 7 + 7) / 8;
00531       } else if (is8bit(dcs)) {           /* 8 bit */
00532          if ((l = packsms8(p + 1, udhl, udh, udl, ud)) < 0) {
00533             l = 0;
00534          }
00535          *p++ = l;
00536          p += l;
00537       } else {                            /* UCS-2 */
00538          if ((l = packsms16(p + 1, udhl, udh, udl, ud)) < 0) {
00539             l = 0;
00540          }
00541          *p++ = l;
00542          p += l;
00543       }
00544    }
00545    return p - base;
00546 }

static int packsms16 ( unsigned char *  o,
int  udhl,
unsigned char *  udh,
int  udl,
unsigned short *  ud 
) [static]

takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using 16 bit UCS-2 character codes The return value is the number of bytes packed in to o, which is internally limited to 140 o can be null, in which case this is used to validate or count only if the input contains invalid characters then the return value is -1

Definition at line 482 of file app_sms.c.

References dummy(), and SMSLEN_8.

Referenced by packsms(), and sms_readfile().

00483 {
00484    unsigned char p = 0;
00485    unsigned char dummy[SMSLEN_8];
00486 
00487    if (o == NULL) {
00488       o = dummy;
00489    }
00490    /* header - no encoding */
00491    if (udhl) {
00492       o[p++] = udhl;
00493       while (udhl--) {
00494          o[p++] = *udh++;
00495          if (p >= SMSLEN_8) {
00496             return p;
00497          }
00498       }
00499    }
00500    while (udl--) {
00501       long u;
00502       u = *ud++;
00503       o[p++] = (u >> 8);
00504       if (p >= SMSLEN_8) {
00505          return p - 1;                   /* could not fit last character */
00506       }
00507       o[p++] = u;
00508       if (p >= SMSLEN_8) {
00509          return p;
00510       }
00511    }
00512    return p;
00513 }

static int packsms7 ( unsigned char *  o,
int  udhl,
unsigned char *  udh,
int  udl,
unsigned short *  ud 
) [static]

takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using SMS 7 bit character codes

Definition at line 366 of file app_sms.c.

References dummy(), and SMSLEN.

Referenced by packsms(), and sms_readfile().

00367 {
00368    unsigned char p = 0;                    /* output pointer (bytes) */
00369    unsigned char b = 0;                    /* bit position */
00370    unsigned char n = 0;                    /* output character count */
00371    unsigned char dummy[SMSLEN];
00372 
00373    if (o == NULL) {                        /* output to a dummy buffer if o not set */
00374       o = dummy;
00375    }
00376 
00377    if (udhl) {                             /* header */
00378       o[p++] = udhl;
00379       b = 1;
00380       n = 1;
00381       while (udhl--) {
00382          o[p++] = *udh++;
00383          b += 8;
00384          while (b >= 7) {
00385             b -= 7;
00386             n++;
00387          }
00388          if (n >= SMSLEN)
00389             return n;
00390       }
00391       if (b) {
00392          b = 7 - b;
00393          if (++n >= SMSLEN)
00394             return n;
00395       }                                   /* filling to septet boundary */
00396    }
00397    o[p] = 0;
00398    /* message */
00399    while (udl--) {
00400       long u;
00401       unsigned char v;
00402       u = *ud++;
00403       /* XXX 0 is invalid ? */
00404       /* look up in defaultalphabet[]. If found, v is the 7-bit code */
00405       for (v = 0; v < 128 && defaultalphabet[v] != u; v++);
00406       if (v == 128 /* not found */ && u && n + 1 < SMSLEN) {
00407          /* if not found, look in the escapes table (we need 2 bytes) */
00408          for (v = 0; v < 128 && escapes[v] != u; v++);
00409          if (v < 128) { /* escaped sequence, esc + v */
00410             /* store the low (8-b) bits in o[p], the remaining bits in o[p+1] */
00411             o[p] |= (27 << b);          /* the low bits go into o[p] */ 
00412             b += 7;
00413             if (b >= 8) {
00414                b -= 8;
00415                p++;
00416                o[p] = (27 >> (7 - b));
00417             }
00418             n++;
00419          }
00420       }
00421       if (v == 128)
00422          return -1;                      /* invalid character */
00423       /* store, same as above */
00424       o[p] |= (v << b);
00425       b += 7;
00426       if (b >= 8) {
00427          b -= 8;
00428          p++;
00429          o[p] = (v >> (7 - b));
00430       }
00431       if (++n >= SMSLEN)
00432          return n;
00433    }
00434    return n;
00435 }

static int packsms8 ( unsigned char *  o,
int  udhl,
unsigned char *  udh,
int  udl,
unsigned short *  ud 
) [static]

takes a binary header (udhl bytes at udh) and UCS-2 message (udl characters at ud) and packs in to o using 8 bit character codes. The return value is the number of bytes packed in to o, which is internally limited to 140. o can be null, in which case this is used to validate or count only. if the input contains invalid characters then the return value is -1

Definition at line 443 of file app_sms.c.

References dummy(), and SMSLEN_8.

Referenced by packsms(), and sms_readfile().

00444 {
00445    unsigned char p = 0;
00446    unsigned char dummy[SMSLEN_8];
00447 
00448    if (o == NULL)
00449       o = dummy;
00450    /* header - no encoding */
00451    if (udhl) {
00452       o[p++] = udhl;
00453       while (udhl--) {
00454          o[p++] = *udh++;
00455          if (p >= SMSLEN_8) {
00456             return p;
00457          }
00458       }
00459    }
00460    while (udl--) {
00461       long u;
00462       u = *ud++;
00463       if (u < 0 || u > 0xFF) {
00464          return -1;                      /* not valid */
00465       }
00466       o[p++] = u;
00467       if (p >= SMSLEN_8) {
00468          return p;
00469       }
00470    }
00471    return p;
00472 }

static void putdummydata_proto2 ( sms_t h  )  [static]

Referenced by sms_compose2().

static struct dirent* readdirqueue ( DIR *  d,
char *  queue 
) [static, read]

read dir skipping dot files...

Definition at line 1094 of file app_sms.c.

References f.

Referenced by sms_nextoutgoing().

01095 {
01096    struct dirent *f;
01097    do {
01098       f = readdir(d);
01099    } while (f && (*f->d_name == '.' || strncmp(f->d_name, queue, strlen(queue)) || f->d_name[strlen(queue)] != '.'));
01100    return f;
01101 }

static void* sms_alloc ( struct ast_channel chan,
void *  sms_t_ptr 
) [static]

Just return the pointer to the descriptor that we received.

Definition at line 1674 of file app_sms.c.

01675 {
01676    return sms_t_ptr;
01677 }

static void sms_compose1 ( sms_t h,
int  more 
) [static]

compose a message for protocol 1

Definition at line 1392 of file app_sms.c.

References sms_s::da, sms_s::dcs, sms_s::mr, sms_s::oa, sms_s::omsg, packaddress(), packdate(), packsms(), sms_s::pid, sms_s::rp, sms_s::scts, sms_s::smsc, sms_s::srr, sms_s::ud, sms_s::udh, sms_s::udhi, sms_s::udhl, sms_s::udl, and sms_s::vp.

Referenced by sms_nextoutgoing().

01393 {
01394    unsigned int p = 2;                     /* next byte to write. Skip type and len */
01395 
01396    h->omsg[0] = 0x91;                      /* SMS_DATA */
01397    if (h->smsc) {                          /* deliver */
01398       h->omsg[p++] = (more ? 4 : 0) + ((h->udhl > 0) ? 0x40 : 0);
01399       p += packaddress(h->omsg + p, h->oa);
01400       h->omsg[p++] = h->pid;
01401       h->omsg[p++] = h->dcs;
01402       packdate(h->omsg + p, h->scts.tv_sec);
01403       p += 7;
01404       p += packsms(h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud);
01405    } else {                                /* submit */
01406       h->omsg[p++] =
01407          0x01 + (more ? 4 : 0) + (h->srr ? 0x20 : 0) + (h->rp ? 0x80 : 0) + (h->vp ? 0x10 : 0) + (h->udhi ? 0x40 : 0);
01408       if (h->mr < 0) {
01409          h->mr = message_ref++;
01410       }
01411       h->omsg[p++] = h->mr;
01412       p += packaddress(h->omsg + p, h->da);
01413       h->omsg[p++] = h->pid;
01414       h->omsg[p++] = h->dcs;
01415       if (h->vp) {                        /* relative VP */
01416          if (h->vp < 720) {
01417             h->omsg[p++] = (h->vp + 4) / 5 - 1;
01418          } else if (h->vp < 1440) {
01419             h->omsg[p++] = (h->vp - 720 + 29) / 30 + 143;
01420          } else if (h->vp < 43200) {
01421             h->omsg[p++] = (h->vp + 1439) / 1440 + 166;
01422          } else if (h->vp < 635040) {
01423             h->omsg[p++] = (h->vp + 10079) / 10080 + 192;
01424          } else {
01425             h->omsg[p++] = 255;         /* max */
01426          }
01427       }
01428       p += packsms(h->dcs, h->omsg + p, h->udhl, h->udh, h->udl, h->ud);
01429    }
01430    h->omsg[1] = p - 2;
01431 }

static void sms_compose2 ( sms_t h,
int  more 
) [static]

Definition at line 1205 of file app_sms.c.

References adddata_proto2(), ast_localtime(), sms_s::da, sms_s::oa, sms_s::omsg, putdummydata_proto2(), sms_s::scts, sms_s::smsc, ast_tm::tm_hour, ast_tm::tm_mday, ast_tm::tm_min, and ast_tm::tm_mon.

Referenced by sms_nextoutgoing().

01206 {
01207    struct ast_tm tm;
01208    struct timeval now = h->scts;
01209    char stm[9];
01210 
01211    h->omsg[0] = 0x00;                      /* set later... */
01212    h->omsg[1] = 0;
01213    putdummydata_proto2(h);
01214    if (h->smsc) {                          /* deliver */
01215       h->omsg[0] = 0x11;                  /* SMS_DELIVERY */
01216       /* Required: 10 11 12 13 14 15 17 (seems they must be ordered!) */
01217       ast_localtime(&now, &tm, NULL);
01218       sprintf(stm, "%02d%02d%02d%02d", tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);  /* Date mmddHHMM */
01219       adddata_proto2(h, 0x14, stm, 8);    /* Date */
01220       if (*h->oa == 0) {
01221          strcpy(h->oa, "00000000");
01222       }
01223       adddata_proto2(h, 0x15, h->oa, strlen(h->oa)); /* Originator */
01224       adddata_proto2(h, 0x17, "\1", 1);   /* Calling Terminal ID */
01225    } else {                                /* submit */
01226       h->omsg[0] = 0x10;                  /* SMS_SUBMIT */
01227       /* Required: 10 11 12 13 17 18 1B 1C (seems they must be ordered!) */
01228       adddata_proto2(h, 0x17, "\1", 1);   /* Calling Terminal ID */
01229       if (*h->da == 0) {
01230          strcpy(h->da, "00000000");
01231       }
01232       adddata_proto2(h, 0x18, h->da, strlen(h->da)); /* Originator */
01233       adddata_proto2(h, 0x1B, "\1", 1);         /* Called Terminal ID */
01234       adddata_proto2(h, 0x1C, "\0\0\0", 3);    /* Notification */
01235    }
01236 }

static void sms_debug ( int  dir,
sms_t h 
) [static]

Definition at line 1477 of file app_sms.c.

References ast_verb, DIR_RX, sms_s::ibytep, sms_s::imsg, and sms_s::omsg.

Referenced by sms_messagerx(), and sms_messagetx().

01478 {
01479    char txt[259 * 3 + 1];
01480    char *p = txt;                          /* always long enough */
01481    unsigned char *msg = (dir == DIR_RX) ? h->imsg : h->omsg;
01482    int n = (dir == DIR_RX) ? h->ibytep : msg[1] + 2;
01483    int q = 0;
01484    while (q < n && q < 30) {
01485       sprintf(p, " %02X", (unsigned)msg[q++]);
01486       p += 3;
01487    }
01488    if (q < n) {
01489       sprintf(p, "...");
01490    }
01491    ast_verb(3, "SMS %s%s\n", dir == DIR_RX ? "RX" : "TX", txt);
01492 }

static int sms_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 1869 of file app_sms.c.

References AST_APP_ARG, ast_app_parse_options(), ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_verb, ast_channel::caller, sms_s::cli, sms_s::dcs, ast_party_caller::id, sms_s::ipc0, sms_s::ipc1, LOG_ERROR, ast_party_id::number, sms_s::opause_0, OPTION_ARG_ARRAY_SIZE, OPTION_ARG_PAUSE, OPTION_BE_SMSC, OPTION_DCS, OPTION_SRR, OPTION_TWO, parse(), sms_s::pid, sms_s::protocol, sms_s::queue, S_COR, S_OR, sms_s::smsc, sms_s::srr, ast_party_number::str, and ast_party_number::valid.

Referenced by load_module().

01870 {
01871    int res = -1;
01872    sms_t h = { 0 };
01873    /* argument parsing support */
01874    struct ast_flags flags = { 0 };
01875    char *parse, *sms_opts[OPTION_ARG_ARRAY_SIZE] = { 0, };
01876    char *p;
01877    AST_DECLARE_APP_ARGS(sms_args,
01878       AST_APP_ARG(queue);
01879       AST_APP_ARG(options);
01880       AST_APP_ARG(addr);
01881       AST_APP_ARG(body);
01882    );
01883 
01884    if (!data) {
01885       ast_log(LOG_ERROR, "Requires queue name at least\n");
01886       return -1;
01887    }
01888 
01889    parse = ast_strdupa(data);              /* create a local copy */
01890    AST_STANDARD_APP_ARGS(sms_args, parse);
01891    if (sms_args.argc > 1) {
01892       ast_app_parse_options(sms_options, &flags, sms_opts, sms_args.options);
01893    }
01894 
01895    ast_verb(1, "sms argc %u queue <%s> opts <%s> addr <%s> body <%s>\n",
01896       sms_args.argc, S_OR(sms_args.queue, ""),
01897       S_OR(sms_args.options, ""),
01898       S_OR(sms_args.addr, ""),
01899       S_OR(sms_args.body, "") );
01900 
01901    h.ipc0 = h.ipc1 = 20;                   /* phase for cosine */
01902    h.dcs = 0xF1;                           /* default */
01903 
01904    ast_copy_string(h.cli,
01905       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
01906       sizeof(h.cli));
01907 
01908    if (ast_strlen_zero(sms_args.queue)) {
01909       ast_log(LOG_ERROR, "Requires queue name\n");
01910       goto done;
01911    }
01912    if (strlen(sms_args.queue) >= sizeof(h.queue)) {
01913       ast_log(LOG_ERROR, "Queue name too long\n");
01914       goto done;
01915    }
01916    ast_copy_string(h.queue, sms_args.queue, sizeof(h.queue));
01917 
01918    for (p = h.queue; *p; p++) {
01919       if (!isalnum(*p)) {
01920          *p = '-';                       /* make very safe for filenames */
01921       }
01922    }
01923 
01924    h.smsc = ast_test_flag(&flags, OPTION_BE_SMSC);
01925    h.protocol = ast_test_flag(&flags, OPTION_TWO) ? 2 : 1;
01926    if (!ast_strlen_zero(sms_opts[OPTION_ARG_PAUSE])) {
01927       h.opause_0 = atoi(sms_opts[OPTION_ARG_PAUSE]);
01928    }
01929    if (h.opause_0 < 25 || h.opause_0 > 2000) {
01930       h.opause_0 = 300;                   /* default 300ms */
01931    }
01932    ast_verb(1, "initial delay %dms\n", h.opause_0);
01933 
01934 
01935    /* the following apply if there is an arg3/4 and apply to the created message file */
01936    if (ast_test_flag(&flags, OPTION_SRR)) {
01937       h.srr = 1;
01938    }
01939    if (ast_test_flag(&flags, OPTION_DCS)) {
01940       h.dcs = 1;
01941    }
01942 #if 0 
01943       case '1':
01944       case '2':
01945       case '3':
01946       case '4':
01947       case '5':
01948       case '6':
01949       case '7':                           /* set the pid for saved local message */
01950          h.pid = 0x40 + (*d & 0xF);
01951          break;
01952       }
01953 #endif
01954    if (sms_args.argc > 2) {
01955       unsigned char *up;
01956 
01957       /* submitting a message, not taking call. */
01958       /* deprecated, use smsq instead */
01959       h.scts = ast_tvnow();
01960       if (ast_strlen_zero(sms_args.addr) || strlen(sms_args.addr) >= sizeof(h.oa)) {
01961          ast_log(LOG_ERROR, "Address too long %s\n", sms_args.addr);
01962          goto done;
01963       }
01964       if (h.smsc) {
01965          ast_copy_string(h.oa, sms_args.addr, sizeof(h.oa));
01966       } else {
01967          ast_copy_string(h.da, sms_args.addr, sizeof(h.da));
01968          ast_copy_string(h.oa, h.cli, sizeof(h.oa));
01969       }
01970       h.udl = 0;
01971       if (ast_strlen_zero(sms_args.body)) {
01972          ast_log(LOG_ERROR, "Missing body for %s\n", sms_args.addr);
01973          goto done;
01974       }
01975       up = (unsigned char *)sms_args.body;
01976       while (*up && h.udl < SMSLEN) {
01977          h.ud[h.udl++] = utf8decode(&up);
01978       }
01979       if (is7bit(h.dcs) && packsms7(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
01980          ast_log(LOG_WARNING, "Invalid 7 bit GSM data\n");
01981          goto done;
01982       }
01983       if (is8bit(h.dcs) && packsms8(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
01984          ast_log(LOG_WARNING, "Invalid 8 bit data\n");
01985          goto done;
01986       }
01987       if (is16bit(h.dcs) && packsms16(0, h.udhl, h.udh, h.udl, h.ud) < 0) {
01988          ast_log(LOG_WARNING, "Invalid 16 bit data\n");
01989          goto done;
01990       }
01991       h.rx = 0;                           /* sent message */
01992       h.mr = -1;
01993       sms_writefile(&h);
01994       res = h.err;
01995       goto done;
01996    }
01997    
01998    if (chan->_state != AST_STATE_UP) {    /* make sure channel is answered before any TX */
01999       ast_answer(chan);
02000    }
02001 
02002    if (ast_test_flag(&flags, OPTION_ANSWER)) {
02003       h.framenumber = 1;                  /* Proto 2 */
02004       /* set up SMS_EST initial message */
02005       if (h.protocol == 2) {
02006          h.omsg[0] = DLL2_SMS_EST;
02007          h.omsg[1] = 0;
02008       } else {
02009          h.omsg[0] = DLL1_SMS_EST | DLL1_SMS_COMPLETE;
02010          h.omsg[1] = 0;
02011       }
02012       sms_messagetx(&h);
02013    }
02014 
02015    res = ast_set_write_format(chan, __OUT_FMT);
02016    if (res >= 0) {
02017       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
02018    }
02019    if (res < 0) {
02020       ast_log(LOG_ERROR, "Unable to set to linear mode, giving up\n");
02021       goto done;
02022    }
02023 
02024    if ( (res = ast_activate_generator(chan, &smsgen, &h)) < 0) {
02025       ast_log(LOG_ERROR, "Failed to activate generator on '%s'\n", chan->name);
02026       goto done;
02027    }
02028 
02029    /* Do our thing here */
02030    for (;;) {
02031       struct ast_frame *f;
02032       int i = ast_waitfor(chan, -1);
02033       if (i < 0) {
02034          ast_log(LOG_NOTICE, "waitfor failed\n");
02035          break;
02036       }
02037       if (h.hangup) {
02038          ast_log(LOG_NOTICE, "channel hangup\n");
02039          break;
02040       }
02041       f = ast_read(chan);
02042       if (!f) {
02043          ast_log(LOG_NOTICE, "ast_read failed\n");
02044          break;
02045       }
02046       if (f->frametype == AST_FRAME_VOICE) {
02047          sms_process(&h, f->samples, f->data.ptr);
02048       }
02049 
02050       ast_frfree(f);
02051    }
02052    res = h.err;                            /* XXX */
02053 
02054    /* 
02055     * The SMS generator data is on the stack.  We _MUST_ make sure the generator
02056     * is stopped before returning from this function.
02057     */
02058    ast_deactivate_generator(chan);
02059 
02060    sms_log(&h, '?');                       /* log incomplete message */
02061 done:
02062    return (res);
02063 }

static int sms_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
) [static]

outgoing data are produced by this generator function, that reads from the descriptor whether it has data to send and which ones.

Definition at line 1596 of file app_sms.c.

References __OUT_FMT, ast_alloca, AST_FRAME_VOICE, AST_FRIENDLY_OFFSET, ast_log(), ast_write(), ast_frame_subclass::codec, ast_frame::data, ast_frame::datalen, DLL2_SMS_EST, errno, ast_frame::frametype, LOG_WARNING, ast_frame::mallocd, MAXSAMPLES, sms_s::obitp, sms_s::obyte, sms_s::obyten, sms_s::obytep, ast_frame::offset, sms_s::omsg, sms_s::opause, sms_s::ophase, sms_s::ophasep, sms_s::oseizure, sms_s::osync, sms_s::protocol, ast_frame::ptr, ast_frame::samples, ast_frame::src, and ast_frame::subclass.

01597 {
01598    struct ast_frame f = { 0 };
01599 #define MAXSAMPLES (800)
01600    output_t *buf;
01601    sms_t *h = data;
01602    int i;
01603 
01604    if (samples > MAXSAMPLES) {
01605       ast_log(LOG_WARNING, "Only doing %d samples (%d requested)\n",
01606           MAXSAMPLES, samples);
01607       samples = MAXSAMPLES;
01608    }
01609    len = samples * sizeof(*buf) + AST_FRIENDLY_OFFSET;
01610    buf = ast_alloca(len);
01611 
01612    f.frametype = AST_FRAME_VOICE;
01613    f.subclass.codec = __OUT_FMT;
01614    f.datalen = samples * sizeof(*buf);
01615    f.offset = AST_FRIENDLY_OFFSET;
01616    f.mallocd = 0;
01617    f.data.ptr = buf;
01618    f.samples = samples;
01619    f.src = "app_sms";
01620    /* create a buffer containing the digital sms pattern */
01621    for (i = 0; i < samples; i++) {
01622       buf[i] = wave_out[0];               /* default is silence */
01623 
01624       if (h->opause) {
01625          h->opause--;
01626       } else if (h->obyten || h->osync) { /* sending data */
01627          buf[i] = wave_out[h->ophase];
01628          h->ophase += (h->obyte & 1) ? 13 : 21; /* compute next phase */
01629          if (h->ophase >= 80)
01630             h->ophase -= 80;
01631          if ((h->ophasep += 12) >= 80) { /* time to send the next bit */
01632             h->ophasep -= 80;
01633             if (h->oseizure > 0) {      /* sending channel seizure (proto 2) */
01634                h->oseizure--;
01635                h->obyte ^= 1;          /* toggle low bit */
01636             } else if (h->osync) {
01637                h->obyte = 1;           /* send mark as sync bit */
01638                h->osync--;             /* sending sync bits */
01639                if (h->osync == 0 && h->protocol == 2 && h->omsg[0] == DLL2_SMS_EST) {
01640                   h->obytep = h->obyten = 0; /* we are done */
01641                }
01642             } else {
01643                h->obitp++;
01644                if (h->obitp == 1) {
01645                   h->obyte = 0;       /* start bit; */
01646                } else if (h->obitp == 2) {
01647                   h->obyte = h->omsg[h->obytep];
01648                } else if (h->obitp == 10) {
01649                   h->obyte = 1; /* stop bit */
01650                   h->obitp = 0;
01651                   h->obytep++;
01652                   if (h->obytep == h->obyten) {
01653                      h->obytep = h->obyten = 0; /* sent */
01654                      h->osync = 10;   /* trailing marks */
01655                   }
01656                } else {
01657                   h->obyte >>= 1;
01658                }
01659             }
01660          }
01661       }
01662    }
01663    if (ast_write(chan, &f) < 0) {
01664       ast_log(LOG_WARNING, "Failed to write frame to '%s': %s\n", chan->name, strerror(errno));
01665       return -1;
01666    }
01667    return 0;
01668 #undef MAXSAMPLES
01669 }

static unsigned char sms_handleincoming ( sms_t h  )  [static]

handle the incoming message

Definition at line 1104 of file app_sms.c.

References ast_copy_string(), ast_log(), ast_tvnow(), sms_s::cli, sms_s::da, sms_s::dcs, sms_s::imsg, LOG_WARNING, sms_s::mr, sms_s::oa, sms_s::pid, sms_s::rp, sms_s::rx, sms_s::scts, sms_writefile(), sms_s::smsc, sms_s::srr, sms_s::ud, sms_s::udh, sms_s::udhi, sms_s::udhl, sms_s::udl, unpackaddress(), unpackdate(), unpacksms(), and sms_s::vp.

Referenced by sms_messagerx().

01105 {
01106    unsigned char p = 3;
01107    if (h->smsc) {                          /* SMSC */
01108       if ((h->imsg[2] & 3) == 1) {        /* SMS-SUBMIT */
01109          h->udhl = h->udl = 0;
01110          h->vp = 0;
01111          h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
01112          h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
01113          h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
01114          ast_copy_string(h->oa, h->cli, sizeof(h->oa));
01115          h->scts = ast_tvnow();
01116          h->mr = h->imsg[p++];
01117          p += unpackaddress(h->da, h->imsg + p);
01118          h->pid = h->imsg[p++];
01119          h->dcs = h->imsg[p++];
01120          if ((h->imsg[2] & 0x18) == 0x10) {       /* relative VP */
01121             if (h->imsg[p] < 144) {
01122                h->vp = (h->imsg[p] + 1) * 5;
01123             } else if (h->imsg[p] < 168) {
01124                h->vp = 720 + (h->imsg[p] - 143) * 30;
01125             } else if (h->imsg[p] < 197) {
01126                h->vp = (h->imsg[p] - 166) * 1440;
01127             } else {
01128                h->vp = (h->imsg[p] - 192) * 10080;
01129             }
01130             p++;
01131          } else if (h->imsg[2] & 0x18) {
01132             p += 7;                     /* ignore enhanced / absolute VP */
01133          }
01134          p += unpacksms(h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
01135          h->rx = 1;                      /* received message */
01136          sms_writefile(h);               /* write the file */
01137          if (p != h->imsg[1] + 2) {
01138             ast_log(LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
01139             return 0xFF;        /* duh! */
01140          }
01141       } else {
01142          ast_log(LOG_WARNING, "Unknown message type %02X\n", (unsigned)h->imsg[2]);
01143          return 0xFF;
01144       }
01145    } else {                                /* client */
01146       if (!(h->imsg[2] & 3)) {            /* SMS-DELIVER */
01147          *h->da = h->srr = h->rp = h->vp = h->udhi = h->udhl = h->udl = 0;
01148          h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
01149          h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
01150          h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
01151          h->mr = -1;
01152          p += unpackaddress(h->oa, h->imsg + p);
01153          h->pid = h->imsg[p++];
01154          h->dcs = h->imsg[p++];
01155          h->scts = unpackdate(h->imsg + p);
01156          p += 7;
01157          p += unpacksms(h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
01158          h->rx = 1;                      /* received message */
01159          sms_writefile(h);               /* write the file */
01160          if (p != h->imsg[1] + 2) {
01161             ast_log(LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
01162             return 0xFF;                /* duh! */
01163          }
01164       } else {
01165          ast_log(LOG_WARNING, "Unknown message type %02X\n", (unsigned)h->imsg[2]);
01166          return 0xFF;
01167       }
01168    }
01169    return 0;                               /* no error */
01170 }

static int sms_handleincoming_proto2 ( sms_t h  )  [static]

sms_handleincoming_proto2: handle the incoming message

Definition at line 1254 of file app_sms.c.

References ast_copy_string(), ast_localtime(), ast_mktime(), ast_tvnow(), ast_verb, sms_s::da, f, sms_s::imsg, MAX_DEBUG_LEN, sms_s::oa, sms_s::rx, sms_s::scts, sms_hexdump(), sms_writefile(), ast_tm::tm_hour, ast_tm::tm_mday, ast_tm::tm_min, ast_tm::tm_mon, ast_tm::tm_sec, sms_s::ud, and sms_s::udl.

Referenced by sms_messagerx2().

01255 {
01256    int f, i, sz = 0;
01257    int msg, msgsz;
01258    struct ast_tm tm;
01259    struct timeval now = { 0, 0 };
01260    char debug_buf[MAX_DEBUG_LEN * 3 + 1];
01261 
01262    sz = h->imsg[1] + 2;
01263    /* ast_verb(3, "SMS-P2 Frame: %s\n", sms_hexdump(h->imsg, sz, debug_buf)); */
01264 
01265    /* Parse message body (called payload) */
01266    now = h->scts = ast_tvnow();
01267    for (f = 4; f < sz; ) {
01268       msg = h->imsg[f++];
01269       msgsz = h->imsg[f++];
01270       msgsz += (h->imsg[f++] * 256);
01271       switch (msg) {
01272       case 0x13:                          /* Body */
01273          ast_verb(3, "SMS-P2 Body#%02X=[%.*s]\n", (unsigned)msg, msgsz, &h->imsg[f]);
01274          if (msgsz >= sizeof(h->ud)) {
01275             msgsz = sizeof(h->ud) - 1;
01276          }
01277          for (i = 0; i < msgsz; i++) {
01278             h->ud[i] = h->imsg[f + i];
01279          }
01280          h->udl = msgsz;
01281          break;
01282       case 0x14:                          /* Date SCTS */
01283          now = h->scts = ast_tvnow();
01284          ast_localtime(&now, &tm, NULL);
01285          tm.tm_mon = ( (h->imsg[f] * 10) + h->imsg[f + 1] ) - 1;
01286          tm.tm_mday = ( (h->imsg[f + 2] * 10) + h->imsg[f + 3] );
01287          tm.tm_hour = ( (h->imsg[f + 4] * 10) + h->imsg[f + 5] );
01288          tm.tm_min = ( (h->imsg[f + 6] * 10) + h->imsg[f + 7] );
01289          tm.tm_sec = 0;
01290          h->scts = ast_mktime(&tm, NULL);
01291          ast_verb(3, "SMS-P2 Date#%02X=%02d/%02d %02d:%02d\n", (unsigned)msg, tm.tm_mday, tm.tm_mon + 1, tm.tm_hour, tm.tm_min);
01292          break;
01293       case 0x15:                          /* Calling line (from SMSC) */
01294          if (msgsz >= 20) {
01295             msgsz = 20 - 1;
01296          }
01297          ast_verb(3, "SMS-P2 Origin#%02X=[%.*s]\n", (unsigned)msg, msgsz, &h->imsg[f]);
01298          ast_copy_string(h->oa, (char *)(&h->imsg[f]), msgsz + 1);
01299          break;
01300       case 0x18:                          /* Destination(from TE/phone) */
01301          if (msgsz >= 20) {
01302             msgsz = 20 - 1;
01303          }
01304          ast_verb(3, "SMS-P2 Destination#%02X=[%.*s]\n", (unsigned)msg, msgsz, &h->imsg[f]);
01305          ast_copy_string(h->da, (char *)(&h->imsg[f]), msgsz + 1);
01306          break;
01307       case 0x1C:                          /* Notify */
01308          ast_verb(3, "SMS-P2 Notify#%02X=%s\n", (unsigned)msg, sms_hexdump(&h->imsg[f], 3, debug_buf));
01309          break;
01310       default:
01311          ast_verb(3, "SMS-P2 Par#%02X [%d]: %s\n", (unsigned)msg, msgsz, sms_hexdump(&h->imsg[f], msgsz, debug_buf));
01312          break;
01313       }
01314       f+=msgsz;                           /* Skip to next */
01315    }
01316    h->rx = 1;                              /* received message */
01317    sms_writefile(h);                       /* write the file */
01318    return 0;                               /* no error */
01319 }

static char* sms_hexdump ( unsigned char  buf[],
int  size,
char *  s 
) [static]

Definition at line 1241 of file app_sms.c.

References f, and MAX_DEBUG_LEN.

Referenced by sms_handleincoming_proto2().

01242 {
01243    char *p;
01244    int f;
01245 
01246    for (p = s, f = 0; f < size && f < MAX_DEBUG_LEN; f++, p += 3) {
01247       sprintf(p, "%02X ", (unsigned)buf[f]);
01248    }
01249    return(s);
01250 }

static void sms_log ( sms_t h,
char  status 
) [static]

Log the output, and remove file.

Definition at line 771 of file app_sms.c.

References AST_FILE_MODE, ast_log(), sms_s::da, errno, isodate(), LOG_WARNING, sms_s::mr, sms_s::oa, sms_s::queue, sms_s::rx, S_OR, sms_s::smsc, sms_s::ud, and sms_s::udl.

Referenced by sms_messagerx(), and sms_messagerx2().

00772 {
00773    int o;
00774 
00775    if (*h->oa == '\0' && *h->da == '\0') {
00776       return;
00777    }
00778    o = open(log_file, O_CREAT | O_APPEND | O_WRONLY, AST_FILE_MODE);
00779    if (o >= 0) {
00780       char line[1000], mrs[3] = "", *p;
00781       char buf[30];
00782       unsigned char n;
00783 
00784       if (h->mr >= 0) {
00785          snprintf(mrs, sizeof(mrs), "%02X", (unsigned)h->mr);
00786       }
00787       snprintf(line, sizeof(line), "%s %c%c%c%s %s %s %s ",
00788          isodate(time(NULL), buf, sizeof(buf)),
00789          status, h->rx ? 'I' : 'O', h->smsc ? 'S' : 'M', mrs, h->queue,
00790          S_OR(h->oa, "-"), S_OR(h->da, "-") );
00791       p = line + strlen(line);
00792       for (n = 0; n < h->udl; n++) {
00793          if (h->ud[n] == '\\') {
00794             *p++ = '\\';
00795             *p++ = '\\';
00796          } else if (h->ud[n] == '\n') {
00797             *p++ = '\\';
00798             *p++ = 'n';
00799          } else if (h->ud[n] == '\r') {
00800             *p++ = '\\';
00801             *p++ = 'r';
00802          } else if (h->ud[n] < 32 || h->ud[n] == 127) {
00803             *p++ = 191;
00804          } else {
00805             *p++ = h->ud[n];
00806          }
00807       }
00808       *p++ = '\n';
00809       *p = 0;
00810       if (write(o, line, strlen(line)) < 0) {
00811          ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00812       }
00813       close(o);
00814    }
00815    *h->oa = *h->da = h->udl = 0;
00816 }

static void sms_messagerx ( sms_t h  )  [static]

Definition at line 1495 of file app_sms.c.

References DIR_RX, sms_s::err, sms_s::hangup, sms_s::imsg, sms_s::omsg, sms_s::protocol, sms_debug(), sms_handleincoming(), sms_log(), sms_messagerx2(), sms_messagetx(), and sms_nextoutgoing().

Referenced by sms_process().

01496 {
01497    int cause;
01498 
01499    sms_debug (DIR_RX, h);
01500    if (h->protocol == 2) {
01501       sms_messagerx2(h);
01502       return;
01503    }
01504    /* parse incoming message for Protocol 1 */
01505    switch (h->imsg[0]) {
01506    case 0x91:                              /* SMS_DATA */
01507       cause = sms_handleincoming (h);
01508       if (!cause) {
01509          sms_log(h, 'Y');
01510          h->omsg[0] = 0x95;              /* SMS_ACK */
01511          h->omsg[1] = 0x02;
01512          h->omsg[2] = 0x00;              /* deliver report */
01513          h->omsg[3] = 0x00;              /* no parameters */
01514       } else {                            /* NACK */
01515          sms_log(h, 'N');
01516          h->omsg[0] = 0x96;              /* SMS_NACK */
01517          h->omsg[1] = 3;
01518          h->omsg[2] = 0;                 /* delivery report */
01519          h->omsg[3] = cause;             /* cause */
01520          h->omsg[4] = 0;                 /* no parameters */
01521       }
01522       sms_messagetx(h);
01523       break;
01524 
01525    case 0x92:                              /* SMS_ERROR */
01526       h->err = 1;
01527       sms_messagetx(h);                   /* send whatever we sent again */
01528       break;
01529    case 0x93:                              /* SMS_EST */
01530       sms_nextoutgoing (h);
01531       break;
01532    case 0x94:                              /* SMS_REL */
01533       h->hangup = 1;                      /* hangup */
01534       break;
01535    case 0x95:                              /* SMS_ACK */
01536       sms_log(h, 'Y');
01537       sms_nextoutgoing (h);
01538       break;
01539    case 0x96:                              /* SMS_NACK */
01540       h->err = 1;
01541       sms_log(h, 'N');
01542       sms_nextoutgoing (h);
01543       break;
01544    default:                                /* Unknown */
01545       h->omsg[0] = 0x92;                  /* SMS_ERROR */
01546       h->omsg[1] = 1;
01547       h->omsg[2] = 3;                     /* unknown message type */
01548       sms_messagetx(h);
01549       break;
01550    }
01551 }

static void sms_messagerx2 ( sms_t h  )  [static]

Definition at line 1335 of file app_sms.c.

References ast_log(), DLL2_ACK, DLL2_SMS_ACK0, DLL2_SMS_ACK1, DLL2_SMS_EST, DLL2_SMS_INFO_MO, DLL2_SMS_INFO_MT, DLL2_SMS_NACK, DLL2_SMS_REL, DLL_SMS_MASK, sms_s::hangup, sms_s::imsg, LOG_NOTICE, sms_s::omsg, sms_handleincoming_proto2(), sms_log(), sms_messagetx(), and sms_nextoutgoing().

Referenced by sms_messagerx().

01336 {
01337    int p = h->imsg[0] & DLL_SMS_MASK ; /* mask the high bit */
01338    int cause;
01339 
01340 #define DLL2_ACK(h) ((h->framenumber & 1) ? DLL2_SMS_ACK1: DLL2_SMS_ACK1)
01341    switch (p) {
01342    case DLL2_SMS_EST:                      /* Protocol 2: Connection ready (fake): send message  */
01343       sms_nextoutgoing (h);
01344       /* smssend(h,"11 29 27 00 10 01 00 00 11 06 00 00 00 00 00 00 00 12 03 00 02 00 04 13 01 00 41 14 08 00 30 39 31 35 30 02 30 02 15 02 00 39 30 "); */
01345       break;
01346 
01347    case DLL2_SMS_INFO_MO:                  /* transport SMS_SUBMIT */
01348    case DLL2_SMS_INFO_MT:                  /* transport SMS_DELIVERY */
01349       cause = sms_handleincoming_proto2(h);
01350       if (!cause) {                       /* ACK */
01351          sms_log(h, 'Y');
01352       }
01353       h->omsg[0] = DLL2_ACK(h);
01354       h->omsg[1] = 0x06;                  /* msg len */
01355       h->omsg[2] = 0x04;                  /* payload len */
01356       h->omsg[3] = 0x00;                  /* payload len */
01357       h->omsg[4] = 0x1f;                  /* Response type */
01358       h->omsg[5] = 0x01;                  /* parameter len */
01359       h->omsg[6] = 0x00;                  /* parameter len */
01360       h->omsg[7] = cause;                 /* CONFIRM or error */
01361       sms_messagetx(h);
01362       break;
01363 
01364    case DLL2_SMS_NACK:                     /* Protocol 2: SMS_NAK */
01365       h->omsg[0] = DLL2_SMS_REL;          /* SMS_REL */
01366       h->omsg[1] = 0x00;                  /* msg len */
01367       sms_messagetx(h);
01368       break;
01369 
01370    case DLL2_SMS_ACK0:
01371    case DLL2_SMS_ACK1:
01372       /* SMS_ACK also transport SMS_SUBMIT or SMS_DELIVERY */
01373       if ( (h->omsg[0] & DLL_SMS_MASK) == DLL2_SMS_REL) {
01374          /* a response to our Release, just hangup */
01375          h->hangup = 1;                  /* hangup */
01376       } else {
01377          /* XXX depending on what we are.. */
01378          ast_log(LOG_NOTICE, "SMS_SUBMIT or SMS_DELIVERY\n");
01379          sms_nextoutgoing (h);
01380       }
01381       break;
01382 
01383    case DLL2_SMS_REL:                      /* Protocol 2: SMS_REL (hangup req) */
01384       h->omsg[0] = DLL2_ACK(h);
01385       h->omsg[1] = 0;
01386       sms_messagetx(h);
01387       break;
01388    }
01389 }

static void sms_messagetx ( sms_t h  )  [static]

Definition at line 1553 of file app_sms.c.

References DIR_TX, sms_s::framenumber, len(), sms_s::obitp, sms_s::obyte, sms_s::obyten, sms_s::obytep, sms_s::omsg, sms_s::opause, sms_s::opause_0, sms_s::oseizure, sms_s::osync, OSYNC_BITS, sms_s::protocol, and sms_debug().

Referenced by sms_messagerx(), sms_messagerx2(), sms_nextoutgoing(), and sms_process().

01554 {
01555    unsigned char c = 0, p;
01556    int len = h->omsg[1] + 2;               /* total message length excluding checksum */
01557 
01558    for (p = 0; p < len; p++) {             /* compute checksum */
01559       c += h->omsg[p];
01560    }
01561    h->omsg[len] = 0 - c;                   /* actually, (256 - (c & 0fxx)) & 0xff) */
01562    sms_debug(DIR_TX, h);
01563    h->framenumber++;                       /* Proto 2 */
01564    h->obytep = 0;
01565    h->obitp = 0;
01566    if (h->protocol == 2) {                 /* Proto 2: */
01567       h->oseizure = 300;                  /* 300bits (or more ?) */
01568       h->obyte = 0;                       /* Seizure starts with  space (0) */
01569       if (h->omsg[0] == 0x7F) {
01570          h->opause = 8 * h->opause_0;    /* initial message delay */
01571       } else {
01572          h->opause = 400;
01573       }
01574    } else {                                /* Proto 1: */
01575       h->oseizure = 0;                    /* No seizure */
01576       h->obyte = 1;                       /* send mark ('1') at the beginning */
01577       /* Change the initial message delay. BT requires 300ms,
01578        * but for others this might be way too much and the phone
01579        * could time out. XXX make it configurable.
01580       */
01581       if (h->omsg[0] == 0x93) {
01582          h->opause = 8 * h->opause_0;    /* initial message delay */
01583       } else {
01584          h->opause = 200;
01585       }
01586    }
01587    /* Note - setting osync triggers the generator */
01588    h->osync = OSYNC_BITS;                  /* 80 sync bits */
01589    h->obyten = len + 1;                    /* bytes to send (including checksum) */
01590 }

static void sms_nextoutgoing ( sms_t h  )  [static]

find and fill in next message, or send a REL if none waiting

Definition at line 1434 of file app_sms.c.

References ast_config_AST_SPOOL_DIR, ast_mkdir(), sms_s::da, sms_s::oa, sms_s::omsg, sms_s::protocol, sms_s::queue, readdirqueue(), sms_s::rx, sms_s::sent_rel, sms_compose1(), sms_compose2(), sms_messagetx(), sms_readfile(), and sms_s::smsc.

Referenced by sms_messagerx(), and sms_messagerx2().

01435 {    
01436    char fn[100 + NAME_MAX] = "";
01437    DIR *d;
01438    char more = 0;
01439 
01440    *h->da = *h->oa = '\0';                 /* clear destinations */
01441    h->rx = 0;                              /* outgoing message */
01442    snprintf(fn, sizeof(fn), "%s/sms/%s", ast_config_AST_SPOOL_DIR, h->smsc ? "mttx" : "motx");
01443    ast_mkdir(fn, 0777);                    /* ensure it exists */
01444    d = opendir(fn);
01445    if (d) {
01446       struct dirent *f = readdirqueue(d, h->queue);
01447       if (f) {
01448          snprintf(fn + strlen(fn), sizeof(fn) - strlen(fn), "/%s", f->d_name);
01449          sms_readfile(h, fn);
01450          if (readdirqueue(d, h->queue)) {
01451             more = 1;                   /* more to send */
01452          }
01453       }
01454       closedir(d);
01455    }
01456    if (*h->da || *h->oa) {                 /* message to send */
01457       if (h->protocol == 2) {
01458          sms_compose2(h, more);
01459       } else {
01460          sms_compose1(h, more);
01461       }
01462    } else {                                /* no message */
01463       if (h->protocol == 2) {
01464          h->omsg[0] = 0x17;              /* SMS_REL */
01465          h->omsg[1] = 0;
01466       } else {
01467          h->omsg[0] = 0x94;              /* SMS_REL */
01468          h->omsg[1] = 0;
01469          h->sent_rel = 1;
01470       }
01471    }
01472    sms_messagetx(h);
01473 }

static void sms_process ( sms_t h,
int  samples,
signed short *  data 
) [static]

Process an incoming frame, trying to detect the carrier and decode the message. The two frequencies are 1300 and 2100 Hz. The decoder detects the amplitude of the signal over the last few samples, filtering the absolute values with a lowpass filter. If the magnitude (h->imag) is large enough, multiply the signal by the two carriers, and compute the amplitudes m0 and m1. Record the current sample as '0' or '1' depending on which one is greater. The last 3 bits are stored in h->ibith, with the count of '1' bits in h->ibitt. XXX the rest is to be determined.

Definition at line 1702 of file app_sms.c.

References ast_log(), ast_verb, sms_s::err, sms_s::framenumber, sms_s::hangup, sms_s::ibitc, sms_s::ibith, sms_s::ibitl, sms_s::ibitn, sms_s::ibitt, sms_s::ibytec, sms_s::ibytep, sms_s::ibytev, sms_s::idle, sms_s::ierr, sms_s::imag, sms_s::imc0, sms_s::imc1, sms_s::ims0, sms_s::ims1, sms_s::imsg, sms_s::ipc0, sms_s::ipc1, sms_s::iphasep, sms_s::ips0, sms_s::ips1, LOG_NOTICE, sms_s::obyten, sms_s::omsg, sms_s::osync, sms_s::protocol, sms_s::sent_rel, sms_messagerx(), and sms_messagetx().

01703 {
01704    int bit;
01705 
01706    /*
01707     * Ignore incoming audio while a packet is being transmitted,
01708     * the protocol is half-duplex.
01709     * Unfortunately this means that if the outbound and incoming
01710     * transmission overlap (which is an error condition anyways),
01711     * we may miss some data and this makes debugging harder.
01712     */
01713    if (h->obyten || h->osync) {
01714       return;
01715    }
01716    for ( ; samples-- ; data++) {
01717       unsigned long long m0, m1;
01718       if (abs(*data) > h->imag) {
01719          h->imag = abs(*data);
01720       } else {
01721          h->imag = h->imag * 7 / 8;
01722       }
01723       if (h->imag <= 500) {               /* below [arbitrary] threahold: lost carrier */
01724          if (h->idle++ == 80000) {       /* nothing happening */
01725             ast_log(LOG_NOTICE, "No data, hanging up\n");
01726             h->hangup = 1;
01727             h->err = 1;
01728          }
01729          if (h->ierr) {                  /* error */
01730             ast_log(LOG_NOTICE, "Error %d, hanging up\n", h->ierr);
01731             /* Protocol 1 */
01732             h->err = 1;
01733             h->omsg[0] = 0x92;          /* error */
01734             h->omsg[1] = 1;
01735             h->omsg[2] = h->ierr;
01736             sms_messagetx(h);           /* send error */
01737          }
01738          h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
01739          continue;
01740       }
01741       h->idle = 0;
01742 
01743       /* multiply signal by the two carriers. */
01744       h->ims0 = (h->ims0 * 6 + *data * wave[h->ips0]) / 7;
01745       h->imc0 = (h->imc0 * 6 + *data * wave[h->ipc0]) / 7;
01746       h->ims1 = (h->ims1 * 6 + *data * wave[h->ips1]) / 7;
01747       h->imc1 = (h->imc1 * 6 + *data * wave[h->ipc1]) / 7;
01748       /* compute the amplitudes */
01749       m0 = h->ims0 * h->ims0 + h->imc0 * h->imc0;
01750       m1 = h->ims1 * h->ims1 + h->imc1 * h->imc1;
01751 
01752       /* advance the sin/cos pointers */
01753       if ((h->ips0 += 21) >= 80) {
01754          h->ips0 -= 80;
01755       }
01756       if ((h->ipc0 += 21) >= 80) {
01757          h->ipc0 -= 80;
01758       }
01759       if ((h->ips1 += 13) >= 80) {
01760          h->ips1 -= 80;
01761       }
01762       if ((h->ipc1 += 13) >= 80) {
01763          h->ipc1 -= 80;
01764       }
01765 
01766       /* set new bit to 1 or 0 depending on which value is stronger */
01767       h->ibith <<= 1;
01768       if (m1 > m0) {
01769          h->ibith |= 1;
01770       }
01771       if (h->ibith & 8) {
01772          h->ibitt--;
01773       }
01774       if (h->ibith & 1) {
01775          h->ibitt++;
01776       }
01777       bit = ((h->ibitt > 1) ? 1 : 0);
01778       if (bit != h->ibitl) {
01779          h->ibitc = 1;
01780       } else {
01781          h->ibitc++;
01782       }
01783       h->ibitl = bit;
01784       if (!h->ibitn && h->ibitc == 4 && !bit) {
01785          h->ibitn = 1;
01786          h->iphasep = 0;
01787       }
01788       if (bit && h->ibitc == 200) {       /* sync, restart message */
01789          /* Protocol 2: empty connection ready (I am master) */
01790          if (h->framenumber < 0 && h->ibytec >= 160 && !memcmp(h->imsg, "UUUUUUUUUUUUUUUUUUUU", 20)) {
01791             h->framenumber = 1;
01792             ast_verb(3, "SMS protocol 2 detected\n");
01793             h->protocol = 2;
01794             h->imsg[0] = 0xff;          /* special message (fake) */
01795             h->imsg[1] = h->imsg[2] = 0x00;
01796             h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
01797             sms_messagerx(h);
01798          }
01799          h->ierr = h->ibitn = h->ibytep = h->ibytec = 0;
01800       }
01801       if (h->ibitn) {
01802          h->iphasep += 12;
01803          if (h->iphasep >= 80) {         /* next bit */
01804             h->iphasep -= 80;
01805             if (h->ibitn++ == 9) {      /* end of byte */
01806                if (!bit) {             /* bad stop bit */
01807                   if (h->sent_rel) {
01808                      h->hangup = 1;
01809                   } else {
01810                      ast_log(LOG_NOTICE, "Bad stop bit\n");
01811                      h->ierr = 0xFF;     /* unknown error */
01812                   }
01813                } else {
01814                   if (h->ibytep < sizeof(h->imsg)) {
01815                      h->imsg[h->ibytep] = h->ibytev;
01816                      h->ibytec += h->ibytev;
01817                      h->ibytep++;
01818                   } else if (h->ibytep == sizeof(h->imsg)) {
01819                      ast_log(LOG_NOTICE, "msg too large\n");
01820                      h->ierr = 2;    /* bad message length */
01821                   }
01822                   if (h->ibytep > 1 && h->ibytep == 3 + h->imsg[1] && !h->ierr) {
01823                      if (!h->ibytec) {
01824                         sms_messagerx(h);
01825                      } else {
01826                         ast_log(LOG_NOTICE, "bad checksum\n");
01827                         h->ierr = 1; /* bad checksum */
01828                      }
01829                   }
01830                }
01831                h->ibitn = 0;
01832             }
01833             h->ibytev = (h->ibytev >> 1) + (bit ? 0x80 : 0);
01834          }
01835       }
01836    }
01837 }

static void sms_readfile ( sms_t h,
char *  fn 
) [static]

parse and delete a file

Definition at line 819 of file app_sms.c.

References ast_log(), ast_mktime(), ast_tvnow(), sms_s::da, sms_s::dcs, is16bit, is7bit, is8bit, LOG_NOTICE, LOG_WARNING, sms_s::mr, numcpy(), sms_s::oa, packsms16(), packsms7(), packsms8(), sms_s::pid, sms_s::rp, sms_s::rx, S, sms_s::scts, SMSLEN, sms_s::srr, ast_tm::tm_hour, ast_tm::tm_isdst, ast_tm::tm_mday, ast_tm::tm_min, ast_tm::tm_mon, ast_tm::tm_sec, ast_tm::tm_year, sms_s::ud, sms_s::udh, sms_s::udhi, sms_s::udhl, sms_s::udl, sms_s::udtxt, utf8decode(), and sms_s::vp.

Referenced by sms_nextoutgoing().

00820 {
00821    char line[1000];
00822    FILE *s;
00823    char dcsset = 0;                        /* if DSC set */
00824    ast_log(LOG_NOTICE, "Sending %s\n", fn);
00825    h->rx = h->udl = *h->oa = *h->da = h->pid = h->srr = h->udhi = h->rp = h->vp = h->udhl = 0;
00826    h->mr = -1;
00827    h->dcs = 0xF1;                          /* normal messages class 1 */
00828    h->scts = ast_tvnow();
00829    s = fopen(fn, "r");
00830    if (s) {
00831       if (unlink(fn)) {                   /* concurrent access, we lost */
00832          fclose(s);
00833          return;
00834       }
00835       while (fgets (line, sizeof(line), s)) {   /* process line in file */
00836          char *p;
00837          void *pp = &p;
00838          for (p = line; *p && *p != '\n' && *p != '\r'; p++);
00839          *p = 0;                         /* strip eoln */
00840          p = line;
00841          if (!*p || *p == ';') {
00842             continue;                   /* blank line or comment, ignore */
00843          }
00844          while (isalnum(*p)) {
00845             *p = tolower (*p);
00846             p++;
00847          }
00848          while (isspace (*p)) {
00849             *p++ = 0;
00850          }
00851          if (*p == '=') {
00852             *p++ = 0;
00853             if (!strcmp(line, "ud")) {  /* parse message (UTF-8) */
00854                unsigned char o = 0;
00855                memcpy(h->udtxt, p, SMSLEN); /* for protocol 2 */
00856                while (*p && o < SMSLEN) {
00857                   h->ud[o++] = utf8decode(pp);
00858                }
00859                h->udl = o;
00860                if (*p) {
00861                   ast_log(LOG_WARNING, "UD too long in %s\n", fn);
00862                }
00863             } else {
00864                while (isspace (*p)) {
00865                   p++;
00866                }
00867                if (!strcmp(line, "oa") && strlen(p) < sizeof(h->oa)) {
00868                   numcpy (h->oa, p);
00869                } else if (!strcmp(line, "da") && strlen(p) < sizeof(h->oa)) {
00870                   numcpy (h->da, p);
00871                } else if (!strcmp(line, "pid")) {
00872                   h->pid = atoi(p);
00873                } else if (!strcmp(line, "dcs")) {
00874                   h->dcs = atoi(p);
00875                   dcsset = 1;
00876                } else if (!strcmp(line, "mr")) {
00877                   h->mr = atoi(p);
00878                } else if (!strcmp(line, "srr")) {
00879                   h->srr = (atoi(p) ? 1 : 0);
00880                } else if (!strcmp(line, "vp")) {
00881                   h->vp = atoi(p);
00882                } else if (!strcmp(line, "rp")) {
00883                   h->rp = (atoi(p) ? 1 : 0);
00884                } else if (!strcmp(line, "scts")) {    /* get date/time */
00885                   int Y, m, d, H, M, S;
00886                   /* XXX Why aren't we using ast_strptime here? */
00887                   if (sscanf(p, "%4d-%2d-%2dT%2d:%2d:%2d", &Y, &m, &d, &H, &M, &S) == 6) {
00888                      struct ast_tm t = { 0, };
00889                      t.tm_year = Y - 1900;
00890                      t.tm_mon = m - 1;
00891                      t.tm_mday = d;
00892                      t.tm_hour = H;
00893                      t.tm_min = M;
00894                      t.tm_sec = S;
00895                      t.tm_isdst = -1;
00896                      h->scts = ast_mktime(&t, NULL);
00897                      if (h->scts.tv_sec == 0) {
00898                         ast_log(LOG_WARNING, "Bad date/timein %s: %s", fn, p);
00899                      }
00900                   }
00901                } else {
00902                   ast_log(LOG_WARNING, "Cannot parse in %s: %s=%si\n", fn, line, p);
00903                }
00904             }
00905          } else if (*p == '#') {                   /* raw hex format */
00906             *p++ = 0;
00907             if (*p == '#') {
00908                p++;
00909                if (!strcmp(line, "ud")) {        /* user data */
00910                   int o = 0;
00911                   while (*p && o < SMSLEN) {
00912                      if (isxdigit(*p) && isxdigit(p[1]) && isxdigit(p[2]) && isxdigit(p[3])) {
00913                         h->ud[o++] =
00914                            (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 12) +
00915                            (((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
00916                            (((isalpha(p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha(p[3]) ? 9 : 0) + (p[3] & 0xF));
00917                         p += 4;
00918                      } else
00919                         break;
00920                   }
00921                   h->udl = o;
00922                   if (*p)
00923                      ast_log(LOG_WARNING, "UD too long / invalid UCS-2 hex in %s\n", fn);
00924                } else
00925                   ast_log(LOG_WARNING, "Only ud can use ## format, %s\n", fn);
00926             } else if (!strcmp(line, "ud")) {       /* user data */
00927                int o = 0;
00928                while (*p && o < SMSLEN) {
00929                   if (isxdigit(*p) && isxdigit(p[1])) {
00930                      h->ud[o++] = (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF));
00931                      p += 2;
00932                   } else {
00933                      break;
00934                   }
00935                }
00936                h->udl = o;
00937                if (*p) {
00938                   ast_log(LOG_WARNING, "UD too long / invalid UCS-1 hex in %s\n", fn);
00939                }
00940             } else if (!strcmp(line, "udh")) {      /* user data header */
00941                unsigned char o = 0;
00942                h->udhi = 1;
00943                while (*p && o < SMSLEN) {
00944                   if (isxdigit(*p) && isxdigit(p[1])) {
00945                      h->udh[o] = (((isalpha(*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha(p[1]) ? 9 : 0) + (p[1] & 0xF));
00946                      o++;
00947                      p += 2;
00948                   } else {
00949                      break;
00950                   }
00951                }
00952                h->udhl = o;
00953                if (*p) {
00954                   ast_log(LOG_WARNING, "UDH too long / invalid hex in %s\n", fn);
00955                }
00956             } else {
00957                ast_log(LOG_WARNING, "Only ud and udh can use # format, %s\n", fn);
00958             }
00959          } else {
00960             ast_log(LOG_WARNING, "Cannot parse in %s: %s\n", fn, line);
00961          }
00962       }
00963       fclose(s);
00964       if (!dcsset && packsms7(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
00965          if (packsms8(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
00966             if (packsms16(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
00967                ast_log(LOG_WARNING, "Invalid UTF-8 message even for UCS-2 (%s)\n", fn);
00968             } else {
00969                h->dcs = 0x08;          /* default to 16 bit */
00970                ast_log(LOG_WARNING, "Sending in 16 bit format(%s)\n", fn);
00971             }
00972          } else {
00973             h->dcs = 0xF5;              /* default to 8 bit */
00974             ast_log(LOG_WARNING, "Sending in 8 bit format(%s)\n", fn);
00975          }
00976       }
00977       if (is7bit(h->dcs) && packsms7(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
00978          ast_log(LOG_WARNING, "Invalid 7 bit GSM data %s\n", fn);
00979       }
00980       if (is8bit(h->dcs) && packsms8(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
00981          ast_log(LOG_WARNING, "Invalid 8 bit data %s\n", fn);
00982       }
00983       if (is16bit(h->dcs) && packsms16(0, h->udhl, h->udh, h->udl, h->ud) < 0) {
00984          ast_log(LOG_WARNING, "Invalid 16 bit data %s\n", fn);
00985       }
00986    }
00987 }

static void sms_release ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 1679 of file app_sms.c.

01680 {
01681    return;  /* nothing to do here. */
01682 }

static void sms_writefile ( sms_t h  )  [static]

white a received text message to a file

Definition at line 990 of file app_sms.c.

References ast_config_AST_SPOOL_DIR, ast_copy_string(), ast_log(), ast_mkdir(), ast_tvnow(), ast_tvzero(), sms_s::da, sms_s::dcs, isodate(), LOG_NOTICE, sms_s::mr, sms_s::oa, sms_s::pid, sms_s::queue, sms_s::rp, sms_s::rx, sms_s::scts, sms_s::smsc, sms_s::srr, sms_s::ud, sms_s::udh, sms_s::udhi, sms_s::udhl, sms_s::udl, and sms_s::vp.

Referenced by sms_handleincoming(), and sms_handleincoming_proto2().

00991 {
00992    char fn[200] = "", fn2[200] = "";
00993    char buf[30];
00994    FILE *o;
00995 
00996    if (ast_tvzero(h->scts)) {
00997       h->scts = ast_tvnow();
00998    }
00999    snprintf(fn, sizeof(fn), "%s/sms/%s", ast_config_AST_SPOOL_DIR, h->smsc ? h->rx ? "morx" : "mttx" : h->rx ? "mtrx" : "motx");
01000    ast_mkdir(fn, 0777);                    /* ensure it exists */
01001    ast_copy_string(fn2, fn, sizeof(fn2));
01002    snprintf(fn2 + strlen(fn2), sizeof(fn2) - strlen(fn2), "/%s.%s-%u", h->queue, isodate(h->scts.tv_sec, buf, sizeof(buf)), seq++);
01003    snprintf(fn + strlen(fn), sizeof(fn) - strlen(fn), "/.%s", fn2 + strlen(fn) + 1);
01004    if ((o = fopen(fn, "w")) == NULL) {
01005       return;
01006    }
01007 
01008    if (*h->oa) {
01009       fprintf(o, "oa=%s\n", h->oa);
01010    }
01011    if (*h->da) {
01012       fprintf(o, "da=%s\n", h->da);
01013    }
01014    if (h->udhi) {
01015       unsigned int p;
01016       fprintf(o, "udh#");
01017       for (p = 0; p < h->udhl; p++) {
01018          fprintf(o, "%02X", (unsigned)h->udh[p]);
01019       }
01020       fprintf(o, "\n");
01021    }
01022    if (h->udl) {
01023       unsigned int p;
01024       for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
01025       if (p < h->udl) {
01026          fputc(';', o);                  /* cannot use ud=, but include as a comment for human readable */
01027       }
01028       fprintf(o, "ud=");
01029       for (p = 0; p < h->udl; p++) {
01030          unsigned short v = h->ud[p];
01031          if (v < 32) {
01032             fputc(191, o);
01033          } else if (v < 0x80) {
01034             fputc(v, o);
01035          } else if (v < 0x800) {
01036             fputc(0xC0 + (v >> 6), o);
01037             fputc(0x80 + (v & 0x3F), o);
01038          } else {
01039             fputc(0xE0 + (v >> 12), o);
01040             fputc(0x80 + ((v >> 6) & 0x3F), o);
01041             fputc(0x80 + (v & 0x3F), o);
01042          }
01043       }
01044       fprintf(o, "\n");
01045       for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
01046       if (p < h->udl) {
01047          for (p = 0; p < h->udl && h->ud[p] < 0x100; p++);
01048          if (p == h->udl) {              /* can write in ucs-1 hex */
01049             fprintf(o, "ud#");
01050             for (p = 0; p < h->udl; p++) {
01051                fprintf(o, "%02X", (unsigned)h->ud[p]);
01052             }
01053             fprintf(o, "\n");
01054          } else {                        /* write in UCS-2 */
01055             fprintf(o, "ud##");
01056             for (p = 0; p < h->udl; p++) {
01057                fprintf(o, "%04X", (unsigned)h->ud[p]);
01058             }
01059             fprintf(o, "\n");
01060          }
01061       }
01062    }
01063    if (h->scts.tv_sec) {
01064       char datebuf[30];
01065       fprintf(o, "scts=%s\n", isodate(h->scts.tv_sec, datebuf, sizeof(datebuf)));
01066    }
01067    if (h->pid) {
01068       fprintf(o, "pid=%d\n", h->pid);
01069    }
01070    if (h->dcs != 0xF1) {
01071       fprintf(o, "dcs=%d\n", h->dcs);
01072    }
01073    if (h->vp) {
01074       fprintf(o, "vp=%u\n", h->vp);
01075    }
01076    if (h->srr) {
01077       fprintf(o, "srr=1\n");
01078    }
01079    if (h->mr >= 0) {
01080       fprintf(o, "mr=%d\n", h->mr);
01081    }
01082    if (h->rp) {
01083       fprintf(o, "rp=1\n");
01084    }
01085    fclose(o);
01086    if (rename(fn, fn2)) {
01087       unlink(fn);
01088    } else {
01089       ast_log(LOG_NOTICE, "Received to %s\n", fn2);
01090    }
01091 }

static int unload_module ( void   )  [static]

Definition at line 2065 of file app_sms.c.

References ast_unregister_application().

02066 {
02067    return ast_unregister_application(app);
02068 }

static unsigned char unpackaddress ( char *  o,
unsigned char *  i 
) [static]

unpack an address from i, return byte length, unpack to o

Definition at line 725 of file app_sms.c.

Referenced by sms_handleincoming().

00726 {
00727    unsigned char l = i[0], p;
00728    if (i[1] == 0x91) {
00729       *o++ = '+';
00730    }
00731    for (p = 0; p < l; p++) {
00732       if (p & 1) {
00733          *o++ = (i[2 + p / 2] >> 4) + '0';
00734       } else {
00735          *o++ = (i[2 + p / 2] & 0xF) + '0';
00736       }
00737    }
00738    *o = 0;
00739    return (l + 5) / 2;
00740 }

static struct timeval unpackdate ( unsigned char *  i  )  [static, read]

unpack a date and return

Definition at line 576 of file app_sms.c.

References ast_mktime(), ast_tm::tm_hour, ast_tm::tm_isdst, ast_tm::tm_mday, ast_tm::tm_min, ast_tm::tm_mon, ast_tm::tm_sec, and ast_tm::tm_year.

Referenced by sms_handleincoming().

00577 {
00578    struct ast_tm t;
00579 
00580    t.tm_year = 100 + (i[0] & 0xF) * 10 + (i[0] >> 4);
00581    t.tm_mon = (i[1] & 0xF) * 10 + (i[1] >> 4) - 1;
00582    t.tm_mday = (i[2] & 0xF) * 10 + (i[2] >> 4);
00583    t.tm_hour = (i[3] & 0xF) * 10 + (i[3] >> 4);
00584    t.tm_min = (i[4] & 0xF) * 10 + (i[4] >> 4);
00585    t.tm_sec = (i[5] & 0xF) * 10 + (i[5] >> 4);
00586    t.tm_isdst = 0;
00587    if (i[6] & 0x08) {
00588       t.tm_min += 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
00589    } else {
00590       t.tm_min -= 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
00591    }
00592 
00593    return ast_mktime(&t, NULL);
00594 }

static int unpacksms ( unsigned char  dcs,
unsigned char *  i,
unsigned char *  udh,
int *  udhl,
unsigned short *  ud,
int *  udl,
char  udhi 
) [static]

general unpack - starts with length byte (octet or septet) and returns number of bytes used, inc length

Definition at line 709 of file app_sms.c.

References is7bit, is8bit, unpacksms16(), unpacksms7(), and unpacksms8().

Referenced by sms_handleincoming().

00710 {
00711    int l = *i++;
00712    if (is7bit(dcs)) {
00713       unpacksms7(i, l, udh, udhl, ud, udl, udhi);
00714       l = (l * 7 + 7) / 8;                /* adjust length to return */
00715    } else if (is8bit(dcs)) {
00716       unpacksms8(i, l, udh, udhl, ud, udl, udhi);
00717    } else {
00718       l += l % 2;
00719       unpacksms16(i, l, udh, udhl, ud, udl, udhi);
00720    }
00721    return l + 1;
00722 }

static void unpacksms16 ( unsigned char *  i,
unsigned char  l,
unsigned char *  udh,
int *  udhl,
unsigned short *  ud,
int *  udl,
char  udhi 
) [static]

unpacks bytes (16 bit encoding) at i, len l septets, and places in udh and ud setting udhl and udl. udh not used if udhi not set

Definition at line 681 of file app_sms.c.

Referenced by unpacksms().

00682 {
00683    unsigned short *o = ud;
00684    *udhl = 0;
00685    if (udhi) {
00686       int n = *i;
00687       *udhl = n;
00688       if (n) {
00689          i++;
00690          l--;
00691          while (l && n) {
00692             l--;
00693             n--;
00694             *udh++ = *i++;
00695          }
00696       }
00697    }
00698    while (l--) {
00699       int v = *i++;
00700       if (l && l--) {
00701          v = (v << 8) + *i++;
00702       }
00703       *o++ = v;
00704    }
00705    *udl = (o - ud);
00706 }

static void unpacksms7 ( unsigned char *  i,
unsigned char  l,
unsigned char *  udh,
int *  udhl,
unsigned short *  ud,
int *  udl,
char  udhi 
) [static]

unpacks bytes (7 bit encoding) at i, len l septets, and places in udh and ud setting udhl and udl. udh not used if udhi not set

Definition at line 599 of file app_sms.c.

Referenced by unpacksms().

00600 {
00601    unsigned char b = 0, p = 0;
00602    unsigned short *o = ud;
00603    *udhl = 0;
00604    if (udhi && l) {                        /* header */
00605       int h = i[p];
00606       *udhl = h;
00607       if (h) {
00608          b = 1;
00609          p++;
00610          l--;
00611          while (h-- && l) {
00612             *udh++ = i[p++];
00613             b += 8;
00614             while (b >= 7) {
00615                b -= 7;
00616                l--;
00617                if (!l) {
00618                   break;
00619                }
00620             }
00621          }
00622          /* adjust for fill, septets */
00623          if (b) {
00624             b = 7 - b;
00625             l--;
00626          }
00627       }
00628    }
00629    while (l--) {
00630       unsigned char v;
00631       if (b < 2) {
00632          v = ((i[p] >> b) & 0x7F);       /* everything in one byte */
00633       } else {
00634          v = ((((i[p] >> b) + (i[p + 1] << (8 - b)))) & 0x7F);
00635       }
00636       b += 7;
00637       if (b >= 8) {
00638          b -= 8;
00639          p++;
00640       }
00641       /* 0x00A0 is the encoding of ESC (27) in defaultalphabet */
00642       if (o > ud && o[-1] == 0x00A0 && escapes[v]) {
00643          o[-1] = escapes[v];
00644       } else {
00645          *o++ = defaultalphabet[v];
00646       }
00647    }
00648    *udl = (o - ud);
00649 }

static void unpacksms8 ( unsigned char *  i,
unsigned char  l,
unsigned char *  udh,
int *  udhl,
unsigned short *  ud,
int *  udl,
char  udhi 
) [static]

unpacks bytes (8 bit encoding) at i, len l septets, and places in udh and ud setting udhl and udl. udh not used if udhi not set.

Definition at line 655 of file app_sms.c.

Referenced by unpacksms().

00656 {
00657    unsigned short *o = ud;
00658    *udhl = 0;
00659    if (udhi) {
00660       int n = *i;
00661       *udhl = n;
00662       if (n) {
00663          i++;
00664          l--;
00665          while (l && n) {
00666             l--;
00667             n--;
00668             *udh++ = *i++;
00669          }
00670       }
00671    }
00672    while (l--) {
00673       *o++ = *i++;                        /* not to UTF-8 as explicitly 8 bit coding in DCS */
00674    }
00675    *udl = (o - ud);
00676 }

static long utf8decode ( unsigned char **  pp  )  [static]

Reads next UCS character from NUL terminated UTF-8 string and advance pointer.

Definition at line 312 of file app_sms.c.

Referenced by sms_readfile().

00313 {
00314    unsigned char *p = *pp;
00315    if (!*p) {
00316       return 0;                           /* null termination of string */
00317    }
00318    (*pp)++;
00319    if (*p < 0xC0) {
00320       return *p;                          /* ascii or continuation character */
00321    }
00322    if (*p < 0xE0) {
00323       if (*p < 0xC2 || (p[1] & 0xC0) != 0x80) {
00324          return *p;                      /* not valid UTF-8 */
00325       }
00326       (*pp)++;
00327       return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
00328       }
00329    if (*p < 0xF0) {
00330       if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80) {
00331          return *p;                      /* not valid UTF-8 */
00332       }
00333       (*pp) += 2;
00334       return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
00335    }
00336    if (*p < 0xF8) {
00337       if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80) {
00338          return *p;                      /* not valid UTF-8 */
00339       }
00340       (*pp) += 3;
00341       return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
00342    }
00343    if (*p < 0xFC) {
00344       if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
00345          || (p[4] & 0xC0) != 0x80) {
00346          return *p;                      /* not valid UTF-8 */
00347       }
00348       (*pp) += 4;
00349       return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
00350    }
00351    if (*p < 0xFE) {
00352       if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
00353          || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80) {
00354          return *p;                      /* not valid UTF-8 */
00355       }
00356       (*pp) += 5;
00357       return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) + (p[5] & 0x3F);
00358    }
00359    return *p;                              /* not sensible */
00360 }


Variable Documentation

char* app = "SMS" [static]

Definition at line 122 of file app_sms.c.

const unsigned short defaultalphabet[] [static]

Definition at line 189 of file app_sms.c.

const unsigned short escapes[] [static]

Definition at line 202 of file app_sms.c.

char log_file[255] [static]

Definition at line 120 of file app_sms.c.

volatile unsigned char message_ref [static]

Definition at line 117 of file app_sms.c.

volatile unsigned int seq [static]
struct ast_generator smsgen [static]
Initial value:
 {
   .alloc = sms_alloc,
   .release = sms_release,
   .generate = sms_generate,
}

Definition at line 1684 of file app_sms.c.

const signed short wave[] [static]

Definition at line 130 of file app_sms.c.

const output_t* wave_out = wave [static]

Definition at line 146 of file app_sms.c.


Generated on 17 Aug 2018 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1