Sync froyo ril blob from hyc's codebase.
[xdandroid:rootfs.git] / sms_gsm.c
1 #include "sms_gsm.h"
2 #include "gsm.h"
3 #include <memory.h>
4 #include <stdlib.h>
5 //#include <assert.h>
6
7 /* maximum number of data bytes in a SMS data message */
8 #define  MAX_USER_DATA_BYTES   140
9
10 /* maximum number of 7-bit septets in a SMS text message */
11 #define  MAX_USER_DATA_SEPTETS  160
12
13 /* size of the user data header in bytes */
14 #define  USER_DATA_HEADER_SIZE   6
15
16 /** MESSAGE TEXT
17  **/
18 int
19 sms_utf8_from_message_str( const char*  str, int  strlen, unsigned char*  utf8, int  utf8len )
20 {
21     const char*     p       = str;
22     const char*     end     = p + strlen;
23     int             count   = 0;
24     int             escaped = 0;
25
26     while (p < end)
27     {
28         int  c = p[0];
29
30         /* read the value from the string */
31         p += 1;
32         if (c >= 128) {
33             if ((c & 0xe0) == 0xc0)
34                 c &= 0x1f;
35             else if ((c & 0xf0) == 0xe0)
36                 c &= 0x0f;
37             else
38                 c &= 0x07;
39             p++;
40             while (p < end && (p[0] & 0xc0) == 0x80) {
41                 c = (c << 6) | (p[0] & 0x3f);
42                 p++;
43             }
44         }
45         if (escaped) {
46             switch (c) {
47                 case '\\':
48                     break;
49                 case 'n':  /* \n is line feed */
50                     c = 10;
51                     break;
52
53                 case 'x':  /* \xNN, where NN is a 2-digit hexadecimal value */
54                     if (p+2 > end)
55                         return -1;
56                     c = gsm_hex2_to_byte( p );
57                     if (c < 0)
58                         return -1;
59                     break;
60
61                 case 'u':  /* \uNNNN where NNNN is a 4-digiti hexadecimal value */
62                     if (p + 4 > end)
63                         return -1;
64                     c = gsm_hex4_to_short( p );
65                     if (c < 0)
66                         return -1;
67                     break;
68
69                 default:  /* invalid escape, return -1 */
70                     return -1;
71             }
72             escaped = 0;
73         }
74         else if (c == '\\')
75         {
76             escaped = 1;
77             continue;
78         }
79
80         /* now, try to write it to the destination */
81         if (c < 128) {
82             if (count < utf8len)
83                 utf8[count] = (byte_t) c;
84             count += 1;
85         }
86         else if (c < 0x800) {
87             if (count < utf8len)
88                 utf8[count]   = (byte_t)(0xc0 | ((c >> 6) & 0x1f));
89             if (count+1 < utf8len)
90                 utf8[count+1] = (byte_t)(0x80 | (c & 0x3f));
91             count += 2;
92         }
93         else {
94             if (count < utf8len)
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));
100             count += 3;
101         }
102     }
103
104     if (escaped)   /* bad final escape */
105         return -1;
106
107     return count;
108 }
109
110 /** TIMESTAMPS
111  **/
112 void
113 sms_timestamp_now( SmsTimeStamp  stamp )
114 {
115     time_t     now_time = time(NULL);
116     struct tm  gm       = *(gmtime(&now_time));
117     struct tm  local    = *(localtime(&now_time));
118     int        tzdiff   = 0;
119
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 );
126
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)
129         tzdiff += 24*4;
130     else if (local.tm_yday < gm.tm_yday)
131         tzdiff -= 24*4;
132
133     stamp->data[6] = gsm_int_to_bcdi( tzdiff >= 0 ? tzdiff : -tzdiff );
134     if (tzdiff < 0)
135         stamp->data[6] |= 0x08;
136 }
137
138 int
139 sms_timestamp_to_tm( SmsTimeStamp  stamp, struct tm*  tm )
140 {
141     int  tzdiff;
142
143     tm->tm_year = gsm_int_from_bcdi( stamp->data[0] );
144     if (tm->tm_year < 50)
145         tm->tm_year += 100;
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] );
151
152     tm->tm_isdst = -1;
153
154     tzdiff = gsm_int_from_bcdi( stamp->data[6] & 0xf7 );
155     if (stamp->data[6] & 0x8)
156         tzdiff = -tzdiff;
157
158     return tzdiff;
159 }
160
161 static void
162 gsm_rope_add_timestamp( GsmRope  rope, const SmsTimeStampRec*  ts )
163 {
164     gsm_rope_add( rope, ts->data, 7 );
165 }
166
167
168 /** SMS ADDRESSES
169  **/
170 int
171 sms_address_to_str( SmsAddress  address, char*  str, int  strlen )
172 {
173         bytes_t      data = address->data;
174         if(address->toa == 0x91)
175                 *str++='+';
176         int i;
177         char c;
178         for(i=0;i<address->len;i++) {
179                 c=data[i/2];
180                 if(i&1) c=c>>4;
181                 *str++='0'+(c&15);
182         }
183         *str=0;
184         return 1;
185 }
186                 
187         
188 int
189 sms_address_from_str( SmsAddress  address, const char*  src, int  srclen )
190 {
191     const char*  end   = src + srclen;
192     int          shift = 0, len = 0;
193     bytes_t      data = address->data;
194
195     address->len = 0;
196     address->toa = 0x81;
197
198     if (src >= end)
199         return -1;
200
201     if ( src[0] == '+' ) {
202         address->toa = 0x91;
203         if (++src == end)
204             goto Fail;
205     }
206
207     memset( address->data, 0, sizeof(address->data) );
208
209     shift = 0;
210
211     while (src < end) {
212         int  c = *src++ - '0';
213
214         if ( (unsigned)c >= 10 ||
215               data >= address->data + sizeof(address->data) )
216             goto Fail;
217
218         data[0] |= c << shift;
219         len   += 1;
220         shift += 4;
221         if (shift == 8) {
222             shift = 0;
223             data += 1;
224         }
225     }
226     if (shift != 0)
227         data[0] |= 0xf0;
228
229     address->len = len;
230     return 0;
231
232 Fail:
233     return -1;
234 }
235
236 int
237 sms_address_from_bytes( SmsAddress  address, const unsigned char*  buf, int  buflen )
238 {
239     int   len = sizeof(address->data), num_digits;
240
241     if (buflen < 2)
242         return -1;
243
244     address->len = num_digits = buf[0];
245     address->toa = buf[1];
246
247     len = (num_digits+1)/2;
248     if ( len > sizeof(address->data) )
249         return -1;
250
251     memcpy( address->data, buf+2, len );
252     return 0;
253 }
254
255 int
256 sms_address_to_bytes( SmsAddress  address, unsigned char*  buf, int  bufsize )
257 {
258     int  len = (address->len + 1)/2 + 2;
259
260     if (buf == NULL)
261         bufsize = 0;
262
263     if (bufsize < 1) goto Exit;
264     buf[0] = address->len;
265
266     if (bufsize < 2) goto Exit;
267     buf[1] = address->toa;
268
269     buf     += 2;
270     bufsize -= 2;
271     if (bufsize > len-2)
272         bufsize = len - 2;
273
274     memcpy( buf, address->data, bufsize );
275 Exit:
276     return len;
277 }
278
279 int
280 sms_address_from_hex  ( SmsAddress  address, const char*  hex, int  hexlen )
281 {
282     const char*  hexend = hex + hexlen;
283     int          nn, len, num_digits;
284
285     if (hexlen < 4)
286         return -1;
287
288     address->len = num_digits = gsm_hex2_to_byte( hex );
289     address->toa = gsm_hex2_to_byte( hex+2 );
290     hex += 4;
291
292     len = (num_digits + 1)/2;
293     if (hex + len*2 > hexend)
294         return -1;
295
296     for ( nn = 0; nn < len; nn++ )
297         address->data[nn] = gsm_hex2_to_byte( hex + nn*2 );
298
299     return 0;
300 }
301
302 int
303 sms_address_to_hex    ( SmsAddress  address, char*   hex, int  hexlen )
304 {
305     int  len = (address->len + 1)/2 + 2;
306     int  nn;
307
308     if (hex == NULL)
309         hexlen = 0;
310
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 );
315     hex    += 4;
316     hexlen -= 4;
317     if ( hexlen > 2*(len - 2) )
318         hexlen = (len - 2)/2;
319
320     for ( nn = 0; nn < hexlen; nn += 2 )
321         gsm_hex_from_byte( hex+nn, address->data[nn/2] );
322
323 Exit:
324     return len*2;
325 }
326
327 static void
328 gsm_rope_add_address( GsmRope  rope, const SmsAddressRec*  addr )
329 {
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 );
333     if (addr->len & 1) {
334         if (!rope->error && rope->data != NULL)
335             rope->data[ rope->pos-1 ] |= 0xf0;
336     }
337 }
338
339
340 /** SMS PARSER
341  **/
342 static int
343 sms_get_byte( cbytes_t  *pcur, cbytes_t  end )
344 {
345     cbytes_t  cur    = *pcur;
346     int       result = -1;
347
348     if (cur < end) {
349         result = cur[0];
350         *pcur  = cur + 1;
351     }
352     return result;
353 }
354
355 /* parse a service center address, returns -1 in case of error */
356 static int
357 sms_get_sc_address( cbytes_t   *pcur,
358                     cbytes_t    end,
359                     SmsAddress  address )
360 {
361     cbytes_t  cur    = *pcur;
362     int       result = -1;
363
364     if (cur < end) {
365         int  len = cur[0];
366         int  dlen, adjust = 0;
367
368         cur += 1;
369
370         if (len == 0) {   /* empty address */
371             address->len = 0;
372             address->toa = 0x00;
373             result       = 0;
374             goto Exit;
375         }
376
377         if (cur + len > end) {
378             goto Exit;
379         }
380
381         address->toa = *cur++;
382         len         -= 1;
383         result       = 0;
384
385         for (dlen = 0; dlen < len; dlen+=1)
386         {
387             int  c = cur[dlen];
388             int  v;
389
390             adjust = 0;
391             if (dlen >= sizeof(address->data)) {
392                 result = -1;
393                 break;
394             }
395
396             v = (c & 0xf);
397             if (v >= 0xe)
398                 break;
399
400             adjust              = 1;
401             address->data[dlen] = (byte_t) c;
402
403             v = (c >> 4) & 0xf;
404             if (v >= 0xe) {
405                 break;
406             }
407         }
408         address->len = 2*dlen + adjust;
409     }
410 Exit:
411     if (!result)
412         *pcur = cur;
413
414     return result;
415 }
416
417 static int
418 sms_skip_sc_address( cbytes_t   *pcur,
419                      cbytes_t    end )
420 {
421     cbytes_t  cur    = *pcur;
422     int       result = -1;
423     int       len;
424
425     if (cur >= end)
426         goto Exit;
427
428     len  = cur[0];
429     cur += 1 + len;
430     if (cur > end)
431         goto Exit;
432
433     *pcur  = cur;
434     result = 0;
435 Exit:
436     return result;
437 }
438
439 /* parse a sender/receiver address, returns -1 in case of error */
440 static int
441 sms_get_address( cbytes_t   *pcur,
442                  cbytes_t    end,
443                  SmsAddress  address )
444 {
445     cbytes_t  cur    = *pcur;
446     int       result = -1;
447     int       len, dlen;
448
449     if (cur >= end)
450         goto Exit;
451
452     dlen = *cur++;
453
454     if (dlen == 0) {
455         address->len = 0;
456         address->toa = 0;
457         result       = 0;
458         goto Exit;
459     }
460
461     if (cur + 1 + (dlen+1)/2 > end)
462         goto Exit;
463
464     address->len = dlen;
465     address->toa = *cur++;
466
467     len = (dlen + 1)/2;
468     if (len > sizeof(address->data))
469         goto Exit;
470
471     memcpy( address->data, cur, len );
472     cur   += len;
473     result = 0;
474
475 Exit:
476     if (!result)
477         *pcur = cur;
478
479     return result;
480 }
481
482 static int
483 sms_skip_address( cbytes_t   *pcur,
484                   cbytes_t    end  )
485 {
486     cbytes_t  cur    = *pcur;
487     int       result = -1;
488     int       dlen;
489
490     if (cur + 2 > end)
491         goto Exit;
492
493     dlen = cur[0];
494     cur += 2 + (dlen + 1)/2;
495     if (cur > end)
496         goto Exit;
497
498     result = 0;
499 Exit:
500     return result;
501 }
502
503 /* parse a service center timestamp */
504 static int
505 sms_get_timestamp( cbytes_t     *pcur,
506                    cbytes_t      end,
507                    SmsTimeStamp  ts )
508 {
509     cbytes_t  cur = *pcur;
510
511     if (cur + 7 > end)
512         return -1;
513
514     memcpy( ts->data, cur, 7 );
515     *pcur = cur + 7;
516     return 0;
517 }
518
519 static int
520 sms_skip_timestamp( cbytes_t  *pcur,
521                     cbytes_t   end )
522 {
523     cbytes_t  cur = *pcur;
524
525     if (cur + 7 > end)
526         return -1;
527
528     *pcur = cur + 7;
529     return 0;
530 }
531
532
533 /** SMS PDU
534  **/
535
536 typedef struct SmsPDURec {
537     bytes_t  base;
538     bytes_t  end;
539     bytes_t  tpdu;
540 } SmsPDURec;
541
542 void
543 smspdu_free( SmsPDU  pdu )
544 {
545     if (pdu) {
546         free( pdu->base );
547         pdu->base = NULL;
548         pdu->end  = NULL;
549         pdu->tpdu = NULL;
550     }
551 }
552
553 SmsPduType
554 smspdu_get_type( SmsPDU  pdu )
555 {
556     cbytes_t  data    = pdu->tpdu;
557     cbytes_t  end     = pdu->end;
558     int       mtiByte = sms_get_byte(&data, end);
559
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;
565     }
566 }
567
568 int
569 smspdu_get_sender_address( SmsPDU  pdu, SmsAddress  address )
570 {
571     cbytes_t  data    = pdu->tpdu;
572     cbytes_t  end     = pdu->end;
573     int       mtiByte = sms_get_byte(&data, end);
574
575     switch (mtiByte & 3) {
576         case 0: /* SMS_PDU_DELIVER; */
577             return sms_get_sc_address( &data, end, address );
578
579         default: return -1;
580     }
581 }
582
583 int
584 smspdu_get_sc_timestamp( SmsPDU  pdu, SmsTimeStamp  ts )
585 {
586     cbytes_t  data    = pdu->tpdu;
587     cbytes_t  end     = pdu->end;
588     int       mtiByte = sms_get_byte( &data, end );
589
590     switch (mtiByte & 3) {
591         case 0:  /* SMS_PDU_DELIVER */
592             {
593                 SmsAddressRec  address;
594
595                 if ( sms_get_sc_address( &data, end, &address ) < 0 )
596                     return -1;
597
598                 data += 2;  /* skip protocol identifer + coding scheme */
599
600                 return sms_get_timestamp( &data, end, ts );
601             }
602
603         default: return -1;
604     }
605 }
606
607 int
608 smspdu_get_receiver_address( SmsPDU  pdu, SmsAddress  address )
609 {
610     cbytes_t  data    = pdu->tpdu;
611     cbytes_t  end     = pdu->end;
612     int       mtiByte = sms_get_byte( &data, end );
613
614     switch (mtiByte & 3) {
615         case 1:  /* SMS_PDU_SUBMIT */
616             {
617                 data += 1;  /* skip message reference */
618                 return sms_get_address( &data, end, address );
619             }
620
621         default: return -1;
622     }
623 }
624
625 typedef enum {
626     SMS_CODING_SCHEME_UNKNOWN = 0,
627     SMS_CODING_SCHEME_GSM7,
628     SMS_CODING_SCHEME_UCS2
629
630 } SmsCodingScheme;
631
632 /* see TS 23.038 Section 5 for details */
633 static SmsCodingScheme
634 sms_get_coding_scheme( cbytes_t  *pcur,
635                        cbytes_t   end )
636 {
637     cbytes_t  cur = *pcur;
638     int       dataCoding;
639
640     if (cur >= end)
641         return SMS_CODING_SCHEME_UNKNOWN;
642
643     dataCoding = *cur++;
644     *pcur      = cur;
645
646     switch (dataCoding >> 4) {
647         case 0x00:
648         case 0x02:
649         case 0x03:
650             return SMS_CODING_SCHEME_GSM7;
651
652         case 0x01:
653             if (dataCoding == 0x10) return SMS_CODING_SCHEME_GSM7;
654             if (dataCoding == 0x11) return SMS_CODING_SCHEME_UCS2;
655             break;
656
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;
661             break;
662
663         case 0xF:
664             if (!(dataCoding & 4)) return SMS_CODING_SCHEME_GSM7;
665             break;
666     }
667     return SMS_CODING_SCHEME_UNKNOWN;
668 }
669
670
671 /* see TS 23.040 section 9.2.3.24 for details */
672 static int
673 sms_get_text_utf8( cbytes_t        *pcur,
674                    cbytes_t         end,
675                    int              hasUDH,
676                    SmsCodingScheme  coding,
677                    GsmRope          rope )
678 {
679     cbytes_t  cur    = *pcur;
680     int       result = -1;
681     int       len;
682
683 #ifdef nodroid
684     printf("sms_get_text_utf8 %d %d\n",hasUDH,coding);
685 #endif
686     if (cur >= end)
687         goto Exit;
688
689     len = *cur++;
690
691     /* skip user data header if any */
692     if ( hasUDH )
693     {
694         int  hlen;
695
696         if (cur >= end)
697             goto Exit;
698
699         hlen = *cur++;
700         if (cur + hlen > end)
701             goto Exit;
702
703         cur += hlen;
704
705         if (coding == SMS_CODING_SCHEME_GSM7)
706             len -= (hlen*2-2);
707         else
708             len -= hlen+1;
709
710         if (len < 0)
711             goto Exit;
712     }
713
714     /* switch the user data header if any */
715     if (coding == SMS_CODING_SCHEME_GSM7)
716     {
717         int  count = utf8_from_gsm7( cur, 0, len, NULL );
718
719         if (rope != NULL)
720         {
721             bytes_t  dst = gsm_rope_reserve( rope, count );
722             if(hasUDH && dst)
723                 *dst++=(*cur++)>>1;
724             if (dst != NULL)
725                 utf8_from_gsm7( cur, 0, len, dst );
726         }
727         cur += (len+1)/2;
728     }
729     else if (coding == SMS_CODING_SCHEME_UCS2)
730     {
731         int  count = ucs2_to_utf8( cur, len/2, NULL );
732
733         if (rope != NULL)
734         {
735             bytes_t  dst = gsm_rope_reserve( rope, count );
736             if (dst != NULL)
737                 ucs2_to_utf8( cur, len/2, dst );
738         }
739         cur += len;
740     }
741     result = 0;
742
743 Exit:
744     if (!result)
745         *pcur = cur;
746
747     return result;
748 }
749
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 */
752 int
753 smspdu_get_text_message( SmsPDU  pdu, unsigned char*  utf8, int  utf8len )
754 {
755     cbytes_t  data    = pdu->tpdu;
756     cbytes_t  end     = pdu->end;
757     int       mtiByte = sms_get_byte( &data, end );
758
759     switch (mtiByte & 3) {
760         case 0:  /* SMS_PDU_DELIVER */
761             {
762                 SmsAddressRec    address;
763                 SmsTimeStampRec  timestamp;
764                 SmsCodingScheme  coding;
765                 GsmRopeRec       rope[1];
766                 int              result;
767
768                 if ( sms_get_sc_address( &data, end, &address ) < 0 )
769                     goto Fail;
770
771                 data  += 1;  /* skip protocol identifier */
772                 coding = sms_get_coding_scheme( &data, end );
773                 if (coding == SMS_CODING_SCHEME_UNKNOWN)
774                     goto Fail;
775
776                 if ( sms_get_timestamp( &data, end, &timestamp ) < 0 )
777                     goto Fail;
778
779                 gsm_rope_init_alloc( rope, 0 );
780                 if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 )
781                     goto Fail;
782
783                 result = rope->pos;
784                 if (utf8len > result)
785                     utf8len = result;
786
787                 if (utf8len > 0)
788                     memcpy( utf8, rope->data, utf8len );
789
790                 gsm_rope_done( rope );
791                 return result;
792             }
793
794         case 1:  /* SMS_PDU_SUBMIT */
795             {
796                 SmsAddressRec    address;
797                 SmsCodingScheme  coding;
798                 GsmRopeRec       rope[1];
799                 int              result;
800
801                 data += 1;  /* message reference */
802
803                 if ( sms_get_address( &data, end, &address ) < 0 )
804                     goto Fail;
805
806                 data  += 1;  /* skip protocol identifier */
807                 coding = sms_get_coding_scheme( &data, end );
808                 if (coding == SMS_CODING_SCHEME_UNKNOWN)
809                     goto Fail;
810
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 );
814                     goto Fail;
815                 }
816
817                 result = rope->pos;
818                 if (utf8len > result)
819                     utf8len = result;
820
821                 if (utf8len > 0)
822                     memcpy( utf8, rope->data, utf8len );
823
824                 gsm_rope_done( rope );
825                 return result;
826             }
827     }
828 Fail:
829     return -1;
830 }
831
832
833 static void
834 gsm_rope_add_sms_user_header( GsmRope  rope,
835                               int      ref_number,
836                               int      pdu_count,
837                               int      pdu_index )
838 {
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 */
845 }
846
847 /* write a SMS-DELIVER PDU into a rope */
848 static void
849 gsm_rope_add_sms_deliver_pdu( GsmRope                 rope,
850                               cbytes_t                utf8,
851                               int                     utf8len,
852                               int                     use_gsm7,
853                               const SmsAddressRec*    sender_address,
854                               const SmsTimeStampRec*  timestamp,
855                               int                     ref_num,
856                               int                     pdu_count,
857                               int                     pdu_index)
858 {
859     int  count;
860     int  coding;
861     int  mtiByte  = 0x20;  /* message type - SMS DELIVER */
862
863     if (pdu_count > 1)
864         mtiByte |= 0x40;  /* user data header indicator */
865
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 */
870
871     /* data coding scheme - GSM 7 bits / no class - or - 16-bit UCS2 / class 1 */
872     coding = (use_gsm7 ? 0x00 : 0x09);
873
874     gsm_rope_add_c( rope, coding );               /* data coding scheme       */
875     gsm_rope_add_timestamp( rope, timestamp );    /* service center timestamp */
876
877     if (use_gsm7) {
878         bytes_t  dst;
879         int    count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
880         int    pad   = 0;
881
882         //assert( count <= MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE );
883
884         if (pdu_count > 1)
885         {
886             int  headerBits    = 6*8;  /* 6 is size of header in bytes */
887             int  headerSeptets = headerBits / 7;
888             if (headerBits % 7 > 0)
889                 headerSeptets += 1;
890
891             pad = headerSeptets*7 - headerBits;
892
893             gsm_rope_add_c( rope, count + headerSeptets );
894             gsm_rope_add_sms_user_header(rope, ref_num, pdu_count, pdu_index);
895         }
896         else
897             gsm_rope_add_c( rope, count );
898
899         count = (count*7+pad+7)/8;  /* convert to byte count */
900
901         dst = gsm_rope_reserve( rope, count );
902         if (dst != NULL) {
903             utf8_to_gsm7( utf8, utf8len, dst, pad );
904         }
905     } else {
906         bytes_t  dst;
907         int      count = utf8_to_ucs2( utf8, utf8len, NULL );
908
909         //assert( count*2 <= MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE );
910
911         if (pdu_count > 1)
912         {
913             gsm_rope_add_c( rope, count*2 + 6 );
914             gsm_rope_add_sms_user_header( rope, ref_num, pdu_count, pdu_index );
915         }
916         else
917             gsm_rope_add_c( rope, count*2 );
918
919         gsm_rope_add_c( rope, count*2 );
920         dst = gsm_rope_reserve( rope, count*2 );
921         if (dst != NULL) {
922             utf8_to_ucs2( utf8, utf8len, dst );
923         }
924     }
925 }
926
927
928 static SmsPDU
929 smspdu_create_deliver( cbytes_t               utf8,
930                        int                    utf8len,
931                        int                    use_gsm7,
932                        const SmsAddressRec*   sender_address,
933                        const SmsTimeStampRec* timestamp,
934                        int                    ref_num,
935                        int                    pdu_count,
936                        int                    pdu_index )
937 {
938     SmsPDU      p;
939     GsmRopeRec  rope[1];
940     int         size;
941
942     p = calloc( sizeof(*p), 1 );
943     if (!p) goto Exit;
944
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);
949     if (rope->error)
950         goto Fail;
951
952     gsm_rope_init_alloc( rope, rope->pos );
953
954     gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
955                                  sender_address, timestamp,
956                                  ref_num, pdu_count, pdu_index );
957
958     p->base = gsm_rope_done_acquire( rope, &size );
959     if (p->base == NULL)
960         goto Fail;
961
962     p->end  = p->base + size;
963     p->tpdu = p->base + 1;
964 Exit:
965     return p;
966
967 Fail:
968     free(p);
969     return NULL;
970 }
971
972
973 void
974 smspdu_free_list( SmsPDU*  pdus )
975 {
976     if (pdus) {
977         int  nn;
978         for (nn = 0; pdus[nn] != NULL; nn++)
979             smspdu_free( pdus[nn] );
980
981         free( pdus );
982     }
983 }
984
985
986
987 SmsPDU*
988 smspdu_create_deliver_utf8( const unsigned char*   utf8,
989                             int                    utf8len,
990                             const SmsAddressRec*   sender_address,
991                             const SmsTimeStampRec* timestamp )
992 {
993     SmsTimeStampRec  ts0;
994     int              use_gsm7;
995     int              count, block;
996     int              num_pdus = 0;
997     int              leftover = 0;
998     SmsPDU*          list = NULL;
999
1000     static unsigned char  ref_num = 0;
1001
1002     if (timestamp == NULL) {
1003         sms_timestamp_now( &ts0 );
1004         timestamp = &ts0;
1005     }
1006
1007     /* can we encode the message with the GSM 7-bit alphabet ? */
1008     use_gsm7 = utf8_check_gsm7( utf8, utf8len );
1009
1010     /* count the number of SMS PDUs we'll need */
1011     block = MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE;
1012
1013     if (use_gsm7) {
1014         count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
1015     } else {
1016         count = utf8_to_ucs2( utf8, utf8len, NULL );
1017         block = MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE;
1018     }
1019
1020     num_pdus = count / block;
1021     leftover = count - num_pdus*block;
1022     if (leftover > 0)
1023         num_pdus += 1;
1024
1025     list = calloc( sizeof(SmsPDU*), count + 1 );
1026     if (list == NULL)
1027         return NULL;
1028
1029     /* now create each SMS PDU */
1030     {
1031         cbytes_t   src     = utf8;
1032         cbytes_t   src_end = utf8 + utf8len;
1033         int        nn;
1034
1035         for (nn = 0; nn < num_pdus; nn++)
1036         {
1037             int       skip = block;
1038             cbytes_t  src_next;
1039
1040             if (leftover > 0 && nn == num_pdus-1)
1041                 skip = leftover;
1042
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)
1047                 goto Fail;
1048
1049             src = src_next;
1050         }
1051     }
1052
1053     ref_num++;
1054
1055 Exit:
1056     return list;
1057
1058 Fail:
1059     smspdu_free_list(list);
1060     return NULL;
1061 }
1062
1063
1064 SmsPDU
1065 smspdu_create_from_hex( const char*  hex, int  hexlen )
1066 {
1067     SmsPDU    p;
1068     cbytes_t  data;
1069
1070     p = calloc( sizeof(*p), 1 );
1071     if (!p) goto Exit;
1072
1073     p->base = malloc( (hexlen+1)/2 );
1074     if (p->base == NULL) {
1075         free(p);
1076         p = NULL;
1077         goto Exit;
1078     }
1079
1080     gsm_hex_to_bytes( hex, hexlen, p->base );
1081     p->end = p->base + (hexlen+1)/2;
1082
1083     data = p->base;
1084     if ( sms_skip_sc_address( &data, p->end ) < 0 )
1085         goto Fail;
1086
1087     p->tpdu = (bytes_t) data;
1088
1089 Exit:
1090     return p;
1091
1092 Fail:
1093     free(p->base);
1094     free(p);
1095     return NULL;
1096 }
1097
1098 int
1099 smspdu_to_hex( SmsPDU  pdu, char*  hex, int  hexlen )
1100 {
1101     int  result = (pdu->end - pdu->base)*2;
1102     int  nn;
1103
1104     if (hexlen > result)
1105         hexlen = result;
1106
1107     for (nn = 0; nn*2 < hexlen; nn++) {
1108         gsm_hex_from_byte( &hex[nn*2], pdu->base[nn] );
1109     }
1110     return result;
1111 }
1112