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

smsc_emi_x25.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 /*****************************************************************************
00058 * smsc_emi.c - implement interface to the CMG SMS Center (UCP/EMI).
00059 * Mikael Gueck for WapIT Ltd.
00060 */
00061 
00062 /* This file implements two smsc interfaces: EMI_X25 */
00063 
00064 #include <errno.h>
00065 #include <string.h>
00066 #include <stdio.h>
00067 #include <unistd.h>
00068 #include <fcntl.h>
00069 #include <termios.h>
00070 #include <stdarg.h>
00071 #include <stdlib.h>
00072 #include <string.h>
00073 #include <strings.h>
00074 
00075 #include <sys/time.h>
00076 #include <sys/types.h>
00077 #include <sys/socket.h>
00078 #include <sys/param.h>
00079 #include <sys/ioctl.h>
00080 
00081 #include "gwlib/gwlib.h"
00082 #include "smsc.h"
00083 #include "smsc_p.h"
00084 #include "alt_charsets.h"
00085 #include "sms.h"
00086 
00087 #ifndef CRTSCTS
00088 #define CRTSCTS 0
00089 #endif
00090 
00091 /******************************************************************************
00092 * Static functions
00093 */
00094 static int get_data(SMSCenter *smsc, char *buff, int length);
00095 
00096 static int put_data(SMSCenter *smsc, char *buff, int length, int is_backup);
00097 
00098 static int memorybuffer_append_data(SMSCenter *smsc, char *buff, int length);
00099 
00100 static int memorybuffer_insert_data(SMSCenter *smsc, char *buff, int length);
00101 
00102 static int memorybuffer_has_rawmessage(SMSCenter *smsc, int type, char auth);
00103 
00104 static int memorybuffer_cut_rawmessage(SMSCenter *smsc, char *buff, int length);
00105 
00106 static int parse_rawmessage_to_msg(SMSCenter *smsc, Msg **msg,
00107                                    char *rawmessage, int length);
00108 
00109 static int parse_msg_to_rawmessage(SMSCenter *smsc, Msg *msg,
00110                                    char *rawmessage, int length);
00111 
00112 static int acknowledge_from_rawmessage(SMSCenter *smsc,
00113                                    char *rawmessage, int length);
00114 
00115 static int parse_emi_to_iso88591(char *from, char *to,
00116                                  int length, int alt_charset);
00117 
00118 static int parse_iso88591_to_emi(char *from, char *to,
00119                                  int length, int alt_charset);
00120 static int parse_binary_to_emi(char *from, char *to, int length);
00121 
00122 static int at_dial(char *device, char *phonenum,
00123                    char *at_prefix, time_t how_long);
00124 static int guarantee_link(SMSCenter *smsc);
00125 
00126 
00127 static void generate_checksum(const unsigned char *buffer,
00128                               unsigned char *checksum_out);
00129 static int wait_for_ack(SMSCenter *smsc, int op_type);
00130 
00131 
00132 static char char_iso_to_sms(unsigned char from, int alt_charset);
00133 static char char_sms_to_iso(unsigned char from, int alt_charset);
00134 
00135 /******************************************************************************
00136 * Open the connection and log in - handshake baby
00137 */
00138 static int emi_open_connection(SMSCenter *smsc)
00139 {
00140     char tmpbuff[1024];
00141 
00142     sprintf(tmpbuff, "/dev/%s", smsc->emi_serialdevice);
00143     smsc->emi_fd = at_dial(tmpbuff, smsc->emi_phonenum, "ATD", 30);
00144 
00145     if (smsc->emi_fd <= 0)
00146         return -1;
00147 
00148     return 0;
00149 }
00150 
00151 /* open EMI smscenter */
00152 
00153 SMSCenter *emi_open(char *phonenum, char *serialdevice, char *username, char *password)
00154 {
00155     SMSCenter *smsc;
00156 
00157     smsc = smscenter_construct();
00158     if (smsc == NULL)
00159         goto error;
00160 
00161     smsc->type = SMSC_TYPE_EMI_X25;
00162 
00163     smsc->emi_phonenum = gw_strdup(phonenum);
00164     smsc->emi_serialdevice = gw_strdup(serialdevice);
00165     smsc->emi_username = gw_strdup(username);
00166     smsc->emi_password = gw_strdup(password);
00167 
00168     smsc->emi_current_msg_number = 0;
00169 
00170     if (emi_open_connection(smsc) < 0)
00171         goto error;
00172 
00173     sprintf(smsc->name, "EMI:%s:%s", smsc->emi_phonenum,
00174             smsc->emi_username);
00175     return smsc;
00176 
00177 error:
00178     error(0, "emi_open failed");
00179     smscenter_destruct(smsc);
00180     return NULL;
00181 }
00182 
00183 int emi_reopen(SMSCenter *smsc)
00184 {
00185     emi_close(smsc);
00186 
00187     if (emi_open_connection(smsc) < 0) {
00188         error(0, "emi_reopen failed");
00189         return -1;
00190     }
00191     return 0;
00192 }
00193 
00194 int emi_close(SMSCenter *smsc)
00195 {
00196     return emi_close_ip(smsc);
00197 }
00198 
00199 static int emi_fill_ucp60_login(char *buf, char *OAdC, char *passwd) {
00200     int max_ia5passwd_len;
00201     char *ia5passwd;
00202 
00203     max_ia5passwd_len = strlen(passwd) * 2 + 1;
00204     ia5passwd = gw_malloc(max_ia5passwd_len);
00205 
00206     if (parse_binary_to_emi(passwd, ia5passwd, strlen(passwd)) < 0) {
00207         error(0, "parse_binary_to_emi failed");
00208         gw_free(ia5passwd);
00209         return -1;
00210     }
00211 
00212     sprintf(buf, "%s/%c/%c/%c/%s//%s/////",
00213         OAdC,      /* OAdC: Address code originator */
00214         '6',       /* OTON: 6 = Abbreviated number (short number alias) */
00215         '5',       /* ONPI: 5 = Private (TCP/IP address/abbreviated number address) */
00216         '1',       /* STYP: 1 = open session */
00217         ia5passwd, /* PWD:  Current password encoded into IA5 characters */
00218         "0100"     /* VERS: Version number  0100 */
00219         );
00220 
00221     gw_free(ia5passwd);
00222     return 0;
00223 }
00224 
00225 static int emi_open_session(SMSCenter *smsc)
00226 {
00227     char message_whole  [1024];
00228     char message_body   [1024];
00229     char message_header [50];
00230     char message_footer [10];
00231     char my_buffer      [1024];
00232     int length;
00233 
00234     memset(message_whole,  0, sizeof(message_whole));
00235     memset(message_body,   0, sizeof(message_body));
00236     memset(message_header, 0, sizeof(message_header));
00237     memset(message_footer, 0, sizeof(message_footer));
00238 
00239     if (emi_fill_ucp60_login(message_body, smsc->emi_username, smsc->emi_password) < 0) {
00240         error(0, "emi_fill_ucp60_login failed");
00241         return -1;
00242     }
00243 
00244     length = strlen(message_body);
00245     length += 13;  /* header (fixed) */
00246     length += 2;   /* footer (fixed) */
00247     length += 2;   /* slashes between header, body, footer */
00248 
00249     sprintf(message_header, "%02i/%05i/O/60",
00250             (smsc->emi_current_msg_number++ % 100), length);
00251     
00252     /* FOOTER */
00253 
00254     sprintf(my_buffer, "%s/%s/", message_header, message_body);
00255     generate_checksum((unsigned char *)my_buffer, (unsigned char *)message_footer);
00256 
00257     sprintf(message_whole, "\x02%s/%s/%s\x03", message_header,
00258             message_body, message_footer);
00259 
00260     debug("bb.sms.emi", 0, "final UCP60 msg: <%s>", message_whole);
00261 
00262     put_data(smsc, message_whole, strlen(message_whole), 0);
00263 
00264     if (!wait_for_ack(smsc, 60)) {
00265     info(0, "emi_open_session: wait for ack failed!");
00266     return -1;
00267     }
00268 
00269     return 0;
00270 }
00271 
00272 
00273 /*******************************************************
00274  * the actual protocol open... quite simple here */
00275 
00276 static int emi_open_connection_ip(SMSCenter *smsc)
00277 {
00278     smsc->emi_fd =
00279         tcpip_connect_to_server_with_port(smsc->emi_hostname,
00280                                           smsc->emi_port, smsc->emi_our_port,
00281                       NULL);
00282         /* XXX add interface_name if required */
00283     if (smsc->emi_fd < 0)
00284         return -1;
00285 
00286     if (smsc->emi_username && smsc->emi_password) {
00287     return emi_open_session(smsc);
00288     }
00289     
00290     return 0;
00291 }
00292 
00293 
00294 int emi_reopen_ip(SMSCenter *smsc)
00295 {
00296     emi_close_ip(smsc);
00297 
00298     return emi_open_connection_ip(smsc);
00299 }
00300 
00301 
00302 int emi_close_ip(SMSCenter *smsc)
00303 {
00304 
00305     if (smsc->emi_fd == -1) {
00306         info(0, "Trying to close already closed EMI, ignoring");
00307         return 0;
00308     }
00309     close(smsc->emi_fd);
00310     smsc->emi_fd = -1;
00311 
00312     return 0;
00313 }
00314 
00315 
00316 /******************************************************************************
00317 * Check if the buffers contain any messages
00318 */
00319 int emi_pending_smsmessage(SMSCenter *smsc)
00320 {
00321 
00322     char *tmpbuff;
00323     int n = 0;
00324     /*  time_t timenow; */
00325 
00326     /* Block until we have a connection */
00327     guarantee_link(smsc);
00328 
00329     /* If we have MO-message, then act (return 1) */
00330     if (memorybuffer_has_rawmessage(smsc, 52, 'O') > 0 ||
00331         memorybuffer_has_rawmessage(smsc, 1, 'O') > 0 )
00332         return 1;
00333 
00334     tmpbuff = gw_malloc(10 * 1024);
00335     memset(tmpbuff, 0, 10*1024);
00336 
00337     /* check for data */
00338     n = get_data(smsc, tmpbuff, 10 * 1024);
00339     if (n > 0)
00340         memorybuffer_insert_data(smsc, tmpbuff, n);
00341 
00342     /* delete all ACKs/NACKs/whatever */
00343     while (memorybuffer_has_rawmessage(smsc, 51, 'R') > 0 ||
00344            memorybuffer_has_rawmessage(smsc, 1, 'R') > 0)
00345         memorybuffer_cut_rawmessage(smsc, tmpbuff, 10*1024);
00346 
00347     gw_free(tmpbuff);
00348 
00349     /* If we have MO-message, then act (return 1) */
00350 
00351     if (memorybuffer_has_rawmessage(smsc, 52, 'O') > 0 ||
00352         memorybuffer_has_rawmessage(smsc, 1, 'O') > 0)
00353         return 1;
00354 
00355     /*
00356         time(&timenow);
00357         if( (smsc->emi_last_spoke + 60*20) < timenow) {
00358             time(&smsc->emi_last_spoke);
00359         }
00360     */
00361 
00362     return 0;
00363 
00364 }
00365 
00366 
00367 
00368 
00369 /******************************************************************************
00370  * Submit (send) a Mobile Terminated message to the EMI server
00371  */
00372 int emi_submit_msg(SMSCenter *smsc, Msg *omsg)
00373 {
00374     char *tmpbuff = NULL;
00375 
00376     if (smsc == NULL) goto error;
00377     if (omsg == NULL) goto error;
00378 
00379     tmpbuff = gw_malloc(10 * 1024);
00380     memset(tmpbuff, 0, 10*1024);
00381 
00382     if (parse_msg_to_rawmessage(smsc, omsg, tmpbuff, 10*1024) < 1)
00383         goto error;
00384 
00385     if (put_data(smsc, tmpbuff, strlen(tmpbuff), 0) < 0) {
00386         info(0, "put_data failed!");
00387         goto error;
00388     }
00389 
00390     wait_for_ack(smsc, 51);
00391 
00392     /*  smsc->emi_current_msg_number += 1; */
00393     debug("bb.sms.emi", 0, "Submit Ok...");
00394 
00395     gw_free(tmpbuff);
00396     return 0;
00397 
00398 error:
00399     debug("bb.sms.emi", 0, "Submit Error...");
00400 
00401     gw_free(tmpbuff);
00402     return -1;
00403 }
00404 
00405 /******************************************************************************
00406 * Receive a Mobile Terminated message to the EMI server
00407 */
00408 int emi_receive_msg(SMSCenter *smsc, Msg **tmsg)
00409 {
00410     char *tmpbuff;
00411     Msg *msg = NULL;
00412 
00413     *tmsg = NULL;
00414 
00415     tmpbuff = gw_malloc(10 * 1024);
00416     memset(tmpbuff, 0, 10*1024);
00417 
00418     /* get and delete message from buffer */
00419     memorybuffer_cut_rawmessage(smsc, tmpbuff, 10*1024);
00420     parse_rawmessage_to_msg(smsc, &msg, tmpbuff, strlen(tmpbuff));
00421 
00422     /* yeah yeah, I got the message... */
00423     acknowledge_from_rawmessage(smsc, tmpbuff, strlen(tmpbuff));
00424 
00425     /* return with the joyful news */
00426     gw_free(tmpbuff);
00427 
00428     if (msg == NULL) goto error;
00429 
00430     *tmsg = msg;
00431 
00432     return 1;
00433 
00434 error:
00435     gw_free(tmpbuff);
00436     msg_destroy(msg);
00437     return -1;
00438 }
00439 
00440 
00441 /******************************************************************************
00442 * Internal functions
00443 */
00444 
00445 
00446 /******************************************************************************
00447 * Guarantee that we have a link
00448 */
00449 static int guarantee_link(SMSCenter *smsc)
00450 {
00451     int need_to_connect = 0;
00452 
00453     /* If something is obviously wrong. */
00454     if (strstr(smsc->buffer, "OK")) need_to_connect = 1;
00455     if (strstr(smsc->buffer, "NO CARRIER")) need_to_connect = 1;
00456     if (strstr(smsc->buffer, "NO DIALTONE")) need_to_connect = 1;
00457 
00458     /* Clear the buffer */
00459     while (need_to_connect) {
00460         /* Connect */
00461         need_to_connect = emi_open_connection(smsc) < 0;
00462 
00463         /* Clear the buffer so that the next call to guarantee
00464            doesn't find the "NO CARRIER" string again. */
00465         smsc->buflen = 0;
00466         memset(smsc->buffer, 0, smsc->bufsize);
00467     }
00468 
00469     return 0;
00470 }
00471 
00472 static int at_dial(char *device, char *phonenum, char *at_prefix, time_t how_long)
00473 {
00474     char tmpbuff[1024];
00475     int howmanyread = 0;
00476     int thistime = 0;
00477     int redial;
00478     int fd = -1;
00479     int ret;
00480     time_t timestart;
00481     struct termios tios;
00482 
00483     /* The time at the start of the function is used when
00484        determining whether we have used up our allotted
00485        dial time and have to abort. */
00486     time(&timestart);
00487 
00488     /* Open the device properly. Remember to set the
00489        access codes correctly. */
00490     fd = open(device, O_RDWR | O_NONBLOCK | O_NOCTTY);
00491     if (fd == -1) {
00492         error(errno, "at_dial: error opening character device <%s>", device);
00493         goto error;
00494     }
00495     tcflush(fd, TCIOFLUSH);
00496 
00497     /* The speed initialisation is pretty important. */
00498     tcgetattr(fd, &tios);
00499 #if defined(B115200)
00500     cfsetospeed(&tios, B115200);
00501     cfsetispeed(&tios, B115200);
00502 #elif defined(B76800)
00503     cfsetospeed(&tios, B76800);
00504     cfsetispeed(&tios, B76800);
00505 #elif defined(B57600)
00506     cfsetospeed(&tios, B57600);
00507     cfsetispeed(&tios, B57600);
00508 #elif defined(B38400)
00509     cfsetospeed(&tios, B38400);
00510     cfsetispeed(&tios, B38400);
00511 #elif defined(B19200)
00512     cfsetospeed(&tios, B19200);
00513     cfsetispeed(&tios, B19200);
00514 #elif defined(B9600)
00515     cfsetospeed(&tios, B9600);
00516     cfsetispeed(&tios, B9600);
00517 #endif
00518     kannel_cfmakeraw(&tios);
00519     tios.c_cflag |= (HUPCL | CREAD | CRTSCTS);
00520     ret = tcsetattr(fd, TCSANOW, &tios);
00521     if (ret == -1) {
00522         error(errno, "EMI[X25]: at_dial: fail to set termios attribute");
00523     }
00524 
00525     /* Dial using an AT command string. */
00526     for (redial = 1; redial; ) {
00527         info(0, "at_dial: dialing <%s> on <%s> for <%i> seconds",
00528              phonenum, device,
00529              (int)(how_long - (time(NULL) - timestart)));
00530 
00531         /* Send AT dial request. */
00532         howmanyread = 0;
00533         sprintf(tmpbuff, "%s%s\r\n", at_prefix, phonenum);
00534         ret = write(fd, tmpbuff, strlen(tmpbuff));  /* errors... -mg */
00535         memset(&tmpbuff, 0, sizeof(tmpbuff));
00536 
00537         /* Read the answer to the AT command and react accordingly. */
00538         for (; ; ) {
00539             /* We don't want to dial forever */
00540             if (how_long != 0 && time(NULL) > timestart + how_long)
00541                 goto timeout;
00542 
00543             /* We don't need more space for dialout */
00544             if (howmanyread >= (int) sizeof(tmpbuff))
00545                 goto error;
00546 
00547             /* We read 1 char a time so that we don't
00548                accidentally read past the modem chat and
00549                into the SMSC datastream -mg */
00550             thistime = read(fd, &tmpbuff[howmanyread], 1);
00551             if (thistime == -1) {
00552                 if (errno == EAGAIN) continue;
00553                 if (errno == EINTR) continue;
00554                 goto error;
00555             } else {
00556                 howmanyread += thistime;
00557             }
00558 
00559             /* Search for the newline on the AT status line. */
00560             if (tmpbuff[howmanyread - 1] == '\r'
00561                 || tmpbuff[howmanyread - 1] == '\n') {
00562 
00563                 /* XXX ADD ALL POSSIBLE CHAT STRINGS XXX */
00564 
00565                 if (strstr(tmpbuff, "CONNECT") != NULL) {
00566                     debug("bb.sms.emi", 0, "at_dial: CONNECT");
00567                     redial = 0;
00568                     break;
00569 
00570                 } else if (strstr(tmpbuff, "NO CARRIER") != NULL) {
00571                     debug("bb.sms.emi", 0, "at_dial: NO CARRIER");
00572                     redial = 1;
00573                     break;
00574 
00575                 } else if (strstr(tmpbuff, "BUSY") != NULL) {
00576                     debug("bb.sms.emi", 0, "at_dial: BUSY");
00577                     redial = 1;
00578                     break;
00579 
00580                 } else if (strstr(tmpbuff, "NO DIALTONE") != NULL) {
00581                     debug("bb.sms.emi", 0, "at_dial: NO DIALTONE");
00582                     redial = 1;
00583                     break;
00584 
00585                 }
00586 
00587             } /* End of if lastchr=='\r'||'\n'. */
00588 
00589             /* Thou shall not consume all system resources
00590                by repeatedly looping a strstr search when
00591                the string update latency is very high as it
00592                is in serial communication. -mg */
00593             usleep(1000);
00594 
00595         } /* End of read loop. */
00596 
00597         /* Thou shall not flood the modem with dial requests. -mg */
00598         sleep(1);
00599 
00600     } /* End of dial loop. */
00601 
00602     debug("bb.sms.emi", 0, "at_dial: done with dialing");
00603     return fd;
00604 
00605 timeout:
00606     error(0, "at_dial timed out");
00607     close(fd);
00608     return -1;
00609 
00610 error:
00611     error(0, "at_dial failed");
00612     close(fd);
00613     return -1;
00614 }
00615 
00616 /******************************************************************************
00617  * Wait for an ACK or NACK from the remote
00618  *
00619  * REQUIRED by the protocol that it must be waited...
00620  */
00621 static int wait_for_ack(SMSCenter *smsc, int op_type)
00622 {
00623     char *tmpbuff;
00624     int found = 0;
00625     int n;
00626     time_t start;
00627 
00628     tmpbuff = gw_malloc(10 * 1024);
00629     memset(tmpbuff, 0, 10*1024);
00630     start = time(NULL);
00631     do {
00632         /* check for data */
00633         n = get_data(smsc, tmpbuff, 1024 * 10);
00634 
00635     /* At least the X.31 interface wants to append the data.
00636        Kalle, what about the TCP/IP interface? Am I correct
00637        that you are assuming that the message arrives in a 
00638        single read(2)? -mg */
00639     if (n > 0)
00640         memorybuffer_append_data(smsc, tmpbuff, n);
00641 
00642         /* act on data */
00643         if (memorybuffer_has_rawmessage(smsc, op_type, 'R') > 0) {
00644             memorybuffer_cut_rawmessage(smsc, tmpbuff, 10*1024);
00645             debug("bb.sms.emi", 0, "Found ACK/NACK: <%s>", tmpbuff);
00646             found = 1;
00647         }
00648     } while (!found && ((time(NULL) - start) < 5));
00649 
00650     gw_free(tmpbuff);
00651     return found;
00652 }
00653 
00654 
00655 /******************************************************************************
00656  * Get the modem buffer data to buff, return the amount read
00657  *
00658  * Reads from main fd, but also from backup-fd - does accept if needed
00659  */
00660 static int get_data(SMSCenter *smsc, char *buff, int length)
00661 {
00662     int n = 0;
00663 
00664     struct sockaddr_in client_addr;
00665     socklen_t client_addr_len;
00666 
00667     fd_set rf;
00668     struct timeval to;
00669     int ret;
00670 
00671     memset(buff, 0, length);
00672 
00673     if (smsc->type == SMSC_TYPE_EMI_X25) {
00674         tcdrain(smsc->emi_fd);
00675         n = read(smsc->emi_fd, buff, length);
00676         return n;
00677     }
00678 
00679     FD_ZERO(&rf);
00680     if (smsc->emi_fd >= 0) FD_SET(smsc->emi_fd, &rf);
00681     if (smsc->emi_secondary_fd >= 0) FD_SET(smsc->emi_secondary_fd, &rf);
00682     if (smsc->emi_backup_fd > 0) FD_SET(smsc->emi_backup_fd, &rf);
00683 
00684     FD_SET(0, &rf);
00685     to.tv_sec = 0;
00686     to.tv_usec = 100;
00687 
00688     ret = select(FD_SETSIZE, &rf, NULL, NULL, &to);
00689 
00690     if (ret > 0) {
00691         if (smsc->emi_secondary_fd >= 0 && FD_ISSET(smsc->emi_secondary_fd, &rf)) {
00692             n = read(smsc->emi_secondary_fd, buff, length - 1);
00693 
00694             if (n == -1) {
00695                 error(errno, "Error - Secondary socket closed");
00696                 close(smsc->emi_secondary_fd);
00697                 smsc->emi_secondary_fd = -1;
00698             } else if (n == 0) {
00699                 info(0, "Secondary socket closed by SMSC");
00700                 close(smsc->emi_secondary_fd);
00701                 smsc->emi_secondary_fd = -1;
00702             } else {            /* UGLY! We  put 'X' after message */
00703                 buff[n] = 'X';  /* if it is from secondary fd!!!  */
00704                 n++;
00705             }
00706         } else if (smsc->emi_fd >= 0 && FD_ISSET(smsc->emi_fd, &rf)) {
00707             n = read(smsc->emi_fd, buff, length);
00708             if (n == 0) {
00709                 close(smsc->emi_fd);
00710                 info(0, "Main EMI socket closed by SMSC");
00711                 smsc->emi_fd = -1;  /* ready to be re-opened */
00712             }
00713         }
00714         if ((smsc->emi_backup_fd > 0) && FD_ISSET(smsc->emi_backup_fd, &rf)) {
00715             if (smsc->emi_secondary_fd == -1) {
00716         Octstr *ip, *allow;
00717         
00718                 smsc->emi_secondary_fd = accept(smsc->emi_backup_fd,
00719               (struct sockaddr *)&client_addr, &client_addr_len);
00720 
00721         ip = host_ip(client_addr);
00722         if (smsc->emi_backup_allow_ip == NULL)
00723             allow = NULL;
00724         else
00725             allow = octstr_create(smsc->emi_backup_allow_ip);
00726         if (is_allowed_ip(allow, octstr_imm("*.*.*.*"), ip) == 0) {
00727             info(0, "SMSC secondary connection tried from <%s>, "
00728                     "disconnected",
00729                 octstr_get_cstr(ip));
00730             octstr_destroy(ip);
00731             octstr_destroy(allow);
00732             close(smsc->emi_secondary_fd);
00733             smsc->emi_secondary_fd = -1;
00734             return 0;
00735         }
00736                 info(0, "Secondary socket opened by SMSC from <%s>",
00737              octstr_get_cstr(ip));
00738         octstr_destroy(ip);
00739         octstr_destroy(allow);
00740             } else
00741                 info(0, "New connection request while old secondary is open!");
00742         }
00743     }
00744     if (n > 0) {
00745         debug("bb.sms.emi", 0, "get_data:Read %d bytes: <%.*s>", n, n, buff);
00746         debug("bb.sms.emi", 0, "get_data:smsc->buffer == <%s>", smsc->buffer);
00747     }
00748     return n;
00749 
00750 }
00751 
00752 /******************************************************************************
00753 * Put the buff data to the modem buffer, return the amount of data put
00754 */
00755 static int put_data(SMSCenter *smsc, char *buff, int length, int is_backup)
00756 {
00757     size_t len = length;
00758     int ret;
00759     int fd = -1;
00760 
00761     fd = smsc->emi_fd;
00762     tcdrain(smsc->emi_fd);
00763 
00764     /* Write until all data has been successfully written to the fd. */
00765     while (len > 0) {
00766         ret = write(fd, buff, len);
00767         if (ret == -1) {
00768             if (errno == EINTR) continue;
00769             if (errno == EAGAIN) continue;
00770             error(errno, "Writing to fd failed");
00771             return -1;
00772         }
00773         /* ret may be less than len, if the writing
00774            was interrupted by a signal. */
00775         len -= ret;
00776         buff += ret;
00777     }
00778 
00779     if (smsc->type == SMSC_TYPE_EMI_X25) {
00780         /* Make sure the data gets written immediately.
00781            Wait a while just to add some latency so
00782            that the modem (or the UART) doesn't choke
00783            on the data. */
00784         tcdrain(smsc->emi_fd);
00785         usleep(1000);
00786     }
00787 
00788     return 0;
00789 }
00790 
00791 /******************************************************************************
00792 * Append the buff data to smsc->buffer
00793 */
00794 static int memorybuffer_append_data(SMSCenter *smsc, char *buff, int length)
00795 {
00796     while (smsc->bufsize < (smsc->buflen + length)) { /* buffer too small */
00797         char *p = gw_realloc(smsc->buffer, smsc->bufsize * 2);
00798         smsc->buffer = p;
00799         smsc->bufsize *= 2;
00800     }
00801 
00802     memcpy(smsc->buffer + smsc->buflen, buff, length);
00803     smsc->buflen += length;
00804     return 0;
00805 
00806 }
00807 
00808 /******************************************************************************
00809 * Insert (put to head) the buff data to smsc->buffer
00810 */
00811 static int memorybuffer_insert_data(SMSCenter *smsc, char *buff, int length)
00812 {
00813     while (smsc->bufsize < (smsc->buflen + length)) { /* buffer too small */
00814         char *p = gw_realloc(smsc->buffer, smsc->bufsize * 2);
00815         smsc->buffer = p;
00816         smsc->bufsize *= 2;
00817     }
00818     memmove(smsc->buffer + length, smsc->buffer, smsc->buflen);
00819     memcpy(smsc->buffer, buff, length);
00820     smsc->buflen += length;
00821     return 0;
00822 
00823 }
00824 
00825 /******************************************************************************
00826 * Check the smsc->buffer for a raw STX...ETX message
00827 */
00828 static int memorybuffer_has_rawmessage(SMSCenter *smsc, int type, char auth)
00829 {
00830     char tmpbuff[1024], tmpbuff2[1024];
00831     char *stx, *etx;
00832 
00833     stx = memchr(smsc->buffer, '\2', smsc->buflen);
00834     etx = memchr(smsc->buffer, '\3', smsc->buflen);
00835 
00836     if (stx && etx && stx < etx) {
00837         strncpy(tmpbuff, stx, etx - stx + 1);
00838         tmpbuff[etx - stx + 1] = '\0';
00839         if (auth)
00840             sprintf(tmpbuff2, "/%c/%02i/", auth, type);
00841         else
00842             sprintf(tmpbuff2, "/%02i/", type);
00843 
00844         if (strstr(tmpbuff, tmpbuff2) != NULL) {
00845             debug("bb.sms.emi", 0, "found message <%c/%02i>...msg <%s>", auth, type, tmpbuff);
00846             return 1;
00847         }
00848     }
00849     return 0;
00850 
00851 }
00852 
00853 /******************************************************************************
00854 * Cut the first raw message from the smsc->buffer
00855 * and put it in buff, return success 0, failure -1
00856 */
00857 static int memorybuffer_cut_rawmessage(SMSCenter *smsc, char *buff, int length)
00858 {
00859 
00860     char *stx, *etx;
00861     int size_of_cut_piece;
00862     int size_of_the_rest;
00863 
00864     /* We don't check for NULLs since we're sure that nobody has fooled
00865        around with smsc->buffer since has_rawmessage was last called... */
00866 
00867     stx = memchr(smsc->buffer, '\2', smsc->buflen);
00868     etx = memchr(smsc->buffer, '\3', smsc->buflen);
00869 
00870     if (*(etx + 1) == 'X')  /* secondary! UGLY KLUDGE */
00871         etx++;
00872 
00873     size_of_cut_piece = (etx - stx) + 1;
00874     size_of_the_rest = (smsc->buflen - size_of_cut_piece);
00875 
00876     if (length < size_of_cut_piece) {
00877         error(0, "the buffer you provided for cutting was too small");
00878         return -1;
00879     }
00880 
00881     /* move the part before our magic rawmessage to the safe house */
00882     memcpy(buff, stx, size_of_cut_piece);
00883     buff[size_of_cut_piece] = '\0';     /* NULL-terminate */
00884 
00885     /* move the stuff in membuffer one step down */
00886     memmove(stx, etx + 1, (smsc->buffer + smsc->bufsize) - stx );
00887 
00888     smsc->buflen -= size_of_cut_piece;
00889 
00890     return 0;
00891 
00892 }
00893 
00894 /******************************************************************************
00895 * Parse the raw message to the Msg structure
00896 */
00897 static int parse_rawmessage_to_msg(SMSCenter *smsc, Msg **msg,
00898                                    char *rawmessage, int length)
00899 {
00900 
00901     char emivars[128][1024];
00902     char *leftslash, *rightslash;
00903     char isotext[2048];
00904     int msgnbr;
00905     int tmpint;
00906 
00907     msgnbr = -1;
00908 
00909     memset(isotext, 0, sizeof(isotext));
00910 
00911     strncpy(isotext, rawmessage, length);
00912     leftslash = isotext;
00913 
00914     for (tmpint = 0; leftslash != NULL; tmpint++) {
00915         rightslash = strchr(leftslash + 1, '/');
00916 
00917         if (rightslash == NULL)
00918             rightslash = strchr(leftslash + 1, '\3');
00919 
00920         if (rightslash == NULL)
00921             break;
00922 
00923         *rightslash = '\0';
00924         strcpy(emivars[tmpint], leftslash + 1);
00925         leftslash = rightslash;
00926     }
00927 
00928     if (strcmp(emivars[3], "01") == 0) {
00929         if (strcmp(emivars[7], "2") == 0) {
00930             strcpy(isotext, emivars[8]);
00931         } else if (strcmp(emivars[7], "3") == 0) {
00932             parse_emi_to_iso88591(emivars[8], isotext, sizeof(isotext),
00933                                   smsc->alt_charset);
00934         } else {
00935             error(0, "Unknown 01-type EMI SMS (%s)", emivars[7]);
00936             strcpy(isotext, "");
00937         }
00938     } else if (strcmp(emivars[3], "51") == 0) {
00939         parse_emi_to_iso88591(emivars[24], isotext, sizeof(isotext),
00940                               smsc->alt_charset);
00941     } else if (strcmp(emivars[3], "52") == 0) {
00942         parse_emi_to_iso88591(emivars[24], isotext, sizeof(isotext),
00943                               smsc->alt_charset);
00944     } else {
00945         error(0, "HEY WE SHOULD NOT BE HERE!! Type = %s", emivars[3]);
00946         strcpy(isotext, "");
00947     }
00948 
00949     *msg = msg_create(sms);
00950     if (*msg == NULL) goto error;
00951 
00952     (*msg)->sms.sender = octstr_create(emivars[5]);
00953     (*msg)->sms.receiver = octstr_create(emivars[4]);
00954     (*msg)->sms.msgdata = octstr_create(isotext);
00955     (*msg)->sms.udhdata = NULL;
00956 
00957     return msgnbr;
00958 
00959 error:
00960     return -1;
00961 }
00962 
00963 /*
00964  * notify the SMSC that we got the message
00965  */
00966 static int acknowledge_from_rawmessage(SMSCenter *smsc,
00967                                        char *rawmessage, int length)
00968 {
00969 
00970     char emivars[128][1024];
00971     char timestamp[2048], sender[2048], receiver[2048];
00972     char emitext[2048], isotext[2048];
00973     char *leftslash, *rightslash;
00974     int msgnbr;
00975     int tmpint;
00976     int is_backup = 0;
00977 
00978     msgnbr = -1;
00979     memset(&sender, 0, sizeof(sender));
00980     memset(&receiver, 0, sizeof(receiver));
00981     memset(&emitext, 0, sizeof(emitext));
00982     memset(&isotext, 0, sizeof(isotext));
00983     memset(&timestamp, 0, sizeof(timestamp));
00984 
00985     strncpy(isotext, rawmessage, length);
00986     leftslash = isotext;
00987 
00988     if (isotext[length - 1] == 'X')
00989         is_backup = 1;
00990 
00991     for (tmpint = 0; leftslash != NULL; tmpint++) {
00992         rightslash = strchr(leftslash + 1, '/');
00993 
00994         if (rightslash == NULL)
00995             rightslash = strchr(leftslash + 1, '\3');
00996 
00997         if (rightslash == NULL)
00998             break;
00999 
01000         *rightslash = '\0';
01001         strcpy(emivars[tmpint], leftslash + 1);
01002         leftslash = rightslash;
01003     }
01004 
01005     /* BODY */
01006     sprintf(isotext, "A//%s:%s", emivars[4], emivars[18]);
01007     sprintf(isotext, "A//%s:", emivars[5]);
01008     is_backup = 0;
01009 
01010     /* HEADER */
01011 
01012     debug("bb.sms.emi", 0, "acknowledge: type = '%s'", emivars[3]);
01013 
01014     sprintf(emitext, "%s/%05i/%s/%s", emivars[0], (int) strlen(isotext) + 17,
01015             "R", emivars[3]);
01016 
01017     smsc->emi_current_msg_number = atoi(emivars[0]) + 1;
01018 
01019     /* FOOTER */
01020     sprintf(timestamp, "%s/%s/", emitext, isotext);
01021     generate_checksum((unsigned char *)timestamp, (unsigned char *)receiver);
01022 
01023     sprintf(sender, "%c%s/%s/%s%c", 0x02, emitext, isotext, receiver, 0x03);
01024     put_data(smsc, sender, strlen(sender), is_backup);
01025 
01026     return msgnbr;
01027 
01028 }
01029 
01030 
01031 /******************************************************************************
01032 * Parse the Msg structure to the raw message format
01033 */
01034 static int parse_msg_to_rawmessage(SMSCenter *smsc, Msg *msg, char *rawmessage, int rawmessage_length)
01035 {
01036     char message_whole[10*1024];
01037     char message_body[10*1024];
01038     char message_header[1024];
01039     char message_footer[1024];
01040 
01041     char my_buffer[10*1024];
01042     char my_buffer2[10*1024];
01043     char msgtext[1024];
01044     int length;
01045     char mt;
01046     char mcl[20];
01047     char snumbits[20];
01048     char xser[1024];
01049     int udh_len;
01050 
01051     memset(&message_whole, 0, sizeof(message_whole));
01052     memset(&message_body, 0, sizeof(message_body));
01053     memset(&message_header, 0, sizeof(message_header));
01054     memset(&message_footer, 0, sizeof(message_footer));
01055     memset(&my_buffer, 0, sizeof(my_buffer));
01056     memset(&my_buffer2, 0, sizeof(my_buffer2));
01057     mt = '3';
01058     memset(&snumbits, 0, sizeof(snumbits));
01059     memset(&xser, 0, sizeof(xser));
01060 
01061     /* XXX parse_iso88591_to_emi shouldn't use NUL terminated
01062      * strings, but Octstr directly, or a char* and a length.
01063      */
01064     if (octstr_len(msg->sms.udhdata)) {
01065         char xserbuf[258];
01066         /* we need a properly formated UDH here, there first byte contains his length
01067          * this will be formatted in the xser field of the EMI Protocol
01068          */
01069         udh_len = octstr_get_char(msg->sms.udhdata, 0) + 1;
01070         xserbuf[0] = 1;
01071         xserbuf[1] = udh_len;
01072         octstr_get_many_chars(&xserbuf[2], msg->sms.udhdata, 0, udh_len);
01073         parse_binary_to_emi(xserbuf, xser, udh_len + 2);
01074     } else {
01075         udh_len = 0;
01076     }
01077 
01078     if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF) {
01079         octstr_get_many_chars(msgtext, msg->sms.msgdata, 0, octstr_len(msg->sms.msgdata));
01080         msgtext[octstr_len(msg->sms.msgdata)] = '\0';
01081         parse_iso88591_to_emi(msgtext, my_buffer2,
01082                                            octstr_len(msg->sms.msgdata),
01083                                            smsc->alt_charset);
01084 
01085         strcpy(snumbits, "");
01086         mt = '3';
01087         strcpy(mcl, "");
01088     } else {
01089         octstr_get_many_chars(msgtext, msg->sms.msgdata, 0, octstr_len(msg->sms.msgdata));
01090 
01091         parse_binary_to_emi(msgtext, my_buffer2, octstr_len(msg->sms.msgdata));
01092 
01093         sprintf(snumbits, "%04ld", octstr_len(msg->sms.msgdata)*8);
01094         mt = '4';
01095         strcpy(mcl, "1");
01096     }
01097 
01098     /* XXX Where is DCS ? Is it in XSER like in emi2 ? 
01099      * Please someone encode it with fields_to_dcs 
01100      */
01101 
01102     sprintf(message_body,
01103             "%s/%s/%s/%s/%s//%s////////////%c/%s/%s////%s//////%s//",
01104             octstr_get_cstr(msg->sms.receiver),
01105             msg->sms.sender ? octstr_get_cstr(msg->sms.sender) : "",
01106             "",
01107             "",
01108             "",
01109             "0100",
01110             mt,
01111             snumbits,
01112             my_buffer2,
01113             mcl,
01114             xser);
01115 
01116     /* HEADER */
01117 
01118     length = strlen(message_body);
01119     length += 13;  /* header (fixed) */
01120     length += 2;  /* footer (fixed) */
01121     length += 2;  /* slashes between header, body, footer */
01122 
01123     sprintf(message_header, "%02i/%05i/%s/%s", (smsc->emi_current_msg_number++ % 100), length, "O", "51");
01124 
01125     /* FOOTER */
01126 
01127     sprintf(my_buffer, "%s/%s/", message_header, message_body);
01128     generate_checksum((unsigned char *)my_buffer, (unsigned char *)message_footer);
01129 
01130     sprintf(message_whole, "%c%s/%s/%s%c", 0x02, message_header, message_body, message_footer, 0x03);
01131 
01132     strncpy(rawmessage, message_whole, rawmessage_length);
01133 
01134     if (smsc->type == SMSC_TYPE_EMI_X25) {
01135         /* IC3S braindead EMI stack chokes on this... must fix it at the next time... */
01136         strcat(rawmessage, "\r");
01137     }
01138     debug("bb.sms.emi", 0, "emi %d message %s",
01139           smsc->emi_current_msg_number, rawmessage);
01140     return strlen(rawmessage);
01141 }
01142 
01143 /******************************************************************************
01144 * Parse the data from the two byte EMI code to normal ISO-8869-1
01145 */
01146 static int parse_emi_to_iso88591(char *from, char *to, int length,
01147                                  int alt_charset)
01148 {
01149     int hmtg = 0;
01150     unsigned int mychar;
01151     char tmpbuff[128];
01152 
01153     for (hmtg = 0; hmtg <= (int)strlen(from); hmtg += 2) {
01154         strncpy(tmpbuff, from + hmtg, 2);
01155         sscanf(tmpbuff, "%x", &mychar);
01156         to[hmtg / 2] = char_sms_to_iso(mychar, alt_charset);
01157     }
01158 
01159     to[(hmtg / 2)-1] = '\0';
01160 
01161     return 0;
01162 
01163 }
01164 
01165 /******************************************************************************
01166 * Parse the data from normal ISO-8869-1 to the two byte EMI code
01167 */
01168 static int parse_iso88591_to_emi(char *from, char *to,
01169         int length, int alt_charset)
01170 {
01171     char buf[10];
01172     unsigned char tmpchar;
01173     char *ptr;
01174 
01175     if (!from || !to || length <= 0)
01176         return -1;
01177 
01178     *to = '\0';
01179 
01180     debug("bb.sms.emi", 0, "emi parsing <%s> to emi, length %d", from, length);
01181 
01182     for (ptr = from; length > 0; ptr++, length--) {
01183         tmpchar = char_iso_to_sms(*ptr, alt_charset);
01184         sprintf(buf, "%02X", tmpchar);
01185         strncat(to, buf, 2);
01186     }
01187     return 0;
01188 }
01189 
01190 /******************************************************************************
01191 * Parse the data from binary to the two byte EMI code
01192 */
01193 static int parse_binary_to_emi(char *from, char *to, int length)
01194 {
01195     char buf[10];
01196     char *ptr;
01197 
01198     if (!from || !to || length <= 0)
01199         return -1;
01200 
01201     *to = '\0';
01202 
01203     for (ptr = from; length > 0; ptr++, length--) {
01204         sprintf(buf, "%02X", (unsigned char)*ptr);
01205         strncat(to, buf, 2);
01206     }
01207 
01208     return 0;
01209 }
01210 
01211 
01212 /******************************************************************************
01213 * Generate the EMI message checksum
01214 */
01215 static void generate_checksum(const unsigned char *buf, unsigned char *out)
01216 {
01217     const unsigned char *p;
01218     int j;
01219 
01220     j = 0;
01221     for (p = buf; *p != '\0'; p++) {
01222         j += *p;
01223 
01224         if (j >= 256)
01225             j -= 256;
01226     }
01227 
01228     sprintf((char *)out, "%02X", j);
01229 }
01230 
01231 
01232 
01233 /******************************************************************************
01234 * Translate character from iso to emi_mt
01235 * PGrönholm
01236 */
01237 static char char_iso_to_sms(unsigned char from, int alt_charset)
01238 {
01239 
01240     switch ((char)from) {
01241 
01242     case 'A':
01243         return 0x41;
01244     case 'B':
01245         return 0x42;
01246     case 'C':
01247         return 0x43;
01248     case 'D':
01249         return 0x44;
01250     case 'E':
01251         return 0x45;
01252     case 'F':
01253         return 0x46;
01254     case 'G':
01255         return 0x47;
01256     case 'H':
01257         return 0x48;
01258     case 'I':
01259         return 0x49;
01260     case 'J':
01261         return 0x4A;
01262     case 'K':
01263         return 0x4B;
01264     case 'L':
01265         return 0x4C;
01266     case 'M':
01267         return 0x4D;
01268     case 'N':
01269         return 0x4E;
01270     case 'O':
01271         return 0x4F;
01272     case 'P':
01273         return 0x50;
01274     case 'Q':
01275         return 0x51;
01276     case 'R':
01277         return 0x52;
01278     case 'S':
01279         return 0x53;
01280     case 'T':
01281         return 0x54;
01282     case 'U':
01283         return 0x55;
01284     case 'V':
01285         return 0x56;
01286     case 'W':
01287         return 0x57;
01288     case 'X':
01289         return 0x58;
01290     case 'Y':
01291         return 0x59;
01292     case 'Z':
01293         return 0x5A;
01294 
01295     case 'a':
01296         return 0x61;
01297     case 'b':
01298         return 0x62;
01299     case 'c':
01300         return 0x63;
01301     case 'd':
01302         return 0x64;
01303     case 'e':
01304         return 0x65;
01305     case 'f':
01306         return 0x66;
01307     case 'g':
01308         return 0x67;
01309     case 'h':
01310         return 0x68;
01311     case 'i':
01312         return 0x69;
01313     case 'j':
01314         return 0x6A;
01315     case 'k':
01316         return 0x6B;
01317     case 'l':
01318         return 0x6C;
01319     case 'm':
01320         return 0x6D;
01321     case 'n':
01322         return 0x6E;
01323     case 'o':
01324         return 0x6F;
01325     case 'p':
01326         return 0x70;
01327     case 'q':
01328         return 0x71;
01329     case 'r':
01330         return 0x72;
01331     case 's':
01332         return 0x73;
01333     case 't':
01334         return 0x74;
01335     case 'u':
01336         return 0x75;
01337     case 'v':
01338         return 0x76;
01339     case 'w':
01340         return 0x77;
01341     case 'x':
01342         return 0x78;
01343     case 'y':
01344         return 0x79;
01345     case 'z':
01346         return 0x7A;
01347 
01348     case '0':
01349         return 0x30;
01350     case '1':
01351         return 0x31;
01352     case '2':
01353         return 0x32;
01354     case '3':
01355         return 0x33;
01356     case '4':
01357         return 0x34;
01358     case '5':
01359         return 0x35;
01360     case '6':
01361         return 0x36;
01362     case '7':
01363         return 0x37;
01364     case '8':
01365         return 0x38;
01366     case '9':
01367         return 0x39;
01368     case ':':
01369         return 0x3A;
01370     case ';':
01371         return 0x3B;
01372     case '<':
01373         return 0x3C;
01374     case '=':
01375         return 0x3D;
01376     case '>':
01377         return 0x3E;
01378     case '?':
01379         return 0x3F;
01380 
01381     case 'Ä':
01382         return '[';
01383     case 'Ö':
01384         return '\\';
01385     case 'Å':
01386         return 0x0E;
01387     case 'Ü':
01388         return ']';
01389     case 'ä':
01390         return '{';
01391     case 'ö':
01392         return '|';
01393     case 'å':
01394         return 0x0F;
01395     case 'ü':
01396         return '}';
01397     case 'ß':
01398         return '~';
01399     case '§':
01400         return '^';
01401     case 'Ñ':
01402         return 0x5F;
01403     case 'ø':
01404         return 0x0C;
01405 
01406         /*      case 'Delta': return 0x10;  */
01407         /*      case 'Fii': return 0x12;    */
01408         /*      case 'Lambda': return 0x13; */
01409         /*      case 'Alpha': return 0x14;  */
01410         /*      case 'Omega': return 0x15;  */
01411         /*      case 'Pii': return 0x16;    */
01412         /*      case 'Pii': return 0x17;    */
01413         /*      case 'Delta': return 0x18;  */
01414         /*      case 'Delta': return 0x19;  */
01415         /*      case 'Delta': return 0x1A;  */
01416 
01417     case ' ':
01418         return 0x20;
01419     case '@':
01420         if (alt_charset == EMI_SWAPPED_CHARS)
01421             return 0x00;
01422         else
01423             return 0x40;
01424     case '£':
01425         return 0x01;
01426     case '$':
01427         return 0x24;
01428     case '¥':
01429         return 0x03;
01430     case 'è':
01431         return 0x04;
01432     case 'é':
01433         return 0x05;
01434     case 'ù':
01435         return 0x06;
01436     case 'ì':
01437         return 0x07;
01438     case 'ò':
01439         return 0x08;
01440     case 'Ç':
01441         return 0x09;
01442     case '\r':
01443         return 0x0A;
01444     case 'Ø':
01445         return 0x0B;
01446     case '\n':
01447         return 0x0D;
01448     case 'Æ':
01449         return 0x1C;
01450     case 'æ':
01451         return 0x1D;
01452     case 'É':
01453         return 0x1F;
01454 
01455     case '!':
01456         return 0x21;
01457     case '"':
01458         return 0x22;
01459     case '#':
01460         return 0x23;
01461     case '¤':
01462         return 0x02;
01463     case '%':
01464         return 0x25;
01465 
01466     case '&':
01467         return 0x26;
01468     case '\'':
01469         return 0x27;
01470     case '(':
01471         return 0x28;
01472     case ')':
01473         return 0x29;
01474     case '*':
01475         return 0x2A;
01476 
01477     case '+':
01478         return 0x2B;
01479     case ',':
01480         return 0x2C;
01481     case '-':
01482         return 0x2D;
01483     case '.':
01484         return 0x2E;
01485     case '/':
01486         return 0x2F;
01487 
01488     case '¿':
01489         return 0x60;
01490     case 'ñ':
01491         return 0x1E;
01492     case 'à':
01493         return 0x7F;
01494     case '¡':
01495         if (alt_charset == EMI_SWAPPED_CHARS)
01496             return 0x40;
01497         else
01498             return 0x00;
01499     case '_':
01500         return 0x11;
01501 
01502     default:
01503         return 0x20;  /* space */
01504 
01505     } /* switch */
01506 }
01507 
01508 
01509 /******************************************************************************
01510 * Translate character from emi_mo to iso
01511 * PGrönholm
01512 */
01513 static char char_sms_to_iso(unsigned char from, int alt_charset)
01514 {
01515 
01516     switch ((int)from) {
01517 
01518     case 0x41:
01519         return 'A';
01520     case 0x42:
01521         return 'B';
01522     case 0x43:
01523         return 'C';
01524     case 0x44:
01525         return 'D';
01526     case 0x45:
01527         return 'E';
01528     case 0x46:
01529         return 'F';
01530     case 0x47:
01531         return 'G';
01532     case 0x48:
01533         return 'H';
01534     case 0x49:
01535         return 'I';
01536     case 0x4A:
01537         return 'J';
01538     case 0x4B:
01539         return 'K';
01540     case 0x4C:
01541         return 'L';
01542     case 0x4D:
01543         return 'M';
01544     case 0x4E:
01545         return 'N';
01546     case 0x4F:
01547         return 'O';
01548     case 0x50:
01549         return 'P';
01550     case 0x51:
01551         return 'Q';
01552     case 0x52:
01553         return 'R';
01554     case 0x53:
01555         return 'S';
01556     case 0x54:
01557         return 'T';
01558     case 0x55:
01559         return 'U';
01560     case 0x56:
01561         return 'V';
01562     case 0x57:
01563         return 'W';
01564     case 0x58:
01565         return 'X';
01566     case 0x59:
01567         return 'Y';
01568     case 0x5A:
01569         return 'Z';
01570 
01571     case 0x61:
01572         return 'a';
01573     case 0x62:
01574         return 'b';
01575     case 0x63:
01576         return 'c';
01577     case 0x64:
01578         return 'd';
01579     case 0x65:
01580         return 'e';
01581     case 0x66:
01582         return 'f';
01583     case 0x67:
01584         return 'g';
01585     case 0x68:
01586         return 'h';
01587     case 0x69:
01588         return 'i';
01589     case 0x6A:
01590         return 'j';
01591     case 0x6B:
01592         return 'k';
01593     case 0x6C:
01594         return 'l';
01595     case 0x6D:
01596         return 'm';
01597     case 0x6E:
01598         return 'n';
01599     case 0x6F:
01600         return 'o';
01601     case 0x70:
01602         return 'p';
01603     case 0x71:
01604         return 'q';
01605     case 0x72:
01606         return 'r';
01607     case 0x73:
01608         return 's';
01609     case 0x74:
01610         return 't';
01611     case 0x75:
01612         return 'u';
01613     case 0x76:
01614         return 'v';
01615     case 0x77:
01616         return 'w';
01617     case 0x78:
01618         return 'x';
01619     case 0x79:
01620         return 'y';
01621     case 0x7A:
01622         return 'z';
01623 
01624     case 0x30:
01625         return '0';
01626     case 0x31:
01627         return '1';
01628     case 0x32:
01629         return '2';
01630     case 0x33:
01631         return '3';
01632     case 0x34:
01633         return '4';
01634     case 0x35:
01635         return '5';
01636     case 0x36:
01637         return '6';
01638     case 0x37:
01639         return '7';
01640     case 0x38:
01641         return '8';
01642     case 0x39:
01643         return '9';
01644     case 0x3A:
01645         return ':';
01646     case 0x3B:
01647         return ';';
01648     case 0x3C:
01649         return '<';
01650     case 0x3D:
01651         return '=';
01652     case 0x3E:
01653         return '>';
01654     case 0x3F:
01655         return '?';
01656 
01657     case '[':
01658         return 'Ä';
01659     case '\\':
01660         return 'Ö';
01661     case '\xC5':
01662         return 'Å';
01663     case ']':
01664         return 'Ü';
01665     case '{':
01666         return 'ä';
01667     case '|':
01668         return 'ö';
01669     case 0xE5:
01670         return 'å';
01671     case '}':
01672         return 'ü';
01673     case '~':
01674         return 'ß';
01675     case 0xA7:
01676         return '§';
01677     case 0xD1:
01678         return 'Ñ';
01679     case 0xF8:
01680         return 'ø';
01681 
01682         /*      case 'Delta':   return 0x10;    */
01683         /*      case 'Fii':     return 0x12;    */
01684         /*      case 'Lambda':  return 0x13;    */
01685         /*      case 'Alpha':   return 0x14;    */
01686         /*      case 'Omega':   return 0x15;    */
01687         /*      case 'Pii':     return 0x16;    */
01688         /*      case 'Pii':     return 0x17;    */
01689         /*      case 'Delta':   return 0x18;    */
01690         /*      case 'Delta':   return 0x19;    */
01691         /*      case 'Delta':   return 0x1A;    */
01692 
01693     case 0x20:
01694         return ' ';
01695     case 0x40:
01696         return '@';
01697     case 0xA3:
01698         return '£';
01699     case 0x24:
01700         return '$';
01701     case 0xA5:
01702         return '¥';
01703     case 0xE8:
01704         return 'è';
01705     case 0xE9:
01706         return 'é';
01707     case 0xF9:
01708         return 'ù';
01709     case 0xEC:
01710         return 'ì';
01711     case 0xF2:
01712         return 'ò';
01713     case 0xC7:
01714         return 'Ç';
01715     case 0x0A:
01716         return '\r';
01717     case 0xD8:
01718         return 'Ø';
01719     case 0x0D:
01720         return '\n';
01721     case 0xC6:
01722         return 'Æ';
01723     case 0xE6:
01724         return 'æ';
01725     case 0x1F:
01726         return 'É';
01727 
01728     case 0x21:
01729         return '!';
01730     case 0x22:
01731         return '"';
01732     case 0x23:
01733         return '#';
01734     case 0xA4:
01735         return '¤';
01736     case 0x25:
01737         return '%';
01738 
01739     case 0x26:
01740         return '&';
01741     case 0x27:
01742         return '\'';
01743     case 0x28:
01744         return '(';
01745     case 0x29:
01746         return ')';
01747     case 0x2A:
01748         return '*';
01749 
01750     case 0x2B:
01751         return '+';
01752     case 0x2C:
01753         return ',';
01754     case 0x2D:
01755         return '-';
01756     case 0x2E:
01757         return '.';
01758     case 0x2F:
01759         return '/';
01760 
01761     case 0xBF:
01762         return '¿';
01763     case 0xF1:
01764         return 'ñ';
01765     case 0xE0:
01766         return 'à';
01767     case 0xA1:
01768         return '¡';
01769     case 0x5F:
01770         return '_';
01771 
01772     default:
01773         return ' ';
01774 
01775     } /* switch */
01776 }
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.