blob: 812ac7ce28b351c775423768a28863d669d09fbc [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12#include "sms.h"
13#include "gsm.h"
14#include <memory.h>
15#include <stdlib.h>
David 'Digit' Turner7891dd32014-04-28 10:59:47 +020016#include <string.h>
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080017#include <assert.h>
18
19#define DEBUG 1
20
21#if 1
22# include "android/utils/debug.h"
23# define D_ACTIVE VERBOSE_CHECK(modem)
24#else
25# define D_ACTIVE DEBUG
26#endif
27
28#if DEBUG
29# define D(...) VERBOSE_PRINT(modem,__VA_ARGS__)
30#else
31# define D(...) ((void)0)
32#endif
33
34/* maximum number of data bytes in a SMS data message */
35#define MAX_USER_DATA_BYTES 140
36
37/* maximum number of 7-bit septets in a SMS text message */
38#define MAX_USER_DATA_SEPTETS 160
39
40/* size of the user data header in bytes */
41#define USER_DATA_HEADER_SIZE 6
42
43/** MESSAGE TEXT
44 **/
45int
46sms_utf8_from_message_str( const char* str, int strlen, unsigned char* utf8, int utf8len )
47{
48 cbytes_t p = (cbytes_t)str;
49 cbytes_t end = p + strlen;
50 int count = 0;
51 int escaped = 0;
52
53 while (p < end)
54 {
55 int c = p[0];
56
57 /* read the value from the string */
58 p += 1;
59 if (c >= 128) {
60 if ((c & 0xe0) == 0xc0)
61 c &= 0x1f;
62 else if ((c & 0xf0) == 0xe0)
63 c &= 0x0f;
64 else
65 c &= 0x07;
66 p++;
67 while (p < end && (p[0] & 0xc0) == 0x80) {
68 c = (c << 6) | (p[0] & 0x3f);
69 p++;
70 }
71 }
72 if (escaped) {
73 switch (c) {
74 case '\\':
75 break;
76 case 'n': /* \n is line feed */
77 c = 10;
78 break;
79
80 case 'x': /* \xNN, where NN is a 2-digit hexadecimal value */
81 if (p+2 > end)
82 return -1;
83 c = gsm_hex2_to_byte( (const char*)p );
84 if (c < 0)
85 return -1;
86 p += 2;
87 break;
88
89 case 'u': /* \uNNNN where NNNN is a 4-digiti hexadecimal value */
90 if (p + 4 > end)
91 return -1;
92 c = gsm_hex4_to_short( (const char*)p );
93 if (c < 0)
94 return -1;
95 p += 4;
96 break;
97
98 default: /* invalid escape, return -1 */
99 return -1;
100 }
101 escaped = 0;
102 }
103 else if (c == '\\')
104 {
105 escaped = 1;
106 continue;
107 }
108
109 /* now, try to write it to the destination */
110 if (c < 128) {
111 if (count < utf8len)
112 utf8[count] = (byte_t) c;
113 count += 1;
114 }
115 else if (c < 0x800) {
116 if (count < utf8len)
117 utf8[count] = (byte_t)(0xc0 | ((c >> 6) & 0x1f));
118 if (count+1 < utf8len)
119 utf8[count+1] = (byte_t)(0x80 | (c & 0x3f));
120 count += 2;
121 }
122 else {
123 if (count < utf8len)
124 utf8[count] = (byte_t)(0xc0 | ((c >> 12) & 0xf));
125 if (count+1 < utf8len)
126 utf8[count+1] = (byte_t)(0x80 | ((c >> 6) & 0x3f));
127 if (count+2 < utf8len)
128 utf8[count+2] = (byte_t)(0x80 | (c & 0x3f));
129 count += 3;
130 }
131 }
132
133 if (escaped) /* bad final escape */
134 return -1;
135
136 return count;
137}
138
139/* to convert utf-8 to a message string, we only need to deal with control characters
140 * and that's it */
141int sms_utf8_to_message_str( const unsigned char* utf8, int utf8len, char* str, int strlen )
142{
143 cbytes_t p = utf8;
144 cbytes_t end = p + utf8len;
145 int count = 0;
146
147 while (p < end)
148 {
149 int c = p[0];
150 int escape = 0;
151
152 /* read the value from the string */
153 p += 1;
154 if (c >= 128) {
155 if ((c & 0xe0) == 0xc0)
156 c &= 0x1f;
157 else if ((c & 0xf0) == 0xe0)
158 c &= 0x0f;
159 else
160 c &= 0x07;
161 p++;
162 while (p < end && (p[0] & 0xc0) == 0x80) {
163 c = (c << 6) | (p[0] & 0x3f);
164 p++;
165 }
166 }
167
168 if (c < ' ') {
169 escape = 1;
170 if (c == '\n') {
171 c = 'n';
172 escape = 2;
173 }
174 }
175 else if (c == '\\')
176 escape = 2;
177
178 switch (escape) {
179 case 0:
180 if (c < 128) {
181 if (count < strlen)
182 str[count] = (char) c;
183 count += 1;
184 }
185 else if (c < 0x800) {
186 if (count < strlen)
187 str[count] = (byte_t)(0xc0 | ((c >> 6) & 0x1f));
188 if (count+1 < strlen)
189 str[count+1] = (byte_t)(0x80 | (c & 0x3f));
190 count += 2;
191 }
192 else {
193 if (count < strlen)
194 str[count] = (byte_t)(0xc0 | ((c >> 12) & 0xf));
195 if (count+1 < strlen)
196 str[count+1] = (byte_t)(0x80 | ((c >> 6) & 0x3f));
197 if (count+2 < strlen)
198 str[count+2] = (byte_t)(0x80 | (c & 0x3f));
199 count += 3;
200 }
201 break;
202
203 case 1:
204 if (count+3 < strlen) {
205 str[count+0] = '\\';
206 str[count+1] = 'x';
207 gsm_hex_from_byte(str + count + 2, c);
208 }
209 count += 4;
210 break;
211
212 default:
213 if (count+2 < strlen) {
214 str[count+0] = '\\';
215 str[count+1] = (char) c;
216 }
217 count += 2;
218 }
219 }
220 return count;
221}
222
223
224/** TIMESTAMPS
225 **/
226void
227sms_timestamp_now( SmsTimeStamp stamp )
228{
229 time_t now_time = time(NULL);
230 struct tm gm = *(gmtime(&now_time));
231 struct tm local = *(localtime(&now_time));
232 int tzdiff = 0;
233
234 stamp->data[0] = gsm_int_to_bcdi( local.tm_year % 100 );
235 stamp->data[1] = gsm_int_to_bcdi( local.tm_mon+1 );
236 stamp->data[2] = gsm_int_to_bcdi( local.tm_mday );
237 stamp->data[3] = gsm_int_to_bcdi( local.tm_hour );
238 stamp->data[4] = gsm_int_to_bcdi( local.tm_min );
239 stamp->data[5] = gsm_int_to_bcdi( local.tm_sec );
240
241 tzdiff = (local.tm_hour*4 + local.tm_min/15) - (gm.tm_hour*4 + gm.tm_min/15);
242 if (local.tm_yday > gm.tm_yday)
243 tzdiff += 24*4;
244 else if (local.tm_yday < gm.tm_yday)
245 tzdiff -= 24*4;
246
247 stamp->data[6] = gsm_int_to_bcdi( tzdiff >= 0 ? tzdiff : -tzdiff );
248 if (tzdiff < 0)
249 stamp->data[6] |= 0x08;
250}
251
252int
253sms_timestamp_to_tm( SmsTimeStamp stamp, struct tm* tm )
254{
255 int tzdiff;
256
257 tm->tm_year = gsm_int_from_bcdi( stamp->data[0] );
258 if (tm->tm_year < 50)
259 tm->tm_year += 100;
260 tm->tm_mon = gsm_int_from_bcdi( stamp->data[1] ) -1;
261 tm->tm_mday = gsm_int_from_bcdi( stamp->data[2] );
262 tm->tm_hour = gsm_int_from_bcdi( stamp->data[3] );
263 tm->tm_min = gsm_int_from_bcdi( stamp->data[4] );
264 tm->tm_sec = gsm_int_from_bcdi( stamp->data[5] );
265
266 tm->tm_isdst = -1;
267
268 tzdiff = gsm_int_from_bcdi( stamp->data[6] & 0xf7 );
269 if (stamp->data[6] & 0x8)
270 tzdiff = -tzdiff;
271
272 return tzdiff;
273}
274
275static void
276gsm_rope_add_timestamp( GsmRope rope, const SmsTimeStampRec* ts )
277{
278 gsm_rope_add( rope, ts->data, 7 );
279}
280
281
282/** SMS ADDRESSES
283 **/
284
285int
286sms_address_from_str( SmsAddress address, const char* src, int srclen )
287{
288 const char* end = src + srclen;
289 int shift = 0, len = 0;
290 bytes_t data = address->data;
291
292 address->len = 0;
293 address->toa = 0x81;
294
295 if (src >= end)
296 return -1;
297
298 if ( src[0] == '+' ) {
299 address->toa = 0x91;
300 if (++src == end)
301 goto Fail;
302 }
303
304 memset( address->data, 0, sizeof(address->data) );
305
306 shift = 0;
307
308 while (src < end) {
309 int c = *src++ - '0';
310
311 if ( (unsigned)c >= 10 ||
312 data >= address->data + sizeof(address->data) )
313 goto Fail;
314
315 data[0] |= c << shift;
316 len += 1;
317 shift += 4;
318 if (shift == 8) {
319 shift = 0;
320 data += 1;
321 }
322 }
323 if (shift != 0)
324 data[0] |= 0xf0;
325
326 address->len = len;
327 return 0;
328
329Fail:
330 return -1;
331}
332
333int
334sms_address_to_str( SmsAddress address, char* str, int strlen )
335{
336 static const char dialdigits[16] = "0123456789*#,N%";
337 int n, count = 0;
338
339 if (address->toa == 0x91) {
340 if (count < strlen)
341 str[count] = '+';
342 count++;
343 }
344 for (n = 0; n < address->len; n += 2)
345 {
346 int c = address->data[n/2];
347
348 if (count < strlen)
349 str[count] = dialdigits[c & 0xf];
350 count += 1;
351
352 if (n+1 > address->len)
353 break;
354
355 if (count < strlen)
356 str[count] = dialdigits[(c >> 4) & 0xf];
Marc Petit-Huguenina1b379c2010-07-14 12:33:15 -0700357 if (str[count])
358 count += 1;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800359 }
360 return count;
361}
362
363int
364sms_address_from_bytes( SmsAddress address, const unsigned char* buf, int buflen )
365{
366 int len = sizeof(address->data), num_digits;
367
368 if (buflen < 2)
369 return -1;
370
371 address->len = num_digits = buf[0];
372 address->toa = buf[1];
373
374 len = (num_digits+1)/2;
375 if ( len > sizeof(address->data) )
376 return -1;
377
378 memcpy( address->data, buf+2, len );
379 return 0;
380}
381
382int
383sms_address_to_bytes( SmsAddress address, unsigned char* buf, int bufsize )
384{
385 int len = (address->len + 1)/2 + 2;
386
387 if (buf == NULL)
388 bufsize = 0;
389
390 if (bufsize < 1) goto Exit;
391 buf[0] = address->len;
392
393 if (bufsize < 2) goto Exit;
394 buf[1] = address->toa;
395
396 buf += 2;
397 bufsize -= 2;
398 if (bufsize > len-2)
399 bufsize = len - 2;
400
401 memcpy( buf, address->data, bufsize );
402Exit:
403 return len;
404}
405
406int
407sms_address_from_hex ( SmsAddress address, const char* hex, int hexlen )
408{
409 const char* hexend = hex + hexlen;
410 int nn, len, num_digits;
411
412 if (hexlen < 4)
413 return -1;
414
415 address->len = num_digits = gsm_hex2_to_byte( hex );
416 address->toa = gsm_hex2_to_byte( hex+2 );
417 hex += 4;
418
419 len = (num_digits + 1)/2;
420 if (hex + len*2 > hexend)
421 return -1;
422
423 for ( nn = 0; nn < len; nn++ )
424 address->data[nn] = gsm_hex2_to_byte( hex + nn*2 );
425
426 return 0;
427}
428
429int
430sms_address_to_hex ( SmsAddress address, char* hex, int hexlen )
431{
432 int len = (address->len + 1)/2 + 2;
433 int nn;
434
435 if (hex == NULL)
436 hexlen = 0;
437
438 if (hexlen < 2) goto Exit;
439 gsm_hex_from_byte( hex, address->len );
440 if (hexlen < 4) goto Exit;
441 gsm_hex_from_byte( hex+2, address->toa );
442 hex += 4;
443 hexlen -= 4;
444 if ( hexlen > 2*(len - 2) )
445 hexlen = (len - 2)/2;
446
447 for ( nn = 0; nn < hexlen; nn += 2 )
448 gsm_hex_from_byte( hex+nn, address->data[nn/2] );
449
450Exit:
451 return len*2;
452}
453
454static void
455gsm_rope_add_address( GsmRope rope, const SmsAddressRec* addr )
456{
457 gsm_rope_add_c( rope, addr->len );
458 gsm_rope_add_c( rope, addr->toa );
459 gsm_rope_add( rope, addr->data, (addr->len+1)/2 );
460 if (addr->len & 1) {
461 if (!rope->error && rope->data != NULL)
462 rope->data[ rope->pos-1 ] |= 0xf0;
463 }
464}
465
466static int
467sms_address_eq( const SmsAddressRec* addr1, const SmsAddressRec* addr2 )
468{
469 if ( addr1->toa != addr2->toa ||
470 addr1->len != addr2->len )
471 return 0;
472
473 return ( !memcmp( addr1->data, addr2->data, addr1->len ) );
474}
475
476/** SMS PARSER
477 **/
478static int
479sms_get_byte( cbytes_t *pcur, cbytes_t end )
480{
481 cbytes_t cur = *pcur;
482 int result = -1;
483
484 if (cur < end) {
485 result = cur[0];
486 *pcur = cur + 1;
487 }
488 return result;
489}
490
491/* parse a service center address, returns -1 in case of error */
492static int
493sms_get_sc_address( cbytes_t *pcur,
494 cbytes_t end,
495 SmsAddress address )
496{
497 cbytes_t cur = *pcur;
498 int result = -1;
499
500 if (cur < end) {
501 int len = cur[0];
502 int dlen, adjust = 0;
503
504 cur += 1;
505
506 if (len == 0) { /* empty address */
507 address->len = 0;
508 address->toa = 0x00;
509 result = 0;
510 goto Exit;
511 }
512
513 if (cur + len > end) {
514 goto Exit;
515 }
516
517 address->toa = *cur++;
518 len -= 1;
519 result = 0;
520
521 for (dlen = 0; dlen < len; dlen+=1)
522 {
523 int c = cur[dlen];
524 int v;
525
526 adjust = 0;
527 if (dlen >= sizeof(address->data)) {
528 result = -1;
529 break;
530 }
531
532 v = (c & 0xf);
533 if (v >= 0xe)
534 break;
535
536 adjust = 1;
537 address->data[dlen] = (byte_t) c;
538
539 v = (c >> 4) & 0xf;
540 if (v >= 0xe) {
541 break;
542 }
543 }
544 address->len = 2*dlen + adjust;
545 }
546Exit:
547 if (!result)
548 *pcur = cur;
549
550 return result;
551}
552
553static int
554sms_skip_sc_address( cbytes_t *pcur,
555 cbytes_t end )
556{
557 cbytes_t cur = *pcur;
558 int result = -1;
559 int len;
560
561 if (cur >= end)
562 goto Exit;
563
564 len = cur[0];
565 cur += 1 + len;
566 if (cur > end)
567 goto Exit;
568
569 *pcur = cur;
570 result = 0;
571Exit:
572 return result;
573}
574
575/* parse a sender/receiver address, returns -1 in case of error */
576static int
577sms_get_address( cbytes_t *pcur,
578 cbytes_t end,
579 SmsAddress address )
580{
581 cbytes_t cur = *pcur;
582 int result = -1;
583 int len, dlen;
584
585 if (cur >= end)
586 goto Exit;
587
588 dlen = *cur++;
589
590 if (dlen == 0) {
591 address->len = 0;
592 address->toa = 0;
593 result = 0;
594 goto Exit;
595 }
596
597 if (cur + 1 + (dlen+1)/2 > end)
598 goto Exit;
599
600 address->len = dlen;
601 address->toa = *cur++;
602
603 len = (dlen + 1)/2;
604 if (len > sizeof(address->data))
605 goto Exit;
606
607 memcpy( address->data, cur, len );
608 cur += len;
609 result = 0;
610
611Exit:
612 if (!result)
613 *pcur = cur;
614
615 return result;
616}
617
618static int
619sms_skip_address( cbytes_t *pcur,
620 cbytes_t end )
621{
622 cbytes_t cur = *pcur;
623 int result = -1;
624 int dlen;
625
626 if (cur + 2 > end)
627 goto Exit;
628
629 dlen = cur[0];
630 cur += 2 + (dlen + 1)/2;
631 if (cur > end)
632 goto Exit;
633
634 result = 0;
635Exit:
636 return result;
637}
638
639/* parse a service center timestamp */
640static int
641sms_get_timestamp( cbytes_t *pcur,
642 cbytes_t end,
643 SmsTimeStamp ts )
644{
645 cbytes_t cur = *pcur;
646
647 if (cur + 7 > end)
648 return -1;
649
650 memcpy( ts->data, cur, 7 );
651 *pcur = cur + 7;
652 return 0;
653}
654
655static int
656sms_skip_timestamp( cbytes_t *pcur,
657 cbytes_t end )
658{
659 cbytes_t cur = *pcur;
660
661 if (cur + 7 > end)
662 return -1;
663
664 *pcur = cur + 7;
665 return 0;
666}
667
668
669static int
670sms_skip_validity_period( cbytes_t *pcur,
671 cbytes_t end,
672 int mtiByte )
673{
674 cbytes_t cur = *pcur;
675
676 switch ((mtiByte >> 3) & 3) {
677 case 1: /* relative format */
678 cur += 1;
679 break;
680
681 case 2: /* enhanced format */
682 case 3: /* absolute format */
683 cur += 7;
684 }
685 if (cur > end)
686 return -1;
687
688 *pcur = cur;
689 return 0;
690}
691
692/** SMS PDU
693 **/
694
695typedef struct SmsPDURec {
696 bytes_t base;
697 bytes_t end;
698 bytes_t tpdu;
699} SmsPDURec;
700
701void
702smspdu_free( SmsPDU pdu )
703{
704 if (pdu) {
705 free( pdu->base );
706 pdu->base = NULL;
707 pdu->end = NULL;
708 pdu->tpdu = NULL;
709 }
710}
711
712SmsPduType
713smspdu_get_type( SmsPDU pdu )
714{
715 cbytes_t data = pdu->tpdu;
716 cbytes_t end = pdu->end;
717 int mtiByte = sms_get_byte(&data, end);
718
719 switch (mtiByte & 3) {
720 case 0: return SMS_PDU_DELIVER;
721 case 1: return SMS_PDU_SUBMIT;
722 case 2: return SMS_PDU_STATUS_REPORT;
723 default: return SMS_PDU_INVALID;
724 }
725}
726
727int
728smspdu_get_sender_address( SmsPDU pdu, SmsAddress address )
729{
730 cbytes_t data = pdu->tpdu;
731 cbytes_t end = pdu->end;
732 int mtiByte = sms_get_byte(&data, end);
733
734 switch (mtiByte & 3) {
735 case 0: /* SMS_PDU_DELIVER; */
736 return sms_get_sc_address( &data, end, address );
737
738 default: return -1;
739 }
740}
741
742int
743smspdu_get_sc_timestamp( SmsPDU pdu, SmsTimeStamp ts )
744{
745 cbytes_t data = pdu->tpdu;
746 cbytes_t end = pdu->end;
747 int mtiByte = sms_get_byte( &data, end );
748
749 switch (mtiByte & 3) {
750 case 0: /* SMS_PDU_DELIVER */
751 {
752 SmsAddressRec address;
753
754 if ( sms_get_sc_address( &data, end, &address ) < 0 )
755 return -1;
756
757 data += 2; /* skip protocol identifer + coding scheme */
758
759 return sms_get_timestamp( &data, end, ts );
760 }
761
762 default: return -1;
763 }
764}
765
766int
767smspdu_get_receiver_address( SmsPDU pdu, SmsAddress address )
768{
769 cbytes_t data = pdu->tpdu;
770 cbytes_t end = pdu->end;
771 int mtiByte = sms_get_byte( &data, end );
772
773 switch (mtiByte & 3) {
774 case 1: /* SMS_PDU_SUBMIT */
775 {
776 data += 1; /* skip message reference */
777 return sms_get_address( &data, end, address );
778 }
779
780 default: return -1;
781 }
782}
783
784typedef enum {
785 SMS_CODING_SCHEME_UNKNOWN = 0,
786 SMS_CODING_SCHEME_GSM7,
787 SMS_CODING_SCHEME_UCS2
788
789} SmsCodingScheme;
790
791/* see TS 23.038 Section 5 for details */
792static SmsCodingScheme
793sms_get_coding_scheme( cbytes_t *pcur,
794 cbytes_t end )
795{
796 cbytes_t cur = *pcur;
797 int dataCoding;
798
799 if (cur >= end)
800 return SMS_CODING_SCHEME_UNKNOWN;
801
802 dataCoding = *cur++;
803 *pcur = cur;
804
805 switch (dataCoding >> 4) {
806 case 0x00:
807 case 0x02:
808 case 0x03:
809 return SMS_CODING_SCHEME_GSM7;
810
811 case 0x01:
812 if (dataCoding == 0x10) return SMS_CODING_SCHEME_GSM7;
813 if (dataCoding == 0x11) return SMS_CODING_SCHEME_UCS2;
814 break;
815
816 case 0x04: case 0x05: case 0x06: case 0x07:
817 if (dataCoding & 0x20) return SMS_CODING_SCHEME_UNKNOWN; /* compressed 7-bits */
818 if (((dataCoding >> 2) & 3) == 0) return SMS_CODING_SCHEME_GSM7;
819 if (((dataCoding >> 2) & 3) == 2) return SMS_CODING_SCHEME_UCS2;
820 break;
821
822 case 0xF:
823 if (!(dataCoding & 4)) return SMS_CODING_SCHEME_GSM7;
824 break;
825 }
826 return SMS_CODING_SCHEME_UNKNOWN;
827}
828
829
830/* see TS 23.040 section 9.2.3.24 for details */
831static int
832sms_get_text_utf8( cbytes_t *pcur,
833 cbytes_t end,
834 int hasUDH,
835 SmsCodingScheme coding,
836 GsmRope rope )
837{
838 cbytes_t cur = *pcur;
839 int result = -1;
840 int len;
841
842 if (cur >= end)
843 goto Exit;
844
845 len = *cur++;
846
847 /* skip user data header if any */
848 if ( hasUDH )
849 {
850 int hlen;
851
852 if (cur >= end)
853 goto Exit;
854
855 hlen = *cur++;
856 if (cur + hlen > end)
857 goto Exit;
858
859 cur += hlen;
860
861 if (coding == SMS_CODING_SCHEME_GSM7)
862 len -= 2*(hlen+1);
863 else
864 len -= hlen+1;
865
866 if (len < 0)
867 goto Exit;
868 }
869
870 /* switch the user data header if any */
871 if (coding == SMS_CODING_SCHEME_GSM7)
872 {
873 int count = utf8_from_gsm7( cur, 0, len, NULL );
874
875 if (rope != NULL)
876 {
877 bytes_t dst = gsm_rope_reserve( rope, count );
878 if (dst != NULL)
879 utf8_from_gsm7( cur, 0, len, dst );
880 }
881 cur += (len+1)/2;
882 }
883 else if (coding == SMS_CODING_SCHEME_UCS2)
884 {
885 int count = ucs2_to_utf8( cur, len/2, NULL );
886
887 if (rope != NULL)
888 {
889 bytes_t dst = gsm_rope_reserve( rope, count );
890 if (dst != NULL)
891 ucs2_to_utf8( cur, len/2, dst );
892 }
893 cur += len;
894 }
895 result = 0;
896
897Exit:
898 if (!result)
899 *pcur = cur;
900
901 return result;
902}
903
904/* get the message embedded in a SMS PDU as a utf8 byte array, returns the length of the message in bytes */
905/* or -1 in case of error */
906int
907smspdu_get_text_message( SmsPDU pdu, unsigned char* utf8, int utf8len )
908{
909 cbytes_t data = pdu->tpdu;
910 cbytes_t end = pdu->end;
911 int mtiByte = sms_get_byte( &data, end );
912
913 switch (mtiByte & 3) {
914 case 0: /* SMS_PDU_DELIVER */
915 {
916 SmsAddressRec address;
917 SmsTimeStampRec timestamp;
918 SmsCodingScheme coding;
919 GsmRopeRec rope[1];
920 int result;
921
922 if ( sms_get_sc_address( &data, end, &address ) < 0 )
923 goto Fail;
924
925 data += 1; /* skip protocol identifier */
926 coding = sms_get_coding_scheme( &data, end );
927 if (coding == SMS_CODING_SCHEME_UNKNOWN)
928 goto Fail;
929
930 if ( sms_get_timestamp( &data, end, &timestamp ) < 0 )
931 goto Fail;
932
933 if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 )
934 goto Fail;
935
936 result = rope->pos;
937 if (utf8len > result)
938 utf8len = result;
939
940 if (utf8len > 0)
941 memcpy( utf8, rope->data, utf8len );
942
943 gsm_rope_done( rope );
944 return result;
945 }
946
947 case 1: /* SMS_PDU_SUBMIT */
948 {
949 SmsAddressRec address;
950 SmsCodingScheme coding;
951 GsmRopeRec rope[1];
952 int result;
953
954 data += 1; /* message reference */
955
956 if ( sms_get_address( &data, end, &address ) < 0 )
957 goto Fail;
958
959 data += 1; /* skip protocol identifier */
960 coding = sms_get_coding_scheme( &data, end );
961 if (coding == SMS_CODING_SCHEME_UNKNOWN)
962 goto Fail;
963
964 gsm_rope_init_alloc( rope, 0 );
965 if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 ) {
966 gsm_rope_done( rope );
967 goto Fail;
968 }
969
970 result = rope->pos;
971 if (utf8len > result)
972 utf8len = result;
973
974 if (utf8len > 0)
975 memcpy( utf8, rope->data, utf8len );
976
977 gsm_rope_done( rope );
978 return result;
979 }
980 }
981Fail:
982 return -1;
983}
984
985static cbytes_t
986smspdu_get_user_data_ref( SmsPDU pdu )
987{
988 cbytes_t data = pdu->tpdu;
989 cbytes_t end = pdu->end;
990 int mtiByte = sms_get_byte( &data, end );
991 int len;
992
993 /* if there is no user-data-header, there is no message reference here */
994 if ((mtiByte & 0x40) == 0)
995 goto Fail;
996
997 switch (mtiByte & 3) {
998 case 0: /* SMS_PDU_DELIVER */
999 if ( sms_skip_address( &data, end ) < 0 )
1000 goto Fail;
1001
1002 data += 2; /* skip protocol identifier + coding scheme */
1003
1004 if ( sms_skip_timestamp( &data, end ) < 0 )
1005 goto Fail;
1006
1007 break;
1008
1009 case 1: /* SMS_PDU_SUBMIT */
1010 data += 1; /* skip message reference */
1011
1012 if ( sms_skip_address( &data, end ) < 0 )
1013 goto Fail;
1014
1015 data += 2; /* protocol identifier + oding schene */
1016 if ( sms_skip_validity_period( &data, end, mtiByte ) < 0 )
1017 goto Fail;
1018
1019 break;
1020
1021 default:
1022 goto Fail;
1023 }
1024
1025 /* skip user-data length */
1026 if (data+1 >= end)
1027 goto Fail;
1028
1029 len = data[1];
1030 data += 2;
1031
1032 while (len >= 2 && data + 2 <= end) {
1033 int htype = data[0];
1034 int hlen = data[1];
1035
1036 if (htype == 00 && hlen == 3 && data + 5 <= end) {
1037 return data + 2;
1038 }
1039
1040 data += hlen;
1041 len -= hlen - 2;
1042 }
1043Fail:
1044 return NULL;
1045}
1046
1047int
1048smspdu_get_ref( SmsPDU pdu )
1049{
1050 cbytes_t user_ref = smspdu_get_user_data_ref( pdu );
1051
1052 if (user_ref != NULL)
1053 {
1054 return user_ref[0];
1055 }
1056 else
1057 {
1058 cbytes_t data = pdu->tpdu;
1059 cbytes_t end = pdu->end;
1060 int mtiByte = sms_get_byte( &data, end );
1061
1062 if ((mtiByte & 3) == 1) {
1063 /* try to extract directly the reference for a SMS-SUBMIT */
1064 if (data < end)
1065 return data[0];
1066 }
1067 }
1068 return -1;
1069}
1070
1071int
1072smspdu_get_max_index( SmsPDU pdu )
1073{
1074 cbytes_t user_ref = smspdu_get_user_data_ref( pdu );
1075
1076 if (user_ref != NULL) {
1077 return user_ref[1];
1078 } else {
1079 return 1;
1080 }
1081}
1082
1083int
1084smspdu_get_cur_index( SmsPDU pdu )
1085{
1086 cbytes_t user_ref = smspdu_get_user_data_ref( pdu );
1087
1088 if (user_ref != NULL) {
1089 return user_ref[2] - 1;
1090 } else {
1091 return 0;
1092 }
1093}
1094
1095
1096static void
1097gsm_rope_add_sms_user_header( GsmRope rope,
1098 int ref_number,
1099 int pdu_count,
1100 int pdu_index )
1101{
1102 gsm_rope_add_c( rope, 0x05 ); /* total header length == 5 bytes */
1103 gsm_rope_add_c( rope, 0x00 ); /* element id: concatenated message reference number */
1104 gsm_rope_add_c( rope, 0x03 ); /* element len: 3 bytes */
1105 gsm_rope_add_c( rope, (byte_t)ref_number ); /* reference number */
1106 gsm_rope_add_c( rope, (byte_t)pdu_count ); /* max pdu index */
1107 gsm_rope_add_c( rope, (byte_t)pdu_index+1 ); /* current pdu index */
1108}
1109
1110/* write a SMS-DELIVER PDU into a rope */
1111static void
1112gsm_rope_add_sms_deliver_pdu( GsmRope rope,
1113 cbytes_t utf8,
1114 int utf8len,
1115 int use_gsm7,
1116 const SmsAddressRec* sender_address,
1117 const SmsTimeStampRec* timestamp,
1118 int ref_num,
1119 int pdu_count,
1120 int pdu_index)
1121{
1122 int coding;
1123 int mtiByte = 0x20; /* message type - SMS DELIVER */
1124
1125 if (pdu_count > 1)
1126 mtiByte |= 0x40; /* user data header indicator */
1127
1128 gsm_rope_add_c( rope, 0 ); /* no SC Address */
1129 gsm_rope_add_c( rope, mtiByte ); /* message type - SMS-DELIVER */
1130 gsm_rope_add_address( rope, sender_address );
1131 gsm_rope_add_c( rope, 0 ); /* protocol identifier */
1132
1133 /* data coding scheme - GSM 7 bits / no class - or - 16-bit UCS2 / class 1 */
1134 coding = (use_gsm7 ? 0x00 : 0x09);
1135
1136 gsm_rope_add_c( rope, coding ); /* data coding scheme */
1137 gsm_rope_add_timestamp( rope, timestamp ); /* service center timestamp */
1138
1139 if (use_gsm7) {
1140 bytes_t dst;
1141 int count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
1142 int pad = 0;
1143
1144 assert( count <= MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE );
1145
1146 if (pdu_count > 1)
1147 {
1148 int headerBits = 6*8; /* 6 is size of header in bytes */
1149 int headerSeptets = headerBits / 7;
1150 if (headerBits % 7 > 0)
1151 headerSeptets += 1;
1152
1153 pad = headerSeptets*7 - headerBits;
1154
1155 gsm_rope_add_c( rope, count + headerSeptets );
1156 gsm_rope_add_sms_user_header(rope, ref_num, pdu_count, pdu_index);
1157 }
1158 else
1159 gsm_rope_add_c( rope, count );
1160
1161 count = (count*7+pad+7)/8; /* convert to byte count */
1162
1163 dst = gsm_rope_reserve( rope, count );
1164 if (dst != NULL) {
1165 utf8_to_gsm7( utf8, utf8len, dst, pad );
1166 }
1167 } else {
1168 bytes_t dst;
1169 int count = utf8_to_ucs2( utf8, utf8len, NULL );
1170
1171 assert( count*2 <= MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE );
1172
1173 if (pdu_count > 1)
1174 {
1175 gsm_rope_add_c( rope, count*2 + 6 );
1176 gsm_rope_add_sms_user_header( rope, ref_num, pdu_count, pdu_index );
1177 }
1178 else
1179 gsm_rope_add_c( rope, count*2 );
1180
1181 gsm_rope_add_c( rope, count*2 );
1182 dst = gsm_rope_reserve( rope, count*2 );
1183 if (dst != NULL) {
1184 utf8_to_ucs2( utf8, utf8len, dst );
1185 }
1186 }
1187}
1188
1189
1190static SmsPDU
1191smspdu_create_deliver( cbytes_t utf8,
1192 int utf8len,
1193 int use_gsm7,
1194 const SmsAddressRec* sender_address,
1195 const SmsTimeStampRec* timestamp,
1196 int ref_num,
1197 int pdu_count,
1198 int pdu_index )
1199{
1200 SmsPDU p;
1201 GsmRopeRec rope[1];
1202 int size;
1203
1204 p = calloc( sizeof(*p), 1 );
1205 if (!p) goto Exit;
1206
1207 gsm_rope_init( rope );
1208 gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
1209 sender_address, timestamp,
1210 ref_num, pdu_count, pdu_index);
1211 if (rope->error)
1212 goto Fail;
1213
1214 gsm_rope_init_alloc( rope, rope->pos );
1215
1216 gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
1217 sender_address, timestamp,
1218 ref_num, pdu_count, pdu_index );
1219
1220 p->base = gsm_rope_done_acquire( rope, &size );
1221 if (p->base == NULL)
1222 goto Fail;
1223
1224 p->end = p->base + size;
1225 p->tpdu = p->base + 1;
1226Exit:
1227 return p;
1228
1229Fail:
1230 free(p);
1231 return NULL;
1232}
1233
1234
1235void
1236smspdu_free_list( SmsPDU* pdus )
1237{
1238 if (pdus) {
1239 int nn;
1240 for (nn = 0; pdus[nn] != NULL; nn++)
1241 smspdu_free( pdus[nn] );
1242
1243 free( pdus );
1244 }
1245}
1246
1247
1248
1249SmsPDU*
1250smspdu_create_deliver_utf8( const unsigned char* utf8,
1251 int utf8len,
1252 const SmsAddressRec* sender_address,
1253 const SmsTimeStampRec* timestamp )
1254{
1255 SmsTimeStampRec ts0;
1256 int use_gsm7;
1257 int count, block;
1258 int num_pdus = 0;
1259 int leftover = 0;
1260 SmsPDU* list = NULL;
1261
1262 static unsigned char ref_num = 0;
1263
1264 if (timestamp == NULL) {
1265 sms_timestamp_now( &ts0 );
1266 timestamp = &ts0;
1267 }
1268
1269 /* can we encode the message with the GSM 7-bit alphabet ? */
1270 use_gsm7 = utf8_check_gsm7( utf8, utf8len );
1271
1272 /* count the number of SMS PDUs we'll need */
1273 block = MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE;
1274
1275 if (use_gsm7) {
1276 count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
1277 } else {
1278 count = utf8_to_ucs2( utf8, utf8len, NULL );
1279 block = MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE;
1280 }
1281
1282 num_pdus = count / block;
1283 leftover = count - num_pdus*block;
1284 if (leftover > 0)
1285 num_pdus += 1;
1286
1287 list = calloc( sizeof(SmsPDU*), num_pdus + 1 );
1288 if (list == NULL)
1289 return NULL;
1290
1291 /* now create each SMS PDU */
1292 {
1293 cbytes_t src = utf8;
1294 cbytes_t src_end = utf8 + utf8len;
1295 int nn;
1296
1297 for (nn = 0; nn < num_pdus; nn++)
1298 {
1299 int skip = block;
1300 cbytes_t src_next;
1301
1302 if (leftover > 0 && nn == num_pdus-1)
1303 skip = leftover;
1304
1305 src_next = utf8_skip_gsm7( src, src_end, skip );
1306
1307 list[nn] = smspdu_create_deliver( src, src_next - src, use_gsm7, sender_address, timestamp,
1308 ref_num, num_pdus, nn );
1309 if (list[nn] == NULL)
1310 goto Fail;
1311
1312 src = src_next;
1313 }
1314 }
1315
1316 ref_num++;
1317 return list;
1318
1319Fail:
1320 smspdu_free_list(list);
1321 return NULL;
1322}
1323
1324
1325SmsPDU
1326smspdu_create_from_hex( const char* hex, int hexlen )
1327{
1328 SmsPDU p;
1329 cbytes_t data;
1330
1331 p = calloc( sizeof(*p), 1 );
1332 if (!p) goto Exit;
1333
1334 p->base = malloc( (hexlen+1)/2 );
1335 if (p->base == NULL) {
1336 free(p);
1337 p = NULL;
1338 goto Exit;
1339 }
1340
1341 if ( gsm_hex_to_bytes( (cbytes_t)hex, hexlen, p->base ) < 0 )
1342 goto Fail;
1343
1344 p->end = p->base + (hexlen+1)/2;
1345
1346 data = p->base;
1347 if ( sms_skip_sc_address( &data, p->end ) < 0 )
1348 goto Fail;
1349
1350 p->tpdu = (bytes_t) data;
1351
1352Exit:
1353 return p;
1354
1355Fail:
1356 free(p->base);
1357 free(p);
1358 return NULL;
1359}
1360
1361int
1362smspdu_to_hex( SmsPDU pdu, char* hex, int hexlen )
1363{
1364 int result = (pdu->end - pdu->base)*2;
1365 int nn;
1366
1367 if (hexlen > result)
1368 hexlen = result;
1369
1370 for (nn = 0; nn*2 < hexlen; nn++) {
1371 gsm_hex_from_byte( &hex[nn*2], pdu->base[nn] );
1372 }
1373 return result;
1374}
1375
1376
1377/** SMS SUBMIT RECEIVER
1378 ** collects one or more SMS-SUBMIT PDUs to generate a single message to deliver
1379 **/
1380
1381typedef struct SmsFragmentRec {
1382 struct SmsFragmentRec* next;
1383 SmsAddressRec from[1];
1384 byte_t ref;
1385 byte_t max;
1386 byte_t count;
1387 int index;
1388 SmsPDU* pdus;
1389
1390} SmsFragmentRec, *SmsFragment;
1391
1392
1393typedef struct SmsReceiverRec {
1394 int last;
1395 SmsFragment fragments;
1396
1397} SmsReceiverRec;
1398
1399
1400static void
1401sms_fragment_free( SmsFragment frag )
1402{
1403 int nn;
1404
1405 for (nn = 0; nn < frag->max; nn++) {
1406 if (frag->pdus[nn] != NULL) {
1407 smspdu_free( frag->pdus[nn] );
1408 frag->pdus[nn] = NULL;
1409 }
1410 }
1411 frag->pdus = NULL;
1412 frag->count = 0;
1413 frag->max = 0;
1414 frag->index = 0;
1415 free( frag );
1416}
1417
1418static SmsFragment
1419sms_fragment_alloc( SmsReceiver rec, const SmsAddressRec* from, int ref, int max )
1420{
1421 SmsFragment frag = calloc(sizeof(*frag) + max*sizeof(SmsPDU), 1 );
1422
1423 if (frag != NULL) {
1424 frag->from[0] = from[0];
1425 frag->ref = ref;
1426 frag->max = max;
1427 frag->pdus = (SmsPDU*)(frag + 1);
1428 frag->index = ++rec->last;
1429 }
1430 return frag;
1431}
1432
1433
1434
1435SmsReceiver sms_receiver_create( void )
1436{
1437 SmsReceiver rec = calloc(sizeof(*rec),1);
1438 return rec;
1439}
1440
1441void
1442sms_receiver_destroy( SmsReceiver rec )
1443{
1444 while (rec->fragments) {
1445 SmsFragment frag = rec->fragments;
1446 rec->fragments = frag->next;
1447 sms_fragment_free(frag);
1448 }
1449}
1450
1451static SmsFragment*
1452sms_receiver_find_p( SmsReceiver rec, const SmsAddressRec* from, int ref )
1453{
1454 SmsFragment* pnode = &rec->fragments;
1455 SmsFragment node;
1456
1457 for (;;) {
1458 node = *pnode;
1459 if (node == NULL)
1460 break;
1461 if (node->ref == ref && sms_address_eq( node->from, from ))
1462 break;
1463 pnode = &node->next;
1464 }
1465 return pnode;
1466}
1467
1468static SmsFragment*
1469sms_receiver_find_index_p( SmsReceiver rec, int index )
1470{
1471 SmsFragment* pnode = &rec->fragments;
1472 SmsFragment node;
1473
1474 for (;;) {
1475 node = *pnode;
1476 if (node == NULL)
1477 break;
1478 if (node->index == index)
1479 break;
1480 pnode = &node->next;
1481 }
1482 return pnode;
1483}
1484
1485int
1486sms_receiver_add_submit_pdu( SmsReceiver rec, SmsPDU submit_pdu )
1487{
1488 SmsAddressRec from[1];
1489 int ref, max, cur;
1490 SmsFragment* pnode;
1491 SmsFragment frag;
1492
1493 if ( smspdu_get_receiver_address( submit_pdu, from ) < 0 ) {
1494 D( "%s: could not extract receiver address\n", __FUNCTION__ );
1495 return -1;
1496 }
1497
1498 ref = smspdu_get_ref( submit_pdu );
1499 if (ref < 0) {
1500 D( "%s: could not extract message reference from pdu\n", __FUNCTION__ );
1501 return -1;
1502 }
1503 max = smspdu_get_max_index( submit_pdu );
1504 if (max < 0) {
1505 D( "%s: invalid max fragment value: %d should be >= 1\n",
1506 __FUNCTION__, max );
1507 return -1;
1508 }
1509 pnode = sms_receiver_find_p( rec, from, ref );
1510 frag = *pnode;
1511 if (frag == NULL) {
1512 frag = sms_fragment_alloc( rec, from, ref, max );
1513 if (frag == NULL) {
1514 D("%s: not enough memory to allocate new fragment\n", __FUNCTION__ );
1515 return -1;
1516 }
1517 if (D_ACTIVE) {
1518 char tmp[32];
1519 int len;
1520
1521 len = sms_address_to_str( from, tmp, sizeof(tmp) );
1522 if (len < 0) {
1523 strcpy( tmp, "<unknown>" );
1524 len = strlen(tmp);
1525 }
1526 D("%s: created SMS index %d, from %.*s, ref %d, max %d\n", __FUNCTION__,
1527 frag->index, len, tmp, frag->ref, frag->max);
1528 }
1529 *pnode = frag;
1530 }
1531
1532 cur = smspdu_get_cur_index( submit_pdu );
1533 if (cur < 0) {
1534 D("%s: SMS fragment index is too small: %d should be >= 1\n", __FUNCTION__, cur+1 );
1535 return -1;
1536 }
1537 if (cur >= max) {
1538 D("%s: SMS fragment index is too large (%d >= %d)\n", __FUNCTION__, cur, max);
1539 return -1;
1540 }
1541 if ( frag->pdus[cur] != NULL ) {
1542 D("%s: receiving duplicate SMS fragment for %d/%d, ref=%d, discarding old one\n",
1543 __FUNCTION__, cur+1, max, ref);
1544 smspdu_free( frag->pdus[cur] );
1545 frag->count -= 1;
1546 }
1547 frag->pdus[cur] = submit_pdu;
1548 frag->count += 1;
1549
1550 if (frag->count >= frag->max) {
1551 /* yes, we received all fragments for this SMS */
1552 D( "%s: SMS index %d, received all %d fragments\n", __FUNCTION__, frag->index, frag->count );
1553 return frag->index;
1554 }
1555 else {
1556 /* still waiting for more */
1557 D( "%s: SMS index %d, received %d/%d, waiting for %d more\n", __FUNCTION__,
1558 frag->index, cur+1, max, frag->max - frag->count );
1559 return 0;
1560 }
1561}
1562
1563
1564int
1565sms_receiver_get_text_message( SmsReceiver rec, int index, bytes_t utf8, int utf8len )
1566{
1567 SmsFragment* pnode = sms_receiver_find_index_p( rec, index );
1568 SmsFragment frag = *pnode;
1569 int nn, total;
1570
1571 if (frag == NULL) {
1572 D( "%s: invalid SMS index %d\n", __FUNCTION__, index );
1573 return -1;
1574 }
1575 if (frag->count != frag->max) {
1576 D( "%s: SMS index %d still needs %d fragments\n", __FUNCTION__,
1577 frag->index, frag->max - frag->count );
1578 return -1;
1579 }
1580 /* get the size of all combined text */
1581 total = 0;
1582 for ( nn = 0; nn < frag->count; nn++ ) {
1583 int partial;
1584 if (utf8 && utf8len > 0) {
1585 partial = smspdu_get_text_message( frag->pdus[nn], utf8, utf8len );
1586 utf8 += partial;
1587 utf8len -= partial;
1588 } else {
1589 partial = smspdu_get_text_message( frag->pdus[nn], NULL, 0 );
1590 }
1591 total += partial;
1592 }
1593 return total;
1594}
1595
1596
1597static void
1598sms_receiver_remove( SmsReceiver rec, int index )
1599{
1600 SmsFragment* pnode = sms_receiver_find_index_p( rec, index );
1601 SmsFragment frag = *pnode;
1602 if (frag != NULL) {
1603 *pnode = frag->next;
1604 sms_fragment_free(frag);
1605 }
1606}
1607
1608
1609SmsPDU*
1610sms_receiver_create_deliver( SmsReceiver rec, int index, const SmsAddressRec* from )
1611{
1612 SmsPDU* result = NULL;
1613 SmsFragment* pnode = sms_receiver_find_index_p( rec, index );
1614 SmsFragment frag = *pnode;
1615 SmsTimeStampRec now[1];
1616 int nn, total;
1617 bytes_t utf8;
1618 int utf8len;
1619
1620 if (frag == NULL) {
1621 D( "%s: invalid SMS index %d\n", __FUNCTION__, index );
1622 return NULL;
1623 }
1624 if (frag->count != frag->max) {
1625 D( "%s: SMS index %d still needs %d fragments\n", __FUNCTION__,
1626 frag->index, frag->max - frag->count );
1627 return NULL;
1628 }
1629
1630 /* get the combined text message */
1631 utf8len = sms_receiver_get_text_message( rec, index, NULL, 0 );
1632 if (utf8len < 0)
1633 goto Exit;
1634
1635 utf8 = malloc( utf8len + 1 );
1636 if (utf8 == NULL) {
1637 D( "%s: not enough memory to allocate %d bytes\n",
1638 __FUNCTION__, utf8len+1 );
1639 goto Exit;
1640 }
1641
1642 total = 0;
1643 for ( nn = 0; nn < frag->count; nn++ ) {
1644 total += smspdu_get_text_message( frag->pdus[nn], utf8 + total, utf8len - total );
1645 }
1646
1647 sms_timestamp_now( now );
1648
1649 result = smspdu_create_deliver_utf8( utf8, utf8len, from, now );
1650
1651 free(utf8);
1652
1653Exit:
1654 sms_receiver_remove( rec, index );
1655 return result;
1656}
1657