1 /* ====================================================================
2 * The Kannel Software License, Version 1.0
3 *
4 * Copyright (c) 2001-2014 Kannel Group
5 * Copyright (c) 1998-2001 WapIT Ltd.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution,
21 * if any, must include the following acknowledgment:
22 * "This product includes software developed by the
23 * Kannel Group (http://www.kannel.org/)."
24 * Alternately, this acknowledgment may appear in the software itself,
25 * if and wherever such third-party acknowledgments normally appear.
26 *
27 * 4. The names "Kannel" and "Kannel Group" must not be used to
28 * endorse or promote products derived from this software without
29 * prior written permission. For written permission, please
30 * contact org@kannel.org.
31 *
32 * 5. Products derived from this software may not be called "Kannel",
33 * nor may "Kannel" appear in their name, without prior written
34 * permission of the Kannel Group.
35 *
36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Kannel Group. For more information on
51 * the Kannel Group, please see <http://www.kannel.org/>.
52 *
53 * Portions of this software are based upon software originally written at
54 * WapIT Ltd., Helsinki, Finland for the Kannel project.
55 */
56
57 /*****************************************************************************
58 * smsc_emi.c - implement interface to the CMG SMS Center (UCP/EMI).
59 * Mikael Gueck for WapIT Ltd.
60 */
61
62 /* This file implements two smsc interfaces: EMI_X25 */
63
64 #include <errno.h>
65 #include <string.h>
66 #include <stdio.h>
67 #include <unistd.h>
68 #include <fcntl.h>
69 #include <termios.h>
70 #include <stdarg.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <strings.h>
74
75 #include <sys/time.h>
76 #include <sys/types.h>
77 #include <sys/socket.h>
78 #include <sys/param.h>
79 #include <sys/ioctl.h>
80
81 #include "gwlib/gwlib.h"
82 #include "smsc.h"
83 #include "smsc_p.h"
84 #include "alt_charsets.h"
85 #include "sms.h"
86
87 #ifndef CRTSCTS
88 #define CRTSCTS 0
89 #endif
90
91 /******************************************************************************
92 * Static functions
93 */
94 static int get_data(SMSCenter *smsc, char *buff, int length);
95
96 static int put_data(SMSCenter *smsc, char *buff, int length, int is_backup);
97
98 static int memorybuffer_append_data(SMSCenter *smsc, char *buff, int length);
99
100 static int memorybuffer_insert_data(SMSCenter *smsc, char *buff, int length);
101
102 static int memorybuffer_has_rawmessage(SMSCenter *smsc, int type, char auth);
103
104 static int memorybuffer_cut_rawmessage(SMSCenter *smsc, char *buff, int length);
105
106 static int parse_rawmessage_to_msg(SMSCenter *smsc, Msg **msg,
107 char *rawmessage, int length);
108
109 static int parse_msg_to_rawmessage(SMSCenter *smsc, Msg *msg,
110 char *rawmessage, int length);
111
112 static int acknowledge_from_rawmessage(SMSCenter *smsc,
113 char *rawmessage, int length);
114
115 static int parse_emi_to_iso88591(char *from, char *to,
116 int length, int alt_charset);
117
118 static int parse_iso88591_to_emi(char *from, char *to,
119 int length, int alt_charset);
120 static int parse_binary_to_emi(char *from, char *to, int length);
121
122 static int at_dial(char *device, char *phonenum,
123 char *at_prefix, time_t how_long);
124 static int guarantee_link(SMSCenter *smsc);
125
126
127 static void generate_checksum(const unsigned char *buffer,
128 unsigned char *checksum_out);
129 static int wait_for_ack(SMSCenter *smsc, int op_type);
130
131
132 static char char_iso_to_sms(unsigned char from, int alt_charset);
133 static char char_sms_to_iso(unsigned char from, int alt_charset);
134
135 /******************************************************************************
136 * Open the connection and log in - handshake baby
137 */
emi_open_connection(SMSCenter * smsc)138 static int emi_open_connection(SMSCenter *smsc)
139 {
140 char tmpbuff[1024];
141
142 sprintf(tmpbuff, "/dev/%s", smsc->emi_serialdevice);
143 smsc->emi_fd = at_dial(tmpbuff, smsc->emi_phonenum, "ATD", 30);
144
145 if (smsc->emi_fd <= 0)
146 return -1;
147
148 return 0;
149 }
150
151 /* open EMI smscenter */
152
emi_open(char * phonenum,char * serialdevice,char * username,char * password)153 SMSCenter *emi_open(char *phonenum, char *serialdevice, char *username, char *password)
154 {
155 SMSCenter *smsc;
156
157 smsc = smscenter_construct();
158 if (smsc == NULL)
159 goto error;
160
161 smsc->type = SMSC_TYPE_EMI_X25;
162
163 smsc->emi_phonenum = gw_strdup(phonenum);
164 smsc->emi_serialdevice = gw_strdup(serialdevice);
165 smsc->emi_username = gw_strdup(username);
166 smsc->emi_password = gw_strdup(password);
167
168 smsc->emi_current_msg_number = 0;
169
170 if (emi_open_connection(smsc) < 0)
171 goto error;
172
173 sprintf(smsc->name, "EMI:%s:%s", smsc->emi_phonenum,
174 smsc->emi_username);
175 return smsc;
176
177 error:
178 error(0, "emi_open failed");
179 smscenter_destruct(smsc);
180 return NULL;
181 }
182
emi_reopen(SMSCenter * smsc)183 int emi_reopen(SMSCenter *smsc)
184 {
185 emi_close(smsc);
186
187 if (emi_open_connection(smsc) < 0) {
188 error(0, "emi_reopen failed");
189 return -1;
190 }
191 return 0;
192 }
193
emi_close(SMSCenter * smsc)194 int emi_close(SMSCenter *smsc)
195 {
196 return emi_close_ip(smsc);
197 }
198
emi_fill_ucp60_login(char * buf,char * OAdC,char * passwd)199 static int emi_fill_ucp60_login(char *buf, char *OAdC, char *passwd) {
200 int max_ia5passwd_len;
201 char *ia5passwd;
202
203 max_ia5passwd_len = strlen(passwd) * 2 + 1;
204 ia5passwd = gw_malloc(max_ia5passwd_len);
205
206 if (parse_binary_to_emi(passwd, ia5passwd, strlen(passwd)) < 0) {
207 error(0, "parse_binary_to_emi failed");
208 gw_free(ia5passwd);
209 return -1;
210 }
211
212 sprintf(buf, "%s/%c/%c/%c/%s//%s/////",
213 OAdC, /* OAdC: Address code originator */
214 '6', /* OTON: 6 = Abbreviated number (short number alias) */
215 '5', /* ONPI: 5 = Private (TCP/IP address/abbreviated number address) */
216 '1', /* STYP: 1 = open session */
217 ia5passwd, /* PWD: Current password encoded into IA5 characters */
218 "0100" /* VERS: Version number 0100 */
219 );
220
221 gw_free(ia5passwd);
222 return 0;
223 }
224
emi_open_session(SMSCenter * smsc)225 static int emi_open_session(SMSCenter *smsc)
226 {
227 char message_whole [1024];
228 char message_body [1024];
229 char message_header [50];
230 char message_footer [10];
231 char my_buffer [1024];
232 int length;
233
234 memset(message_whole, 0, sizeof(message_whole));
235 memset(message_body, 0, sizeof(message_body));
236 memset(message_header, 0, sizeof(message_header));
237 memset(message_footer, 0, sizeof(message_footer));
238
239 if (emi_fill_ucp60_login(message_body, smsc->emi_username, smsc->emi_password) < 0) {
240 error(0, "emi_fill_ucp60_login failed");
241 return -1;
242 }
243
244 length = strlen(message_body);
245 length += 13; /* header (fixed) */
246 length += 2; /* footer (fixed) */
247 length += 2; /* slashes between header, body, footer */
248
249 sprintf(message_header, "%02i/%05i/O/60",
250 (smsc->emi_current_msg_number++ % 100), length);
251
252 /* FOOTER */
253
254 sprintf(my_buffer, "%s/%s/", message_header, message_body);
255 generate_checksum((unsigned char *)my_buffer, (unsigned char *)message_footer);
256
257 sprintf(message_whole, "\x02%s/%s/%s\x03", message_header,
258 message_body, message_footer);
259
260 debug("bb.sms.emi", 0, "final UCP60 msg: <%s>", message_whole);
261
262 put_data(smsc, message_whole, strlen(message_whole), 0);
263
264 if (!wait_for_ack(smsc, 60)) {
265 info(0, "emi_open_session: wait for ack failed!");
266 return -1;
267 }
268
269 return 0;
270 }
271
272
273 /*******************************************************
274 * the actual protocol open... quite simple here */
275
emi_open_connection_ip(SMSCenter * smsc)276 static int emi_open_connection_ip(SMSCenter *smsc)
277 {
278 smsc->emi_fd =
279 tcpip_connect_to_server_with_port(smsc->emi_hostname,
280 smsc->emi_port, smsc->emi_our_port,
281 NULL);
282 /* XXX add interface_name if required */
283 if (smsc->emi_fd < 0)
284 return -1;
285
286 if (smsc->emi_username && smsc->emi_password) {
287 return emi_open_session(smsc);
288 }
289
290 return 0;
291 }
292
293
emi_reopen_ip(SMSCenter * smsc)294 int emi_reopen_ip(SMSCenter *smsc)
295 {
296 emi_close_ip(smsc);
297
298 return emi_open_connection_ip(smsc);
299 }
300
301
emi_close_ip(SMSCenter * smsc)302 int emi_close_ip(SMSCenter *smsc)
303 {
304
305 if (smsc->emi_fd == -1) {
306 info(0, "Trying to close already closed EMI, ignoring");
307 return 0;
308 }
309 close(smsc->emi_fd);
310 smsc->emi_fd = -1;
311
312 return 0;
313 }
314
315
316 /******************************************************************************
317 * Check if the buffers contain any messages
318 */
emi_pending_smsmessage(SMSCenter * smsc)319 int emi_pending_smsmessage(SMSCenter *smsc)
320 {
321
322 char *tmpbuff;
323 int n = 0;
324 /* time_t timenow; */
325
326 /* Block until we have a connection */
327 guarantee_link(smsc);
328
329 /* If we have MO-message, then act (return 1) */
330 if (memorybuffer_has_rawmessage(smsc, 52, 'O') > 0 ||
331 memorybuffer_has_rawmessage(smsc, 1, 'O') > 0 )
332 return 1;
333
334 tmpbuff = gw_malloc(10 * 1024);
335 memset(tmpbuff, 0, 10*1024);
336
337 /* check for data */
338 n = get_data(smsc, tmpbuff, 10 * 1024);
339 if (n > 0)
340 memorybuffer_insert_data(smsc, tmpbuff, n);
341
342 /* delete all ACKs/NACKs/whatever */
343 while (memorybuffer_has_rawmessage(smsc, 51, 'R') > 0 ||
344 memorybuffer_has_rawmessage(smsc, 1, 'R') > 0)
345 memorybuffer_cut_rawmessage(smsc, tmpbuff, 10*1024);
346
347 gw_free(tmpbuff);
348
349 /* If we have MO-message, then act (return 1) */
350
351 if (memorybuffer_has_rawmessage(smsc, 52, 'O') > 0 ||
352 memorybuffer_has_rawmessage(smsc, 1, 'O') > 0)
353 return 1;
354
355 /*
356 time(&timenow);
357 if( (smsc->emi_last_spoke + 60*20) < timenow) {
358 time(&smsc->emi_last_spoke);
359 }
360 */
361
362 return 0;
363
364 }
365
366
367
368
369 /******************************************************************************
370 * Submit (send) a Mobile Terminated message to the EMI server
371 */
emi_submit_msg(SMSCenter * smsc,Msg * omsg)372 int emi_submit_msg(SMSCenter *smsc, Msg *omsg)
373 {
374 char *tmpbuff = NULL;
375
376 if (smsc == NULL) goto error;
377 if (omsg == NULL) goto error;
378
379 tmpbuff = gw_malloc(10 * 1024);
380 memset(tmpbuff, 0, 10*1024);
381
382 if (parse_msg_to_rawmessage(smsc, omsg, tmpbuff, 10*1024) < 1)
383 goto error;
384
385 if (put_data(smsc, tmpbuff, strlen(tmpbuff), 0) < 0) {
386 info(0, "put_data failed!");
387 goto error;
388 }
389
390 wait_for_ack(smsc, 51);
391
392 /* smsc->emi_current_msg_number += 1; */
393 debug("bb.sms.emi", 0, "Submit Ok...");
394
395 gw_free(tmpbuff);
396 return 0;
397
398 error:
399 debug("bb.sms.emi", 0, "Submit Error...");
400
401 gw_free(tmpbuff);
402 return -1;
403 }
404
405 /******************************************************************************
406 * Receive a Mobile Terminated message to the EMI server
407 */
emi_receive_msg(SMSCenter * smsc,Msg ** tmsg)408 int emi_receive_msg(SMSCenter *smsc, Msg **tmsg)
409 {
410 char *tmpbuff;
411 Msg *msg = NULL;
412
413 *tmsg = NULL;
414
415 tmpbuff = gw_malloc(10 * 1024);
416 memset(tmpbuff, 0, 10*1024);
417
418 /* get and delete message from buffer */
419 memorybuffer_cut_rawmessage(smsc, tmpbuff, 10*1024);
420 parse_rawmessage_to_msg(smsc, &msg, tmpbuff, strlen(tmpbuff));
421
422 /* yeah yeah, I got the message... */
423 acknowledge_from_rawmessage(smsc, tmpbuff, strlen(tmpbuff));
424
425 /* return with the joyful news */
426 gw_free(tmpbuff);
427
428 if (msg == NULL) goto error;
429
430 *tmsg = msg;
431
432 return 1;
433
434 error:
435 gw_free(tmpbuff);
436 msg_destroy(msg);
437 return -1;
438 }
439
440
441 /******************************************************************************
442 * Internal functions
443 */
444
445
446 /******************************************************************************
447 * Guarantee that we have a link
448 */
guarantee_link(SMSCenter * smsc)449 static int guarantee_link(SMSCenter *smsc)
450 {
451 int need_to_connect = 0;
452
453 /* If something is obviously wrong. */
454 if (strstr(smsc->buffer, "OK")) need_to_connect = 1;
455 if (strstr(smsc->buffer, "NO CARRIER")) need_to_connect = 1;
456 if (strstr(smsc->buffer, "NO DIALTONE")) need_to_connect = 1;
457
458 /* Clear the buffer */
459 while (need_to_connect) {
460 /* Connect */
461 need_to_connect = emi_open_connection(smsc) < 0;
462
463 /* Clear the buffer so that the next call to guarantee
464 doesn't find the "NO CARRIER" string again. */
465 smsc->buflen = 0;
466 memset(smsc->buffer, 0, smsc->bufsize);
467 }
468
469 return 0;
470 }
471
at_dial(char * device,char * phonenum,char * at_prefix,time_t how_long)472 static int at_dial(char *device, char *phonenum, char *at_prefix, time_t how_long)
473 {
474 char tmpbuff[1024];
475 int howmanyread = 0;
476 int thistime = 0;
477 int redial;
478 int fd = -1;
479 int ret;
480 time_t timestart;
481 struct termios tios;
482
483 /* The time at the start of the function is used when
484 determining whether we have used up our allotted
485 dial time and have to abort. */
486 time(×tart);
487
488 /* Open the device properly. Remember to set the
489 access codes correctly. */
490 fd = open(device, O_RDWR | O_NONBLOCK | O_NOCTTY);
491 if (fd == -1) {
492 error(errno, "at_dial: error opening character device <%s>", device);
493 goto error;
494 }
495 tcflush(fd, TCIOFLUSH);
496
497 /* The speed initialisation is pretty important. */
498 tcgetattr(fd, &tios);
499 #if defined(B115200)
500 cfsetospeed(&tios, B115200);
501 cfsetispeed(&tios, B115200);
502 #elif defined(B76800)
503 cfsetospeed(&tios, B76800);
504 cfsetispeed(&tios, B76800);
505 #elif defined(B57600)
506 cfsetospeed(&tios, B57600);
507 cfsetispeed(&tios, B57600);
508 #elif defined(B38400)
509 cfsetospeed(&tios, B38400);
510 cfsetispeed(&tios, B38400);
511 #elif defined(B19200)
512 cfsetospeed(&tios, B19200);
513 cfsetispeed(&tios, B19200);
514 #elif defined(B9600)
515 cfsetospeed(&tios, B9600);
516 cfsetispeed(&tios, B9600);
517 #endif
518 kannel_cfmakeraw(&tios);
519 tios.c_cflag |= (HUPCL | CREAD | CRTSCTS);
520 ret = tcsetattr(fd, TCSANOW, &tios);
521 if (ret == -1) {
522 error(errno, "EMI[X25]: at_dial: fail to set termios attribute");
523 }
524
525 /* Dial using an AT command string. */
526 for (redial = 1; redial; ) {
527 info(0, "at_dial: dialing <%s> on <%s> for <%i> seconds",
528 phonenum, device,
529 (int)(how_long - (time(NULL) - timestart)));
530
531 /* Send AT dial request. */
532 howmanyread = 0;
533 sprintf(tmpbuff, "%s%s\r\n", at_prefix, phonenum);
534 ret = write(fd, tmpbuff, strlen(tmpbuff)); /* errors... -mg */
535 memset(&tmpbuff, 0, sizeof(tmpbuff));
536
537 /* Read the answer to the AT command and react accordingly. */
538 for (; ; ) {
539 /* We don't want to dial forever */
540 if (how_long != 0 && time(NULL) > timestart + how_long)
541 goto timeout;
542
543 /* We don't need more space for dialout */
544 if (howmanyread >= (int) sizeof(tmpbuff))
545 goto error;
546
547 /* We read 1 char a time so that we don't
548 accidentally read past the modem chat and
549 into the SMSC datastream -mg */
550 thistime = read(fd, &tmpbuff[howmanyread], 1);
551 if (thistime == -1) {
552 if (errno == EAGAIN) continue;
553 if (errno == EINTR) continue;
554 goto error;
555 } else {
556 howmanyread += thistime;
557 }
558
559 /* Search for the newline on the AT status line. */
560 if (tmpbuff[howmanyread - 1] == '\r'
561 || tmpbuff[howmanyread - 1] == '\n') {
562
563 /* XXX ADD ALL POSSIBLE CHAT STRINGS XXX */
564
565 if (strstr(tmpbuff, "CONNECT") != NULL) {
566 debug("bb.sms.emi", 0, "at_dial: CONNECT");
567 redial = 0;
568 break;
569
570 } else if (strstr(tmpbuff, "NO CARRIER") != NULL) {
571 debug("bb.sms.emi", 0, "at_dial: NO CARRIER");
572 redial = 1;
573 break;
574
575 } else if (strstr(tmpbuff, "BUSY") != NULL) {
576 debug("bb.sms.emi", 0, "at_dial: BUSY");
577 redial = 1;
578 break;
579
580 } else if (strstr(tmpbuff, "NO DIALTONE") != NULL) {
581 debug("bb.sms.emi", 0, "at_dial: NO DIALTONE");
582 redial = 1;
583 break;
584
585 }
586
587 } /* End of if lastchr=='\r'||'\n'. */
588
589 /* Thou shall not consume all system resources
590 by repeatedly looping a strstr search when
591 the string update latency is very high as it
592 is in serial communication. -mg */
593 usleep(1000);
594
595 } /* End of read loop. */
596
597 /* Thou shall not flood the modem with dial requests. -mg */
598 sleep(1);
599
600 } /* End of dial loop. */
601
602 debug("bb.sms.emi", 0, "at_dial: done with dialing");
603 return fd;
604
605 timeout:
606 error(0, "at_dial timed out");
607 close(fd);
608 return -1;
609
610 error:
611 error(0, "at_dial failed");
612 close(fd);
613 return -1;
614 }
615
616 /******************************************************************************
617 * Wait for an ACK or NACK from the remote
618 *
619 * REQUIRED by the protocol that it must be waited...
620 */
wait_for_ack(SMSCenter * smsc,int op_type)621 static int wait_for_ack(SMSCenter *smsc, int op_type)
622 {
623 char *tmpbuff;
624 int found = 0;
625 int n;
626 time_t start;
627
628 tmpbuff = gw_malloc(10 * 1024);
629 memset(tmpbuff, 0, 10*1024);
630 start = time(NULL);
631 do {
632 /* check for data */
633 n = get_data(smsc, tmpbuff, 1024 * 10);
634
635 /* At least the X.31 interface wants to append the data.
636 Kalle, what about the TCP/IP interface? Am I correct
637 that you are assuming that the message arrives in a
638 single read(2)? -mg */
639 if (n > 0)
640 memorybuffer_append_data(smsc, tmpbuff, n);
641
642 /* act on data */
643 if (memorybuffer_has_rawmessage(smsc, op_type, 'R') > 0) {
644 memorybuffer_cut_rawmessage(smsc, tmpbuff, 10*1024);
645 debug("bb.sms.emi", 0, "Found ACK/NACK: <%s>", tmpbuff);
646 found = 1;
647 }
648 } while (!found && ((time(NULL) - start) < 5));
649
650 gw_free(tmpbuff);
651 return found;
652 }
653
654
655 /******************************************************************************
656 * Get the modem buffer data to buff, return the amount read
657 *
658 * Reads from main fd, but also from backup-fd - does accept if needed
659 */
get_data(SMSCenter * smsc,char * buff,int length)660 static int get_data(SMSCenter *smsc, char *buff, int length)
661 {
662 int n = 0;
663
664 struct sockaddr_in client_addr;
665 socklen_t client_addr_len;
666
667 fd_set rf;
668 struct timeval to;
669 int ret;
670
671 memset(buff, 0, length);
672
673 if (smsc->type == SMSC_TYPE_EMI_X25) {
674 tcdrain(smsc->emi_fd);
675 n = read(smsc->emi_fd, buff, length);
676 return n;
677 }
678
679 FD_ZERO(&rf);
680 if (smsc->emi_fd >= 0) FD_SET(smsc->emi_fd, &rf);
681 if (smsc->emi_secondary_fd >= 0) FD_SET(smsc->emi_secondary_fd, &rf);
682 if (smsc->emi_backup_fd > 0) FD_SET(smsc->emi_backup_fd, &rf);
683
684 FD_SET(0, &rf);
685 to.tv_sec = 0;
686 to.tv_usec = 100;
687
688 ret = select(FD_SETSIZE, &rf, NULL, NULL, &to);
689
690 if (ret > 0) {
691 if (smsc->emi_secondary_fd >= 0 && FD_ISSET(smsc->emi_secondary_fd, &rf)) {
692 n = read(smsc->emi_secondary_fd, buff, length - 1);
693
694 if (n == -1) {
695 error(errno, "Error - Secondary socket closed");
696 close(smsc->emi_secondary_fd);
697 smsc->emi_secondary_fd = -1;
698 } else if (n == 0) {
699 info(0, "Secondary socket closed by SMSC");
700 close(smsc->emi_secondary_fd);
701 smsc->emi_secondary_fd = -1;
702 } else { /* UGLY! We put 'X' after message */
703 buff[n] = 'X'; /* if it is from secondary fd!!! */
704 n++;
705 }
706 } else if (smsc->emi_fd >= 0 && FD_ISSET(smsc->emi_fd, &rf)) {
707 n = read(smsc->emi_fd, buff, length);
708 if (n == 0) {
709 close(smsc->emi_fd);
710 info(0, "Main EMI socket closed by SMSC");
711 smsc->emi_fd = -1; /* ready to be re-opened */
712 }
713 }
714 if ((smsc->emi_backup_fd > 0) && FD_ISSET(smsc->emi_backup_fd, &rf)) {
715 if (smsc->emi_secondary_fd == -1) {
716 Octstr *ip, *allow;
717
718 smsc->emi_secondary_fd = accept(smsc->emi_backup_fd,
719 (struct sockaddr *)&client_addr, &client_addr_len);
720
721 ip = host_ip(client_addr);
722 if (smsc->emi_backup_allow_ip == NULL)
723 allow = NULL;
724 else
725 allow = octstr_create(smsc->emi_backup_allow_ip);
726 if (is_allowed_ip(allow, octstr_imm("*.*.*.*"), ip) == 0) {
727 info(0, "SMSC secondary connection tried from <%s>, "
728 "disconnected",
729 octstr_get_cstr(ip));
730 octstr_destroy(ip);
731 octstr_destroy(allow);
732 close(smsc->emi_secondary_fd);
733 smsc->emi_secondary_fd = -1;
734 return 0;
735 }
736 info(0, "Secondary socket opened by SMSC from <%s>",
737 octstr_get_cstr(ip));
738 octstr_destroy(ip);
739 octstr_destroy(allow);
740 } else
741 info(0, "New connection request while old secondary is open!");
742 }
743 }
744 if (n > 0) {
745 debug("bb.sms.emi", 0, "get_data:Read %d bytes: <%.*s>", n, n, buff);
746 debug("bb.sms.emi", 0, "get_data:smsc->buffer == <%s>", smsc->buffer);
747 }
748 return n;
749
750 }
751
752 /******************************************************************************
753 * Put the buff data to the modem buffer, return the amount of data put
754 */
put_data(SMSCenter * smsc,char * buff,int length,int is_backup)755 static int put_data(SMSCenter *smsc, char *buff, int length, int is_backup)
756 {
757 size_t len = length;
758 int ret;
759 int fd = -1;
760
761 fd = smsc->emi_fd;
762 tcdrain(smsc->emi_fd);
763
764 /* Write until all data has been successfully written to the fd. */
765 while (len > 0) {
766 ret = write(fd, buff, len);
767 if (ret == -1) {
768 if (errno == EINTR) continue;
769 if (errno == EAGAIN) continue;
770 error(errno, "Writing to fd failed");
771 return -1;
772 }
773 /* ret may be less than len, if the writing
774 was interrupted by a signal. */
775 len -= ret;
776 buff += ret;
777 }
778
779 if (smsc->type == SMSC_TYPE_EMI_X25) {
780 /* Make sure the data gets written immediately.
781 Wait a while just to add some latency so
782 that the modem (or the UART) doesn't choke
783 on the data. */
784 tcdrain(smsc->emi_fd);
785 usleep(1000);
786 }
787
788 return 0;
789 }
790
791 /******************************************************************************
792 * Append the buff data to smsc->buffer
793 */
memorybuffer_append_data(SMSCenter * smsc,char * buff,int length)794 static int memorybuffer_append_data(SMSCenter *smsc, char *buff, int length)
795 {
796 while (smsc->bufsize < (smsc->buflen + length)) { /* buffer too small */
797 char *p = gw_realloc(smsc->buffer, smsc->bufsize * 2);
798 smsc->buffer = p;
799 smsc->bufsize *= 2;
800 }
801
802 memcpy(smsc->buffer + smsc->buflen, buff, length);
803 smsc->buflen += length;
804 return 0;
805
806 }
807
808 /******************************************************************************
809 * Insert (put to head) the buff data to smsc->buffer
810 */
memorybuffer_insert_data(SMSCenter * smsc,char * buff,int length)811 static int memorybuffer_insert_data(SMSCenter *smsc, char *buff, int length)
812 {
813 while (smsc->bufsize < (smsc->buflen + length)) { /* buffer too small */
814 char *p = gw_realloc(smsc->buffer, smsc->bufsize * 2);
815 smsc->buffer = p;
816 smsc->bufsize *= 2;
817 }
818 memmove(smsc->buffer + length, smsc->buffer, smsc->buflen);
819 memcpy(smsc->buffer, buff, length);
820 smsc->buflen += length;
821 return 0;
822
823 }
824
825 /******************************************************************************
826 * Check the smsc->buffer for a raw STX...ETX message
827 */
memorybuffer_has_rawmessage(SMSCenter * smsc,int type,char auth)828 static int memorybuffer_has_rawmessage(SMSCenter *smsc, int type, char auth)
829 {
830 char tmpbuff[1024], tmpbuff2[1024];
831 char *stx, *etx;
832
833 stx = memchr(smsc->buffer, '\2', smsc->buflen);
834 etx = memchr(smsc->buffer, '\3', smsc->buflen);
835
836 if (stx && etx && stx < etx) {
837 strncpy(tmpbuff, stx, etx - stx + 1);
838 tmpbuff[etx - stx + 1] = '\0';
839 if (auth)
840 sprintf(tmpbuff2, "/%c/%02i/", auth, type);
841 else
842 sprintf(tmpbuff2, "/%02i/", type);
843
844 if (strstr(tmpbuff, tmpbuff2) != NULL) {
845 debug("bb.sms.emi", 0, "found message <%c/%02i>...msg <%s>", auth, type, tmpbuff);
846 return 1;
847 }
848 }
849 return 0;
850
851 }
852
853 /******************************************************************************
854 * Cut the first raw message from the smsc->buffer
855 * and put it in buff, return success 0, failure -1
856 */
memorybuffer_cut_rawmessage(SMSCenter * smsc,char * buff,int length)857 static int memorybuffer_cut_rawmessage(SMSCenter *smsc, char *buff, int length)
858 {
859
860 char *stx, *etx;
861 int size_of_cut_piece;
862
863 /* We don't check for NULLs since we're sure that nobody has fooled
864 around with smsc->buffer since has_rawmessage was last called... */
865
866 stx = memchr(smsc->buffer, '\2', smsc->buflen);
867 etx = memchr(smsc->buffer, '\3', smsc->buflen);
868
869 if (*(etx + 1) == 'X') /* secondary! UGLY KLUDGE */
870 etx++;
871
872 size_of_cut_piece = (etx - stx) + 1;
873
874 if (length < size_of_cut_piece) {
875 error(0, "the buffer you provided for cutting was too small");
876 return -1;
877 }
878
879 /* move the part before our magic rawmessage to the safe house */
880 memcpy(buff, stx, size_of_cut_piece);
881 buff[size_of_cut_piece] = '\0'; /* NULL-terminate */
882
883 /* move the stuff in membuffer one step down */
884 memmove(stx, etx + 1, (smsc->buffer + smsc->bufsize) - stx );
885
886 smsc->buflen -= size_of_cut_piece;
887
888 return 0;
889
890 }
891
892 /******************************************************************************
893 * Parse the raw message to the Msg structure
894 */
parse_rawmessage_to_msg(SMSCenter * smsc,Msg ** msg,char * rawmessage,int length)895 static int parse_rawmessage_to_msg(SMSCenter *smsc, Msg **msg,
896 char *rawmessage, int length)
897 {
898
899 char emivars[128][1024];
900 char *leftslash, *rightslash;
901 char isotext[2048];
902 int msgnbr;
903 int tmpint;
904
905 msgnbr = -1;
906
907 memset(isotext, 0, sizeof(isotext));
908
909 strncpy(isotext, rawmessage, length);
910 leftslash = isotext;
911
912 for (tmpint = 0; leftslash != NULL; tmpint++) {
913 rightslash = strchr(leftslash + 1, '/');
914
915 if (rightslash == NULL)
916 rightslash = strchr(leftslash + 1, '\3');
917
918 if (rightslash == NULL)
919 break;
920
921 *rightslash = '\0';
922 strcpy(emivars[tmpint], leftslash + 1);
923 leftslash = rightslash;
924 }
925
926 if (strcmp(emivars[3], "01") == 0) {
927 if (strcmp(emivars[7], "2") == 0) {
928 strcpy(isotext, emivars[8]);
929 } else if (strcmp(emivars[7], "3") == 0) {
930 parse_emi_to_iso88591(emivars[8], isotext, sizeof(isotext),
931 smsc->alt_charset);
932 } else {
933 error(0, "Unknown 01-type EMI SMS (%s)", emivars[7]);
934 strcpy(isotext, "");
935 }
936 } else if (strcmp(emivars[3], "51") == 0) {
937 parse_emi_to_iso88591(emivars[24], isotext, sizeof(isotext),
938 smsc->alt_charset);
939 } else if (strcmp(emivars[3], "52") == 0) {
940 parse_emi_to_iso88591(emivars[24], isotext, sizeof(isotext),
941 smsc->alt_charset);
942 } else {
943 error(0, "HEY WE SHOULD NOT BE HERE!! Type = %s", emivars[3]);
944 strcpy(isotext, "");
945 }
946
947 *msg = msg_create(sms);
948 if (*msg == NULL) goto error;
949
950 (*msg)->sms.sender = octstr_create(emivars[5]);
951 (*msg)->sms.receiver = octstr_create(emivars[4]);
952 (*msg)->sms.msgdata = octstr_create(isotext);
953 (*msg)->sms.udhdata = NULL;
954
955 return msgnbr;
956
957 error:
958 return -1;
959 }
960
961 /*
962 * notify the SMSC that we got the message
963 */
acknowledge_from_rawmessage(SMSCenter * smsc,char * rawmessage,int length)964 static int acknowledge_from_rawmessage(SMSCenter *smsc,
965 char *rawmessage, int length)
966 {
967
968 char emivars[128][1024];
969 char timestamp[2048], sender[2048], receiver[2048];
970 char emitext[2048], isotext[2048];
971 char *leftslash, *rightslash;
972 int msgnbr;
973 int tmpint;
974 int is_backup = 0;
975
976 msgnbr = -1;
977 memset(&sender, 0, sizeof(sender));
978 memset(&receiver, 0, sizeof(receiver));
979 memset(&emitext, 0, sizeof(emitext));
980 memset(&isotext, 0, sizeof(isotext));
981 memset(×tamp, 0, sizeof(timestamp));
982
983 strncpy(isotext, rawmessage, length);
984 leftslash = isotext;
985
986 if (isotext[length - 1] == 'X')
987 is_backup = 1;
988
989 for (tmpint = 0; leftslash != NULL; tmpint++) {
990 rightslash = strchr(leftslash + 1, '/');
991
992 if (rightslash == NULL)
993 rightslash = strchr(leftslash + 1, '\3');
994
995 if (rightslash == NULL)
996 break;
997
998 *rightslash = '\0';
999 strcpy(emivars[tmpint], leftslash + 1);
1000 leftslash = rightslash;
1001 }
1002
1003 /* BODY */
1004 sprintf(isotext, "A//%s:%s", emivars[4], emivars[18]);
1005 sprintf(isotext, "A//%s:", emivars[5]);
1006 is_backup = 0;
1007
1008 /* HEADER */
1009
1010 debug("bb.sms.emi", 0, "acknowledge: type = '%s'", emivars[3]);
1011
1012 sprintf(emitext, "%s/%05i/%s/%s", emivars[0], (int) strlen(isotext) + 17,
1013 "R", emivars[3]);
1014
1015 smsc->emi_current_msg_number = atoi(emivars[0]) + 1;
1016
1017 /* FOOTER */
1018 sprintf(timestamp, "%s/%s/", emitext, isotext);
1019 generate_checksum((unsigned char *)timestamp, (unsigned char *)receiver);
1020
1021 sprintf(sender, "%c%s/%s/%s%c", 0x02, emitext, isotext, receiver, 0x03);
1022 put_data(smsc, sender, strlen(sender), is_backup);
1023
1024 return msgnbr;
1025
1026 }
1027
1028
1029 /******************************************************************************
1030 * Parse the Msg structure to the raw message format
1031 */
parse_msg_to_rawmessage(SMSCenter * smsc,Msg * msg,char * rawmessage,int rawmessage_length)1032 static int parse_msg_to_rawmessage(SMSCenter *smsc, Msg *msg, char *rawmessage, int rawmessage_length)
1033 {
1034 char message_whole[10*1024];
1035 char message_body[10*1024];
1036 char message_header[1024];
1037 char message_footer[1024];
1038
1039 char my_buffer[10*1024];
1040 char my_buffer2[10*1024];
1041 char msgtext[1024];
1042 int length;
1043 char mt;
1044 char mcl[20];
1045 char snumbits[20];
1046 char xser[1024];
1047 int udh_len;
1048
1049 memset(&message_whole, 0, sizeof(message_whole));
1050 memset(&message_body, 0, sizeof(message_body));
1051 memset(&message_header, 0, sizeof(message_header));
1052 memset(&message_footer, 0, sizeof(message_footer));
1053 memset(&my_buffer, 0, sizeof(my_buffer));
1054 memset(&my_buffer2, 0, sizeof(my_buffer2));
1055 mt = '3';
1056 memset(&snumbits, 0, sizeof(snumbits));
1057 memset(&xser, 0, sizeof(xser));
1058
1059 /* XXX parse_iso88591_to_emi shouldn't use NUL terminated
1060 * strings, but Octstr directly, or a char* and a length.
1061 */
1062 if (octstr_len(msg->sms.udhdata)) {
1063 char xserbuf[258];
1064 /* we need a properly formated UDH here, there first byte contains his length
1065 * this will be formatted in the xser field of the EMI Protocol
1066 */
1067 udh_len = octstr_get_char(msg->sms.udhdata, 0) + 1;
1068 xserbuf[0] = 1;
1069 xserbuf[1] = udh_len;
1070 octstr_get_many_chars(&xserbuf[2], msg->sms.udhdata, 0, udh_len);
1071 parse_binary_to_emi(xserbuf, xser, udh_len + 2);
1072 } else {
1073 udh_len = 0;
1074 }
1075
1076 if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF) {
1077 octstr_get_many_chars(msgtext, msg->sms.msgdata, 0, octstr_len(msg->sms.msgdata));
1078 msgtext[octstr_len(msg->sms.msgdata)] = '\0';
1079 parse_iso88591_to_emi(msgtext, my_buffer2,
1080 octstr_len(msg->sms.msgdata),
1081 smsc->alt_charset);
1082
1083 strcpy(snumbits, "");
1084 mt = '3';
1085 strcpy(mcl, "");
1086 } else {
1087 octstr_get_many_chars(msgtext, msg->sms.msgdata, 0, octstr_len(msg->sms.msgdata));
1088
1089 parse_binary_to_emi(msgtext, my_buffer2, octstr_len(msg->sms.msgdata));
1090
1091 sprintf(snumbits, "%04ld", octstr_len(msg->sms.msgdata)*8);
1092 mt = '4';
1093 strcpy(mcl, "1");
1094 }
1095
1096 /* XXX Where is DCS ? Is it in XSER like in emi2 ?
1097 * Please someone encode it with fields_to_dcs
1098 */
1099
1100 sprintf(message_body,
1101 "%s/%s/%s/%s/%s//%s////////////%c/%s/%s////%s//////%s//",
1102 octstr_get_cstr(msg->sms.receiver),
1103 msg->sms.sender ? octstr_get_cstr(msg->sms.sender) : "",
1104 "",
1105 "",
1106 "",
1107 "0100",
1108 mt,
1109 snumbits,
1110 my_buffer2,
1111 mcl,
1112 xser);
1113
1114 /* HEADER */
1115
1116 length = strlen(message_body);
1117 length += 13; /* header (fixed) */
1118 length += 2; /* footer (fixed) */
1119 length += 2; /* slashes between header, body, footer */
1120
1121 sprintf(message_header, "%02i/%05i/%s/%s", (smsc->emi_current_msg_number++ % 100), length, "O", "51");
1122
1123 /* FOOTER */
1124
1125 sprintf(my_buffer, "%s/%s/", message_header, message_body);
1126 generate_checksum((unsigned char *)my_buffer, (unsigned char *)message_footer);
1127
1128 sprintf(message_whole, "%c%s/%s/%s%c", 0x02, message_header, message_body, message_footer, 0x03);
1129
1130 strncpy(rawmessage, message_whole, rawmessage_length);
1131
1132 if (smsc->type == SMSC_TYPE_EMI_X25) {
1133 /* IC3S braindead EMI stack chokes on this... must fix it at the next time... */
1134 strcat(rawmessage, "\r");
1135 }
1136 debug("bb.sms.emi", 0, "emi %d message %s",
1137 smsc->emi_current_msg_number, rawmessage);
1138 return strlen(rawmessage);
1139 }
1140
1141 /******************************************************************************
1142 * Parse the data from the two byte EMI code to normal ISO-8869-1
1143 */
parse_emi_to_iso88591(char * from,char * to,int length,int alt_charset)1144 static int parse_emi_to_iso88591(char *from, char *to, int length,
1145 int alt_charset)
1146 {
1147 int hmtg = 0;
1148 unsigned int mychar;
1149 char tmpbuff[128];
1150
1151 for (hmtg = 0; hmtg <= (int)strlen(from); hmtg += 2) {
1152 strncpy(tmpbuff, from + hmtg, 2);
1153 sscanf(tmpbuff, "%x", &mychar);
1154 to[hmtg / 2] = char_sms_to_iso(mychar, alt_charset);
1155 }
1156
1157 to[(hmtg / 2)-1] = '\0';
1158
1159 return 0;
1160
1161 }
1162
1163 /******************************************************************************
1164 * Parse the data from normal ISO-8869-1 to the two byte EMI code
1165 */
parse_iso88591_to_emi(char * from,char * to,int length,int alt_charset)1166 static int parse_iso88591_to_emi(char *from, char *to,
1167 int length, int alt_charset)
1168 {
1169 char buf[10];
1170 unsigned char tmpchar;
1171 char *ptr;
1172
1173 if (!from || !to || length <= 0)
1174 return -1;
1175
1176 *to = '\0';
1177
1178 debug("bb.sms.emi", 0, "emi parsing <%s> to emi, length %d", from, length);
1179
1180 for (ptr = from; length > 0; ptr++, length--) {
1181 tmpchar = char_iso_to_sms(*ptr, alt_charset);
1182 sprintf(buf, "%02X", tmpchar);
1183 strncat(to, buf, 2);
1184 }
1185 return 0;
1186 }
1187
1188 /******************************************************************************
1189 * Parse the data from binary to the two byte EMI code
1190 */
parse_binary_to_emi(char * from,char * to,int length)1191 static int parse_binary_to_emi(char *from, char *to, int length)
1192 {
1193 char buf[10];
1194 char *ptr;
1195
1196 if (!from || !to || length <= 0)
1197 return -1;
1198
1199 *to = '\0';
1200
1201 for (ptr = from; length > 0; ptr++, length--) {
1202 sprintf(buf, "%02X", (unsigned char)*ptr);
1203 strncat(to, buf, 2);
1204 }
1205
1206 return 0;
1207 }
1208
1209
1210 /******************************************************************************
1211 * Generate the EMI message checksum
1212 */
generate_checksum(const unsigned char * buf,unsigned char * out)1213 static void generate_checksum(const unsigned char *buf, unsigned char *out)
1214 {
1215 const unsigned char *p;
1216 int j;
1217
1218 j = 0;
1219 for (p = buf; *p != '\0'; p++) {
1220 j += *p;
1221
1222 if (j >= 256)
1223 j -= 256;
1224 }
1225
1226 sprintf((char *)out, "%02X", j);
1227 }
1228
1229
1230
1231 /******************************************************************************
1232 * Translate character from iso to emi_mt
1233 * PGr�nholm
1234 */
char_iso_to_sms(unsigned char from,int alt_charset)1235 static char char_iso_to_sms(unsigned char from, int alt_charset)
1236 {
1237
1238 switch ((char)from) {
1239
1240 case 'A':
1241 return 0x41;
1242 case 'B':
1243 return 0x42;
1244 case 'C':
1245 return 0x43;
1246 case 'D':
1247 return 0x44;
1248 case 'E':
1249 return 0x45;
1250 case 'F':
1251 return 0x46;
1252 case 'G':
1253 return 0x47;
1254 case 'H':
1255 return 0x48;
1256 case 'I':
1257 return 0x49;
1258 case 'J':
1259 return 0x4A;
1260 case 'K':
1261 return 0x4B;
1262 case 'L':
1263 return 0x4C;
1264 case 'M':
1265 return 0x4D;
1266 case 'N':
1267 return 0x4E;
1268 case 'O':
1269 return 0x4F;
1270 case 'P':
1271 return 0x50;
1272 case 'Q':
1273 return 0x51;
1274 case 'R':
1275 return 0x52;
1276 case 'S':
1277 return 0x53;
1278 case 'T':
1279 return 0x54;
1280 case 'U':
1281 return 0x55;
1282 case 'V':
1283 return 0x56;
1284 case 'W':
1285 return 0x57;
1286 case 'X':
1287 return 0x58;
1288 case 'Y':
1289 return 0x59;
1290 case 'Z':
1291 return 0x5A;
1292
1293 case 'a':
1294 return 0x61;
1295 case 'b':
1296 return 0x62;
1297 case 'c':
1298 return 0x63;
1299 case 'd':
1300 return 0x64;
1301 case 'e':
1302 return 0x65;
1303 case 'f':
1304 return 0x66;
1305 case 'g':
1306 return 0x67;
1307 case 'h':
1308 return 0x68;
1309 case 'i':
1310 return 0x69;
1311 case 'j':
1312 return 0x6A;
1313 case 'k':
1314 return 0x6B;
1315 case 'l':
1316 return 0x6C;
1317 case 'm':
1318 return 0x6D;
1319 case 'n':
1320 return 0x6E;
1321 case 'o':
1322 return 0x6F;
1323 case 'p':
1324 return 0x70;
1325 case 'q':
1326 return 0x71;
1327 case 'r':
1328 return 0x72;
1329 case 's':
1330 return 0x73;
1331 case 't':
1332 return 0x74;
1333 case 'u':
1334 return 0x75;
1335 case 'v':
1336 return 0x76;
1337 case 'w':
1338 return 0x77;
1339 case 'x':
1340 return 0x78;
1341 case 'y':
1342 return 0x79;
1343 case 'z':
1344 return 0x7A;
1345
1346 case '0':
1347 return 0x30;
1348 case '1':
1349 return 0x31;
1350 case '2':
1351 return 0x32;
1352 case '3':
1353 return 0x33;
1354 case '4':
1355 return 0x34;
1356 case '5':
1357 return 0x35;
1358 case '6':
1359 return 0x36;
1360 case '7':
1361 return 0x37;
1362 case '8':
1363 return 0x38;
1364 case '9':
1365 return 0x39;
1366 case ':':
1367 return 0x3A;
1368 case ';':
1369 return 0x3B;
1370 case '<':
1371 return 0x3C;
1372 case '=':
1373 return 0x3D;
1374 case '>':
1375 return 0x3E;
1376 case '?':
1377 return 0x3F;
1378
1379 case '�':
1380 return '[';
1381 case '�':
1382 return '\\';
1383 case '�':
1384 return 0x0E;
1385 case '�':
1386 return ']';
1387 case '�':
1388 return '{';
1389 case '�':
1390 return '|';
1391 case '�':
1392 return 0x0F;
1393 case '�':
1394 return '}';
1395 case '�':
1396 return '~';
1397 case '�':
1398 return '^';
1399 case '�':
1400 return 0x5F;
1401 case '�':
1402 return 0x0C;
1403
1404 /* case 'Delta': return 0x10; */
1405 /* case 'Fii': return 0x12; */
1406 /* case 'Lambda': return 0x13; */
1407 /* case 'Alpha': return 0x14; */
1408 /* case 'Omega': return 0x15; */
1409 /* case 'Pii': return 0x16; */
1410 /* case 'Pii': return 0x17; */
1411 /* case 'Delta': return 0x18; */
1412 /* case 'Delta': return 0x19; */
1413 /* case 'Delta': return 0x1A; */
1414
1415 case ' ':
1416 return 0x20;
1417 case '@':
1418 if (alt_charset == EMI_SWAPPED_CHARS)
1419 return 0x00;
1420 else
1421 return 0x40;
1422 case '�':
1423 return 0x01;
1424 case '$':
1425 return 0x24;
1426 case '�':
1427 return 0x03;
1428 case '�':
1429 return 0x04;
1430 case '�':
1431 return 0x05;
1432 case '�':
1433 return 0x06;
1434 case '�':
1435 return 0x07;
1436 case '�':
1437 return 0x08;
1438 case '�':
1439 return 0x09;
1440 case '\r':
1441 return 0x0A;
1442 case '�':
1443 return 0x0B;
1444 case '\n':
1445 return 0x0D;
1446 case '�':
1447 return 0x1C;
1448 case '�':
1449 return 0x1D;
1450 case '�':
1451 return 0x1F;
1452
1453 case '!':
1454 return 0x21;
1455 case '"':
1456 return 0x22;
1457 case '#':
1458 return 0x23;
1459 case '�':
1460 return 0x02;
1461 case '%':
1462 return 0x25;
1463
1464 case '&':
1465 return 0x26;
1466 case '\'':
1467 return 0x27;
1468 case '(':
1469 return 0x28;
1470 case ')':
1471 return 0x29;
1472 case '*':
1473 return 0x2A;
1474
1475 case '+':
1476 return 0x2B;
1477 case ',':
1478 return 0x2C;
1479 case '-':
1480 return 0x2D;
1481 case '.':
1482 return 0x2E;
1483 case '/':
1484 return 0x2F;
1485
1486 case '�':
1487 return 0x60;
1488 case '�':
1489 return 0x1E;
1490 case '�':
1491 return 0x7F;
1492 case '�':
1493 if (alt_charset == EMI_SWAPPED_CHARS)
1494 return 0x40;
1495 else
1496 return 0x00;
1497 case '_':
1498 return 0x11;
1499
1500 default:
1501 return 0x20; /* space */
1502
1503 } /* switch */
1504 }
1505
1506
1507 /******************************************************************************
1508 * Translate character from emi_mo to iso
1509 * PGr�nholm
1510 */
char_sms_to_iso(unsigned char from,int alt_charset)1511 static char char_sms_to_iso(unsigned char from, int alt_charset)
1512 {
1513
1514 switch ((int)from) {
1515
1516 case 0x41:
1517 return 'A';
1518 case 0x42:
1519 return 'B';
1520 case 0x43:
1521 return 'C';
1522 case 0x44:
1523 return 'D';
1524 case 0x45:
1525 return 'E';
1526 case 0x46:
1527 return 'F';
1528 case 0x47:
1529 return 'G';
1530 case 0x48:
1531 return 'H';
1532 case 0x49:
1533 return 'I';
1534 case 0x4A:
1535 return 'J';
1536 case 0x4B:
1537 return 'K';
1538 case 0x4C:
1539 return 'L';
1540 case 0x4D:
1541 return 'M';
1542 case 0x4E:
1543 return 'N';
1544 case 0x4F:
1545 return 'O';
1546 case 0x50:
1547 return 'P';
1548 case 0x51:
1549 return 'Q';
1550 case 0x52:
1551 return 'R';
1552 case 0x53:
1553 return 'S';
1554 case 0x54:
1555 return 'T';
1556 case 0x55:
1557 return 'U';
1558 case 0x56:
1559 return 'V';
1560 case 0x57:
1561 return 'W';
1562 case 0x58:
1563 return 'X';
1564 case 0x59:
1565 return 'Y';
1566 case 0x5A:
1567 return 'Z';
1568
1569 case 0x61:
1570 return 'a';
1571 case 0x62:
1572 return 'b';
1573 case 0x63:
1574 return 'c';
1575 case 0x64:
1576 return 'd';
1577 case 0x65:
1578 return 'e';
1579 case 0x66:
1580 return 'f';
1581 case 0x67:
1582 return 'g';
1583 case 0x68:
1584 return 'h';
1585 case 0x69:
1586 return 'i';
1587 case 0x6A:
1588 return 'j';
1589 case 0x6B:
1590 return 'k';
1591 case 0x6C:
1592 return 'l';
1593 case 0x6D:
1594 return 'm';
1595 case 0x6E:
1596 return 'n';
1597 case 0x6F:
1598 return 'o';
1599 case 0x70:
1600 return 'p';
1601 case 0x71:
1602 return 'q';
1603 case 0x72:
1604 return 'r';
1605 case 0x73:
1606 return 's';
1607 case 0x74:
1608 return 't';
1609 case 0x75:
1610 return 'u';
1611 case 0x76:
1612 return 'v';
1613 case 0x77:
1614 return 'w';
1615 case 0x78:
1616 return 'x';
1617 case 0x79:
1618 return 'y';
1619 case 0x7A:
1620 return 'z';
1621
1622 case 0x30:
1623 return '0';
1624 case 0x31:
1625 return '1';
1626 case 0x32:
1627 return '2';
1628 case 0x33:
1629 return '3';
1630 case 0x34:
1631 return '4';
1632 case 0x35:
1633 return '5';
1634 case 0x36:
1635 return '6';
1636 case 0x37:
1637 return '7';
1638 case 0x38:
1639 return '8';
1640 case 0x39:
1641 return '9';
1642 case 0x3A:
1643 return ':';
1644 case 0x3B:
1645 return ';';
1646 case 0x3C:
1647 return '<';
1648 case 0x3D:
1649 return '=';
1650 case 0x3E:
1651 return '>';
1652 case 0x3F:
1653 return '?';
1654
1655 case '[':
1656 return '�';
1657 case '\\':
1658 return '�';
1659 case '\xC5':
1660 return '�';
1661 case ']':
1662 return '�';
1663 case '{':
1664 return '�';
1665 case '|':
1666 return '�';
1667 case 0xE5:
1668 return '�';
1669 case '}':
1670 return '�';
1671 case '~':
1672 return '�';
1673 case 0xA7:
1674 return '�';
1675 case 0xD1:
1676 return '�';
1677 case 0xF8:
1678 return '�';
1679
1680 /* case 'Delta': return 0x10; */
1681 /* case 'Fii': return 0x12; */
1682 /* case 'Lambda': return 0x13; */
1683 /* case 'Alpha': return 0x14; */
1684 /* case 'Omega': return 0x15; */
1685 /* case 'Pii': return 0x16; */
1686 /* case 'Pii': return 0x17; */
1687 /* case 'Delta': return 0x18; */
1688 /* case 'Delta': return 0x19; */
1689 /* case 'Delta': return 0x1A; */
1690
1691 case 0x20:
1692 return ' ';
1693 case 0x40:
1694 return '@';
1695 case 0xA3:
1696 return '�';
1697 case 0x24:
1698 return '$';
1699 case 0xA5:
1700 return '�';
1701 case 0xE8:
1702 return '�';
1703 case 0xE9:
1704 return '�';
1705 case 0xF9:
1706 return '�';
1707 case 0xEC:
1708 return '�';
1709 case 0xF2:
1710 return '�';
1711 case 0xC7:
1712 return '�';
1713 case 0x0A:
1714 return '\r';
1715 case 0xD8:
1716 return '�';
1717 case 0x0D:
1718 return '\n';
1719 case 0xC6:
1720 return '�';
1721 case 0xE6:
1722 return '�';
1723 case 0x1F:
1724 return '�';
1725
1726 case 0x21:
1727 return '!';
1728 case 0x22:
1729 return '"';
1730 case 0x23:
1731 return '#';
1732 case 0xA4:
1733 return '�';
1734 case 0x25:
1735 return '%';
1736
1737 case 0x26:
1738 return '&';
1739 case 0x27:
1740 return '\'';
1741 case 0x28:
1742 return '(';
1743 case 0x29:
1744 return ')';
1745 case 0x2A:
1746 return '*';
1747
1748 case 0x2B:
1749 return '+';
1750 case 0x2C:
1751 return ',';
1752 case 0x2D:
1753 return '-';
1754 case 0x2E:
1755 return '.';
1756 case 0x2F:
1757 return '/';
1758
1759 case 0xBF:
1760 return '�';
1761 case 0xF1:
1762 return '�';
1763 case 0xE0:
1764 return '�';
1765 case 0xA1:
1766 return '�';
1767 case 0x5F:
1768 return '_';
1769
1770 default:
1771 return ' ';
1772
1773 } /* switch */
1774 }
1775