Sat Nov 1 06:28:22 2008

Asterisk developer's documentation


app_sms.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2004 - 2005, Adrian Kennard, rights assigned to Digium
00005  *
00006  * See http://www.asterisk.org for more information about
00007  * the Asterisk project. Please do not directly contact
00008  * any of the maintainers of this project for assistance;
00009  * the project provides a web site, mailing lists and IRC
00010  * channels for your use.
00011  *
00012  * This program is free software, distributed under the terms of
00013  * the GNU General Public License Version 2. See the LICENSE file
00014  * at the top of the source tree.
00015  */
00016 
00017 /*! \file
00018  *
00019  * \brief SMS application - ETSI ES 201 912 protocol 1 implimentation
00020  * \ingroup applications
00021  *
00022  * \author Adrian Kennard
00023  */
00024 
00025 #include "asterisk.h"
00026 
00027 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 107461 $")
00028 
00029 #include <stdlib.h>
00030 #include <stdio.h>
00031 #include <string.h>
00032 #include <unistd.h>
00033 #include <errno.h>
00034 #include <dirent.h>
00035 #include <ctype.h>
00036 #include <sys/types.h>
00037 #include <sys/stat.h>
00038 
00039 #include "asterisk/lock.h"
00040 #include "asterisk/file.h"
00041 #include "asterisk/logger.h"
00042 #include "asterisk/options.h"
00043 #include "asterisk/channel.h"
00044 #include "asterisk/pbx.h"
00045 #include "asterisk/module.h"
00046 #include "asterisk/alaw.h"
00047 #include "asterisk/callerid.h"
00048 
00049 /* output using Alaw rather than linear */
00050 /* #define OUTALAW */
00051 
00052 /* ToDo */
00053 /* Add full VP support */
00054 /* Handle status report messages (generation and reception) */
00055 /* Time zones on time stamps */
00056 /* user ref field */
00057 
00058 static volatile unsigned char message_ref;      /* arbitary message ref */
00059 static volatile unsigned int seq;       /* arbitrary message sequence number for unqiue files */
00060 
00061 static char log_file[255];
00062 static char spool_dir[255];
00063 
00064 static char *app = "SMS";
00065 
00066 static char *synopsis = "Communicates with SMS service centres and SMS capable analogue phones";
00067 
00068 static char *descrip =
00069    "  SMS(name|[a][s]):  SMS handles exchange of SMS data with a call to/from SMS capabale\n"
00070    "phone or SMS PSTN service center. Can send and/or receive SMS messages.\n"
00071    "Works to ETSI ES 201 912 compatible with BT SMS PSTN service in UK\n"
00072    "Typical usage is to use to handle called from the SMS service centre CLI,\n"
00073    "or to set up a call using 'outgoing' or manager interface to connect\n"
00074    "service centre to SMS()\n"
00075    "name is the name of the queue used in /var/spool/asterisk/sms\n"
00076    "Arguments:\n"
00077    " a: answer, i.e. send initial FSK packet.\n"
00078    " s: act as service centre talking to a phone.\n"
00079    "Messages are processed as per text file message queues.\n" 
00080    "smsq (a separate software) is a command to generate message\n"
00081    "queues and send messages.\n";
00082 
00083 static signed short wave[] = {
00084    0, 392, 782, 1167, 1545, 1913, 2270, 2612, 2939, 3247, 3536, 3802, 4045, 4263, 4455, 4619, 4755, 4862, 4938, 4985,
00085    5000, 4985, 4938, 4862, 4755, 4619, 4455, 4263, 4045, 3802, 3536, 3247, 2939, 2612, 2270, 1913, 1545, 1167, 782, 392,
00086    0, -392, -782, -1167,
00087     -1545, -1913, -2270, -2612, -2939, -3247, -3536, -3802, -4045, -4263, -4455, -4619, -4755, -4862, -4938, -4985, -5000,
00088    -4985, -4938, -4862,
00089    -4755, -4619, -4455, -4263, -4045, -3802, -3536, -3247, -2939, -2612, -2270, -1913, -1545, -1167, -782, -392
00090 };
00091 
00092 #ifdef OUTALAW
00093 static unsigned char wavea[80];
00094 #endif
00095 
00096 
00097 /* SMS 7 bit character mapping to UCS-2 */
00098 static const unsigned short defaultalphabet[] = {
00099    0x0040, 0x00A3, 0x0024, 0x00A5, 0x00E8, 0x00E9, 0x00F9, 0x00EC,
00100    0x00F2, 0x00E7, 0x000A, 0x00D8, 0x00F8, 0x000D, 0x00C5, 0x00E5,
00101    0x0394, 0x005F, 0x03A6, 0x0393, 0x039B, 0x03A9, 0x03A0, 0x03A8,
00102    0x03A3, 0x0398, 0x039E, 0x00A0, 0x00C6, 0x00E6, 0x00DF, 0x00C9,
00103    ' ', '!', '"', '#', 164, '%', '&', 39, '(', ')', '*', '+', ',', '-', '.', '/',
00104    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
00105    161, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
00106    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 196, 214, 209, 220, 167,
00107    191, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
00108    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 228, 246, 241, 252, 224,
00109 };
00110 
00111 static const unsigned short escapes[] = {
00112    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x000C, 0, 0, 0, 0, 0,
00113    0, 0, 0, 0, 0x005E, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00114    0, 0, 0, 0, 0, 0, 0, 0, 0x007B, 0x007D, 0, 0, 0, 0, 0, 0x005C,
00115    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x005B, 0x007E, 0x005D, 0,
00116    0x007C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00117    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00118    0, 0, 0, 0, 0, 0x20AC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00119    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00120 };
00121 
00122 #define SMSLEN 160              /* max SMS length */
00123 
00124 typedef struct sms_s
00125 {
00126    unsigned char hangup;        /* we are done... */
00127    unsigned char err;           /* set for any errors */
00128    unsigned char smsc:1;        /* we are SMSC */
00129    unsigned char rx:1;          /* this is a received message */
00130    char queue[30];              /* queue name */
00131    char oa[20];                 /* originating address */
00132    char da[20];                 /* destination address */
00133    time_t scts;                 /* time stamp, UTC */
00134    unsigned char pid;           /* protocol ID */
00135    unsigned char dcs;           /* data coding scheme */
00136    short mr;                    /* message reference - actually a byte, but usde -1 for not set */
00137    int udl;                     /* user data length */
00138    int udhl;                    /* user data header length */
00139    unsigned char srr:1;         /* Status Report request */
00140    unsigned char udhi:1;        /* User Data Header required, even if length 0 */
00141    unsigned char rp:1;          /* Reply Path */
00142    unsigned int vp;             /* validity period in minutes, 0 for not set */
00143    unsigned short ud[SMSLEN];   /* user data (message), UCS-2 coded */
00144    unsigned char udh[SMSLEN];   /* user data header */
00145    char cli[20];                /* caller ID */
00146    unsigned char ophase;        /* phase (0-79) for 0 and 1 frequencies (1300Hz and 2100Hz) */
00147    unsigned char ophasep;       /* phase (0-79) for 1200 bps */
00148    unsigned char obyte;         /* byte being sent */
00149    unsigned int opause;         /* silent pause before sending (in sample periods) */
00150    unsigned char obitp;         /* bit in byte */
00151    unsigned char osync;         /* sync bits to send */
00152    unsigned char obytep;        /* byte in data */
00153    unsigned char obyten;        /* bytes in data */
00154    unsigned char omsg[256];     /* data buffer (out) */
00155    unsigned char imsg[200];     /* data buffer (in) */
00156    signed long long ims0,
00157       imc0,
00158       ims1,
00159       imc1;                      /* magnitude averages sin/cos 0/1 */
00160    unsigned int idle;
00161    unsigned short imag;         /* signal level */
00162    unsigned char ips0,
00163       ips1,
00164       ipc0,
00165       ipc1;                      /* phase sin/cos 0/1 */
00166    unsigned char ibitl;         /* last bit */
00167    unsigned char ibitc;         /* bit run length count */
00168    unsigned char iphasep;       /* bit phase (0-79) for 1200 bps */
00169    unsigned char ibitn;         /* bit number in byte being received */
00170    unsigned char ibytev;        /* byte value being received */
00171    unsigned char ibytep;        /* byte pointer in messafe */
00172    unsigned char ibytec;        /* byte checksum for message */
00173    unsigned char ierr;          /* error flag */
00174    unsigned char ibith;         /* history of last bits */
00175    unsigned char ibitt;         /* total of 1's in last 3 bites */
00176    /* more to go here */
00177 } sms_t;
00178 
00179 /* different types of encoding */
00180 #define is7bit(dcs) (((dcs)&0xC0)?(!((dcs)&4)):(!((dcs)&12)))
00181 #define is8bit(dcs) (((dcs)&0xC0)?(((dcs)&4)):(((dcs)&12)==4))
00182 #define is16bit(dcs) (((dcs)&0xC0)?0:(((dcs)&12)==8))
00183 
00184 static void *sms_alloc (struct ast_channel *chan, void *params)
00185 {
00186    return params;
00187 }
00188 
00189 static void sms_release (struct ast_channel *chan, void *data)
00190 {
00191    return;
00192 }
00193 
00194 static void sms_messagetx (sms_t * h);
00195 
00196 /*! \brief copy number, skipping non digits apart from leading + */
00197 static void numcpy (char *d, char *s)
00198 {
00199    if (*s == '+')
00200       *d++ = *s++;
00201    while (*s) {
00202       if (isdigit (*s))
00203             *d++ = *s;
00204       s++;
00205    }
00206    *d = 0;
00207 }
00208 
00209 /*! \brief static, return a date/time in ISO format */
00210 static char * isodate (time_t t)
00211 {
00212    static char date[20];
00213    strftime (date, sizeof (date), "%Y-%m-%dT%H:%M:%S", localtime (&t));
00214    return date;
00215 }
00216 
00217 /*! \brief reads next UCS character from null terminated UTF-8 string and advanced pointer */
00218 /* for non valid UTF-8 sequences, returns character as is */
00219 /* Does not advance pointer for null termination */
00220 static long utf8decode (unsigned char **pp)
00221 {
00222    unsigned char *p = *pp;
00223    if (!*p)
00224       return 0;                 /* null termination of string */
00225    (*pp)++;
00226    if (*p < 0xC0)
00227       return *p;                /* ascii or continuation character */
00228    if (*p < 0xE0) {
00229       if (*p < 0xC2 || (p[1] & 0xC0) != 0x80)
00230          return *p;             /* not valid UTF-8 */
00231       (*pp)++;
00232       return ((*p & 0x1F) << 6) + (p[1] & 0x3F);
00233       }
00234    if (*p < 0xF0) {
00235       if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80)
00236           return *p;             /* not valid UTF-8 */
00237       (*pp) += 2;
00238       return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F);
00239    }
00240    if (*p < 0xF8) {
00241       if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80)
00242          return *p;             /* not valid UTF-8 */
00243       (*pp) += 3;
00244       return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F);
00245    }
00246    if (*p < 0xFC) {
00247       if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
00248          || (p[4] & 0xC0) != 0x80)
00249          return *p;             /* not valid UTF-8 */
00250       (*pp) += 4;
00251       return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F);
00252    }
00253    if (*p < 0xFE) {
00254       if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80
00255          || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80)
00256          return *p;             /* not valid UTF-8 */
00257       (*pp) += 5;
00258       return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) + (p[5] & 0x3F);
00259    }
00260    return *p;                   /* not sensible */
00261 }
00262 
00263 /*! \brief 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 */
00264 /* The return value is the number of septets packed in to o, which is internally limited to SMSLEN */
00265 /* o can be null, in which case this is used to validate or count only */
00266 /* if the input contains invalid characters then the return value is -1 */
00267 static int packsms7 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
00268 {
00269     unsigned char p = 0, b = 0, n = 0;
00270 
00271    if (udhl) {                            /* header */
00272       if (o)
00273          o[p++] = udhl;
00274       b = 1;
00275       n = 1;
00276       while (udhl--) {
00277          if (o)
00278             o[p++] = *udh++;
00279          b += 8;
00280          while (b >= 7) {
00281             b -= 7;
00282             n++;
00283          }
00284          if (n >= SMSLEN)
00285             return n;
00286       }
00287       if (b) {
00288          b = 7 - b;
00289          if (++n >= SMSLEN)
00290             return n;
00291          }; /* filling to septet boundary */
00292       }
00293       if (o)
00294          o[p] = 0;
00295       /* message */
00296       while (udl--) {
00297          long u;
00298          unsigned char v;
00299          u = *ud++;
00300          for (v = 0; v < 128 && defaultalphabet[v] != u; v++);
00301          if (v == 128 && u && n + 1 < SMSLEN) {
00302             for (v = 0; v < 128 && escapes[v] != u; v++);
00303             if (v < 128) { /* escaped sequence */
00304             if (o)
00305                o[p] |= (27 << b);
00306             b += 7;
00307             if (b >= 8) {
00308                b -= 8;
00309                p++;
00310                if (o)
00311                   o[p] = (27 >> (7 - b));
00312             }
00313             n++;
00314          }
00315       }
00316       if (v == 128)
00317          return -1;             /* invalid character */
00318       if (o)
00319          o[p] |= (v << b);
00320       b += 7;
00321       if (b >= 8) {
00322          b -= 8;
00323          p++;
00324          if (o)
00325             o[p] = (v >> (7 - b));
00326       }
00327       if (++n >= SMSLEN)
00328          return n;
00329    }
00330    return n;
00331 }
00332 
00333 /*! \brief 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 */
00334 /* The return value is the number of bytes packed in to o, which is internally limited to 140 */
00335 /* o can be null, in which case this is used to validate or count only */
00336 /* if the input contains invalid characters then the return value is -1 */
00337 static int packsms8 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
00338 {
00339    unsigned char p = 0;
00340 
00341    /* header - no encoding */
00342    if (udhl) {
00343       if (o)
00344          o[p++] = udhl;
00345       while (udhl--) {
00346          if (o)
00347             o[p++] = *udh++;
00348          if (p >= 140)
00349             return p;
00350       }
00351    }
00352    while (udl--) {
00353       long u;
00354       u = *ud++;
00355       if (u < 0 || u > 0xFF)
00356          return -1;             /* not valid */
00357       if (o)
00358          o[p++] = u;
00359       if (p >= 140)
00360          return p;
00361    }
00362    return p;
00363 }
00364 
00365 /*! \brief takes a binary header (udhl bytes at udh) and UCS-2 
00366    message (udl characters at ud) and packs in to o using 16 bit 
00367    UCS-2 character codes 
00368    The return value is the number of bytes packed in to o, which is 
00369    internally limited to 140 
00370    o can be null, in which case this is used to validate or count 
00371    only if the input contains invalid characters then 
00372    the return value is -1 */
00373 static int packsms16 (unsigned char *o, int udhl, unsigned char *udh, int udl, unsigned short *ud)
00374 {
00375    unsigned char p = 0;
00376    /* header - no encoding */
00377    if (udhl) {
00378       if (o)
00379          o[p++] = udhl;
00380       while (udhl--) {
00381          if (o)
00382             o[p++] = *udh++;
00383          if (p >= 140)
00384             return p;
00385       }
00386    }
00387    while (udl--) {
00388       long u;
00389       u = *ud++;
00390       if (o)
00391          o[p++] = (u >> 8);
00392       if (p >= 140)
00393          return p - 1;          /* could not fit last character */
00394       if (o)
00395          o[p++] = u;
00396       if (p >= 140)
00397          return p;
00398    }
00399    return p;
00400 }
00401 
00402 /*! \brief general pack, with length and data, 
00403    returns number of bytes of target used */
00404 static int packsms (unsigned char dcs, unsigned char *base, unsigned int udhl, unsigned char *udh, int udl, unsigned short *ud)
00405 {
00406    unsigned char *p = base;
00407    if (udl) {
00408       int l = 0;
00409       if (is7bit (dcs)) {      /* 7 bit */
00410          l = packsms7 (p + 1, udhl, udh, udl, ud);
00411          if (l < 0)
00412             l = 0;
00413          *p++ = l;
00414          p += (l * 7 + 7) / 8;
00415       } else if (is8bit (dcs)) {                       /* 8 bit */
00416          l = packsms8 (p + 1, udhl, udh, udl, ud);
00417          if (l < 0)
00418             l = 0;
00419          *p++ = l;
00420          p += l;
00421       } else {        /* UCS-2 */
00422          l = packsms16 (p + 1, udhl, udh, udl, ud);
00423          if (l < 0)
00424             l = 0;
00425          *p++ = l;
00426          p += l;
00427       }
00428    } else
00429       *p++ = 0;           /* no user data */
00430    return p - base;
00431 }
00432 
00433 
00434 /*! \brief pack a date and return */
00435 static void packdate (unsigned char *o, time_t w)
00436 {
00437    struct tm *t = localtime (&w);
00438 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__)
00439    int z = -t->tm_gmtoff / 60 / 15;
00440 #else
00441    int z = timezone / 60 / 15;
00442 #endif
00443    *o++ = ((t->tm_year % 10) << 4) + (t->tm_year % 100) / 10;
00444    *o++ = (((t->tm_mon + 1) % 10) << 4) + (t->tm_mon + 1) / 10;
00445    *o++ = ((t->tm_mday % 10) << 4) + t->tm_mday / 10;
00446    *o++ = ((t->tm_hour % 10) << 4) + t->tm_hour / 10;
00447    *o++ = ((t->tm_min % 10) << 4) + t->tm_min / 10;
00448    *o++ = ((t->tm_sec % 10) << 4) + t->tm_sec / 10;
00449    if (z < 0)
00450       *o++ = (((-z) % 10) << 4) + (-z) / 10 + 0x08;
00451    else
00452       *o++ = ((z % 10) << 4) + z / 10;
00453 }
00454 
00455 /*! \brief unpack a date and return */
00456 static time_t unpackdate (unsigned char *i)
00457 {
00458    struct tm t;
00459    t.tm_year = 100 + (i[0] & 0xF) * 10 + (i[0] >> 4);
00460    t.tm_mon = (i[1] & 0xF) * 10 + (i[1] >> 4) - 1;
00461    t.tm_mday = (i[2] & 0xF) * 10 + (i[2] >> 4);
00462    t.tm_hour = (i[3] & 0xF) * 10 + (i[3] >> 4);
00463    t.tm_min = (i[4] & 0xF) * 10 + (i[4] >> 4);
00464    t.tm_sec = (i[5] & 0xF) * 10 + (i[5] >> 4);
00465    t.tm_isdst = 0;
00466    if (i[6] & 0x08)
00467       t.tm_min += 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
00468    else
00469       t.tm_min -= 15 * ((i[6] & 0x7) * 10 + (i[6] >> 4));
00470    return ast_mktime(&t, NULL);
00471 }
00472 
00473 /*! \brief unpacks bytes (7 bit encoding) at i, len l septets, 
00474    and places in udh and ud setting udhl and udl. udh not used 
00475    if udhi not set */
00476 static void unpacksms7 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
00477 {
00478    unsigned char b = 0, p = 0;
00479    unsigned short *o = ud;
00480    *udhl = 0;
00481    if (udhi && l) {      /* header */
00482       int h = i[p];
00483       *udhl = h;
00484       if (h) {
00485          b = 1;
00486          p++;
00487          l--;
00488          while (h-- && l) {
00489             *udh++ = i[p++];
00490             b += 8;
00491             while (b >= 7) {
00492                b -= 7;
00493                l--;
00494                if (!l)
00495                   break;
00496             }
00497          }
00498          /* adjust for fill, septets */
00499          if (b) {
00500             b = 7 - b;
00501             l--;
00502          }
00503       }
00504    }
00505    while (l--) {
00506       unsigned char v;
00507       if (b < 2)
00508          v = ((i[p] >> b) & 0x7F);
00509       else
00510          v = ((((i[p] >> b) + (i[p + 1] << (8 - b)))) & 0x7F);
00511       b += 7;
00512       if (b >= 8) {
00513          b -= 8;
00514          p++;
00515       }
00516       if (o > ud && o[-1] == 0x00A0 && escapes[v])
00517          o[-1] = escapes[v];
00518       else
00519          *o++ = defaultalphabet[v];
00520    }
00521    *udl = (o - ud);
00522 }
00523 
00524 /*! \brief unpacks bytes (8 bit encoding) at i, len l septets, 
00525       and places in udh and ud setting udhl and udl. udh not used 
00526       if udhi not set */
00527 static void unpacksms8 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
00528 {
00529    unsigned short *o = ud;
00530    *udhl = 0;
00531    if (udhi) {
00532       int n = *i;
00533       *udhl = n;
00534       if (n) {
00535          i++;
00536          l--;
00537          while (l && n) {
00538             l--;
00539             n--;
00540             *udh++ = *i++;
00541          }
00542       }
00543    }
00544    while (l--)
00545       *o++ = *i++;     /* not to UTF-8 as explicitely 8 bit coding in DCS */
00546    *udl = (o - ud);
00547 }
00548 
00549 /*! \brief unpacks bytes (16 bit encoding) at i, len l septets,
00550     and places in udh and ud setting udhl and udl. 
00551    udh not used if udhi not set */
00552 static void unpacksms16 (unsigned char *i, unsigned char l, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
00553 {
00554    unsigned short *o = ud;
00555    *udhl = 0;
00556    if (udhi) {
00557       int n = *i;
00558       *udhl = n;
00559       if (n) {
00560          i++;
00561          l--;
00562          while (l && n) {
00563             l--;
00564             n--;
00565             *udh++ = *i++;
00566          }
00567       }
00568    }
00569    while (l--) {
00570       int v = *i++;
00571       if (l--)
00572          v = (v << 8) + *i++;
00573       *o++ = v;
00574    }
00575    *udl = (o - ud);
00576 }
00577 
00578 /*! \brief general unpack - starts with length byte (octet or septet) and returns number of bytes used, inc length */
00579 static int unpacksms (unsigned char dcs, unsigned char *i, unsigned char *udh, int *udhl, unsigned short *ud, int *udl, char udhi)
00580 {
00581    int l = *i++;
00582    if (is7bit (dcs)) {
00583       unpacksms7 (i, l, udh, udhl, ud, udl, udhi);
00584       l = (l * 7 + 7) / 8;    /* adjust length to return */
00585    } else if (is8bit (dcs))
00586       unpacksms8 (i, l, udh, udhl, ud, udl, udhi);
00587    else
00588       unpacksms16 (i, l, udh, udhl, ud, udl, udhi);
00589    return l + 1;
00590 }
00591 
00592 /*! \brief unpack an address from i, return byte length, unpack to o */
00593 static unsigned char unpackaddress (char *o, unsigned char *i)
00594 {
00595    unsigned char l = i[0],
00596       p;
00597    if (i[1] == 0x91)
00598       *o++ = '+';
00599    for (p = 0; p < l; p++) {
00600       if (p & 1)
00601          *o++ = (i[2 + p / 2] >> 4) + '0';
00602       else
00603          *o++ = (i[2 + p / 2] & 0xF) + '0';
00604    }
00605    *o = 0;
00606    return (l + 5) / 2;
00607 }
00608 
00609 /*! \brief store an address at o, and return number of bytes used */
00610 static unsigned char packaddress (unsigned char *o, char *i)
00611 {
00612    unsigned char p = 2;
00613    o[0] = 0;
00614    if (*i == '+') {
00615       i++;
00616       o[1] = 0x91;
00617    } else
00618       o[1] = 0x81;
00619    while (*i)
00620       if (isdigit (*i)) {
00621          if (o[0] & 1)
00622             o[p++] |= ((*i & 0xF) << 4);
00623          else
00624             o[p] = (*i & 0xF);
00625          o[0]++;
00626          i++;
00627       } else
00628          i++;
00629    if (o[0] & 1)
00630       o[p++] |= 0xF0;           /* pad */
00631    return p;
00632 }
00633 
00634 /*! \brief Log the output, and remove file */
00635 static void sms_log (sms_t * h, char status)
00636 {
00637    if (*h->oa || *h->da) {
00638       int o = open (log_file, O_CREAT | O_APPEND | O_WRONLY, 0666);
00639       if (o >= 0) {
00640          char line[1000], mrs[3] = "", *p;
00641          unsigned char n;
00642 
00643          if (h->mr >= 0)
00644             snprintf (mrs, sizeof (mrs), "%02X", h->mr);
00645          snprintf (line, sizeof (line), "%s %c%c%c%s %s %s %s ",
00646              isodate (time (0)), status, h->rx ? 'I' : 'O', h->smsc ? 'S' : 'M', mrs, h->queue, *h->oa ? h->oa : "-",
00647              *h->da ? h->da : "-");
00648          p = line + strlen (line);
00649          for (n = 0; n < h->udl; n++)
00650             if (h->ud[n] == '\\') {
00651                *p++ = '\\';
00652                *p++ = '\\';
00653             } else if (h->ud[n] == '\n') {
00654                *p++ = '\\';
00655                *p++ = 'n';
00656             } else if (h->ud[n] == '\r') {
00657                *p++ = '\\';
00658                *p++ = 'r';
00659             } else if (h->ud[n] < 32 || h->ud[n] == 127)
00660                *p++ = 191;
00661             else
00662                *p++ = h->ud[n];
00663          *p++ = '\n';
00664          *p = 0;
00665          write (o, line, strlen (line));
00666          close (o);
00667       }
00668       *h->oa = *h->da = h->udl = 0;
00669    }
00670 }
00671 
00672 /*! \brief parse and delete a file */
00673 static void sms_readfile (sms_t * h, char *fn)
00674 {
00675    char line[1000];
00676    FILE *s;
00677    char dcsset = 0;            /* if DSC set */
00678    ast_log (LOG_EVENT, "Sending %s\n", fn);
00679    h->rx = h->udl = *h->oa = *h->da = h->pid = h->srr = h->udhi = h->rp = h->vp = h->udhl = 0;
00680    h->mr = -1;
00681    h->dcs = 0xF1;             /* normal messages class 1 */
00682    h->scts = time (0);
00683    s = fopen (fn, "r");
00684    if (s)
00685    {
00686       if (unlink (fn))
00687       {                        /* concurrent access, we lost */
00688          fclose (s);
00689          return;
00690       }
00691       while (fgets (line, sizeof (line), s))
00692       {                        /* process line in file */
00693          char *p;
00694          void *pp = &p;
00695          for (p = line; *p && *p != '\n' && *p != '\r'; p++);
00696          *p = 0;               /* strip eoln */
00697          p = line;
00698          if (!*p || *p == ';')
00699             continue;           /* blank line or comment, ignore */
00700          while (isalnum (*p))
00701          {
00702             *p = tolower (*p);
00703             p++;
00704          }
00705          while (isspace (*p))
00706             *p++ = 0;
00707          if (*p == '=')
00708          {
00709             *p++ = 0;
00710             if (!strcmp (line, "ud"))
00711             {                  /* parse message (UTF-8) */
00712                unsigned char o = 0;
00713                while (*p && o < SMSLEN)
00714                   h->ud[o++] = utf8decode(pp);
00715                h->udl = o;
00716                if (*p)
00717                   ast_log (LOG_WARNING, "UD too long in %s\n", fn);
00718             } else
00719             {
00720                while (isspace (*p))
00721                   p++;
00722                if (!strcmp (line, "oa") && strlen (p) < sizeof (h->oa))
00723                   numcpy (h->oa, p);
00724                else if (!strcmp (line, "da") && strlen (p) < sizeof (h->oa))
00725                   numcpy (h->da, p);
00726                else if (!strcmp (line, "pid"))
00727                   h->pid = atoi (p);
00728                else if (!strcmp (line, "dcs"))
00729                {
00730                   h->dcs = atoi (p);
00731                   dcsset = 1;
00732                } else if (!strcmp (line, "mr"))
00733                   h->mr = atoi (p);
00734                else if (!strcmp (line, "srr"))
00735                   h->srr = (atoi (p) ? 1 : 0);
00736                else if (!strcmp (line, "vp"))
00737                   h->vp = atoi (p);
00738                else if (!strcmp (line, "rp"))
00739                   h->rp = (atoi (p) ? 1 : 0);
00740                else if (!strcmp (line, "scts"))
00741                {               /* get date/time */
00742                   int Y,
00743                     m,
00744                     d,
00745                     H,
00746                     M,
00747                     S;
00748                   if (sscanf (p, "%d-%d-%dT%d:%d:%d", &Y, &m, &d, &H, &M, &S) == 6)
00749                   {
00750                      struct tm t;
00751                      t.tm_year = Y - 1900;
00752                      t.tm_mon = m - 1;
00753                      t.tm_mday = d;
00754                      t.tm_hour = H;
00755                      t.tm_min = M;
00756                      t.tm_sec = S;
00757                      t.tm_isdst = -1;
00758                      h->scts = ast_mktime(&t, NULL);
00759                      if (h->scts == (time_t) - 1)
00760                         ast_log (LOG_WARNING, "Bad date/timein %s: %s", fn, p);
00761                   }
00762                } else
00763                   ast_log (LOG_WARNING, "Cannot parse in %s: %s=%si\n", fn, line, p);
00764             }
00765          } else if (*p == '#')
00766          {                     /* raw hex format */
00767             *p++ = 0;
00768             if (*p == '#')
00769             {
00770                p++;
00771                if (!strcmp (line, "ud"))
00772                {               /* user data */
00773                   int o = 0;
00774                   while (*p && o < SMSLEN)
00775                   {
00776                      if (isxdigit (*p) && isxdigit (p[1]) && isxdigit (p[2]) && isxdigit (p[3]))
00777                      {
00778                         h->ud[o++] =
00779                            (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 12) +
00780                            (((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) +
00781                            (((isalpha (p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha (p[3]) ? 9 : 0) + (p[3] & 0xF));
00782                         p += 4;
00783                      } else
00784                         break;
00785                   }
00786                   h->udl = o;
00787                   if (*p)
00788                      ast_log (LOG_WARNING, "UD too long / invalid UCS-2 hex in %s\n", fn);
00789                } else
00790                   ast_log (LOG_WARNING, "Only ud can use ## format, %s\n", fn);
00791             } else if (!strcmp (line, "ud"))
00792             {                  /* user data */
00793                int o = 0;
00794                while (*p && o < SMSLEN)
00795                {
00796                   if (isxdigit (*p) && isxdigit (p[1]))
00797                   {
00798                      h->ud[o++] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF));
00799                      p += 2;
00800                   } else
00801                      break;
00802                }
00803                h->udl = o;
00804                if (*p)
00805                   ast_log (LOG_WARNING, "UD too long / invalid UCS-1 hex in %s\n", fn);
00806             } else if (!strcmp (line, "udh"))
00807             {                  /* user data header */
00808                unsigned char o = 0;
00809                h->udhi = 1;
00810                while (*p && o < SMSLEN)
00811                {
00812                   if (isxdigit (*p) && isxdigit (p[1]))
00813                   {
00814                      h->udh[o] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF));
00815                      o++;
00816                      p += 2;
00817                   } else
00818                      break;
00819                }
00820                h->udhl = o;
00821                if (*p)
00822                   ast_log (LOG_WARNING, "UDH too long / invalid hex in %s\n", fn);
00823             } else
00824                ast_log (LOG_WARNING, "Only ud and udh can use # format, %s\n", fn);
00825          } else
00826             ast_log (LOG_WARNING, "Cannot parse in %s: %s\n", fn, line);
00827       }
00828       fclose (s);
00829       if (!dcsset && packsms7 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
00830       {
00831          if (packsms8 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
00832          {
00833             if (packsms16 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
00834                ast_log (LOG_WARNING, "Invalid UTF-8 message even for UCS-2 (%s)\n", fn);
00835             else
00836             {
00837                h->dcs = 0x08; /* default to 16 bit */
00838                ast_log (LOG_WARNING, "Sending in 16 bit format (%s)\n", fn);
00839             }
00840          } else
00841          {
00842             h->dcs = 0xF5;    /* default to 8 bit */
00843             ast_log (LOG_WARNING, "Sending in 8 bit format (%s)\n", fn);
00844          }
00845       }
00846       if (is7bit (h->dcs) && packsms7 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
00847          ast_log (LOG_WARNING, "Invalid 7 bit GSM data %s\n", fn);
00848       if (is8bit (h->dcs) && packsms8 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
00849          ast_log (LOG_WARNING, "Invalid 8 bit data %s\n", fn);
00850       if (is16bit (h->dcs) && packsms16 (0, h->udhl, h->udh, h->udl, h->ud) < 0)
00851          ast_log (LOG_WARNING, "Invalid 16 bit data %s\n", fn);
00852    }
00853 }
00854 
00855 /*! \brief white a received text message to a file */
00856 static void sms_writefile (sms_t * h)
00857 {
00858    char fn[200] = "", fn2[200] = "";
00859    FILE *o;
00860    ast_copy_string (fn, spool_dir, sizeof (fn));
00861    mkdir (fn, 0777);       /* ensure it exists */
00862    snprintf (fn + strlen (fn), sizeof (fn) - strlen (fn), "/%s", h->smsc ? h->rx ? "morx" : "mttx" : h->rx ? "mtrx" : "motx");
00863    mkdir (fn, 0777);       /* ensure it exists */
00864    ast_copy_string (fn2, fn, sizeof (fn2));
00865    snprintf (fn2 + strlen (fn2), sizeof (fn2) - strlen (fn2), "/%s.%s-%d", h->queue, isodate (h->scts), seq++);
00866    snprintf (fn + strlen (fn), sizeof (fn) - strlen (fn), "/.%s", fn2 + strlen (fn) + 1);
00867    o = fopen (fn, "w");
00868    if (o) {
00869       if (*h->oa)
00870          fprintf (o, "oa=%s\n", h->oa);
00871       if (*h->da)
00872          fprintf (o, "da=%s\n", h->da);
00873       if (h->udhi) {
00874          unsigned int p;
00875          fprintf (o, "udh#");
00876          for (p = 0; p < h->udhl; p++)
00877             fprintf (o, "%02X", h->udh[p]);
00878          fprintf (o, "\n");
00879       }
00880       if (h->udl) {
00881          unsigned int p;
00882          for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
00883          if (p < h->udl)
00884             fputc (';', o);     /* cannot use ud=, but include as a comment for human readable */
00885          fprintf (o, "ud=");
00886          for (p = 0; p < h->udl; p++) {
00887             unsigned short v = h->ud[p];
00888             if (v < 32)
00889                fputc (191, o);
00890             else if (v < 0x80)
00891                fputc (v, o);
00892             else if (v < 0x800)
00893             {
00894                fputc (0xC0 + (v >> 6), o);
00895                fputc (0x80 + (v & 0x3F), o);
00896             } else
00897             {
00898                fputc (0xE0 + (v >> 12), o);
00899                fputc (0x80 + ((v >> 6) & 0x3F), o);
00900                fputc (0x80 + (v & 0x3F), o);
00901             }
00902          }
00903          fprintf (o, "\n");
00904          for (p = 0; p < h->udl && h->ud[p] >= ' '; p++);
00905          if (p < h->udl) {
00906             for (p = 0; p < h->udl && h->ud[p] < 0x100; p++);
00907             if (p == h->udl) {                   /* can write in ucs-1 hex */
00908                fprintf (o, "ud#");
00909                for (p = 0; p < h->udl; p++)
00910                   fprintf (o, "%02X", h->ud[p]);
00911                fprintf (o, "\n");
00912             } else {                 /* write in UCS-2 */
00913                fprintf (o, "ud##");
00914                for (p = 0; p < h->udl; p++)
00915                   fprintf (o, "%04X", h->ud[p]);
00916                fprintf (o, "\n");
00917             }
00918          }
00919       }
00920       if (h->scts)
00921          fprintf (o, "scts=%s\n", isodate (h->scts));
00922       if (h->pid)
00923          fprintf (o, "pid=%d\n", h->pid);
00924       if (h->dcs != 0xF1)
00925          fprintf (o, "dcs=%d\n", h->dcs);
00926       if (h->vp)
00927          fprintf (o, "vp=%d\n", h->vp);
00928       if (h->srr)
00929          fprintf (o, "srr=1\n");
00930       if (h->mr >= 0)
00931          fprintf (o, "mr=%d\n", h->mr);
00932       if (h->rp)
00933          fprintf (o, "rp=1\n");
00934       fclose (o);
00935       if (rename (fn, fn2))
00936          unlink (fn);
00937       else
00938          ast_log (LOG_EVENT, "Received to %s\n", fn2);
00939    }
00940 }
00941 
00942 /*! \brief read dir skipping dot files... */
00943 static struct dirent *readdirqueue (DIR * d, char *queue)
00944 {
00945    struct dirent *f;
00946    do {
00947       f = readdir (d);
00948    } while (f && (*f->d_name == '.' || strncmp (f->d_name, queue, strlen (queue)) || f->d_name[strlen (queue)] != '.'));
00949    return f;
00950 }
00951 
00952 /*! \brief handle the incoming message */
00953 static unsigned char sms_handleincoming (sms_t * h)
00954 {
00955    unsigned char p = 3;
00956    if (h->smsc) {                          /* SMSC */
00957       if ((h->imsg[2] & 3) == 1) {           /* SMS-SUBMIT */
00958          h->udhl = h->udl = 0;
00959          h->vp = 0;
00960          h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);
00961          h->udhi = ((h->imsg[2] & 0x40) ? 1 : 0);
00962          h->rp = ((h->imsg[2] & 0x80) ? 1 : 0);
00963          ast_copy_string (h->oa, h->cli, sizeof (h->oa));
00964          h->scts = time (0);
00965          h->mr = h->imsg[p++];
00966          p += unpackaddress (h->da, h->imsg + p);
00967          h->pid = h->imsg[p++];
00968          h->dcs = h->imsg[p++];
00969          if ((h->imsg[2] & 0x18) == 0x10) {                     /* relative VP */
00970             if (h->imsg[p] < 144)
00971                h->vp = (h->imsg[p] + 1) * 5;
00972             else if (h->imsg[p] < 168)
00973                h->vp = 720 + (h->imsg[p] - 143) * 30;
00974             else if (h->imsg[p] < 197)
00975                h->vp = (h->imsg[p] - 166) * 1440;
00976             else
00977                h->vp = (h->imsg[p] - 192) * 10080;
00978             p++;
00979          } else if (h->imsg[2] & 0x18)
00980             p += 7;            /* ignore enhanced / absolute VP */
00981          p += unpacksms (h->dcs, h->imsg + p, h->udh, &h->udhl, h->ud, &h->udl, h->udhi);
00982          h->rx = 1;            /* received message */
00983          sms_writefile (h);     /* write the file */
00984          if (p != h->imsg[1] + 2) {
00985             ast_log (LOG_WARNING, "Mismatch receive unpacking %d/%d\n", p, h->imsg[1] + 2);
00986             return 0xFF;        /* duh! */
00987          }
00988       } else {
00989          ast_log (LOG_WARNING, "Unknown message type %02X\n", h->imsg[2]);
00990          return 0xFF;
00991       }
00992    } else {                          /* client */
00993       if (!(h->imsg[2] & 3)) {                         /* SMS-DELIVER */
00994          *h->da = h->srr = h->rp = h->vp = h->udhi = h->udhl = h->udl = 0;
00995          h->srr = ((h->imsg[2] & 0x20) ? 1 : 0);