Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

smsc_cimd2.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /* Driver for CIMD 2 SMS centres.
00058  * Copyright 2000  WapIT Oy Ltd.
00059  * Author: Richard Braakman
00060  *
00061  * Upgrade to SMSCConn API: 2002 Kalle Marjola / 2003 Angel Fradejas
00062  */
00063 
00064 /* TODO: Check checksums on incoming packets */
00065 /* TODO: Leading or trailing spaces are not allowed on parameters
00066  * "user identity" and "password".  Check this. */
00067 /* TODO: Try to use the "More messages to send" flag */
00068 
00069 /* This code is based on the CIMD 2 spec, version 2-0 en.
00070  * All USSD-specific parts have been left out, since we only want to
00071  * communicate with SMSC's.
00072  *
00073  * I found one contradiction in the spec:
00074  *
00075  * - The definition of Integer parameters specifies decimal digits only,
00076  *   but at least one Integer parameter (Validity Period Relative) can
00077  *   be negative.  I assume that this means a leading - is valid.
00078  */
00079 
00080 #include <ctype.h>
00081 #include <time.h>
00082 #include <errno.h>
00083 #include <limits.h>
00084 #include <string.h>
00085 
00086 #include <unistd.h>
00087 
00088 #include "gwlib/gwlib.h"
00089 #include "smscconn.h"
00090 #include "smscconn_p.h"
00091 #include "bb_smscconn_cb.h"
00092 
00093 #include "shared.h"
00094 #include "sms.h"
00095 #include "dlr.h"
00096 
00097 
00098 typedef struct privdata {
00099     Octstr  *username;
00100     Octstr  *password;
00101     Octstr  *host;
00102     long    port;
00103     long    our_port;
00104     long    keepalive;
00105     Octstr  *my_number;
00106     int no_dlr;
00107 
00108     int     socket;
00109     int     send_seq;
00110     int     receive_seq;
00111 
00112     Octstr  *inbuffer;
00113     List    *received;
00114 
00115     time_t  next_ping;
00116 
00117     List *outgoing_queue;
00118     SMSCConn *conn;
00119     int io_thread;
00120     int quitting;
00121     List *stopped; /* list-trick for suspend/isolate */
00122 
00123 } PrivData;
00124 
00125 
00126 
00127 /* Microseconds before giving up on a request */
00128 #define RESPONSE_TIMEOUT (60 * 1000000)
00129 
00130 /* Textual names for the operation codes defined by the CIMD 2 spec. */
00131 /* If you make changes here, also change the operation table. */
00132 enum {
00133     /* Requests from client */
00134     LOGIN = 1,
00135     LOGOUT = 2,
00136     SUBMIT_MESSAGE = 3,
00137     ENQUIRE_MESSAGE_STATUS = 4,
00138     DELIVERY_REQUEST = 5,
00139     CANCEL_MESSAGE = 6,
00140     SET_REQ = 8,
00141     GET_REQ = 9,
00142 
00143     /* Requests from server */
00144     DELIVER_MESSAGE = 20,
00145     DELIVER_STATUS_REPORT = 23,
00146 
00147     /* Requests from either */
00148     ALIVE = 40,
00149 
00150     /* Not a request; add to any request to make it a response */
00151     RESPONSE = 50,
00152 
00153     /* Responses not related to requests */
00154     GENERAL_ERROR_RESPONSE = 98,
00155     NACK = 99
00156 };
00157 
00158 /* Textual names for the parameters defined by the CIMD 2 spec. */
00159 /* If you make changes here, also change the parameter table. */
00160 enum {
00161     P_USER_IDENTITY = 10,
00162     P_PASSWORD = 11,
00163     P_DESTINATION_ADDRESS = 21,
00164     P_ORIGINATING_ADDRESS = 23,
00165     P_ORIGINATING_IMSI = 26,
00166     P_ALPHANUMERIC_ORIGINATING_ADDRESS = 27,
00167     P_ORIGINATED_VISITED_MSC = 28,
00168     P_DATA_CODING_SCHEME = 30,
00169     P_USER_DATA_HEADER = 32,
00170     P_USER_DATA = 33,
00171     P_USER_DATA_BINARY = 34,
00172     P_MORE_MESSAGES_TO_SEND = 44,
00173     P_VALIDITY_PERIOD_RELATIVE = 50,
00174     P_VALIDITY_PERIOD_ABSOLUTE = 51,
00175     P_PROTOCOL_IDENTIFIER = 52,
00176     P_FIRST_DELIVERY_TIME_RELATIVE = 53,
00177     P_FIRST_DELIVERY_TIME_ABSOLUTE = 54,
00178     P_REPLY_PATH = 55,
00179     P_STATUS_REPORT_REQUEST = 56,
00180     P_CANCEL_ENABLED = 58,
00181     P_CANCEL_MODE = 59,
00182     P_MC_TIMESTAMP = 60,
00183     P_STATUS_CODE = 61,
00184     P_STATUS_ERROR_CODE = 62,
00185     P_DISCHARGE_TIME = 63,
00186     P_TARIFF_CLASS = 64,
00187     P_SERVICE_DESCRIPTION = 65,
00188     P_MESSAGE_COUNT = 66,
00189     P_PRIORITY = 67,
00190     P_DELIVERY_REQUEST_MODE = 68,
00191     P_SERVICE_CENTER_ADDRESS = 69,
00192     P_GET_PARAMETER = 500,
00193     P_MC_TIME = 501,
00194     P_ERROR_CODE = 900,
00195     P_ERROR_TEXT = 901
00196 };
00197 
00198 /***************************************************************************/
00199 /* Table of properties of the parameters defined by CIMD 2, and some       */
00200 /* functions to look up fields.                                            */
00201 /***************************************************************************/
00202 
00203 /* Parameter types, internal.  CIMD 2 spec considers P_TIME to be "Integer"
00204  * and P_SMS to be "User Data". */
00205 enum { P_INT, P_STRING, P_ADDRESS, P_TIME, P_HEX, P_SMS };
00206 
00207 /* Information about the parameters defined by the CIMD 2 spec.
00208  * Used for warning about invalid incoming messages, and for validating
00209  * outgoing messages. */
00210 static const struct
00211 {
00212     char *name;
00213     int number;
00214     int maxlen;
00215     int type;  /* P_ values */
00216     int minval, maxval;  /* For P_INT */
00217 }
00218 parameters[] = {
00219     { "user identity", P_USER_IDENTITY, 32, P_STRING },
00220     { "password", P_PASSWORD, 32, P_STRING },
00221     { "destination address", P_DESTINATION_ADDRESS, 20, P_ADDRESS },
00222     { "originating address", P_ORIGINATING_ADDRESS, 20, P_ADDRESS },
00223     /* IMSI is International Mobile Subscriber Identity number */
00224     { "originating IMSI", P_ORIGINATING_IMSI, 20, P_ADDRESS },
00225     { "alphanumeric originating address", P_ALPHANUMERIC_ORIGINATING_ADDRESS, 11, P_STRING },
00226     { "originated visited MSC", P_ORIGINATED_VISITED_MSC, 20, P_ADDRESS },
00227     { "data coding scheme", P_DATA_CODING_SCHEME, 3, P_INT, 0, 255 },
00228     { "user data header", P_USER_DATA_HEADER, 280, P_HEX },
00229     { "user data", P_USER_DATA, 480, P_SMS },
00230     { "user data binary", P_USER_DATA_BINARY, 280, P_HEX },
00231     { "more messages to send", P_MORE_MESSAGES_TO_SEND, 1, P_INT, 0, 1 },
00232     { "validity period relative", P_VALIDITY_PERIOD_RELATIVE, 3, P_INT, -1, 255 },
00233     { "validity period absolute", P_VALIDITY_PERIOD_ABSOLUTE, 12, P_TIME },
00234     { "protocol identifier", P_PROTOCOL_IDENTIFIER, 3, P_INT, 0, 255 },
00235     { "first delivery time relative", P_FIRST_DELIVERY_TIME_RELATIVE, 3, P_INT, -1, 255 },
00236     { "first delivery time absolute", P_FIRST_DELIVERY_TIME_ABSOLUTE, 12, P_TIME },
00237     { "reply path", P_REPLY_PATH, 1, P_INT, 0, 1 },
00238     { "status report request", P_STATUS_REPORT_REQUEST, 2, P_INT, 0, 63 },
00239     { "cancel enabled", P_CANCEL_ENABLED, 1, P_INT, 0, 1 },
00240     { "cancel mode", P_CANCEL_MODE, 1, P_INT, 0, 2 },
00241     { "service centre timestamp", P_MC_TIMESTAMP, 12, P_TIME },
00242     { "status code", P_STATUS_CODE, 2, P_INT, 0, 9 },
00243     { "status error code", P_STATUS_ERROR_CODE, 3, P_INT, 0, 999 },
00244     { "discharge time", P_DISCHARGE_TIME, 12, P_TIME },
00245     { "tariff class", P_TARIFF_CLASS, 2, P_INT, 0, 99 },
00246     { "service description", P_SERVICE_DESCRIPTION, 2, P_INT, 0, 9 },
00247     { "message count", P_MESSAGE_COUNT, 3, P_INT, 0, 999 },
00248     { "priority", P_PRIORITY, 1, P_INT, 1, 9 },
00249     { "delivery request mode", P_DELIVERY_REQUEST_MODE, 1, P_INT, 0, 2 },
00250     { "service center address", P_SERVICE_CENTER_ADDRESS, 20, P_ADDRESS },
00251     { "get parameter", P_GET_PARAMETER, 3, P_INT, 501, 999 },
00252     { "MC time", P_MC_TIME, 12, P_TIME },
00253     { "error code", P_ERROR_CODE, 3, P_INT, 0, 999 },
00254     { "error text", P_ERROR_TEXT, 64, P_STRING },
00255     { NULL }
00256 };
00257 
00258 /* Return the index in the parameters array for this parameter id.
00259  * Return -1 if it is not found. */
00260 static int parm_index(int parmno)
00261 {
00262     int i;
00263 
00264     for (i = 0; parameters[i].name != NULL; i++) {
00265         if (parameters[i].number == parmno)
00266             return i;
00267     }
00268 
00269     return -1;
00270 }
00271 
00272 #ifndef NO_GWASSERT
00273 /* Return the type of this parameter id.  Return -1 if the id is unknown. */
00274 static int parm_type(int parmno)
00275 {
00276     int i = parm_index(parmno);
00277 
00278     if (i < 0)
00279         return -1;
00280 
00281     return parameters[i].type;
00282 }
00283 #endif
00284 
00285 /* Return the max length for this parameter id.
00286  * Return -1 if the id is unknown. */
00287 static int parm_maxlen(int parmno)
00288 {
00289     int i = parm_index(parmno);
00290 
00291     if (i < 0)
00292         return -1;
00293 
00294     return parameters[i].maxlen;
00295 }
00296 
00297 static const char *parm_name(int parmno)
00298 {
00299     int i = parm_index(parmno);
00300 
00301     if (i < 0)
00302         return NULL;
00303 
00304     return parameters[i].name;
00305 }
00306 
00307 #ifndef NO_GWASSERT
00308 /* Return 1 if the value for this (Integer) parameter is in range.
00309  * Return 0 otherwise.  Return -1 if the parameter was not found.  */
00310 static int parm_in_range(int parmno, long value)
00311 {
00312     int i;
00313 
00314     i = parm_index(parmno);
00315 
00316     if (i < 0)
00317         return -1;
00318 
00319     return (value >= parameters[i].minval && value <= parameters[i].maxval);
00320 }
00321 #endif
00322 
00323 /* Helper function to check P_ADDRESS type */
00324 static int isphonedigit(int c)
00325 {
00326     return isdigit(c) || c == '+' || c == '-';
00327 }
00328 
00329 static int parm_valid_address(Octstr *value)
00330 {
00331     return octstr_check_range(value, 0, octstr_len(value), isphonedigit);
00332 }
00333 
00334 /***************************************************************************/
00335 /* Some functions to look up information about operation codes             */
00336 /***************************************************************************/
00337 
00338 static int operation_find(int operation);
00339 static Octstr *operation_name(int operation);
00340 static int operation_can_send(int operation);
00341 static int operation_can_receive(int operation);
00342 
00343 static const struct
00344 {
00345     char *name;
00346     int code;
00347     int can_send;
00348     int can_receive;
00349 }
00350 operations[] = {
00351     { "Login", LOGIN, 1, 0 },
00352     { "Logout", LOGOUT, 1, 0 },
00353     { "Submit message", SUBMIT_MESSAGE, 1, 0 },
00354     { "Enquire message status", ENQUIRE_MESSAGE_STATUS, 1, 0 },
00355     { "Delivery request", DELIVERY_REQUEST, 1, 0 },
00356     { "Cancel message", CANCEL_MESSAGE, 1, 0 },
00357     { "Set parameter", SET_REQ, 1, 0 },
00358     { "Get parameter", GET_REQ, 1, 0 },
00359 
00360     { "Deliver message", DELIVER_MESSAGE, 0, 1 },
00361     { "Deliver status report", DELIVER_STATUS_REPORT, 0, 1 },
00362 
00363     { "Alive", ALIVE, 1, 1 },
00364 
00365     { "NACK", NACK, 1, 1 },
00366     { "General error response", GENERAL_ERROR_RESPONSE, 0, 1 },
00367 
00368     { NULL, 0, 0, 0 }
00369 };
00370 
00371 static int operation_find(int operation)
00372 {
00373     int i;
00374 
00375     for (i = 0; operations[i].name != NULL; i++) {
00376         if (operations[i].code == operation)
00377             return i;
00378     }
00379 
00380     return -1;
00381 }
00382 
00383 /* Return a human-readable representation of this operation code */
00384 static Octstr *operation_name(int operation)
00385 {
00386     int i;
00387 
00388     i = operation_find(operation);
00389     if (i >= 0)
00390         return octstr_create(operations[i].name);
00391 
00392     if (operation >= RESPONSE) {
00393         i = operation_find(operation - RESPONSE);
00394         if (i >= 0) {
00395             Octstr *name = octstr_create(operations[i].name);
00396             octstr_append_cstr(name, " response");
00397             return name;
00398         }
00399     }
00400 
00401     /* Put the operation number here when we have octstr_format */
00402     return octstr_create("(unknown)");
00403 }
00404 
00405 /* Return true if a CIMD2 client may send this operation */
00406 static int operation_can_send(int operation)
00407 {
00408     int i = operation_find(operation);
00409 
00410     if (i >= 0)
00411         return operations[i].can_send;
00412 
00413     /* If we can receive the request, then we can send the response. */
00414     if (operation >= RESPONSE)
00415         return operation_can_receive(operation - RESPONSE);
00416 
00417     return 0;
00418 }
00419 
00420 
00421 /* Return true if a CIMD2 server may send this operation */
00422 static int operation_can_receive(int operation)
00423 {
00424     int i = operation_find(operation);
00425 
00426     if (i >= 0)
00427         return operations[i].can_receive;
00428 
00429     /* If we can send the request, then we can receive the response. */
00430     if (operation >= RESPONSE)
00431         return operation_can_send(operation - RESPONSE);
00432 
00433     return 0;
00434 }
00435 
00436 /***************************************************************************/
00437 /* Packet encoding/decoding functions.  They handle packets at the octet   */
00438 /* level, and know nothing of the network.                                 */
00439 /***************************************************************************/
00440 
00441 struct packet
00442 {
00443     /* operation and seq are -1 if their value could not be parsed */
00444     int operation;
00445     int seq;   /* Sequence number */
00446     Octstr *data;   /* Encoded packet */
00447     /* CIMD 2 packet structure is so simple that packet information is
00448      * stored as a valid encoded packet, and decoded as necessary.
00449      * Exceptions: operation code and sequence number are also stored
00450      * as ints for speed, and the checksum is not added until the packet
00451      * is about to be sent.  Since checksums are optional, the packet
00452      * is still valid without a checksum.
00453      *
00454      * The sequence number is kept at 0 until it's time to actually
00455      * send the packet, so that the send functions have control over
00456      * the sequence numbers.
00457      */
00458 };
00459 
00460 /* These are the separators defined by the CIMD 2 spec */
00461 #define STX 2   /* Start of packet */
00462 #define ETX 3   /* End of packet */
00463 #define TAB 9   /* End of parameter */
00464 
00465 /* The same separators, in string form */
00466 #define STX_str "\02"
00467 #define ETX_str "\03"
00468 #define TAB_str "\011"
00469 
00470 /* A reminder that packets are created without a valid sequence number */
00471 #define BOGUS_SEQUENCE 0
00472 
00473 static Msg *cimd2_accept_delivery_report_message(struct packet *request,
00474                          SMSCConn *conn);
00475 /* Look for the STX OO:SSS TAB header defined by CIMD 2, where OO is the
00476  * operation code in two decimals and SSS is the sequence number in three
00477  * decimals.  Leave the results in the proper fields of the packet.
00478  * Try to make sense of headers that don't fit this pattern; validating
00479  * the packet format is not our job. */
00480 static void packet_parse_header(struct packet *packet)
00481 {
00482     int pos;
00483     long number;
00484 
00485     /* Set default values, in case we can't parse the fields */
00486     packet->operation = -1;
00487     packet->seq = -1;
00488 
00489     pos = octstr_parse_long(&number, packet->data, 1, 10);
00490     if (pos < 0)
00491         return;
00492     packet->operation = number;
00493 
00494     if (octstr_get_char(packet->data, pos++) != ':')
00495         return;
00496 
00497     pos = octstr_parse_long(&number, packet->data, pos, 10);
00498     if (pos < 0)
00499         return;
00500     packet->seq = number;
00501 }
00502 
00503 
00504 /* Accept an Octstr containing one packet, build a struct packet around
00505  * it, and return that struct.  The Octstr is stored in the struct.
00506  * No error checking is done here yet. */
00507 static struct packet *packet_parse(Octstr *packet_data)
00508 {
00509     struct packet *packet;
00510 
00511     packet = gw_malloc(sizeof(*packet));
00512     packet->data = packet_data;
00513 
00514     /* Fill in packet->operation and packet->seq */
00515     packet_parse_header(packet);
00516 
00517     return packet;
00518 }
00519 
00520 /* Deallocate this packet */
00521 static void packet_destroy(struct packet *packet)
00522 {
00523     if (packet != NULL) {
00524         octstr_destroy(packet->data);
00525         gw_free(packet);
00526     }
00527 }
00528 
00529 /* Find the first packet in "in", delete it from "in", and return it as
00530  * a struct.  Return NULL if "in" contains no packet.  Always delete
00531  * leading non-packet data from "in".  (The CIMD 2 spec says we should
00532  * ignore any data between the packet markers). */
00533 static struct packet *packet_extract(Octstr *in, SMSCConn *conn)
00534 {
00535     int stx, etx;
00536     Octstr *packet;
00537 
00538     /* Find STX, and delete everything up to it */
00539     stx = octstr_search_char(in, STX, 0);
00540     if (stx < 0) {
00541         octstr_delete(in, 0, octstr_len(in));
00542         return NULL;
00543     } else {
00544         octstr_delete(in, 0, stx);
00545     }
00546 
00547     /* STX is now in position 0.  Find ETX. */
00548     etx = octstr_search_char(in, ETX, 1);
00549     if (etx < 0)
00550         return NULL;
00551 
00552     /* What shall we do with STX data... STX data... ETX?
00553      * Either skip to the second STX, or assume an ETX marker before
00554      * the STX.  Doing the latter has a chance of succeeding, and
00555      * will at least allow good logging of the error. */
00556     stx = octstr_search_char(in, STX, 1);
00557     if (stx >= 0 && stx < etx) {
00558         warning(0, "CIMD2[%s]: packet without end marker",
00559                 octstr_get_cstr(conn->id));
00560         packet = octstr_copy(in, 0, stx);
00561         octstr_delete(in, 0, stx);
00562         octstr_append_cstr(packet, ETX_str);
00563     } else {
00564         /* Normal case. Copy packet, and cut it from the source. */
00565         packet = octstr_copy(in, 0, etx + 1);
00566         octstr_delete(in, 0, etx + 1);
00567     }
00568 
00569     return packet_parse(packet);
00570 }
00571 
00572 /* The get_parm functions always return the first parameter with the
00573  * correct id.  There is only one case where the spec allows multiple
00574  * parameters with the same id, and that is when an SMS has multiple
00575  * destination addresses.  We only support one destination address anyway. */
00576 
00577 /* Look for the first parameter with id 'parmno' and return its value.
00578  * Return NULL if the parameter was not found. */
00579 static Octstr *packet_get_parm(struct packet *packet, int parmno)
00580 {
00581     long pos, next;
00582     long valuepos;
00583     long number;
00584 
00585     gw_assert(packet != NULL);
00586     pos = octstr_search_char(packet->data, TAB, 0);
00587     if (pos < 0)
00588         return NULL;  /* Bad packet, nothing we can do */
00589 
00590     /* Parameters have a tab on each end.  If we don't find the
00591      * closing tab, we're at the checksum, so we stop. */
00592     for ( ;
00593           (next = octstr_search_char(packet->data, TAB, pos + 1)) >= 0;
00594           pos = next) {
00595         if (octstr_parse_long(&number, packet->data, pos + 1, 10) < 0)
00596             continue;
00597         if (number != parmno)
00598             continue;
00599         valuepos = octstr_search_char(packet->data, ':', pos + 1);
00600         if (valuepos < 0)
00601             continue;  /* badly formatted parm */
00602         valuepos++;  /* skip the ':' */
00603 
00604         /* Found the right parameter */
00605         return octstr_copy(packet->data, valuepos, next - valuepos);
00606     }
00607 
00608     return NULL;
00609 }
00610 
00611 
00612 /* Look for an Integer parameter with id 'parmno' in the packet and
00613  * return its value.  Return INT_MIN if the parameter was not found.
00614  * (Unfortunately, -1 is a valid parameter value for at least one
00615  * parameter.) */
00616 static long packet_get_int_parm(struct packet *packet, int parmno)
00617 {
00618     Octstr *valuestr = NULL;
00619     long value;
00620 
00621     /* Our code should never even try a bad parameter access. */
00622     gw_assert(parm_type(parmno) == P_INT);
00623 
00624     valuestr = packet_get_parm(packet, parmno);
00625     if (!valuestr)
00626         goto error;
00627 
00628     if (octstr_parse_long(&value, valuestr, 0, 10) < 0)
00629         goto error;
00630 
00631     octstr_destroy(valuestr);
00632     return value;
00633 
00634 error:
00635     octstr_destroy(valuestr);
00636     return INT_MIN;
00637 }
00638 
00639 /* Look for a String parameter with id 'parmno' in the packet and
00640  * return its value.  Return NULL if the parameter was not found.
00641  * No translations are done on the value. */
00642 static Octstr *packet_get_string_parm(struct packet *packet, int parmno)
00643 {
00644     /* Our code should never even try a bad parameter access. */
00645     gw_assert(parm_type(parmno) == P_STRING);
00646 
00647     return packet_get_parm(packet, parmno);
00648 }
00649 
00650 /* Look for an Address parameter with id 'parmno' in the packet and
00651  * return its value.  Return NULL if the parameter was not found.
00652  * No translations are done on the value. */
00653 static Octstr *packet_get_address_parm(struct packet *packet, int parmno)
00654 {
00655     /* Our code should never even try a bad parameter access. */
00656     gw_assert(parm_type(parmno) == P_ADDRESS);
00657 
00658     return packet_get_parm(packet, parmno);
00659 }
00660 
00661 /* Look for an SMS parameter with id 'parmno' in the packet and return its
00662  * value.  Return NULL if the parameter was not found.  No translations
00663  * are done on the value, so it will be in the ISO-Latin-1 character set
00664  * with CIMD2-specific escapes. */
00665 static Octstr *packet_get_sms_parm(struct packet *packet, int parmno)
00666 {
00667     /* Our code should never even try a bad parameter access. */
00668     gw_assert(parm_type(parmno) == P_SMS);
00669 
00670     return packet_get_parm(packet, parmno);
00671 }
00672 
00673 /* There is no packet_get_time_parm because the CIMD 2 timestamp
00674  * format is useless.  It's in the local time of the MC, with
00675  * a 2-digit year and no DST information.  We can do without.
00676  */
00677 
00678 /* Look for a Hex parameter with id 'parmno' in the packet and return
00679  * its value.  Return NULL if the parameter was not found.  The value
00680  * is de-hexed. */
00681 static Octstr *packet_get_hex_parm(struct packet *packet, int parmno)
00682 {
00683     Octstr *value = NULL;
00684 
00685     /* Our code should never even try a bad parameter access. */
00686     gw_assert(parm_type(parmno) == P_HEX);
00687 
00688     value = packet_get_parm(packet, parmno);
00689     if (!value)
00690         goto error;
00691 
00692     if (octstr_hex_to_binary(value) < 0)
00693         goto error;
00694 
00695     return value;
00696 
00697 error:
00698     octstr_destroy(value);
00699     return NULL;
00700 }
00701 
00702 
00703 /* Check if the header is according to CIMD 2 spec, generating log
00704  * entries as necessary.  Return -1 if anything was wrong, otherwise 0. */
00705 static int packet_check_header(struct packet *packet, SMSCConn *conn)
00706 {
00707     Octstr *data;
00708 
00709     gw_assert(packet != NULL);
00710     data = packet->data;
00711 
00712     /* The header must have a two-digit operation code, a colon,
00713      * and a three-digit sequence number, followed by a tab.
00714      * (CIMD2, 3.1) */
00715     if (octstr_len(data) < 8 ||
00716         !octstr_check_range(data, 1, 2, gw_isdigit) ||
00717         octstr_get_char(data, 3) != ':' ||
00718         !octstr_check_range(data, 4, 3, gw_isdigit) ||
00719         octstr_get_char(data, 7) != TAB) {
00720         warning(0, "CIMD2[%s]: packet header in wrong format",
00721                 octstr_get_cstr(conn->id));
00722         return -1;
00723     }
00724 
00725     return 0;
00726 }
00727 
00728 static int packet_check_parameter(struct packet *packet, long pos, long len, SMSCConn *conn)
00729 {
00730     Octstr *data;
00731     long parm;
00732     long dpos, dlen;
00733     int negative;
00734     long value;
00735     int i;
00736     int errors = 0;
00737 
00738     gw_assert(packet != NULL);
00739     data = packet->data;
00740 
00741     /* The parameter header should be TAB, followed by a three-digit
00742      * parameter number, a colon, and the data.  We already know about
00743      * the tab. */
00744 
00745     if (len < 5 ||
00746         !octstr_check_range(data, pos + 1, 3, gw_isdigit) ||
00747         octstr_get_char(data, pos + 4) != ':') {
00748         warning(0, "CIMD2[%s]: parameter at offset %ld in wrong format",
00749                 octstr_get_cstr(conn->id),
00750                 pos);
00751         errors++;
00752     }
00753 
00754     /* If we can't parse a parameter number, there's nothing more
00755      * that we can check. */
00756     dpos = octstr_parse_long(&parm, data, pos + 1, 10);
00757     if (dpos < 0)
00758         return -1;
00759     if (octstr_get_char(data, dpos) == ':')
00760         dpos++;
00761     dlen = len - (dpos - pos);
00762     /* dlen can not go negative because octstr_parse_long must have
00763      * been stopped by the TAB at the end of the parameter data. */
00764     gw_assert(dlen >= 0);
00765 
00766     i = parm_index(parm);
00767 
00768     if (i < 0) {
00769         warning(0, "CIMD2[%s]: packet contains unknown parameter %ld", 
00770                 octstr_get_cstr(conn->id),
00771                 parm);
00772         return -1;
00773     }
00774 
00775     if (dlen > parameters[i].maxlen) {
00776         warning(0, "CIMD2[%s]: packet has '%s' parameter with length %ld, spec says max %d",
00777                 octstr_get_cstr(conn->id),
00778                 parameters[i].name, len, parameters[i].maxlen);
00779         errors++;
00780     }
00781 
00782     switch (parameters[i].type) {
00783     case P_INT:
00784         /* Allow a leading - */
00785         negative = (octstr_get_char(data, dpos) == '-');
00786         if (!octstr_check_range(data, dpos + negative,
00787                                 dlen - negative, gw_isdigit)) {
00788             warning(0, "CIMD2[%s]: packet has '%s' parameter with non-integer contents", 
00789                     octstr_get_cstr(conn->id),
00790                     parameters[i].name);
00791             errors++;
00792         }
00793         if (octstr_parse_long(&value, data, dpos, 10) >= 0 &&
00794             (value < parameters[i].minval || value > parameters[i].maxval)) {
00795             warning(0, "CIMD2[%s]: packet has '%s' parameter out of range (value %ld, min %d, max %d)",
00796                     octstr_get_cstr(conn->id),
00797                     parameters[i].name, value,
00798                     parameters[i].minval, parameters[i].maxval);
00799             errors++;
00800         }
00801         break;
00802     case P_TIME:
00803         if (!octstr_check_range(data, dpos, dlen, gw_isdigit)) {
00804             warning(0, "CIMD2[%s]: packet has '%s' parameter with non-digit contents", 
00805                     octstr_get_cstr(conn->id),
00806                     parameters[i].name);
00807             errors++;
00808         }
00809         break;
00810     case P_ADDRESS:
00811         if (!octstr_check_range(data, dpos, dlen, isphonedigit)) {
00812             warning(0, "CIMD2[%s]: packet has '%s' parameter with non phone number contents", 
00813                     octstr_get_cstr(conn->id),
00814                     parameters[i].name);
00815             errors++;
00816         }
00817         break;
00818     case P_HEX:
00819         if (!octstr_check_range(data, dpos, dlen, gw_isxdigit)) {
00820             warning(0, "CIMD2[%s]: packet has '%s' parameter with non-hex contents", 
00821                     octstr_get_cstr(conn->id),
00822                     parameters[i].name);
00823             errors++;
00824         }
00825         if (dlen % 2 != 0) {
00826             warning(0, "CIMD2[%s]: packet has odd-length '%s' parameter", 
00827                     octstr_get_cstr(conn->id),
00828                     parameters[i].name);
00829             errors++;
00830         }
00831         break;
00832     case P_SMS:
00833     case P_STRING:  /* nothing to check */
00834         break;
00835     }
00836 
00837     if (errors > 0)
00838         return -1;
00839     return 0;
00840 }
00841 
00842 
00843 /* Check the packet against the CIMD 2 spec, generating log entries as
00844  * necessary. Return -1 if anything was wrong, otherwise 0. */
00845 /* TODO: Check if parameters found actually belong in the packet type */
00846 static int packet_check(struct packet *packet, SMSCConn *conn)
00847 {
00848     int errors = 0;
00849     long pos, len, next;
00850     Octstr *data;
00851 
00852     gw_assert(packet != NULL);
00853     data = packet->data;
00854 
00855     if (octstr_search_char(data, 0, 0) >= 0) {
00856         /* CIMD2 spec does not allow NUL bytes in a packet */
00857         warning(0, "CIMD2[%s]: packet contains NULs",
00858                 octstr_get_cstr(conn->id));
00859         errors++;
00860     }
00861 
00862     /* Assume the packet starts with STX and ends with ETX,
00863      * because we parsed it that way in the first place. */
00864 
00865     errors += (packet_check_header(packet,conn) < 0);
00866 
00867     /* Parameters are separated by tabs.  After the last parameter
00868      * there is a tab, an optional two-digit checksum, and the ETX.
00869      * Check each parameter in turn, by skipping from tab to tab.
00870      */
00871     len = octstr_len(data);
00872     /* Start at the first tab, wherever it is, so that we can still
00873      * check parameters if the header was weird. */
00874     pos = octstr_search_char(data, TAB, 0);
00875     for ( ; pos >= 0; pos = next) {
00876         next = octstr_search_char(data, TAB, pos + 1);
00877         if (next >= 0) {
00878             errors += (packet_check_parameter(packet, pos, next - pos, conn) < 0);
00879         } else {
00880             /* Check if the checksum has the right format.  Don't
00881              * check the sum itself here, that will be done in a
00882              * separate call later. */
00883             /* There are two valid formats: TAB ETX (no checksum)
00884              * and TAB digit digit ETX.  We already know the TAB
00885              * and the ETX are there. */
00886             if (!(octstr_len(data) - pos == 2 ||
00887                   (octstr_len(data) - pos == 4 &&
00888                    octstr_check_range(data, pos + 1, 2, gw_isxdigit)))) {
00889                 warning(0, "CIMD2[%s]: packet checksum in wrong format",
00890                         octstr_get_cstr(conn->id));
00891                 errors++;
00892             }
00893         }
00894     }
00895 
00896 
00897     if (errors > 0) {
00898         octstr_dump(packet->data, 0);
00899         return -1;
00900     }
00901 
00902     return 0;
00903 }
00904 
00905 static void packet_check_can_receive(struct packet *packet, SMSCConn *conn)
00906 {
00907     gw_assert(packet != NULL);
00908 
00909     if (!operation_can_receive(packet->operation)) {
00910         Octstr *name = operation_name(packet->operation);
00911         warning(0, "CIMD2[%s]: SMSC sent us %s request",
00912                 octstr_get_cstr(conn->id),
00913                 octstr_get_cstr(name));
00914         octstr_destroy(name);
00915     }
00916 }
00917 
00918 /* Table of known error codes */
00919 static struct
00920 {
00921     int code;
00922     char *text;
00923 }
00924 cimd2_errors[] = {
00925     { 0, "No error" },
00926     { 1, "Unexpected operation" },
00927     { 2, "Syntax error" },
00928     { 3, "Unsupported parameter error" },
00929     { 4, "Connection to message center lost" },
00930     { 5, "No response from message center" },
00931     { 6, "General system error" },
00932     { 7, "Cannot find information" },
00933     { 8, "Parameter formatting error" },
00934     { 9, "Requested operation failed" },
00935     /* LOGIN error codes */
00936     { 100, "Invalid login" },
00937     { 101, "Incorrect access type" },
00938     { 102, "Too many users with this login id" },
00939     { 103, "Login refused by message center" },
00940     /* SUBMIT MESSAGE error codes */
00941     { 300, "Incorrect destination address" },
00942     { 301, "Incorrect number of destination addresses" },
00943     { 302, "Syntax error in user data parameter" },
00944     { 303, "Incorrect bin/head/normal user data parameter combination" },
00945     { 304, "Incorrect data coding scheme parameter usage" },
00946     { 305, "Incorrect validity period parameters usage" },
00947     { 306, "Incorrect originator address usage" },
00948     { 307, "Incorrect pid paramterer usage" },
00949     { 308, "Incorrect first delivery parameter usage" },
00950     { 309, "Incorrect reply path usage" },
00951     { 310, "Incorrect status report request parameter usage" },
00952     { 311, "Incorrect cancel enabled parameter usage" },
00953     { 312, "Incorrect priority parameter usage" },
00954     { 313, "Incorrect tariff class parameter usage" },
00955     { 314, "Incorrect service description parameter usage" },
00956     { 315, "Incorrect transport type parameter usage" },
00957     { 316, "Incorrect message type parameter usage" },
00958     { 318, "Incorrect mms parameter usage" },
00959     { 319, "Incorrect operation timer parameter usage" },
00960     /* ENQUIRE MESSAGE STATUS error codes */
00961     { 400, "Incorrect address parameter usage" },
00962     { 401, "Incorrect scts parameter usage" },
00963     /* DELIVERY REQUEST error codes */
00964     { 500, "Incorrect scts parameter usage" },
00965     { 501, "Incorrect mode parameter usage" },
00966     { 502, "Incorrect parameter combination" },
00967     /* CANCEL MESSAGE error codes */
00968     { 600, "Incorrect scts parameter usage" },
00969     { 601, "Incorrect address parameter usage" },
00970     { 602, "Incorrect mode parameter usage" },
00971     { 603, "Incorrect parameter combination" },
00972     /* SET error codes */
00973     { 800, "Changing password failed" },
00974     { 801, "Changing password not allowed" },
00975     /* GET error codes */
00976     { 900, "Unsupported item requested" },
00977     { -1, NULL }
00978 };
00979 
00980 static int packet_display_error(struct packet *packet, SMSCConn *conn)
00981 {
00982     int code;
00983     Octstr *text = NULL;
00984     Octstr *opname = NULL;
00985 
00986     code = packet_get_int_parm(packet, P_ERROR_CODE);
00987     text = packet_get_string_parm(packet, P_ERROR_TEXT);
00988 
00989     if (code <= 0) {
00990         octstr_destroy(text);
00991         return 0;
00992     }
00993 
00994     if (text == NULL) {
00995         /* No error text.  Try to find it in the table. */
00996         int i;
00997         for (i = 0; cimd2_errors[i].text != NULL; i++) {
00998             if (cimd2_errors[i].code == code) {
00999                 text = octstr_create(cimd2_errors[i].text);
01000                 break;
01001             }
01002         }
01003     }
01004 
01005     if (text == NULL) {
01006         /* Still no error text.  Make one up. */
01007         text = octstr_create("Unknown error");
01008     }
01009 
01010     opname = operation_name(packet->operation);
01011     error(0, "CIMD2[%s]: %s contained error message:",
01012           octstr_get_cstr(conn->id),
01013           octstr_get_cstr(opname));
01014     error(0, "code %03d: %s", code, octstr_get_cstr(text));
01015     octstr_destroy(opname);
01016     octstr_destroy(text);
01017     return code;
01018 }
01019 
01020 /* Table of special combinations, for convert_gsm_to_latin1. */
01021 /* Each cimd1, cimd2 pair is mapped to a character in the GSM default
01022  * character set. */
01023 static const struct
01024 {
01025     unsigned char cimd1, cimd2;
01026     unsigned char gsm;
01027 }
01028 cimd_combinations[] = {
01029     { 'O', 'a', 0 },     /* @ */
01030     { 'L', '-', 1 },     /* Pounds sterling */
01031     { 'Y', '-', 3 },     /* Yen */
01032     { 'e', '`', 4 },     /* egrave */
01033     { 'e', '\'', 5 },    /* eacute */
01034     { 'u', '`', 6 },     /* ugrave */
01035     { 'i', '`', 7 },     /* igrave */
01036     { 'o', '`', 8 },     /* ograve */
01037     { 'C', ',', 9 },     /* C cedilla */
01038     { 'O', '/', 11 },    /* Oslash */
01039     { 'o', '/', 12 },    /* oslash */
01040     { 'A', '*', 14 },    /* Aring */
01041     { 'a', '*', 15 },    /* aring */
01042     { 'g', 'd', 16 },    /* greek delta */
01043     { '-', '-', 17 },    /* underscore */
01044     { 'g', 'f', 18 },    /* greek phi */
01045     { 'g', 'g', 19 },    /* greek gamma */
01046     { 'g', 'l', 20 },    /* greek lambda */
01047     { 'g', 'o', 21 },    /* greek omega */
01048     { 'g', 'p', 22 },    /* greek pi */
01049     { 'g', 'i', 23 },    /* greek psi */
01050     { 'g', 's', 24 },    /* greek sigma */
01051     { 'g', 't', 25 },    /* greek theta */
01052     { 'g', 'x', 26 },    /* greek xi */
01053     { 'X', 'X', 27 },    /* escape */
01054     { 'A', 'E', 28 },    /* AE ligature */
01055     { 'a', 'e', 29 },    /* ae ligature */
01056     { 's', 's', 30 },    /* german double s */
01057     { 'E', '\'', 31 },   /* Eacute */
01058     { 'q', 'q', '"' },
01059     { 'o', 'x', 36 },    /* international currency symbol */
01060     { '!', '!', 64 },    /* inverted ! */
01061     { 'A', '"', 91 },    /* Adieresis */
01062     { 'O', '"', 92 },    /* Odieresis */
01063     { 'N', '~', 93 },    /* N tilde */
01064     { 'U', '"', 94 },    /* Udieresis */
01065     { 's', 'o', 95 },    /* section mark */
01066     { '?', '?', 96 },    /* inverted ? */
01067     { 'a', '"', 123 },   /* adieresis */
01068     { 'o', '"', 124 },   /* odieresis */
01069     { 'n', '~', 125 },   /* n tilde */
01070     { 'u', '"', 126 },   /* udieresis */
01071     { 'a', '`', 127 },   /* agrave */
01072     { 0, 0, 0 }
01073 };
01074 
01075 
01076 /* Convert text in the CIMD2 User Data format to the GSM default
01077  * character set.
01078  * CIMD2 allows 8-bit characters in this format; they map directly
01079  * to the corresponding ISO-8859-1 characters.  Since we are heading
01080  * toward that character set in the end, we don't bother converting
01081  * those to GSM. */
01082 static void convert_cimd2_to_gsm(Octstr *text, SMSCConn *conn)
01083 {
01084     long pos, len;
01085     int cimd1, cimd2;
01086     int c;
01087     int i;
01088 
01089     /* CIMD2 uses four single-character mappings that do not map
01090      * to themselves:
01091      * '@' from 64 to 0, '$' from 36 to 2, ']' from 93 to 14 (A-ring),
01092      * and '}' from 125 to 15 (a-ring).
01093      * Other than those, we only have to worry about the escape
01094      * sequences introduced by _ (underscore).
01095      */
01096 
01097     len = octstr_len(text);
01098     for (pos = 0; pos < len; pos++) {
01099         c = octstr_get_char(text, pos);
01100         if (c == '@')
01101             octstr_set_char(text, pos, 0);
01102         else if (c == '$')
01103             octstr_set_char(text, pos, 2);
01104         else if (c == ']')
01105             octstr_set_char(text, pos, 14);
01106         else if (c == '}')
01107             octstr_set_char(text, pos, 15);
01108         else if (c == '_' && pos + 2 < len) {
01109             cimd1 = octstr_get_char(text, pos + 1);
01110             cimd2 = octstr_get_char(text, pos + 2);
01111             for (i = 0; cimd_combinations[i].cimd1 != 0; i++) {
01112                 if (cimd_combinations[i].cimd1 == cimd1 &&
01113                     cimd_combinations[i].cimd2 == cimd2)
01114                     break;
01115             }
01116             if (cimd_combinations[i].cimd1 == 0)
01117                 warning(0, "CIMD2[%s]: Encountered unknown "
01118                         "escape code _%c%c, ignoring.",
01119                         octstr_get_cstr(conn->id),
01120                         cimd1, cimd2);
01121             else {
01122                 octstr_delete(text, pos, 2);
01123                 octstr_set_char(text, pos, cimd_combinations[i].gsm);
01124                 len = octstr_len(text);
01125             }
01126         }
01127     }
01128 }
01129 
01130 
01131 /* Convert text in the GSM default character set to the CIMD2 User Data
01132  * format, which is a representation of the GSM default character set
01133  * in the lower 7 bits of ISO-8859-1.  (8-bit characters are also
01134  * allowed, but it's just as easy not to use them.) */
01135 static void convert_gsm_to_cimd2(Octstr *text)
01136 {
01137     long pos, len;
01138 
01139     len = octstr_len(text);
01140     for (pos = 0; pos < len; pos++) {
01141         int c, i;
01142 
01143         c = octstr_get_char(text, pos);
01144         /* If c is not in the GSM alphabet at this point,
01145          * the caller did something badly wrong. */
01146         gw_assert(c >= 0);
01147         gw_assert(c < 128);
01148 
01149         for (i = 0; cimd_combinations[i].cimd1 != 0; i++) {
01150             if (cimd_combinations[i].gsm == c)
01151                 break;
01152         }
01153 
01154         if (cimd_combinations[i].gsm == c) {
01155             /* Escape sequence */
01156             octstr_insert_data(text, pos, "_ ", 2);
01157             pos += 2;
01158             len += 2;
01159             octstr_set_char(text, pos - 1, cimd_combinations[i].cimd1);
01160             octstr_set_char(text, pos, cimd_combinations[i].cimd2);
01161         } else if (c == 2) {
01162             /* The dollar sign is the only GSM character that
01163                  * does not have a CIMD escape sequence and does not
01164              * map to itself. */
01165             octstr_set_char(text, pos, '$');
01166         }
01167     }
01168 }
01169 
01170 
01171 /***************************************************************************/
01172 /* Packet encoding functions.  They do not allow the creation of invalid   */
01173 /* CIMD 2 packets.                                                         */
01174 /***************************************************************************/
01175 
01176 /* Build a new packet struct with this operation code and sequence number. */
01177 static struct packet *packet_create(int operation, int seq)
01178 {
01179     struct packet *packet;
01180     char minpacket[sizeof("sOO:SSSte")];
01181 
01182     packet = gw_malloc(sizeof(*packet));
01183     packet->operation = operation;
01184     packet->seq = seq;
01185     sprintf(minpacket, STX_str "%02d:%03d" TAB_str ETX_str, operation, seq);
01186     packet->data = octstr_create(minpacket);
01187 
01188     return packet;
01189 }
01190 
01191 /* Add a parameter to the end of packet */
01192 static void packet_add_parm(struct packet *packet, int parmtype,
01193                             int parmno, Octstr *value, SMSCConn *conn)
01194 {
01195     char parmh[sizeof("tPPP:")];
01196     long position;
01197     long len;
01198     int copied = 0;
01199 
01200     len = octstr_len(value);
01201 
01202     gw_assert(packet != NULL);
01203     gw_assert(parm_type(parmno) == parmtype);
01204 
01205     if (len > parm_maxlen(parmno)) {
01206         warning(0, "CIMD2[%s]: %s parameter too long, truncating from "
01207                 "%ld to %ld characters",
01208                 octstr_get_cstr(conn->id), 
01209                 parm_name(parmno),
01210                 len, 
01211                 (long) parm_maxlen(parmno));
01212         value = octstr_copy(value, 0, parm_maxlen(parmno));
01213         copied = 1;
01214     }
01215 
01216     /* There's a TAB and ETX at the end; insert it before those.
01217      * The new parameter will come with a new starting TAB. */
01218     position = octstr_len(packet->data) - 2;
01219 
01220     sprintf(parmh, TAB_str "%03d:", parmno);
01221     octstr_insert_data(packet->data, position, parmh, strlen(parmh));
01222     octstr_insert(packet->data, value, position + strlen(parmh));
01223     if (copied)
01224         octstr_destroy(value);
01225 }
01226 
01227 /* Add a String parameter to the packet */
01228 static void packet_add_string_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
01229 {
01230     packet_add_parm(packet, P_STRING, parmno, value, conn);
01231 }
01232 
01233 /* Add an Address parameter to the packet */
01234 static void packet_add_address_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
01235 {
01236     gw_assert(octstr_check_range(value, 0, octstr_len(value), isphonedigit));
01237     packet_add_parm(packet, P_ADDRESS, parmno, value, conn);
01238 }
01239 
01240 /* Add an SMS parameter to the packet.  The caller is expected to have done
01241  * the translation to the GSM character set already.  */
01242 static void packet_add_sms_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
01243 {
01244     packet_add_parm(packet, P_SMS, parmno, value, conn);
01245 }
01246 
01247 /* There is no function for adding a Time parameter to the packet, because
01248  * the format makes Time parameters useless for us.  If you find that you
01249  * need to use them, then also add code for querying the SMS center timestamp
01250  * and using that for synchronization.  And beware of DST changes. */
01251 
01252 /* Add a Hexadecimal parameter to the packet */
01253 static void packet_add_hex_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
01254 {
01255     value = octstr_duplicate(value);
01256     octstr_binary_to_hex(value, 1);   /* 1 for uppercase hex, i.e. A .. F */
01257     packet_add_parm(packet, P_HEX, parmno, value, conn);
01258     octstr_destroy(value);
01259 }
01260 
01261 /* Add an Integer parameter to the packet */
01262 static void packet_add_int_parm(struct packet *packet, int parmno, long value, SMSCConn *conn)
01263 {
01264     char buf[128];
01265     Octstr *valuestr;
01266 
01267     gw_assert(parm_in_range(parmno, value));
01268 
01269     sprintf(buf, "%ld", value);
01270     valuestr = octstr_create(buf);
01271     packet_add_parm(packet, P_INT, parmno, valuestr, conn);
01272     octstr_destroy(valuestr);
01273 }
01274 
01275 static void packet_set_checksum(struct packet *packet)
01276 {
01277     Octstr *data;
01278     int checksum;
01279     long pos, len;
01280     char buf[16];
01281 
01282     gw_assert(packet != NULL);
01283 
01284     data = packet->data;
01285     if (octstr_get_char(data, octstr_len(data) - 2) != TAB) {
01286         /* Packet already has checksum; kill it. */
01287         octstr_delete(data, octstr_len(data) - 3, 2);
01288     }
01289 
01290     gw_assert(octstr_get_char(data, octstr_len(data) - 2) == TAB);
01291 
01292     /* Sum all the way up to the last TAB */
01293     checksum = 0;
01294     for (pos = 0, len = octstr_len(data); pos < len - 1; pos++) {
01295         checksum += octstr_get_char(data, pos);
01296         checksum &= 0xff;
01297     }
01298 
01299     sprintf(buf, "%02X", checksum);
01300     octstr_insert_data(data, len - 1, buf, 2);
01301 }
01302 
01303 static void packet_set_sequence(struct packet *packet, int seq)
01304 {
01305     char buf[16];
01306 
01307     gw_assert(packet != NULL);
01308     gw_assert(seq >= 0);
01309     gw_assert(seq < 256);
01310 
01311     sprintf(buf, "%03d", seq);
01312 
01313     /* Start at 4 to skip the <STX> ZZ: part of the header. */
01314     octstr_set_char(packet->data, 4, buf[0]);
01315     octstr_set_char(packet->data, 5, buf[1]);
01316     octstr_set_char(packet->data, 6, buf[2]);
01317     packet->seq = seq;
01318 }
01319 
01320 static struct packet *packet_encode_message(Msg *msg, Octstr *sender_prefix, SMSCConn *conn)
01321 {
01322     struct packet *packet;
01323     PrivData *pdata = conn->data;
01324     Octstr *text;
01325     int spaceleft;
01326     long truncated;
01327     int dcs = 0;
01328     int setvalidity = 0;
01329 
01330     gw_assert(msg != NULL);
01331     gw_assert(msg->type == sms);
01332     gw_assert(msg->sms.receiver != NULL);
01333 
01334     dcs = fields_to_dcs(msg, (msg->sms.alt_dcs != -1 ? 
01335         msg->sms.alt_dcs : conn->alt_dcs));
01336     if (msg->sms.sender == NULL)
01337         msg->sms.sender = octstr_create("");
01338 
01339     if (!parm_valid_address(msg->sms.receiver)) {
01340         warning(0, "CIMD2[%s]: non-digits in destination phone number '%s', discarded",
01341                 octstr_get_cstr(conn->id),
01342                 octstr_get_cstr(msg->sms.receiver));
01343         return NULL;
01344     }
01345 
01346     packet = packet_create(SUBMIT_MESSAGE, BOGUS_SEQUENCE);
01347 
01348     packet_add_address_parm(packet, P_DESTINATION_ADDRESS, msg->sms.receiver, conn);
01349 
01350     /* CIMD2 interprets the originating address as a sub-address to
01351      * our connection number (so if the connection is "400" and we
01352      * fill in "600" as the sender number, the user sees "400600").
01353      * Since we have no way to ask what this number is, it has to
01354      * be configured. */
01355 
01356     /* Quick and dirty check to see if we are using alphanumeric sender */
01357     if (parm_valid_address(msg->sms.sender)) {
01358         /* We are not, so send in the usual way */
01359         /* Speed up the default case */
01360         if (octstr_len(sender_prefix) == 0) {
01361             packet_add_address_parm(packet, P_ORIGINATING_ADDRESS,msg->sms.sender, conn);
01362         }
01363         else if (octstr_compare(sender_prefix, octstr_imm("never")) != 0) {
01364             if (octstr_ncompare(sender_prefix, msg->sms.sender,
01365                                 octstr_len(sender_prefix)) == 0) {
01366                 Octstr *sender;
01367                 sender = octstr_copy(msg->sms.sender,
01368                                      octstr_len(sender_prefix), octstr_len(msg->sms.sender));
01369                 packet_add_address_parm(packet, P_ORIGINATING_ADDRESS, sender, conn);
01370                 octstr_destroy(sender);
01371             } else {
01372                 warning(0, "CIMD2[%s]: Sending message with originating address <%s>, "
01373                         "which does not start with the sender-prefix.",
01374                         octstr_get_cstr(conn->id),
01375                         octstr_get_cstr(msg->sms.sender));
01376             }
01377         }
01378     }
01379     else {
01380         /* The test above to check if sender was all digits failed, so assume we want alphanumeric sender */
01381         packet_add_string_parm(packet, P_ALPHANUMERIC_ORIGINATING_ADDRESS,msg->sms.sender, conn);
01382     }
01383 
01384     /* Add the validity period if necessary.  This sets the relative validity
01385      * period as this is the description of the "validity" parameter of the
01386      * sendsms interface.
01387      *
01388      * Convert from minutes to GSM 03.40 specification (section 9.2.3.12).
01389      * 0-143   = 0 to 12 hours in 5 minute increments.
01390      * 144-167 = 12hrs30min to 24hrs in 30 minute increments.
01391      * 168-196 = 2days to 30days in 1 day increments.
01392      * 197-255 = 5weeks to 63weeks in 1 week increments.
01393      *
01394      * This code was copied from smsc_at2.c.
01395      */
01396     if (msg->sms.validity >= 0) {
01397       if (msg->sms.validity > 635040)
01398     setvalidity = 255;
01399       if (msg->sms.validity >= 50400 && msg->sms.validity <= 635040)
01400     setvalidity = (msg->sms.validity - 1) / 7 / 24 / 60 + 192 + 1;
01401       if (msg->sms.validity > 43200 && msg->sms.validity < 50400)
01402     setvalidity = 197;
01403       if (msg->sms.validity >= 2880 && msg->sms.validity <= 43200)
01404     setvalidity = (msg->sms.validity - 1) / 24 / 60 + 166 + 1;
01405       if (msg->sms.validity > 1440 && msg->sms.validity < 2880)
01406     setvalidity = 168;
01407       if (msg->sms.validity >= 750 && msg->sms.validity <= 1440)
01408     setvalidity = (msg->sms.validity - 720 - 1) / 30 + 143 + 1;
01409       if (msg->sms.validity > 720 && msg->sms.validity < 750)
01410     setvalidity = 144;
01411       if (msg->sms.validity >= 5 && msg->sms.validity <= 720)
01412     setvalidity = (msg->sms.validity - 1) / 5 - 1 + 1;
01413       if (msg->sms.validity < 5)
01414     setvalidity = 0;
01415 
01416       packet_add_int_parm(packet, P_VALIDITY_PERIOD_RELATIVE, setvalidity, conn);
01417     }
01418 
01419     /* Explicitly ask not to get status reports.
01420      * If we do not do this, the server's default might be to
01421      * send status reports in some cases, and we don't do anything
01422      * with those reports anyway. */
01423     /* ask for the delivery reports if needed*/
01424 
01425     if (!pdata->no_dlr)
01426         if (DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask))
01427             packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 14, conn);
01428         else
01429             packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 0, conn);
01430     else if( pdata->no_dlr && DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask)) 
01431         warning(0, "CIMD2[%s]: dlr request make no sense while no-dlr set to true",
01432              octstr_get_cstr(conn->id));
01433 
01434     /* Turn off reply path as default.
01435      * This avoids phones automatically asking for a reply
01436      */
01437     if (msg->sms.rpi > 0)
01438     packet_add_int_parm(packet, P_REPLY_PATH, 1, conn);
01439     else
01440         packet_add_int_parm(packet, P_REPLY_PATH, 0, conn);
01441 
01442     /* Use binfo to set the tariff class */
01443     if (octstr_len(msg->sms.binfo))
01444     packet_add_parm(packet, P_INT, P_TARIFF_CLASS, msg->sms.binfo, conn);
01445 
01446     /* Set the protocol identifier if requested */
01447     if (msg->sms.pid > 0)
01448         packet_add_int_parm(packet, P_PROTOCOL_IDENTIFIER, msg->sms.pid, conn);
01449  
01450     /* If there are more messages to the same destination, then set the
01451     * More Messages to Send flag. This allow faster delivery of many messages 
01452     * to the same destination
01453     */
01454     if (msg->sms.msg_left > 0)
01455         packet_add_int_parm(packet, P_MORE_MESSAGES_TO_SEND, 1, conn);
01456     else
01457         packet_add_int_parm(packet, P_MORE_MESSAGES_TO_SEND, 0, conn);
01458     
01459     truncated = 0;
01460 
01461     spaceleft = 140;
01462     if (octstr_len(msg->sms.udhdata)) {
01463         /* udhdata will be truncated and warned about if
01464          * it does not fit. */
01465         packet_add_hex_parm(packet, P_USER_DATA_HEADER, msg->sms.udhdata, conn);
01466     }
01467     if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF)
01468         spaceleft = spaceleft * 8 / 7;
01469     if (spaceleft < 0)
01470         spaceleft = 0;
01471 
01472     text = octstr_duplicate(msg->sms.msgdata);
01473 
01474     if (octstr_len(text) > 0 && spaceleft == 0) {
01475         warning(0, "CIMD2[%s]: message filled up with UDH, no room for message text",
01476                 octstr_get_cstr(conn->id));
01477     } else if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
01478         if (octstr_len(text) > spaceleft) {
01479             truncated = octstr_len(text) - spaceleft;
01480             octstr_truncate(text, spaceleft);
01481         }
01482         packet_add_hex_parm(packet, P_USER_DATA_BINARY, text, conn);
01483     } else {
01484         /* Going from latin1 to GSM to CIMD2 may seem like a
01485          * detour, but it's the only way to get all the escape
01486          * codes right. */
01487         charset_utf8_to_gsm(text);
01488         truncated = charset_gsm_truncate(text, spaceleft);
01489         convert_gsm_to_cimd2(text);
01490         packet_add_sms_parm(packet, P_USER_DATA, text, conn);
01491     }
01492 
01493     if (dcs != 0)
01494         packet_add_int_parm(packet, P_DATA_CODING_SCHEME, dcs, conn);
01495 
01496     if (truncated > 0) {
01497         warning(0, "CIMD2[%s]: truncating message text to fit in %d characters.", 
01498                 octstr_get_cstr(conn->id),
01499                 spaceleft);
01500     }
01501 
01502     octstr_destroy(text);
01503     return packet;
01504 }
01505 
01506 /***************************************************************************/
01507 /* Protocol functions.  These implement various transactions.              */
01508 /***************************************************************************/
01509 
01510 /* Give this packet a proper sequence number for sending. */
01511 static void packet_set_send_sequence(struct packet *packet, PrivData *pdata)
01512 {
01513     gw_assert(pdata != NULL);
01514     /* LOGIN packets always have sequence number 001 */
01515     if (packet->operation == LOGIN)
01516         pdata->send_seq = 1;
01517     /* Send sequence numbers are always odd, receiving are always even */
01518     gw_assert(pdata->send_seq % 2 == 1);
01519 
01520     packet_set_sequence(packet, pdata->send_seq);
01521     pdata->send_seq += 2;
01522     if (pdata->send_seq > 256)
01523         pdata->send_seq = 1;
01524 }
01525 
01526 static struct packet *cimd2_get_packet(PrivData *pdata, Octstr **ts)
01527 {
01528     struct packet *packet = NULL;
01529 
01530     gw_assert(pdata != NULL);
01531 
01532     /* If packet is already available, don't try to read anything */
01533     packet = packet_extract(pdata->inbuffer, pdata->conn);
01534 
01535     while (packet == NULL) {
01536         if (read_available(pdata->socket, RESPONSE_TIMEOUT) != 1) {
01537             warning(0, "CIMD2[%s]: SMSC is not responding",
01538                     octstr_get_cstr(pdata->conn->id));
01539             return NULL;
01540         }
01541 
01542         if (octstr_append_from_socket(pdata->inbuffer, pdata->socket) <= 0) {
01543             error(0, "CIMD2[%s]: cimd2_get_packet: read failed",
01544                   octstr_get_cstr(pdata->conn->id));
01545             return NULL;
01546         }
01547 
01548         packet = packet_extract(pdata->inbuffer, pdata->conn);
01549     }
01550 
01551     packet_check(packet,pdata->conn);
01552     packet_check_can_receive(packet,pdata->conn);
01553     debug("bb.sms.cimd2", 0, "CIMD2[%s]: received: <%s>",
01554           octstr_get_cstr(pdata->conn->id), 
01555           octstr_get_cstr(packet->data));
01556     if (ts)
01557         *ts = packet_get_parm(packet,P_MC_TIMESTAMP);
01558 
01559     if (pdata->keepalive > 0)
01560         pdata->next_ping = time(NULL) + pdata->keepalive;
01561 
01562     return packet;
01563 }
01564 
01565 /* Acknowledge a request.  The CIMD 2 spec only defines positive responses
01566  * to the server, because the server is perfect. */
01567 static void cimd2_send_response(struct packet *request, PrivData *pdata)
01568 {
01569     struct packet *response;
01570 
01571     gw_assert(request != NULL);
01572     gw_assert(request->operation < RESPONSE);
01573 
01574     response = packet_create(request->operation + RESPONSE, request->seq);
01575     packet_set_checksum(response);
01576 
01577     debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending <%s>",
01578           octstr_get_cstr(pdata->conn->id),
01579           octstr_get_cstr(response->data));
01580 
01581     /* Don't check errors here because if there is something
01582      * wrong with the socket, the main loop will detect it. */
01583     octstr_write_to_socket(pdata->socket, response->data);
01584 
01585     packet_destroy(response);
01586 }
01587 
01588 static Msg *cimd2_accept_message(struct packet *request, SMSCConn *conn)
01589 {
01590     Msg *message = NULL;
01591     Octstr *destination = NULL;
01592     Octstr *origin = NULL;
01593     Octstr *UDH = NULL;
01594     Octstr *text = NULL;
01595     int DCS;
01596 
01597     /* See GSM 03.38.  The bit patterns we can handle are:
01598      *   000xyyxx  Uncompressed text, yy indicates alphabet.
01599      *                   yy = 00, default alphabet
01600      *                   yy = 01, 8-bit data
01601      *                   yy = 10, UCS-2
01602      *                   yy = 11, reserved
01603      *   1111xyxx  Data, y indicates alphabet.
01604      *                   y = 0, default alphabet
01605      *                   y = 1, 8-bit data
01606      */
01607     DCS = packet_get_int_parm(request, P_DATA_CODING_SCHEME);
01608 
01609     destination = packet_get_address_parm(request, P_DESTINATION_ADDRESS);
01610     origin = packet_get_address_parm(request, P_ORIGINATING_ADDRESS);
01611     UDH = packet_get_hex_parm(request, P_USER_DATA_HEADER);
01612     /* Text is either in User Data or User Data Binary field. */
01613     text = packet_get_sms_parm(request, P_USER_DATA);
01614     if (text != NULL) {
01615         convert_cimd2_to_gsm(text,conn);
01616         charset_gsm_to_utf8(text);
01617     } else {
01618         /*
01619          * FIXME: If DCS indicates GSM charset, and we get it in binary,
01620          * then it's probably bit-packed.  We'll have to undo it because
01621          * our "charset_gsm" means one gsm character per octet.  This is
01622          * not currently supported. -- RB
01623          */
01624         text = packet_get_hex_parm(request, P_USER_DATA_BINARY);
01625     }
01626 
01627     /* Code elsewhere in the gateway always expects the sender and
01628      * receiver fields to be filled, so we discard messages that
01629      * lack them.  If they should not be discarded, then the code
01630      * handling sms messages should be reviewed.  -- RB */
01631     if (!destination || octstr_len(destination) == 0) {
01632         info(0, "CIMD2[%s]: Got SMS without receiver, discarding.",
01633               octstr_get_cstr(conn->id));
01634         goto error;
01635     }
01636     if (!origin || octstr_len(origin) == 0) {
01637         info(0, "CIMD2[%s]: Got SMS without sender, discarding.",
01638               octstr_get_cstr(conn->id));
01639         goto error;
01640     }
01641 
01642     if (!text && (!UDH || octstr_len(UDH) == 0)) {
01643         info(0, "CIMD2[%s]: Got empty SMS, ignoring.",
01644               octstr_get_cstr(conn->id));
01645         goto error;
01646     }
01647     
01648     message = msg_create(sms);
01649     if (! dcs_to_fields(&message, DCS)) {
01650     /* XXX Should reject this message ? */
01651         debug("bb.sms.cimd2", 0, "CIMD2[%s]: Invalid DCS",
01652               octstr_get_cstr(conn->id));
01653     dcs_to_fields(&message, 0);
01654     }
01655     time(&message->sms.time);
01656     message->sms.sender = origin;
01657     message->sms.receiver = destination;
01658     if (UDH) {
01659         message->sms.udhdata = UDH;
01660     }
01661     message->sms.msgdata = text;
01662     return message;
01663 
01664 error:
01665     msg_destroy(message);
01666     octstr_destroy(destination);
01667     octstr_destroy(origin);
01668     octstr_destroy(UDH);
01669     octstr_destroy(text);
01670     return NULL;
01671 }
01672 
01673 /* Deal with a request from the CIMD2 server, and acknowledge it. */
01674 static void cimd2_handle_request(struct packet *request, SMSCConn *conn)
01675 {
01676     PrivData *pdata = conn->data;
01677     Msg *message = NULL;
01678 
01679     if ((request->seq == 254 && pdata->receive_seq == 0) ||
01680             request->seq == pdata->receive_seq - 2) {
01681         warning(0, "CIMD2[%s]: request had same sequence number as previous.",
01682                 octstr_get_cstr(conn->id));
01683     }
01684     else {
01685         pdata->receive_seq = request->seq + 2;
01686         if (pdata->receive_seq > 254)
01687             pdata->receive_seq = 0;
01688 
01689         if (request->operation == DELIVER_STATUS_REPORT) {
01690             message = cimd2_accept_delivery_report_message(request, conn);
01691             if (message)
01692                 gwlist_append(pdata->received, message);
01693         }
01694         else if (request->operation == DELIVER_MESSAGE) {
01695             message = cimd2_accept_message(request,conn);
01696             if (message)
01697                 gwlist_append(pdata->received, message);
01698         }
01699     }
01700 
01701     cimd2_send_response(request, pdata);
01702 }
01703 
01704 /* Send a request and wait for the ack.  If the other side responds with
01705  * an error code, attempt to correct and retry.
01706  * If other packets arrive while we wait for the ack, handle them.
01707  *
01708  * Return -1 if the SMSC refused the request.  Return -2 for other
01709  * errors, such as being unable to send the request at all.  If the
01710  * function returns -2, the caller would do well to try to reopen the
01711  * connection.
01712  *
01713  * The SMSCenter must be already open.
01714  *
01715  * TODO: This function has grown large and complex.  Break it up
01716  * into smaller pieces.
01717  */
01718 static int cimd2_request(struct packet *request, SMSCConn *conn, Octstr **ts)
01719 {
01720     PrivData *pdata = conn->data;
01721     int ret;
01722     struct packet *reply = NULL;
01723     int errorcode;
01724     int tries = 0;
01725 
01726     gw_assert(pdata != NULL);
01727     gw_assert(request != NULL);
01728     gw_assert(operation_can_send(request->operation));
01729 
01730     if (pdata->socket < 0) {
01731         warning(0, "CIMD2[%s]: cimd2_request: socket not open.",
01732                 octstr_get_cstr(conn->id));
01733         return -2;        
01734     }
01735     
01736 retransmit:
01737     packet_set_send_sequence(request, pdata);
01738     packet_set_checksum(request);
01739 
01740     debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending <%s>",
01741           octstr_get_cstr(conn->id),
01742           octstr_get_cstr(request->data));
01743 
01744     ret = octstr_write_to_socket(pdata->socket, request->data);
01745     if (ret < 0)
01746         goto io_error;
01747 
01748 next_reply:
01749     packet_destroy(reply);  /* destroy old, if any */
01750     reply = cimd2_get_packet(pdata, ts);
01751     if (!reply)
01752         goto io_error;
01753 
01754     errorcode = packet_display_error(reply,conn);
01755 
01756     if (reply->operation == NACK) {
01757         warning(0, "CIMD2[%s]: received NACK",
01758                 octstr_get_cstr(conn->id));
01759         octstr_dump(reply->data, 0);
01760         /* Correct sequence number if server says it was wrong,
01761          * but only if server's number is sane. */
01762         if (reply->seq != request->seq && (reply->seq % 2) == 1) {
01763             warning(0, "CIMD2[%s]: correcting sequence number from %ld to %ld.",
01764                     octstr_get_cstr(conn->id),
01765                     (long) pdata->send_seq, 
01766                     (long) reply->seq);
01767             pdata->send_seq = reply->seq;
01768         }
01769         goto retry;
01770     }
01771 
01772     if (reply->operation == GENERAL_ERROR_RESPONSE) {
01773         error(0, "CIMD2[%s]: received general error response",
01774               octstr_get_cstr(conn->id));
01775         goto io_error;
01776     }
01777 
01778     /* The server sent us a request.  Handle it, then wait for
01779      * a new reply. */
01780     if (reply->operation < RESPONSE) {
01781         cimd2_handle_request(reply, conn);
01782         goto next_reply;
01783     }
01784 
01785     if (reply->seq != request->seq) {
01786         /* We got a response to a different request number than
01787          * what we send.  Strange. */
01788         warning(0, "CIMD2[%s]: response had unexpected sequence number; ignoring.",
01789                 octstr_get_cstr(conn->id));
01790         goto next_reply;
01791     }
01792 
01793     if (reply->operation != request->operation + RESPONSE) {
01794         /* We got a response that didn't match our request */
01795         Octstr *request_name = operation_name(request->operation);
01796         Octstr *reply_name = operation_name(reply->operation);
01797         warning(0, "CIMD2[%s]: %s request got a %s",
01798                 octstr_get_cstr(conn->id),
01799                 octstr_get_cstr(request_name),
01800                 octstr_get_cstr(reply_name));
01801 
01802         octstr_destroy(request_name);
01803         octstr_destroy(reply_name);
01804         octstr_dump(reply->data, 0);
01805         goto retry;
01806     }
01807 
01808     if (errorcode > 0)
01809         goto error;
01810 
01811     /* The reply passed all the checks... looks like the SMSC accepted
01812      * our request! */
01813     packet_destroy(reply);
01814     return 0;
01815 
01816 io_error:
01817     packet_destroy(reply);
01818     return -2;
01819 
01820 error:
01821     packet_destroy(reply);
01822     return -1;
01823 
01824 retry:
01825     if (++tries < 3) {
01826         warning(0, "CIMD2[%s]: Retransmitting (take %d)", 
01827                 octstr_get_cstr(conn->id),
01828                 tries);
01829         goto retransmit;
01830     }
01831     warning(0, "CIMD2[%s]: Giving up.",
01832             octstr_get_cstr(conn->id));
01833     goto io_error;
01834 }
01835 
01836 /* Close the SMSC socket without fanfare. */
01837 static void cimd2_close_socket(PrivData *pdata)
01838 {
01839     gw_assert(pdata != NULL);
01840 
01841     if (pdata->socket < 0)
01842         return;
01843 
01844     if (close(pdata->socket) < 0)
01845         warning(errno, "CIMD2[%s]: error closing socket",
01846                 octstr_get_cstr(pdata->conn->id));
01847     pdata->socket = -1;
01848 }
01849 
01850 /* Open a socket to the SMSC, send a login packet, and wait for ack.
01851  * This may block.  Return 0 for success, or -1 for failure. */
01852 /* Make sure the socket is closed before calling this function, otherwise
01853  * we will leak fd's. */
01854 static int cimd2_login(SMSCConn *conn)
01855 {
01856     PrivData *pdata = conn->data;
01857     int ret;
01858     struct packet *packet = NULL;
01859 
01860     gw_assert(pdata != NULL);
01861 
01862     if (pdata->socket >= 0) {
01863         warning(0, "CIMD2[%s]: login: socket was already open; closing",
01864                 octstr_get_cstr(conn->id));
01865         cimd2_close_socket(pdata);
01866     }
01867     
01868     pdata->socket = tcpip_connect_to_server_with_port(
01869                                             octstr_get_cstr(pdata->host),
01870                                             pdata->port,
01871                                             pdata->our_port,
01872                                             (conn->our_host ? octstr_get_cstr(conn->our_host) : NULL)); 
01873     if (pdata->socket != -1) {
01874     
01875         packet = packet_create(LOGIN, BOGUS_SEQUENCE);
01876         packet_add_string_parm(packet, P_USER_IDENTITY, pdata->username, conn);
01877         packet_add_string_parm(packet, P_PASSWORD, pdata->password, conn);
01878         
01879         ret = cimd2_request(packet, conn, NULL);
01880         
01881         if (ret >= 0) {
01882             packet_destroy(packet);
01883             info(0, "CIMD2[%s] logged in.",
01884                  octstr_get_cstr(conn->id));
01885             return 0;
01886         }
01887     }
01888     error(0, "CIMD2[%s] login failed.",
01889           octstr_get_cstr(conn->id));
01890     cimd2_close_socket(pdata);
01891     packet_destroy(packet);
01892     return -1;
01893 }
01894 
01895 static void cimd2_logout(SMSCConn *conn)
01896 {
01897     struct packet *packet = NULL;
01898     int ret;
01899 
01900     packet = packet_create(LOGOUT, BOGUS_SEQUENCE);
01901 
01902     /* TODO: Don't wait very long for a response in this case. */
01903     ret = cimd2_request(packet, conn, NULL);
01904 
01905     if (ret == 0) {
01906         info(0, "CIMD2[%s] logged out.",
01907              octstr_get_cstr(conn->id));
01908     }
01909     packet_destroy(packet);
01910 }
01911 
01912 static int cimd2_send_alive(SMSCConn *conn)
01913 {
01914     struct packet *packet = NULL;
01915     int ret;
01916 
01917     packet = packet_create(ALIVE, BOGUS_SEQUENCE);
01918     ret = cimd2_request(packet, conn, NULL);
01919     packet_destroy(packet);
01920 
01921     if (ret < 0)
01922         warning(0, "CIMD2[%s]: SMSC not alive.",
01923                 octstr_get_cstr(conn->id));
01924 
01925     return ret;
01926 }
01927 
01928 
01929 static void cimd2_destroy(PrivData *pdata)
01930 {
01931     int discarded;
01932 
01933     if (pdata == NULL) 
01934         return;
01935         
01936     octstr_destroy(pdata->host);
01937     octstr_destroy(pdata->username);
01938     octstr_destroy(pdata->password);
01939     octstr_destroy(pdata->inbuffer);
01940     octstr_destroy(pdata->my_number);
01941 
01942     discarded = gwlist_len(pdata->received);
01943     if (discarded > 0)
01944         warning(0, "CIMD2[%s]: discarded %d received messages",
01945                 octstr_get_cstr(pdata->conn->id), 
01946                 discarded);
01947 
01948     gwlist_destroy(pdata->received, msg_destroy_item);
01949     gwlist_destroy(pdata->outgoing_queue, NULL);
01950     gwlist_destroy(pdata->stopped, NULL);
01951 
01952     gw_free(pdata);
01953 }
01954 
01955 
01956 static int cimd2_submit_msg(SMSCConn *conn, Msg *msg)
01957 {
01958     PrivData *pdata = conn->data;
01959     struct packet *packet;
01960     Octstr *ts = NULL;
01961     int ret;
01962 
01963     gw_assert(pdata != NULL);
01964     debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending message",
01965         octstr_get_cstr(conn->id));
01966 
01967     packet = packet_encode_message(msg, pdata->my_number,conn);
01968     if (!packet) {
01969         /* This is a protocol error. Does this help? I doubt..
01970          * But nevermind that.
01971          */
01972         bb_smscconn_send_failed(conn, msg,
01973                 SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED"));
01974         return -1;
01975     }
01976 
01977     ret = cimd2_request(packet, conn, &ts);
01978     if((ret == 0) && (ts) && DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask) && !pdata->no_dlr) {
01979         dlr_add(conn->name, ts, msg);
01980     }
01981     octstr_destroy(ts);
01982     packet_destroy(packet);
01983     
01984     if (ret == -1) {
01985         bb_smscconn_send_failed(conn, msg,
01986                 SMSCCONN_FAILED_REJECTED, octstr_create("REJECTED"));
01987     }
01988     else if (ret == -2) {
01989         cimd2_close_socket(pdata);
01990         bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY, NULL);
01991         mutex_lock(conn->flow_mutex);
01992         conn->status = SMSCCONN_DISCONNECTED;
01993         mutex_unlock(conn->flow_mutex);
01994     }
01995     else {
01996         bb_smscconn_sent(conn,msg, NULL);
01997     }
01998 
01999     return ret;
02000 }
02001 
02002 static int cimd2_receive_msg(SMSCConn *conn, Msg **msg)
02003 {
02004     PrivData *pdata = conn->data;
02005     long ret;
02006     struct packet *packet;
02007 
02008     gw_assert(pdata != NULL);
02009 
02010     if (gwlist_len(pdata->received) > 0) {
02011         *msg = gwlist_consume(pdata->received);
02012         return 1;
02013     }
02014 
02015     if (pdata->socket < 0) {
02016         /* XXX We have to assume that smsc_send_message is
02017          * currently trying to reopen, so we have to make
02018          * this thread wait.  It should be done in a nicer
02019          * way. */
02020         return 0;
02021     }
02022     
02023     ret = read_available(pdata->socket, 0);
02024     if (ret == 0) {
02025         if (pdata->keepalive > 0 && pdata->next_ping < time(NULL)) {
02026             if (cimd2_send_alive(conn) < 0)
02027         return -1;
02028         }
02029         return 0;
02030     }
02031 
02032     if (ret < 0) {
02033         warning(errno, "CIMD2[%s]: cimd2_receive_msg: read_available failed",
02034                 octstr_get_cstr(conn->id));
02035         return -1;
02036     }
02037 
02038     /* We have some data waiting... see if it is an sms delivery. */
02039     ret = octstr_append_from_socket(pdata->inbuffer, pdata->socket);
02040 
02041     if (ret == 0) {
02042         warning(0, "CIMD2[%s]: cimd2_receive_msg: service center closed connection.",
02043                 octstr_get_cstr(conn->id));
02044         return -1;
02045     }
02046     if (ret < 0) {
02047         warning(0, "CIMD2[%s]: cimd2_receive_msg: read failed",
02048                 octstr_get_cstr(conn->id));
02049         return -1;
02050     }
02051 
02052 
02053     for (;;) {
02054         packet = packet_extract(pdata->inbuffer,conn);
02055         if (!packet)
02056             break;
02057 
02058         packet_check(packet,conn);
02059         packet_check_can_receive(packet,conn);
02060         debug("bb.sms.cimd2", 0, "CIMD2[%s]: received: <%s>",
02061               octstr_get_cstr(pdata->conn->id), 
02062               octstr_get_cstr(packet->data));
02063 
02064         if (packet->operation < RESPONSE)
02065             cimd2_handle_request(packet, conn);
02066         else {
02067             error(0, "CIMD2[%s]: cimd2_receive_msg: unexpected response packet",
02068                 octstr_get_cstr(conn->id));
02069             octstr_dump(packet->data, 0);
02070         }
02071 
02072         packet_destroy(packet);
02073     }
02074 
02075     if (gwlist_len(pdata->received) > 0) {
02076     *msg = gwlist_consume(pdata->received);
02077         return 1;
02078     }
02079     return 0;
02080 }
02081 
02082 
02083 
02084 static Msg *cimd2_accept_delivery_report_message(struct packet *request,
02085                          SMSCConn *conn)
02086 {
02087     Msg *msg = NULL;
02088     Octstr *destination = NULL;
02089     Octstr *timestamp = NULL;
02090     Octstr *statuscode = NULL;
02091     int st_code;
02092     int code;
02093 
02094     destination = packet_get_parm(request, P_DESTINATION_ADDRESS);
02095     timestamp = packet_get_parm(request, P_MC_TIMESTAMP);
02096     statuscode = packet_get_parm(request, P_STATUS_CODE);
02097 
02098     st_code = atoi(octstr_get_cstr(statuscode));
02099 
02100     switch(st_code)
02101     {
02102     case 2:  /* validity period expired */
02103     case 3:  /* delivery failed */
02104     case 6: /* last no response */
02105     case 7: /* message cancelled */
02106     case 8: /* message deleted */
02107     case 9: /* message deleted by cancel */
02108     code = DLR_FAIL;
02109         break;
02110     case 4: /* delivery successful */
02111         code = DLR_SUCCESS;
02112         break;
02113     default:
02114         code = 0;
02115     }
02116     if(code)
02117         msg = dlr_find(conn->name, timestamp, destination, code);
02118     else
02119         msg = NULL;
02120 
02121     /* recode the body into msgdata */
02122     if (msg) {
02123         msg->sms.msgdata = packet_get_parm(request, P_USER_DATA);
02124         if (!msg->sms.msgdata) {
02125             msg->sms.msgdata = statuscode;
02126             statuscode = NULL;
02127         }
02128     }
02129 
02130     octstr_destroy(statuscode);
02131     octstr_destroy(destination);
02132     octstr_destroy(timestamp);
02133 
02134     return msg;
02135  }
02136 
02137 static Msg *sms_receive(SMSCConn *conn)
02138 {
02139     PrivData *pdata = conn->data;
02140     int ret;
02141     Msg *newmsg = NULL;
02142 
02143     ret = cimd2_receive_msg(conn, &newmsg);
02144     if (ret == 1) {
02145         /* if any smsc_id available, use it */
02146         newmsg->sms.smsc_id = octstr_duplicate(conn->id);
02147         return newmsg;
02148     } 
02149     else if (ret == 0) { /* no message, just retry... */
02150         return NULL;
02151     } 
02152     /* error. reconnect. */
02153     msg_destroy(newmsg);
02154     mutex_lock(conn->flow_mutex);
02155     cimd2_close_socket(pdata);
02156     conn->status = SMSCCONN_DISCONNECTED;
02157     mutex_unlock(conn->flow_mutex);
02158     return NULL;
02159 }
02160 
02161 
02162 static void io_thread (void *arg)
02163 {
02164     Msg       *msg;
02165     SMSCConn  *conn = arg;
02166     PrivData *pdata = conn->data;
02167     double    sleep = 0.0001;
02168 
02169     /* Make sure we log into our own log-file if defined */
02170     log_thread_to(conn->log_idx);
02171 
02172     /* remove messages from SMSC until we are killed */
02173     while (!pdata->quitting) {
02174     
02175         gwlist_consume(pdata->stopped); /* block here if suspended/isolated */
02176       
02177         /* check that connection is active */
02178         if (conn->status != SMSCCONN_ACTIVE) {
02179             if (cimd2_login(conn) != 0) { 
02180                 error(0, "CIMD2[%s]: Couldn't connect to SMSC (retrying in %ld seconds).",
02181                       octstr_get_cstr(conn->id), 
02182                       conn->reconnect_delay);
02183                 gwthread_sleep(conn->reconnect_delay);
02184                 mutex_lock(conn->flow_mutex);
02185                 conn->status = SMSCCONN_RECONNECTING; 
02186                 mutex_unlock(conn->flow_mutex);
02187                 continue; 
02188             } 
02189             mutex_lock(conn->flow_mutex);
02190             conn->status = SMSCCONN_ACTIVE;
02191             conn->connect_time = time(NULL);
02192             bb_smscconn_connected(conn);
02193             mutex_unlock(conn->flow_mutex);
02194         }
02195 
02196         /* receive messages */
02197         do { 
02198             msg = sms_receive(conn);
02199             if (msg) {
02200                 sleep = 0;
02201                 debug("bb.sms.cimd2", 0, "CIMD2[%s]: new message received",
02202                       octstr_get_cstr(conn->id));
02203                 bb_smscconn_receive(conn, msg);
02204             }
02205         } while (msg);
02206  
02207         /* send messages */
02208         do {
02209             msg = gwlist_extract_first(pdata->outgoing_queue);
02210             if (msg) {
02211                 sleep = 0;
02212                 if (cimd2_submit_msg(conn,msg) != 0) break;
02213             }
02214         } while (msg);
02215  
02216         if (sleep > 0) {
02217 
02218             /* note that this implementations means that we sleep even
02219              * when we fail connection.. but time is very short, anyway
02220              */
02221             gwthread_sleep(sleep);
02222             /* gradually sleep longer and longer times until something starts to
02223              * happen - this of course reduces response time, but that's better than
02224              * extensive CPU usage when it is not used
02225              */
02226             sleep *= 2;
02227             if (sleep >= 2.0)
02228                 sleep = 1.999999;
02229         }
02230         else {
02231             sleep = 0.0001;
02232         }
02233     }
02234 }
02235 
02236 
02237 static int cimd2_add_msg_cb (SMSCConn *conn, Msg *sms)
02238 {
02239     PrivData *pdata = conn->data;
02240     Msg *copy;
02241 
02242     copy = msg_duplicate(sms);
02243     gwlist_produce(pdata->outgoing_queue, copy);
02244     gwthread_wakeup(pdata->io_thread);
02245 
02246     return 0;
02247 }
02248 
02249 
02250 static int cimd2_shutdown_cb (SMSCConn *conn, int finish_sending)
02251 {
02252     PrivData *pdata = conn->data;
02253     
02254     debug("bb.sms", 0, "Shutting down SMSCConn CIMD2 %s (%s)",
02255           octstr_get_cstr(conn->id), 
02256           finish_sending ? "slow" : "instant");
02257 
02258     /* Documentation claims this would have been done by smscconn.c,
02259        but isn't when this code is being written. */
02260     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
02261     pdata->quitting = 1;     /* Separate from why_killed to avoid locking, as
02262                               * why_killed may be changed from outside? */
02263 
02264     if (finish_sending == 0) {
02265         Msg *msg;
02266         while ((msg = gwlist_extract_first(pdata->outgoing_queue)) != NULL) {
02267             bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN, NULL);
02268         }
02269     }
02270 
02271     cimd2_logout(conn);
02272     if (conn->is_stopped) {
02273         gwlist_remove_producer(pdata->stopped);
02274         conn->is_stopped = 0;
02275     }
02276 
02277     if (pdata->io_thread != -1) {
02278         gwthread_wakeup(pdata->io_thread);
02279         gwthread_join(pdata->io_thread);
02280     }
02281 
02282     cimd2_close_socket(pdata);
02283     cimd2_destroy(pdata); 
02284      
02285     debug("bb.sms", 0, "SMSCConn CIMD2 %s shut down.",  
02286           octstr_get_cstr(conn->id)); 
02287     conn->status = SMSCCONN_DEAD; 
02288     bb_smscconn_killed(); 
02289     return 0;
02290 }    
02291 
02292 static void cimd2_start_cb (SMSCConn *conn)
02293 {
02294     PrivData *pdata = conn->data;
02295 
02296     gwlist_remove_producer(pdata->stopped);
02297     /* in case there are messages in the buffer already */
02298     gwthread_wakeup(pdata->io_thread);
02299     debug("bb.sms", 0, "SMSCConn CIMD2 %s, start called",
02300           octstr_get_cstr(conn->id));
02301 }
02302 
02303 static void cimd2_stop_cb (SMSCConn *conn)
02304 {
02305     PrivData *pdata = conn->data;
02306     gwlist_add_producer(pdata->stopped);
02307     debug("bb.sms", 0, "SMSCConn CIMD2 %s, stop called",
02308           octstr_get_cstr(conn->id));
02309 }
02310 
02311 static long cimd2_queued_cb (SMSCConn *conn)
02312 {
02313     PrivData *pdata = conn->data;
02314     conn->load = (pdata ? (conn->status != SMSCCONN_DEAD ? 
02315                   gwlist_len(pdata->outgoing_queue) : 0) : 0);
02316     return conn->load; 
02317 }
02318 
02319 int smsc_cimd2_create(SMSCConn *conn, CfgGroup *grp)
02320 {
02321     PrivData *pdata;
02322     int ok;
02323     int maxlen;
02324 
02325     pdata = gw_malloc(sizeof(PrivData));
02326     conn->data = pdata;
02327     pdata->conn = conn;
02328    
02329     pdata->no_dlr = 0;
02330     pdata->quitting = 0;
02331     pdata->socket = -1;
02332     pdata->received = gwlist_create();
02333     pdata->inbuffer = octstr_create("");
02334     pdata->send_seq = 1;
02335     pdata->receive_seq = 0;
02336     pdata->outgoing_queue = gwlist_create();
02337     pdata->stopped = gwlist_create();
02338     gwlist_add_producer(pdata->outgoing_queue);
02339 
02340     if (conn->is_stopped)
02341       gwlist_add_producer(pdata->stopped);
02342 
02343     pdata->host = cfg_get(grp, octstr_imm("host"));
02344     if (cfg_get_integer(&(pdata->port), grp, octstr_imm("port")) == -1)
02345       pdata->port = 0;
02346     if (cfg_get_integer(&(pdata->our_port), grp, octstr_imm("our-port")) == -1)
02347       pdata->our_port = 0;
02348     pdata->username = cfg_get(grp, octstr_imm("smsc-username"));
02349     pdata->password = cfg_get(grp, octstr_imm("smsc-password"));
02350     pdata->my_number = cfg_get(grp, octstr_imm("my-number"));
02351     if (cfg_get_integer(&(pdata->keepalive), grp,octstr_imm("keepalive")) == -1)
02352         pdata->keepalive = 0;
02353 
02354     cfg_get_bool(&pdata->no_dlr, grp, octstr_imm("no-dlr"));
02355     
02356     /* Check that config is OK */
02357     ok = 1;
02358     if (pdata->host == NULL) {
02359         error(0,"CIMD2[%s]: Configuration file doesn't specify host",
02360               octstr_get_cstr(conn->id));
02361         ok = 0;
02362     }
02363     if (pdata->port == 0) {
02364         error(0,"CIMD2[%s]: Configuration file doesn't specify port",
02365               octstr_get_cstr(conn->id));
02366         ok = 0;
02367     }
02368     if (pdata->username == NULL) {
02369         error(0, "CIMD2[%s]: Configuration file doesn't specify username.",
02370               octstr_get_cstr(conn->id));
02371         ok = 0;
02372     }
02373     if (pdata->password == NULL) {
02374         error(0, "CIMD2[%s]: Configuration file doesn't specify password.",
02375               octstr_get_cstr(conn->id));
02376         ok = 0;
02377     }
02378 
02379     if (!ok) {
02380         cimd2_destroy(pdata);
02381         return -1;
02382     }
02383 
02384     conn->name = octstr_format("CIMD2:%s:%d:%s",
02385                      octstr_get_cstr(pdata->host),
02386                      pdata->port,
02387                      octstr_get_cstr(pdata->username));
02388 
02389 
02390     if (pdata->keepalive > 0) {
02391       debug("bb.sms.cimd2", 0, "CIMD2[%s]: Keepalive set to %ld seconds", 
02392             octstr_get_cstr(conn->id),
02393             pdata->keepalive);
02394         pdata->next_ping = time(NULL) + pdata->keepalive;
02395     }
02396 
02397     maxlen = parm_maxlen(P_USER_IDENTITY);
02398     if (octstr_len(pdata->username) > maxlen) {
02399         octstr_truncate(pdata->username, maxlen);
02400         warning(0, "CIMD2[%s]: Truncating username to %d chars", 
02401                 octstr_get_cstr(conn->id),
02402                 maxlen);
02403     }
02404 
02405     maxlen = parm_maxlen(P_PASSWORD);
02406     if (octstr_len(pdata->password) > maxlen) {
02407         octstr_truncate(pdata->password, maxlen);
02408         warning(0, "CIMD2[%s]: Truncating password to %d chars", 
02409                 octstr_get_cstr(conn->id),
02410                 maxlen);
02411     }
02412 
02413     pdata->io_thread = gwthread_create(io_thread, conn);
02414 
02415     if (pdata->io_thread == -1) {  
02416 
02417         error(0,"CIMD2[%s]: Couldn't start I/O thread.",
02418               octstr_get_cstr(conn->id));
02419         pdata->quitting = 1;
02420         gwthread_wakeup(pdata->io_thread);
02421         gwthread_join(pdata->io_thread);
02422         cimd2_destroy(pdata);
02423         return -1;  
02424     } 
02425 
02426     conn->send_msg = cimd2_add_msg_cb;
02427     conn->shutdown = cimd2_shutdown_cb;
02428     conn->queued = cimd2_queued_cb;
02429     conn->start_conn = cimd2_start_cb;
02430     conn->stop_conn = cimd2_stop_cb;
02431 
02432     return 0;
02433 }
02434 
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.