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