1 /* -------------------------------------------------------------------- */
2 /* SMS Client, send messages to mobile phones and pagers                */
3 /*                                                                      */
4 /* ucp.c                                                                */
5 /*                                                                      */
6 /*  Copyright (C) 1997,1998,1999 Angelo Masci and Chuck Hurd            */
7 /*                                                                      */
8 /*  This library is free software; you can redistribute it and/or       */
9 /*  modify it under the terms of the GNU Library General Public         */
10 /*  License as published by the Free Software Foundation; either        */
11 /*  version 2 of the License, or (at your option) any later version.    */
12 /*                                                                      */
13 /*  This library is distributed in the hope that it will be useful,     */
14 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of      */
15 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   */
16 /*  Library General Public License for more details.                    */
17 /*                                                                      */
18 /*  You should have received a copy of the GNU Library General Public   */
19 /*  License along with this library; if not, write to the Free          */
20 /*  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
21 /*                                                                      */
22 /*  You can contact the author at this e-mail address:                  */
23 /*                                                                      */
24 /*  angelo@styx.demon.co.uk                                             */
25 /*  hurd@hurd.is.ge.com							*/
26 /*                                                                      */
27 /* -------------------------------------------------------------------- */
28 /* $Id$
29    -------------------------------------------------------------------- */
30 
31 /* -------------------------------------------------------------------- */
32 /* UCP Protocol: European Telecommunications Standards Institute (ETSI)	*/
33 /*                      http://www.etsi.org				*/
34 /*               European Telecommunication Standard ETS 300 133-3	*/
35 /*  "Enhanced Radio MEssage System (ERMES) Part 3: Network aspects", 	*/
36 /*  277 pages. very partial summary of Section 8: I5 Interface, 	*/
37 /*  Universal Computer Protocol						*/
38 /* 									*/
39 /* also: Short Message Service Centre, External Machine Interface, 	*/
40 /*       Specification Version 3.1.2   					*/
41 /*       CMG Telecommunications & Utilities BV, Netherlands		*/
42 /*       obtained from the Dutch telco KPN				*/
43 /*       describes an "EMI extended subset" of UCP which defines the 	*/
44 /*       51-58 oper. codes.						*/
45 /* 									*/
46 /* 									*/
47 /* Message format:                             All in ascii characters	*/
48 /*      <STX> HEADER DATA CHECKSUM <ETX>				*/
49 /* 									*/
50 /*       Fields within HEADER and DATA are terminated by the char "/".	*/
51 /*       Empty optional fields are indicated by two successive "/" 	*/
52 /*       separators.							*/
53 /*       Bytes in alphanumeric message fields are encoded as		*/
54 /*          two ascii characters (0-9,A-F) representing the hexidecimal	*/
55 /*          value of the encoded byte ("hex encoding").			*/
56 /*          For example, ascii "A" is encoded as "41"			*/
57 /*       Transparent data messages are also hex encoded, but 4 bits at 	*/
58 /*       a time, with each nyble being encoded as 1 ascii char 0-9,A-F.	*/
59 /* 									*/
60 /*    CHECKSUM:  add up all of the bytes of HEADER and DATA, including 	*/
61 /*               the field separators.  The checksum is the least 	*/
62 /*               significant byte (8 bits) of the sum.  This byte is 	*/
63 /*               sent hex encoded (i.e., two ascii characters 		*/
64 /*               representing the hex value of the checksum.)		*/
65 /* 									*/
66 /* HEADER FORMAT:							*/
67 /* 									*/
68 /*       TRN / LEN / ORC / OT /       ("/" is literal field separator)	*/
69 /* 									*/
70 /*       TRN  2 numeric char      Transaction reference number		*/
71 /*       LEN  5 numeric char      Total number of chars between 	*/
72 /*                                STX & ETX, right justified, 		*/
73 /*                                zero filled.				*/
74 /*       ORC  "O", "R", or "C"    "Operation", "Result", or repeated 	*/
75 /*                                operation				*/
76 /*       OT   2 numeric char      Operation Type			*/
77 /*              OT=01:  Call Input (message submission)			*/
78 /* 									*/
79 /*       An "Operation" message is sent (to the service center), and a 	*/
80 /*       "Result" is returned.  If the "Result" is syntatically 	*/
81 /*       incorrect or does not arrive within a timeout period, the 	*/
82 /*       operation may be retried, with the same TRN and data, but with */
83 /*       ORC set to "C" - to notify the service center that this 	*/
84 /*       operation is a possible duplicate.  (Note: "C" is not 		*/
85 /*       available in the EMI extended subset.)				*/
86 /* 									*/
87 /* 									*/
88 /*  examples:								*/
89 /*     01/00068/O/01/...  Call input operation, reference 1, total 	*/
90 /*                        length 68					*/
91 /*     01/00042/R/01/...  Call input result (response), reference 1, 	*/
92 /*                        total length 42				*/
93 /* 									*/
94 /* 									*/
95 /* DATA FORMAT  for ORC="O", OT=01   "Call input operation"  		*/
96 /*                                   (submit message)			*/
97 /* 									*/
98 /*      AdC / OAdC / OAC / MT / ... additional parameters depending 	*/
99 /*                                  on MT				*/
100 /* 									*/
101 /*   additional parameters:						*/
102 /*      MT = 1:   (none)                    ("Tone-only")		*/
103 /*      MT = 2:   NM /                      (Numeric message)		*/
104 /*      MT=  3:   AM /                      (Alphanumeric ascii 	*/
105 /*                                           message)			*/
106 /*      MT=  4:   NB / TD /                 (Transparent data)		*/
107 /*      MT=  6:   AM / CS /                 (Alphanumeric, special 	*/
108 /*                                           character set)		*/
109 /* 									*/
110 /*      AdC   string of numeric char    Address code, recipient		*/
111 /*      OAdC  string of numeric char    Address code, originator 	*/
112 /*                                      (optional)			*/
113 /*      OAC   string of char            Authentication code, originator	*/
114 /*					(optional)			*/
115 /*      MT    1 numeric char (1-4,6)    Message type			*/
116 /*      NM    string of numeric char    Numeric Message (optional)	*/
117 /*      AM    hex encoded string        Alphanumeric message (optional)	*/
118 /*      TD    hex encoded string        Transparent data (optional)	*/
119 /*      NB    string of numeric char    number of bits in TD message	*/
120 /*      CS    2 numeric char (00-31)    Character set number (optional)	*/
121 /* 									*/
122 /*      Note: the EMI extended subset is limited to MT = 2, 3		*/
123 /* 									*/
124 /*  example:								*/
125 /*   0653328374///3/414243203132/  Alphanumeric message "ABC 12" 	*/
126 /*                                 for 0653328374			*/
127 /* 									*/
128 /* DATA FORMAT  for ORC="R", OT=01   "Call input result"		*/
129 /* 									*/
130 /*      ACK / SM /                      (Positive result)		*/
131 /*      NAC / EC / SM /                 (Negative result)		*/
132 /* 									*/
133 /*      ACK  1 character "A"    Postive acknowledgement			*/
134 /*      NAC  1 character "N"    Negative acknowledgement		*/
135 /*      SM   string of char     System Message (optional)		*/
136 /*      EC   2 numeric char     Error code - partial list:		*/
137 /*             01: checksum error					*/
138 /*             02: syntax error						*/
139 /*             03: operation not supported				*/
140 /*             04: operation not allowed				*/
141 /*             05: call barring active					*/
142 /*             06: recipient address code invalid			*/
143 /*             07: authentication failure				*/
144 /*             08: legitimisation code failure				*/
145 /*             23: message type not supported by system			*/
146 /*             24: message too long					*/
147 /*             26: message type invalid for pager type			*/
148 /*             28: invalid character set				*/
149 /* 									*/
150 /* actual examples:							*/
151 /*      A/0653328374:191098174717/					*/
152 /*         Positive result.  In this case the "system message" is	*/
153 /*         the AdC followed by :ddmmyyhhmmss date-time-stamp		*/
154 /*      N/01/ Checksum error /						*/
155 /*         Negative result						*/
156 /* 									*/
157 /* EXAMPLE EXCHANGE:							*/
158 /*									*/
159 /* -> <stx>00/00060/O/01/0653328374///3/54657374206D6573736167652031/B2<etx> */
160 /* <- <stx>00/00042/R/01/A/0653328374:191098174717/1E<etx>		*/
161 /* -> (hangs up)							*/
162 /* 									*/
163 /* -------------------------------------------------------------------- */
164 
165 #include <stdio.h>
166 #include <string.h>
167 #include <stdarg.h>
168 
169 #include "common/common.h"
170 #include "logfile/logfile.h"
171 #include "driver.h"
172 #include "error.h"
173 #include "ascii.h"
174 #include "ia5table.h"
175 #include "comms/comms.h"
176 #include "resource/resource.h"
177 
178 /* -------------------------------------------------------------------- */
179 
180 static struct ucp_env
181 {
182         DRIVER_DEFAULT_ENV def;
183 
184         /* Place any extended driver    */
185         /* variables here               */
186 
187 } driver_env;
188 
189 /* -------------------------------------------------------------------- */
190 
191 static  RESOURCE resource_list[] =
192   {
193     { RESOURCE_STRING,  "SMS_comms_params",         0, 1, NULL, 0,  "8N1",      0,    &(driver_env.def.comms_params)        },
194     { RESOURCE_STRING,  "SMS_centre_number",        0, 1, NULL, 0,  NULL,       0,    &(driver_env.def.centre_number)       },
195     { RESOURCE_NUMERIC, "SMS_baud",                 0, 1, NULL, 0,  NULL,       9600, &(driver_env.def.baud)                },
196     { RESOURCE_NUMERIC, "SMS_deliver_timeout",      0, 0, NULL, 0,  NULL,       30,   &(driver_env.def.deliver_timeout)     },
197     { RESOURCE_NUMERIC, "SMS_timeout",              0, 0, NULL, 0,  NULL,       10,   &(driver_env.def.timeout)             },
198     { RESOURCE_NUMERIC, "SMS_write_timeout",        0, 0, NULL, 0,  NULL,       10,   &(driver_env.def.write_timeout)       },
199     { RESOURCE_NUMERIC, "SMS_max_deliver",          0, 0, NULL, 0,  NULL,       1,    &(driver_env.def.max_deliver)         },
200     { RESOURCE_NULL,     NULL,                      0, 1, NULL, 0,  NULL,       0,    NULL                                  }
201   };
202 
203 /* -------------------------------------------------------------------- */
204 
205 #define DELIVERTIMEOUT          (driver_env.def.deliver_timeout)
206 #define TIMEOUT                 (driver_env.def.timeout)
207 #define WRITETIMEOUT            (driver_env.def.write_timeout)
208 
209 /* -------------------------------------------------------------------- */
210 
211 #define FD                      (driver_env.def.fd)
212 
213 /* -------------------------------------------------------------------- */
214 
215 static int UCP_sendmessage(char *msisdn, char *message);
216 static int UCP_parse_response(char *string);
217 static void UCP_hangup(void);
218 
219 /* -------------------------------------------------------------------- */
220 
UCP_header(int tranRefNo,char ORC,int opType,int dataLen)221 static char *UCP_header(int tranRefNo, char ORC, int opType, int dataLen )
222 {
223           int     msgLen;
224    static char    msgHead[20];
225                                                 /*  HEADER section       */
226 
227    msgLen = 14 + dataLen + 2;          /* len(header+data+checksum) */
228 
229                             /* header:  TRN/LEN/ORC/OT/         */
230 
231    sprintf(msgHead, "%2.2d/%5.5d/%c/%2.2d/", tranRefNo, msgLen, ORC, opType);
232 
233    return msgHead;
234 }
235 
236 /* -------------------------------------------------------------------- */
237 
UCP01_data(char * msisdn,char * message,int msgType)238 static char *UCP01_data(char *msisdn, char *message, int msgType)
239 {
240    static char    msgData[800];
241           char   *src;
242           char   *dest;
243           int     nc;
244 
245                                                 /*  DATA section         */
246 
247    msgType = 3;        /* alphanumeric - others aren't implemented here*/
248 
249 
250                             /* data:  AdC/OAdc/OAC/MT/..   */
251                             /*    ->  recipient///3/...    */
252 
253    nc = sprintf(msgData, "%s///%1.1d/", msisdn, msgType);
254 
255                             /* ... additional parameters depending on MT */
256                             /* For MT=3 (alphanumeric) this is hex coded */
257                             /* message data                              */
258 
259    dest = &msgData[nc];
260    for (src = message;  *src;  src++)
261    {
262         sprintf(dest, "%02X", *src);
263         dest += 2;
264    }
265    sprintf( dest, "/" );
266 
267    return msgData;
268 }
269 /* -------------------------------------------------------------------- */
270 
UCP_checksum(char * header,char * data)271 static char *UCP_checksum(char *header, char *data )
272 {
273    char   *p;
274    int     sum;
275    static char    buf[3];
276                                                 /*  CHECKSUM section       */
277    sum = 0;
278    for (p = header; *p != '\0'; p++)
279    {
280         sum += (unsigned char) *p;
281         sum &= 0xFF;
282    }
283    for (p = data;   *p != '\0'; p++)
284    {
285         sum += (unsigned char) *p;
286         sum &= 0xFF;
287    }
288 
289   sprintf(buf, "%02X", sum);
290 
291   return buf;
292 }
293 
294 /* -------------------------------------------------------------------- */
295 
UCP_CallInputOp(char * msisdn,char * message,int msgType)296 static char *UCP_CallInputOp(char *msisdn, char *message, int msgType)
297 {
298    char   *msgHead,
299           *msgData,
300           *msgCksm,
301           ORC;
302 
303    int    tranRefNo,
304           opType;
305 
306    static char    buf[1024];
307 
308 
309    msgType = 3;        /* alphanumeric - others aren't implemented here */
310 
311    msgData = UCP01_data( msisdn, message, msgType );
312 
313    msgHead = UCP_header( tranRefNo=1, ORC='O', opType=1, strlen(msgData) );
314 
315    msgCksm = UCP_checksum( msgHead, msgData );
316 
317               /* operational message:  <stx> header data checksum <etx> */
318 
319    sprintf(buf, "%c%s%s%s%c", S_STX, msgHead, msgData, msgCksm, S_ETX );
320 
321    return buf;
322 }
323 
324 /* -------------------------------------------------------------------- */
325 /* Return Values:                                                       */
326 /*       0 Positive ACK                                                 */
327 /*       1 Negative ACK                                                 */
328 /*      -1 Error                                                        */
329 /* -------------------------------------------------------------------- */
UCP_parse_response(char * string)330 static int UCP_parse_response(char *string)
331 {
332         int     result;
333 
334         int     tranRefNo,
335                 length,
336                 opType;
337 
338         char    ORC,
339                 ack;
340 
341         /* ------------------------------------------------------------ */
342         /* Example:                                                     */
343         /* <STX>01/00045/R/01/A/0041544180972:161298112313/A6<ETX>      */
344         /*     trn len orc ot a        recip:time         chksum        */
345         /* ------------------------------------------------------------ */
346 
347         result = sscanf(string, "\002%02d/%05d/%c/%02d/%c",
348                         &tranRefNo,
349                         &length,
350                         &ORC,
351                         &opType,
352                         &ack        );
353 
354 
355         if (result != 5 || ORC != 'R' || opType != 1 ) return -1;
356 
357         /* ---------------------------- */
358 
359         if     ( ack == 'A' ) result =  0;
360         else if( ack == 'N' ) result =  1;
361         else                  result = -1;
362 
363         return result;
364 }
365 
366 /* -------------------------------------------------------------------- */
367 /* -------------------------------------------------------------------- */
UCP_sendmessage(char * msisdn,char * message)368 static int UCP_sendmessage(char *msisdn, char *message)
369 {
370     char   buf[MAX_RESPONSE_BUFSIZE],
371            *ucpMessage,
372            *msg;
373 
374 
375     int    result;
376     int    msgType = 3;      /*alphanumeric*/
377 
378 
379     /* ------------------------------------ */
380     /* Convert message to ia5 format        */
381     /* This should be done prior to         */
382     /* entering this function.              */
383     /* ------------------------------------ */
384 
385     msg = message;
386     while (*msg != '\0')
387     {
388         *msg = toia5(*msg);
389         msg++;
390     }
391 
392     /* ------------------------------------ */
393 
394 
395     ucpMessage = UCP_CallInputOp(msisdn, message, msgType);
396 
397     twrite(FD, ucpMessage, strlen(ucpMessage), WRITETIMEOUT);
398 
399     if( expstr(FD, buf, "\03", MAX_RESPONSE_BUFSIZE, DELIVERTIMEOUT) == 0)
400     {
401         lprintf(LOG_STANDARD, "SMSC Respsonse: %s\n", buf);
402 
403         result = UCP_parse_response(buf);
404         if     (result == 0)
405         {
406             lprintf(LOG_STANDARD, "Message accepted\n");
407         }
408         else if(result == 1)
409         {
410             lprintf(LOG_ERROR, "Message rejected\n");
411             UCP_hangup();
412             return EUCP_ACKFAILED;
413         }
414         else
415         {
416             lprintf(LOG_ERROR, "Bad message acknowledgement\n");
417             UCP_hangup();
418             return EUCP_BADACK;
419         }
420      }
421      else
422      {
423         lprintf(LOG_ERROR, "No Message Response\n");
424         UCP_hangup();
425         return EUCP_NORESPONSE;
426      }
427 
428      return 0;
429 }
430 
431 /* -------------------------------------------------------------------- */
432 /* -------------------------------------------------------------------- */
UCP_hangup(void)433 static void UCP_hangup(void)
434 {       default_hangup((DRIVER_DEFAULT_ENV *)(&driver_env));
435 }
436 
437 /* -------------------------------------------------------------------- */
438 /* -------------------------------------------------------------------- */
439 DEVICE_ENTRY ucp_device = {
440 
441         "UCP",
442 	"2.1",
443         resource_list,
444         (DRIVER_DEFAULT_ENV *)(&driver_env),
445 
446         default_init,
447         default_main,
448         default_validate_numeric_id,
449         default_dial,
450         default_hangup,
451         default_send_disconnect,
452         default_single_deliver,
453         UCP_sendmessage,
454         default_login
455 };
456