/* -------------------------------------------------------------------- */ /* SMS Client, send messages to mobile phones and pagers */ /* */ /* ucp.c */ /* */ /* Copyright (C) 1997,1998,1999 Angelo Masci and Chuck Hurd */ /* */ /* This library is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Library General Public */ /* License as published by the Free Software Foundation; either */ /* version 2 of the License, or (at your option) any later version. */ /* */ /* This library is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ /* Library General Public License for more details. */ /* */ /* You should have received a copy of the GNU Library General Public */ /* License along with this library; if not, write to the Free */ /* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* */ /* You can contact the author at this e-mail address: */ /* */ /* angelo@styx.demon.co.uk */ /* hurd@hurd.is.ge.com */ /* */ /* -------------------------------------------------------------------- */ /* $Id$ -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ /* UCP Protocol: European Telecommunications Standards Institute (ETSI) */ /* http://www.etsi.org */ /* European Telecommunication Standard ETS 300 133-3 */ /* "Enhanced Radio MEssage System (ERMES) Part 3: Network aspects", */ /* 277 pages. very partial summary of Section 8: I5 Interface, */ /* Universal Computer Protocol */ /* */ /* also: Short Message Service Centre, External Machine Interface, */ /* Specification Version 3.1.2 */ /* CMG Telecommunications & Utilities BV, Netherlands */ /* obtained from the Dutch telco KPN */ /* describes an "EMI extended subset" of UCP which defines the */ /* 51-58 oper. codes. */ /* */ /* */ /* Message format: All in ascii characters */ /* HEADER DATA CHECKSUM */ /* */ /* Fields within HEADER and DATA are terminated by the char "/". */ /* Empty optional fields are indicated by two successive "/" */ /* separators. */ /* Bytes in alphanumeric message fields are encoded as */ /* two ascii characters (0-9,A-F) representing the hexidecimal */ /* value of the encoded byte ("hex encoding"). */ /* For example, ascii "A" is encoded as "41" */ /* Transparent data messages are also hex encoded, but 4 bits at */ /* a time, with each nyble being encoded as 1 ascii char 0-9,A-F. */ /* */ /* CHECKSUM: add up all of the bytes of HEADER and DATA, including */ /* the field separators. The checksum is the least */ /* significant byte (8 bits) of the sum. This byte is */ /* sent hex encoded (i.e., two ascii characters */ /* representing the hex value of the checksum.) */ /* */ /* HEADER FORMAT: */ /* */ /* TRN / LEN / ORC / OT / ("/" is literal field separator) */ /* */ /* TRN 2 numeric char Transaction reference number */ /* LEN 5 numeric char Total number of chars between */ /* STX & ETX, right justified, */ /* zero filled. */ /* ORC "O", "R", or "C" "Operation", "Result", or repeated */ /* operation */ /* OT 2 numeric char Operation Type */ /* OT=01: Call Input (message submission) */ /* */ /* An "Operation" message is sent (to the service center), and a */ /* "Result" is returned. If the "Result" is syntatically */ /* incorrect or does not arrive within a timeout period, the */ /* operation may be retried, with the same TRN and data, but with */ /* ORC set to "C" - to notify the service center that this */ /* operation is a possible duplicate. (Note: "C" is not */ /* available in the EMI extended subset.) */ /* */ /* */ /* examples: */ /* 01/00068/O/01/... Call input operation, reference 1, total */ /* length 68 */ /* 01/00042/R/01/... Call input result (response), reference 1, */ /* total length 42 */ /* */ /* */ /* DATA FORMAT for ORC="O", OT=01 "Call input operation" */ /* (submit message) */ /* */ /* AdC / OAdC / OAC / MT / ... additional parameters depending */ /* on MT */ /* */ /* additional parameters: */ /* MT = 1: (none) ("Tone-only") */ /* MT = 2: NM / (Numeric message) */ /* MT= 3: AM / (Alphanumeric ascii */ /* message) */ /* MT= 4: NB / TD / (Transparent data) */ /* MT= 6: AM / CS / (Alphanumeric, special */ /* character set) */ /* */ /* AdC string of numeric char Address code, recipient */ /* OAdC string of numeric char Address code, originator */ /* (optional) */ /* OAC string of char Authentication code, originator */ /* (optional) */ /* MT 1 numeric char (1-4,6) Message type */ /* NM string of numeric char Numeric Message (optional) */ /* AM hex encoded string Alphanumeric message (optional) */ /* TD hex encoded string Transparent data (optional) */ /* NB string of numeric char number of bits in TD message */ /* CS 2 numeric char (00-31) Character set number (optional) */ /* */ /* Note: the EMI extended subset is limited to MT = 2, 3 */ /* */ /* example: */ /* 0653328374///3/414243203132/ Alphanumeric message "ABC 12" */ /* for 0653328374 */ /* */ /* DATA FORMAT for ORC="R", OT=01 "Call input result" */ /* */ /* ACK / SM / (Positive result) */ /* NAC / EC / SM / (Negative result) */ /* */ /* ACK 1 character "A" Postive acknowledgement */ /* NAC 1 character "N" Negative acknowledgement */ /* SM string of char System Message (optional) */ /* EC 2 numeric char Error code - partial list: */ /* 01: checksum error */ /* 02: syntax error */ /* 03: operation not supported */ /* 04: operation not allowed */ /* 05: call barring active */ /* 06: recipient address code invalid */ /* 07: authentication failure */ /* 08: legitimisation code failure */ /* 23: message type not supported by system */ /* 24: message too long */ /* 26: message type invalid for pager type */ /* 28: invalid character set */ /* */ /* actual examples: */ /* A/0653328374:191098174717/ */ /* Positive result. In this case the "system message" is */ /* the AdC followed by :ddmmyyhhmmss date-time-stamp */ /* N/01/ Checksum error / */ /* Negative result */ /* */ /* EXAMPLE EXCHANGE: */ /* */ /* -> 00/00060/O/01/0653328374///3/54657374206D6573736167652031/B2 */ /* <- 00/00042/R/01/A/0653328374:191098174717/1E */ /* -> (hangs up) */ /* */ /* -------------------------------------------------------------------- */ #include #include #include #include "common/common.h" #include "logfile/logfile.h" #include "driver.h" #include "error.h" #include "ascii.h" #include "ia5table.h" #include "comms/comms.h" #include "resource/resource.h" /* -------------------------------------------------------------------- */ static struct ucp_env { DRIVER_DEFAULT_ENV def; /* Place any extended driver */ /* variables here */ } driver_env; /* -------------------------------------------------------------------- */ static RESOURCE resource_list[] = { { RESOURCE_STRING, "SMS_comms_params", 0, 1, NULL, 0, "8N1", 0, &(driver_env.def.comms_params) }, { RESOURCE_STRING, "SMS_centre_number", 0, 1, NULL, 0, NULL, 0, &(driver_env.def.centre_number) }, { RESOURCE_NUMERIC, "SMS_baud", 0, 1, NULL, 0, NULL, 9600, &(driver_env.def.baud) }, { RESOURCE_NUMERIC, "SMS_deliver_timeout", 0, 0, NULL, 0, NULL, 30, &(driver_env.def.deliver_timeout) }, { RESOURCE_NUMERIC, "SMS_timeout", 0, 0, NULL, 0, NULL, 10, &(driver_env.def.timeout) }, { RESOURCE_NUMERIC, "SMS_write_timeout", 0, 0, NULL, 0, NULL, 10, &(driver_env.def.write_timeout) }, { RESOURCE_NUMERIC, "SMS_max_deliver", 0, 0, NULL, 0, NULL, 1, &(driver_env.def.max_deliver) }, { RESOURCE_NULL, NULL, 0, 1, NULL, 0, NULL, 0, NULL } }; /* -------------------------------------------------------------------- */ #define DELIVERTIMEOUT (driver_env.def.deliver_timeout) #define TIMEOUT (driver_env.def.timeout) #define WRITETIMEOUT (driver_env.def.write_timeout) /* -------------------------------------------------------------------- */ #define FD (driver_env.def.fd) /* -------------------------------------------------------------------- */ static int UCP_sendmessage(char *msisdn, char *message); static int UCP_parse_response(char *string); static void UCP_hangup(void); /* -------------------------------------------------------------------- */ static char *UCP_header(int tranRefNo, char ORC, int opType, int dataLen ) { int msgLen; static char msgHead[20]; /* HEADER section */ msgLen = 14 + dataLen + 2; /* len(header+data+checksum) */ /* header: TRN/LEN/ORC/OT/ */ sprintf(msgHead, "%2.2d/%5.5d/%c/%2.2d/", tranRefNo, msgLen, ORC, opType); return msgHead; } /* -------------------------------------------------------------------- */ static char *UCP01_data(char *msisdn, char *message, int msgType) { static char msgData[800]; char *src; char *dest; int nc; /* DATA section */ msgType = 3; /* alphanumeric - others aren't implemented here*/ /* data: AdC/OAdc/OAC/MT/.. */ /* -> recipient///3/... */ nc = sprintf(msgData, "%s///%1.1d/", msisdn, msgType); /* ... additional parameters depending on MT */ /* For MT=3 (alphanumeric) this is hex coded */ /* message data */ dest = &msgData[nc]; for (src = message; *src; src++) { sprintf(dest, "%02X", *src); dest += 2; } sprintf( dest, "/" ); return msgData; } /* -------------------------------------------------------------------- */ static char *UCP_checksum(char *header, char *data ) { char *p; int sum; static char buf[3]; /* CHECKSUM section */ sum = 0; for (p = header; *p != '\0'; p++) { sum += (unsigned char) *p; sum &= 0xFF; } for (p = data; *p != '\0'; p++) { sum += (unsigned char) *p; sum &= 0xFF; } sprintf(buf, "%02X", sum); return buf; } /* -------------------------------------------------------------------- */ static char *UCP_CallInputOp(char *msisdn, char *message, int msgType) { char *msgHead, *msgData, *msgCksm, ORC; int tranRefNo, opType; static char buf[1024]; msgType = 3; /* alphanumeric - others aren't implemented here */ msgData = UCP01_data( msisdn, message, msgType ); msgHead = UCP_header( tranRefNo=1, ORC='O', opType=1, strlen(msgData) ); msgCksm = UCP_checksum( msgHead, msgData ); /* operational message: header data checksum */ sprintf(buf, "%c%s%s%s%c", S_STX, msgHead, msgData, msgCksm, S_ETX ); return buf; } /* -------------------------------------------------------------------- */ /* Return Values: */ /* 0 Positive ACK */ /* 1 Negative ACK */ /* -1 Error */ /* -------------------------------------------------------------------- */ static int UCP_parse_response(char *string) { int result; int tranRefNo, length, opType; char ORC, ack; /* ------------------------------------------------------------ */ /* Example: */ /* 01/00045/R/01/A/0041544180972:161298112313/A6 */ /* trn len orc ot a recip:time chksum */ /* ------------------------------------------------------------ */ result = sscanf(string, "\002%02d/%05d/%c/%02d/%c", &tranRefNo, &length, &ORC, &opType, &ack ); if (result != 5 || ORC != 'R' || opType != 1 ) return -1; /* ---------------------------- */ if ( ack == 'A' ) result = 0; else if( ack == 'N' ) result = 1; else result = -1; return result; } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ static int UCP_sendmessage(char *msisdn, char *message) { char buf[MAX_RESPONSE_BUFSIZE], *ucpMessage, *msg; int result; int msgType = 3; /*alphanumeric*/ /* ------------------------------------ */ /* Convert message to ia5 format */ /* This should be done prior to */ /* entering this function. */ /* ------------------------------------ */ msg = message; while (*msg != '\0') { *msg = toia5(*msg); msg++; } /* ------------------------------------ */ ucpMessage = UCP_CallInputOp(msisdn, message, msgType); twrite(FD, ucpMessage, strlen(ucpMessage), WRITETIMEOUT); if( expstr(FD, buf, "\03", MAX_RESPONSE_BUFSIZE, DELIVERTIMEOUT) == 0) { lprintf(LOG_STANDARD, "SMSC Respsonse: %s\n", buf); result = UCP_parse_response(buf); if (result == 0) { lprintf(LOG_STANDARD, "Message accepted\n"); } else if(result == 1) { lprintf(LOG_ERROR, "Message rejected\n"); UCP_hangup(); return EUCP_ACKFAILED; } else { lprintf(LOG_ERROR, "Bad message acknowledgement\n"); UCP_hangup(); return EUCP_BADACK; } } else { lprintf(LOG_ERROR, "No Message Response\n"); UCP_hangup(); return EUCP_NORESPONSE; } return 0; } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ static void UCP_hangup(void) { default_hangup((DRIVER_DEFAULT_ENV *)(&driver_env)); } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ DEVICE_ENTRY ucp_device = { "UCP", "2.1", resource_list, (DRIVER_DEFAULT_ENV *)(&driver_env), default_init, default_main, default_validate_numeric_id, default_dial, default_hangup, default_send_disconnect, default_single_deliver, UCP_sendmessage, default_login };