7 /* maximum number of data bytes in a SMS data message */
8 #define MAX_USER_DATA_BYTES 140
10 /* maximum number of 7-bit septets in a SMS text message */
11 #define MAX_USER_DATA_SEPTETS 160
13 /* size of the user data header in bytes */
14 #define USER_DATA_HEADER_SIZE 6
19 sms_utf8_from_message_str( const char* str, int strlen, unsigned char* utf8, int utf8len )
22 const char* end = p + strlen;
30 /* read the value from the string */
33 if ((c & 0xe0) == 0xc0)
35 else if ((c & 0xf0) == 0xe0)
40 while (p < end && (p[0] & 0xc0) == 0x80) {
41 c = (c << 6) | (p[0] & 0x3f);
49 case 'n': /* \n is line feed */
53 case 'x': /* \xNN, where NN is a 2-digit hexadecimal value */
56 c = gsm_hex2_to_byte( p );
61 case 'u': /* \uNNNN where NNNN is a 4-digiti hexadecimal value */
64 c = gsm_hex4_to_short( p );
69 default: /* invalid escape, return -1 */
80 /* now, try to write it to the destination */
83 utf8[count] = (byte_t) c;
88 utf8[count] = (byte_t)(0xc0 | ((c >> 6) & 0x1f));
89 if (count+1 < utf8len)
90 utf8[count+1] = (byte_t)(0x80 | (c & 0x3f));
95 utf8[count] = (byte_t)(0xc0 | ((c >> 12) & 0xf));
96 if (count+1 < utf8len)
97 utf8[count+1] = (byte_t)(0x80 | ((c >> 6) & 0x3f));
98 if (count+2 < utf8len)
99 utf8[count+2] = (byte_t)(0x80 | (c & 0x3f));
104 if (escaped) /* bad final escape */
113 sms_timestamp_now( SmsTimeStamp stamp )
115 time_t now_time = time(NULL);
116 struct tm gm = *(gmtime(&now_time));
117 struct tm local = *(localtime(&now_time));
120 stamp->data[0] = gsm_int_to_bcdi( local.tm_year % 100 );
121 stamp->data[1] = gsm_int_to_bcdi( local.tm_mon+1 );
122 stamp->data[2] = gsm_int_to_bcdi( local.tm_mday );
123 stamp->data[3] = gsm_int_to_bcdi( local.tm_hour );
124 stamp->data[4] = gsm_int_to_bcdi( local.tm_min );
125 stamp->data[5] = gsm_int_to_bcdi( local.tm_sec );
127 tzdiff = (local.tm_hour*4 + local.tm_min/15) - (gm.tm_hour*4 + gm.tm_min/15);
128 if (local.tm_yday > gm.tm_yday)
130 else if (local.tm_yday < gm.tm_yday)
133 stamp->data[6] = gsm_int_to_bcdi( tzdiff >= 0 ? tzdiff : -tzdiff );
135 stamp->data[6] |= 0x08;
139 sms_timestamp_to_tm( SmsTimeStamp stamp, struct tm* tm )
143 tm->tm_year = gsm_int_from_bcdi( stamp->data[0] );
144 if (tm->tm_year < 50)
146 tm->tm_mon = gsm_int_from_bcdi( stamp->data[1] ) -1;
147 tm->tm_mday = gsm_int_from_bcdi( stamp->data[2] );
148 tm->tm_hour = gsm_int_from_bcdi( stamp->data[3] );
149 tm->tm_min = gsm_int_from_bcdi( stamp->data[4] );
150 tm->tm_sec = gsm_int_from_bcdi( stamp->data[5] );
154 tzdiff = gsm_int_from_bcdi( stamp->data[6] & 0xf7 );
155 if (stamp->data[6] & 0x8)
162 gsm_rope_add_timestamp( GsmRope rope, const SmsTimeStampRec* ts )
164 gsm_rope_add( rope, ts->data, 7 );
171 sms_address_to_str( SmsAddress address, char* str, int strlen )
173 bytes_t data = address->data;
174 if(address->toa == 0x91)
178 for(i=0;i<address->len;i++) {
189 sms_address_from_str( SmsAddress address, const char* src, int srclen )
191 const char* end = src + srclen;
192 int shift = 0, len = 0;
193 bytes_t data = address->data;
201 if ( src[0] == '+' ) {
207 memset( address->data, 0, sizeof(address->data) );
212 int c = *src++ - '0';
214 if ( (unsigned)c >= 10 ||
215 data >= address->data + sizeof(address->data) )
218 data[0] |= c << shift;
237 sms_address_from_bytes( SmsAddress address, const unsigned char* buf, int buflen )
239 int len = sizeof(address->data), num_digits;
244 address->len = num_digits = buf[0];
245 address->toa = buf[1];
247 len = (num_digits+1)/2;
248 if ( len > sizeof(address->data) )
251 memcpy( address->data, buf+2, len );
256 sms_address_to_bytes( SmsAddress address, unsigned char* buf, int bufsize )
258 int len = (address->len + 1)/2 + 2;
263 if (bufsize < 1) goto Exit;
264 buf[0] = address->len;
266 if (bufsize < 2) goto Exit;
267 buf[1] = address->toa;
274 memcpy( buf, address->data, bufsize );
280 sms_address_from_hex ( SmsAddress address, const char* hex, int hexlen )
282 const char* hexend = hex + hexlen;
283 int nn, len, num_digits;
288 address->len = num_digits = gsm_hex2_to_byte( hex );
289 address->toa = gsm_hex2_to_byte( hex+2 );
292 len = (num_digits + 1)/2;
293 if (hex + len*2 > hexend)
296 for ( nn = 0; nn < len; nn++ )
297 address->data[nn] = gsm_hex2_to_byte( hex + nn*2 );
303 sms_address_to_hex ( SmsAddress address, char* hex, int hexlen )
305 int len = (address->len + 1)/2 + 2;
311 if (hexlen < 2) goto Exit;
312 gsm_hex_from_byte( hex, address->len );
313 if (hexlen < 4) goto Exit;
314 gsm_hex_from_byte( hex+2, address->toa );
317 if ( hexlen > 2*(len - 2) )
318 hexlen = (len - 2)/2;
320 for ( nn = 0; nn < hexlen; nn += 2 )
321 gsm_hex_from_byte( hex+nn, address->data[nn/2] );
328 gsm_rope_add_address( GsmRope rope, const SmsAddressRec* addr )
330 gsm_rope_add_c( rope, addr->len );
331 gsm_rope_add_c( rope, addr->toa );
332 gsm_rope_add( rope, addr->data, (addr->len+1)/2 );
334 if (!rope->error && rope->data != NULL)
335 rope->data[ rope->pos-1 ] |= 0xf0;
343 sms_get_byte( cbytes_t *pcur, cbytes_t end )
345 cbytes_t cur = *pcur;
355 /* parse a service center address, returns -1 in case of error */
357 sms_get_sc_address( cbytes_t *pcur,
361 cbytes_t cur = *pcur;
366 int dlen, adjust = 0;
370 if (len == 0) { /* empty address */
377 if (cur + len > end) {
381 address->toa = *cur++;
385 for (dlen = 0; dlen < len; dlen+=1)
391 if (dlen >= sizeof(address->data)) {
401 address->data[dlen] = (byte_t) c;
408 address->len = 2*dlen + adjust;
418 sms_skip_sc_address( cbytes_t *pcur,
421 cbytes_t cur = *pcur;
439 /* parse a sender/receiver address, returns -1 in case of error */
441 sms_get_address( cbytes_t *pcur,
445 cbytes_t cur = *pcur;
461 if (cur + 1 + (dlen+1)/2 > end)
465 address->toa = *cur++;
468 if (len > sizeof(address->data))
471 memcpy( address->data, cur, len );
483 sms_skip_address( cbytes_t *pcur,
486 cbytes_t cur = *pcur;
494 cur += 2 + (dlen + 1)/2;
503 /* parse a service center timestamp */
505 sms_get_timestamp( cbytes_t *pcur,
509 cbytes_t cur = *pcur;
514 memcpy( ts->data, cur, 7 );
520 sms_skip_timestamp( cbytes_t *pcur,
523 cbytes_t cur = *pcur;
536 typedef struct SmsPDURec {
543 smspdu_free( SmsPDU pdu )
554 smspdu_get_type( SmsPDU pdu )
556 cbytes_t data = pdu->tpdu;
557 cbytes_t end = pdu->end;
558 int mtiByte = sms_get_byte(&data, end);
560 switch (mtiByte & 3) {
561 case 0: return SMS_PDU_DELIVER;
562 case 1: return SMS_PDU_SUBMIT;
563 case 2: return SMS_PDU_STATUS_REPORT;
564 default: return SMS_PDU_INVALID;
569 smspdu_get_sender_address( SmsPDU pdu, SmsAddress address )
571 cbytes_t data = pdu->tpdu;
572 cbytes_t end = pdu->end;
573 int mtiByte = sms_get_byte(&data, end);
575 switch (mtiByte & 3) {
576 case 0: /* SMS_PDU_DELIVER; */
577 return sms_get_sc_address( &data, end, address );
584 smspdu_get_sc_timestamp( SmsPDU pdu, SmsTimeStamp ts )
586 cbytes_t data = pdu->tpdu;
587 cbytes_t end = pdu->end;
588 int mtiByte = sms_get_byte( &data, end );
590 switch (mtiByte & 3) {
591 case 0: /* SMS_PDU_DELIVER */
593 SmsAddressRec address;
595 if ( sms_get_sc_address( &data, end, &address ) < 0 )
598 data += 2; /* skip protocol identifer + coding scheme */
600 return sms_get_timestamp( &data, end, ts );
608 smspdu_get_receiver_address( SmsPDU pdu, SmsAddress address )
610 cbytes_t data = pdu->tpdu;
611 cbytes_t end = pdu->end;
612 int mtiByte = sms_get_byte( &data, end );
614 switch (mtiByte & 3) {
615 case 1: /* SMS_PDU_SUBMIT */
617 data += 1; /* skip message reference */
618 return sms_get_address( &data, end, address );
626 SMS_CODING_SCHEME_UNKNOWN = 0,
627 SMS_CODING_SCHEME_GSM7,
628 SMS_CODING_SCHEME_UCS2
632 /* see TS 23.038 Section 5 for details */
633 static SmsCodingScheme
634 sms_get_coding_scheme( cbytes_t *pcur,
637 cbytes_t cur = *pcur;
641 return SMS_CODING_SCHEME_UNKNOWN;
646 switch (dataCoding >> 4) {
650 return SMS_CODING_SCHEME_GSM7;
653 if (dataCoding == 0x10) return SMS_CODING_SCHEME_GSM7;
654 if (dataCoding == 0x11) return SMS_CODING_SCHEME_UCS2;
657 case 0x04: case 0x05: case 0x06: case 0x07:
658 if (dataCoding & 0x20) return SMS_CODING_SCHEME_UNKNOWN; /* compressed 7-bits */
659 if (((dataCoding >> 2) & 3) == 0) return SMS_CODING_SCHEME_GSM7;
660 if (((dataCoding >> 2) & 3) == 2) return SMS_CODING_SCHEME_UCS2;
664 if (!(dataCoding & 4)) return SMS_CODING_SCHEME_GSM7;
667 return SMS_CODING_SCHEME_UNKNOWN;
671 /* see TS 23.040 section 9.2.3.24 for details */
673 sms_get_text_utf8( cbytes_t *pcur,
676 SmsCodingScheme coding,
679 cbytes_t cur = *pcur;
684 printf("sms_get_text_utf8 %d %d\n",hasUDH,coding);
691 /* skip user data header if any */
700 if (cur + hlen > end)
705 if (coding == SMS_CODING_SCHEME_GSM7)
714 /* switch the user data header if any */
715 if (coding == SMS_CODING_SCHEME_GSM7)
717 int count = utf8_from_gsm7( cur, 0, len, NULL );
721 bytes_t dst = gsm_rope_reserve( rope, count );
725 utf8_from_gsm7( cur, 0, len, dst );
729 else if (coding == SMS_CODING_SCHEME_UCS2)
731 int count = ucs2_to_utf8( cur, len/2, NULL );
735 bytes_t dst = gsm_rope_reserve( rope, count );
737 ucs2_to_utf8( cur, len/2, dst );
750 /* get the message embedded in a SMS PDU as a utf8 byte array, returns the length of the message in bytes */
751 /* or -1 in case of error */
753 smspdu_get_text_message( SmsPDU pdu, unsigned char* utf8, int utf8len )
755 cbytes_t data = pdu->tpdu;
756 cbytes_t end = pdu->end;
757 int mtiByte = sms_get_byte( &data, end );
759 switch (mtiByte & 3) {
760 case 0: /* SMS_PDU_DELIVER */
762 SmsAddressRec address;
763 SmsTimeStampRec timestamp;
764 SmsCodingScheme coding;
768 if ( sms_get_sc_address( &data, end, &address ) < 0 )
771 data += 1; /* skip protocol identifier */
772 coding = sms_get_coding_scheme( &data, end );
773 if (coding == SMS_CODING_SCHEME_UNKNOWN)
776 if ( sms_get_timestamp( &data, end, ×tamp ) < 0 )
779 gsm_rope_init_alloc( rope, 0 );
780 if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 )
784 if (utf8len > result)
788 memcpy( utf8, rope->data, utf8len );
790 gsm_rope_done( rope );
794 case 1: /* SMS_PDU_SUBMIT */
796 SmsAddressRec address;
797 SmsCodingScheme coding;
801 data += 1; /* message reference */
803 if ( sms_get_address( &data, end, &address ) < 0 )
806 data += 1; /* skip protocol identifier */
807 coding = sms_get_coding_scheme( &data, end );
808 if (coding == SMS_CODING_SCHEME_UNKNOWN)
811 gsm_rope_init_alloc( rope, 0 );
812 if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 ) {
813 gsm_rope_done( rope );
818 if (utf8len > result)
822 memcpy( utf8, rope->data, utf8len );
824 gsm_rope_done( rope );
834 gsm_rope_add_sms_user_header( GsmRope rope,
839 gsm_rope_add_c( rope, 0x05 ); /* total header length == 5 bytes */
840 gsm_rope_add_c( rope, 0x00 ); /* element id: concatenated message reference number */
841 gsm_rope_add_c( rope, 0x03 ); /* element len: 3 bytes */
842 gsm_rope_add_c( rope, (byte_t)ref_number ); /* reference number */
843 gsm_rope_add_c( rope, (byte_t)pdu_count ); /* max pdu index */
844 gsm_rope_add_c( rope, (byte_t)pdu_index+1 ); /* current pdu index */
847 /* write a SMS-DELIVER PDU into a rope */
849 gsm_rope_add_sms_deliver_pdu( GsmRope rope,
853 const SmsAddressRec* sender_address,
854 const SmsTimeStampRec* timestamp,
861 int mtiByte = 0x20; /* message type - SMS DELIVER */
864 mtiByte |= 0x40; /* user data header indicator */
866 gsm_rope_add_c( rope, 0 ); /* no SC Address */
867 gsm_rope_add_c( rope, mtiByte ); /* message type - SMS-DELIVER */
868 gsm_rope_add_address( rope, sender_address );
869 gsm_rope_add_c( rope, 0 ); /* protocol identifier */
871 /* data coding scheme - GSM 7 bits / no class - or - 16-bit UCS2 / class 1 */
872 coding = (use_gsm7 ? 0x00 : 0x09);
874 gsm_rope_add_c( rope, coding ); /* data coding scheme */
875 gsm_rope_add_timestamp( rope, timestamp ); /* service center timestamp */
879 int count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
882 //assert( count <= MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE );
886 int headerBits = 6*8; /* 6 is size of header in bytes */
887 int headerSeptets = headerBits / 7;
888 if (headerBits % 7 > 0)
891 pad = headerSeptets*7 - headerBits;
893 gsm_rope_add_c( rope, count + headerSeptets );
894 gsm_rope_add_sms_user_header(rope, ref_num, pdu_count, pdu_index);
897 gsm_rope_add_c( rope, count );
899 count = (count*7+pad+7)/8; /* convert to byte count */
901 dst = gsm_rope_reserve( rope, count );
903 utf8_to_gsm7( utf8, utf8len, dst, pad );
907 int count = utf8_to_ucs2( utf8, utf8len, NULL );
909 //assert( count*2 <= MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE );
913 gsm_rope_add_c( rope, count*2 + 6 );
914 gsm_rope_add_sms_user_header( rope, ref_num, pdu_count, pdu_index );
917 gsm_rope_add_c( rope, count*2 );
919 gsm_rope_add_c( rope, count*2 );
920 dst = gsm_rope_reserve( rope, count*2 );
922 utf8_to_ucs2( utf8, utf8len, dst );
929 smspdu_create_deliver( cbytes_t utf8,
932 const SmsAddressRec* sender_address,
933 const SmsTimeStampRec* timestamp,
942 p = calloc( sizeof(*p), 1 );
945 gsm_rope_init( rope );
946 gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
947 sender_address, timestamp,
948 ref_num, pdu_count, pdu_index);
952 gsm_rope_init_alloc( rope, rope->pos );
954 gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
955 sender_address, timestamp,
956 ref_num, pdu_count, pdu_index );
958 p->base = gsm_rope_done_acquire( rope, &size );
962 p->end = p->base + size;
963 p->tpdu = p->base + 1;
974 smspdu_free_list( SmsPDU* pdus )
978 for (nn = 0; pdus[nn] != NULL; nn++)
979 smspdu_free( pdus[nn] );
988 smspdu_create_deliver_utf8( const unsigned char* utf8,
990 const SmsAddressRec* sender_address,
991 const SmsTimeStampRec* timestamp )
1000 static unsigned char ref_num = 0;
1002 if (timestamp == NULL) {
1003 sms_timestamp_now( &ts0 );
1007 /* can we encode the message with the GSM 7-bit alphabet ? */
1008 use_gsm7 = utf8_check_gsm7( utf8, utf8len );
1010 /* count the number of SMS PDUs we'll need */
1011 block = MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE;
1014 count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
1016 count = utf8_to_ucs2( utf8, utf8len, NULL );
1017 block = MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE;
1020 num_pdus = count / block;
1021 leftover = count - num_pdus*block;
1025 list = calloc( sizeof(SmsPDU*), count + 1 );
1029 /* now create each SMS PDU */
1031 cbytes_t src = utf8;
1032 cbytes_t src_end = utf8 + utf8len;
1035 for (nn = 0; nn < num_pdus; nn++)
1040 if (leftover > 0 && nn == num_pdus-1)
1043 src_next = utf8_skip( src, src_end, skip );
1044 list[nn] = smspdu_create_deliver( src, src_next - src, use_gsm7, sender_address, timestamp,
1045 ref_num, num_pdus, nn );
1046 if (list[nn] == NULL)
1059 smspdu_free_list(list);
1065 smspdu_create_from_hex( const char* hex, int hexlen )
1070 p = calloc( sizeof(*p), 1 );
1073 p->base = malloc( (hexlen+1)/2 );
1074 if (p->base == NULL) {
1080 gsm_hex_to_bytes( hex, hexlen, p->base );
1081 p->end = p->base + (hexlen+1)/2;
1084 if ( sms_skip_sc_address( &data, p->end ) < 0 )
1087 p->tpdu = (bytes_t) data;
1099 smspdu_to_hex( SmsPDU pdu, char* hex, int hexlen )
1101 int result = (pdu->end - pdu->base)*2;
1104 if (hexlen > result)
1107 for (nn = 0; nn*2 < hexlen; nn++) {
1108 gsm_hex_from_byte( &hex[nn*2], pdu->base[nn] );