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