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, &timestamp ) < 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