Branch update for mygnokii2002_03_17_19_29nl
[gnokii.git] / common / gsm-sms.c
1 /*
2
3   G N O K I I
4
5   A Linux/Unix toolset and driver for Nokia mobile phones.
6
7   Released under the terms of the GNU GPL, see file COPYING for more details.
8         
9 */
10
11 #include "config.h"
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <time.h>
18
19 #include "gsm-api.h"
20 #include "gsm-coding.h"
21
22 /* User data headers */
23 static GSM_UDHHeader UDHHeaders[] = {
24   { GSM_ConcatenatedMessages, 0x05, "\x00\x03\x01\x00\x00" },
25       /* See GSM 03.40 section 9.2.3.24.1 */
26   { GSM_DisableVoice,         0x04, "\x01\x02\x00\x00" },
27   { GSM_DisableFax,           0x04, "\x01\x02\x01\x00" },
28   { GSM_DisableEmail,         0x04, "\x01\x02\x02\x00" },
29   { GSM_EnableVoice,          0x04, "\x01\x02\x00\x01" },
30   { GSM_EnableFax,            0x04, "\x01\x02\x01\x01" },
31   { GSM_EnableEmail,          0x04, "\x01\x02\x02\x01" },
32       /* See GSM 03.40 section 9.2.3.24.2 for voice, fax and email messages */
33   { GSM_VoidSMS,              0x08, "\x01\x02\x02\x01\x01\x02\x02\x00" },
34       /* When send such SMS to some phones, they don't display anything,
35          only beep and enable vibra/light */
36   { GSM_BugSMS,               0x09, "\x01\x02\x02\x01\x01\x02\x02\x00\xc0" },
37
38   /* Short Smart Messaging UDH */
39   /* General format: */
40   /* 1 byte  0x05     : IEI application port addressing scheme, 16 bit address */
41   /* 1 byte  0x04     : IEI length */
42   /* 2 bytes          : destination address: high & low byte */
43   /* 2 bytes 0x00 0x00: originator address: high & low byte */
44   { GSM_RingtoneUDH,          0x06, "\x05\x04\x15\x81\x00\x00" },
45   { GSM_OpLogo,               0x06, "\x05\x04\x15\x82\x00\x00" },
46   { GSM_CallerIDLogo,         0x06, "\x05\x04\x15\x83\x00\x00" },
47   { GSM_WAPBookmarkUDH,       0x06, "\x05\x04\xc3\x4f\x00\x00" },
48
49   /* Long Smart Messaging UDH */
50   /* General format: */
51   /* 1 byte 0x05      : IEI application port addressing scheme, 16 bit address */
52   /* 1 byte 0x04      : IEI length */
53   /* 2 bytes 0x00 0x00: destination address: high & low byte */
54   /* 2 bytes 0x00 0x00: originator address: high & low byte */
55   /* 1 byte 0x00      : null byte for end first part ? */
56   /* 1 byte 0x03      : length for rest ? */
57   /* 1 byte                               */
58   /* 1 byte           : number of all SMS */
59   /* 1 byte           : number of current SMS */
60   { GSM_CalendarNoteUDH,      0x0b, "\x05\x04\x00\xe4\x00\x00\x00\x03\xc7\x00\x00" }, //from 3310 ?
61   { GSM_CalendarNoteUDH2,     0x0b, "\x05\x04\x23\xf5\x00\x00\x00\x03\x01\x00\x00" }, //from 6210 or 9210 Note: last 0x01 changes according to note type
62   { GSM_ProfileUDH,           0x0b, "\x05\x04\x15\x8a\x00\x00\x00\x03\xce\x00\x00" },
63   { GSM_WAPBookmarkUDH,       0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x01\x00\x00" },//note last 0x01 can change
64   { GSM_WAPSettingsUDH,       0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x7f\x00\x00" },
65   { GSM_PhonebookUDH,         0x0b, "\x05\x04\x23\xf4\x00\x00\x00\x03\x01\x00\x00" },
66
67   { GSM_NoUDH,                0x00, "" }
68 };
69
70 #define ByteMask ((1 << Bits) - 1)
71
72 #ifndef UCLINUX
73 static int GSM_PackSevenBitsToEight(int offset, unsigned char *input, unsigned char *output)
74 {
75         unsigned char *OUT = output; /* Current pointer to the output buffer */
76         unsigned char *IN  = input;  /* Current pointer to the input buffer */
77         int Bits;                    /* Number of bits directly copied to
78                                         the output buffer */
79         Bits = (7 + offset) % 8;
80
81         /* If we don't begin with 0th bit, we will write only a part of the
82            first octet */
83         if (offset) {
84                 *OUT = 0x00;
85                 OUT++;
86         }
87
88         while ((IN - input) < strlen(input)) {
89                 unsigned char Byte = EncodeWithDefaultAlphabet(*IN);
90
91                 *OUT = Byte >> (7 - Bits);
92                 /* If we don't write at 0th bit of the octet, we should write
93                    a second part of the previous octet */
94                 if (Bits != 7)
95                         *(OUT-1) |= (Byte & ((1 << (7-Bits)) - 1)) << (Bits+1);
96
97                 Bits--;
98
99                 if (Bits == -1) Bits = 7;
100                 else OUT++;
101
102                 IN++;
103         }
104         return (OUT - output);
105 }
106
107 int GSM_UnpackEightBitsToSeven(int offset, int in_length, int out_length,
108                            unsigned char *input, unsigned char *output)
109 {
110         unsigned char *OUT = output; /* Current pointer to the output buffer */
111         unsigned char *IN  = input;  /* Current pointer to the input buffer */
112         unsigned char Rest = 0x00;
113         int Bits;
114
115         Bits = offset ? offset : 7;
116
117         while ((IN - input) < in_length) {
118
119                 *OUT = ((*IN & ByteMask) << (7 - Bits)) | Rest;
120                 Rest = *IN >> Bits;
121
122                 /* If we don't start from 0th bit, we shouldn't go to the
123                    next char. Under *OUT we have now 0 and under Rest -
124                    _first_ part of the char. */
125                 if ((IN != input) || (Bits == 7)) OUT++;
126                 IN++;
127
128                 if ((OUT - output) >= out_length) break;
129
130                 /* After reading 7 octets we have read 7 full characters but
131                    we have 7 bits as well. This is the next character */
132                 if (Bits == 1) {
133                         *OUT = Rest;
134                         OUT++;
135                         Bits = 7;
136                         Rest = 0x00;
137                 } else {
138                         Bits--;
139                 }
140         }
141
142         return OUT - output;
143 }
144
145 /* This function implements packing of numbers (SMS Center number and
146    destination number) for SMS sending function. */
147 /* See GSM 03.40 9.1.1:
148    1 byte  - length of number given in semioctets or bytes (when given in bytes,
149              includes one byte for byte with number format.
150              Returned by function (set semioctet to true, if want result
151              in semioctets).
152    1 byte  - format of number. See GSM_NumberType; in gsm-common.h. Returned
153              in u8 *Output.
154    n bytes - 2n or 2n-1 semioctets with number. For some types of numbers
155              in the most significant bits of the last byte with 0x0f
156              (1111 binary) are filled if the number is represented
157              with odd number of digits. Returned in u8 *Output. */
158 /* 1 semioctet = 4 bits = half of byte */
159 int GSM_PackSemiOctetNumber(u8 *Number, u8 *Output, bool semioctet) {
160
161   u8 *IN=Number;  /* Pointer to the input number */
162   u8 *OUT=Output; /* Pointer to the output */
163   int length=0,j;
164   unsigned char format=GNT_UNKNOWN; // format of number used by us
165
166   /* Checking for format number */
167   while (*IN) {
168     if (length==0 && *IN=='+')
169       format=GNT_INTERNATIONAL;  // first byte is '+'. It can be international
170     else {
171       if (*IN>'9' || *IN<'0') { 
172         format=GNT_ALPHANUMERIC; //char is not number. It must be alphanumeric
173       }
174     }
175     IN++;length++;
176   }
177
178   /* Now we return to first byte */
179   for (j=0;j<length;j++) IN--;
180
181   /* The first byte in the Semi-octet representation of the address field is
182      the Type-of-Address. This field is described in the official GSM
183      specification 03.40 version 5.3.0, section 9.1.2.5, page 33.*/
184   *OUT++=format;
185
186   /* The next field is the number. See GSM 03.40 section 9.1.2 */
187   switch (format) {
188     case GNT_ALPHANUMERIC:
189       length=GSM_PackSevenBitsToEight(0, IN, OUT)*2;
190       break;
191
192     case GNT_INTERNATIONAL:
193       length--;
194       EncodeBCD (OUT, IN+1, length, true);
195       break;
196
197     default:
198       EncodeBCD (OUT, IN, length, true);
199       break;
200   }
201
202   if (semioctet) {
203     return length;
204   } else {
205     /* Convert number of semioctets to number of chars */
206     if (length % 2) length++;
207     return length / 2 + 1;
208   }
209 }
210
211 char *GSM_UnpackSemiOctetNumber(u8 *Number, bool semioctet) {
212
213   static char Buffer[20]="";  
214   int length=Number[0];
215
216   if (semioctet) {
217     /* Convert number of semioctets to number of chars */
218     if (length % 2) length++;
219     length=length / 2 + 1;
220   }
221   
222   length--; //without leading byte with format of number
223
224   switch (Number[1]) {
225     case GNT_ALPHANUMERIC:
226       GSM_UnpackEightBitsToSeven(0, length, length, Number+2, Buffer);
227       Buffer[length]=0;
228       break;
229
230     case GNT_INTERNATIONAL:
231       Buffer[0]='+';
232       DecodeBCD (Buffer+1, Number+2, length);
233       break;
234
235     default:
236       DecodeBCD (Buffer, Number+2, length);
237       break;
238   }
239
240   return Buffer;
241 }
242
243 /* See GSM 03.40 section 9.2.3.11 */
244 GSM_Error GSM_EncodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
245 {
246 #ifdef DEBUG
247   fprintf(stdout,_("Date & time in saved SMS: %02i/%02i/%04i %02i:%02i:%02i\n"),
248     DT->Day,DT->Month,DT->Year,DT->Hour,DT->Minute,DT->Second);
249 #endif
250   
251   req[0]=EncodeWithBCDAlphabet(DT->Year);
252   req[1]=EncodeWithBCDAlphabet(DT->Month);
253   req[2]=EncodeWithBCDAlphabet(DT->Day);
254   req[3]=EncodeWithBCDAlphabet(DT->Hour);
255   req[4]=EncodeWithBCDAlphabet(DT->Minute);
256   req[5]=EncodeWithBCDAlphabet(DT->Second);
257
258   /* FIXME: do it */
259   req[6]=0; /* TimeZone = +-0 */
260
261   return GE_NONE;
262 }
263
264 /* See GSM 03.40 section 9.2.3.11 */
265 static GSM_Error GSM_DecodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
266 {
267   DT->Year    = DecodeWithBCDAlphabet(req[0]);
268   DT->Month   = DecodeWithBCDAlphabet(req[1]);
269   DT->Day     = DecodeWithBCDAlphabet(req[2]);
270   DT->Hour    = DecodeWithBCDAlphabet(req[3]);
271   DT->Minute  = DecodeWithBCDAlphabet(req[4]);
272   DT->Second  = DecodeWithBCDAlphabet(req[5]);
273
274   DT->Timezone=(10*(req[6]&0x07)+(req[6]>>4))/4;
275   if (req[6]&0x08) DT->Timezone = -DT->Timezone;
276
277 #ifdef DEBUG
278   fprintf(stdout, _("%d%d-"), req[0]&0xf, req[0]>>4);
279   fprintf(stdout, _("%d%d-"), req[1]&0xf, req[1]>>4);
280   fprintf(stdout, _("%d%d "), req[2]&0xf, req[2]>>4);
281   fprintf(stdout, _("%d%d:"), req[3]&0xf, req[3]>>4);
282   fprintf(stdout, _("%d%d:"), req[4]&0xf, req[4]>>4);
283   fprintf(stdout, _("%d%d "), req[5]&0xf, req[5]>>4);
284
285   if (req[6]) {
286     if (req[6] & 0x08) fprintf(stdout, "-");
287                   else fprintf(stdout, "+");
288
289     fprintf(stdout, _("%02d00"), (10*(req[6]&0x07)+(req[6]>>4))/4);
290   }
291
292   fprintf(stdout, "\n");
293 #endif
294
295   return GE_NONE;
296 }
297
298 static int GSM_EncodeETSISMSSubmitData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
299 {
300   int off,size=0,size2=0,w,i;
301
302   /* off - length of the user data header */
303   off = 0;
304
305   if (SMS->UDHType) {
306     /* GSM 03.40 section 9.2.3.23 (TP-User-Data-Header-Indicator) */
307     ETSI->firstbyte |= 0x40;
308
309     /* off - length of the user data header */
310     off = 1 + SMS->UDH[0];
311
312     /* we copy the udh */
313     memcpy(ETSI->MessageText, SMS->UDH, off);
314
315 //  if (SMS->UDHType==GSM_HangSMS) ETSI->TPDCS=0x08; //Such SMS hangs phone (5110, 5.07),
316                                                      //when saved to Outbox atry try to read it
317                                                      /*from phone's menu*/
318   }
319
320   switch (SMS->Coding) {
321     /* When save SMS to SIM and it's 8 bit SMS,
322        "Edit" is not displayed in phone menu and
323        "Message cannot be displayed here" instead of message text */
324     case GSM_Coding_8bit:
325
326       /* the mask for the 8-bit data */
327       /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
328       ETSI->TPDCS |= 0xf4;
329     
330       memcpy(ETSI->MessageText + off, SMS->MessageText, SMS->Length);
331
332       size2 = size = SMS->Length+off;
333
334       break;
335
336     case GSM_Coding_Default:
337
338       w=(7-off)%7;
339       if (w<0) w=(14-off)%14;
340
341       size = GSM_PackSevenBitsToEight(w, SMS->MessageText, ETSI->MessageText + off);
342       size += off;
343       size2 = (off*8 + w) / 7 + strlen(SMS->MessageText);
344
345       break;
346
347     case GSM_Coding_Unicode:
348
349       /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
350       ETSI->TPDCS |= 0x08;
351
352 #ifdef DEBUG
353       fprintf(stdout,_("SMS Length is %ld\n"),(long)strlen(SMS->MessageText));
354 #endif
355
356       EncodeUnicode (ETSI->MessageText+off,SMS->MessageText,strlen(SMS->MessageText));
357       /* here we code "special" chars */
358       for (i=0;i<strlen(SMS->MessageText);i++) {
359         if (SMS->MessageText[i]=='~') ETSI->MessageText[off+1+i*2]=1; //enables/disables blinking
360         if (SMS->MessageText[i]=='`') ETSI->MessageText[off+1+i*2]=0; //hides rest ot contents
361       }
362
363       size=size2=strlen(SMS->MessageText)*2+off;
364
365       break;
366   }
367
368   /* FIXME: support for compression */
369   /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
370   /* See also GSM 03.42 */
371   /* if (SMS->Compression) ETSI->TPDCS |= 0x20; */
372
373   /* SMS->Length is:
374         - integer representation of the number od octets within the user data when UD is coded using 8bit data
375         - the sum of the number of septets in UDH including any padding and number of septets in UD in other case */
376   /* GSM 03.40 section 9.2.3.16 (TP-User-Data-Length) */
377   ETSI->TPUDL = size2;
378
379   return size;
380 }
381
382 GSM_Error GSM_DecodeETSISMSSubmitData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
383 {
384   int off,w,i,tmp=0;
385   unsigned char output[161];
386   bool UDHOK;
387
388   /* off - length of the user data header */
389   off = 0;
390   
391   SMS->UDHType = GSM_NoUDH;
392
393   if (ETSI->firstbyte & 64) { /* UDH header available */
394
395     off = (ETSI->MessageText[0] + 1); /* Length of UDH header */
396     
397     /* Copy UDH header into SMS->UDH */
398     for (i = 0; i < off; i++) SMS->UDH[i] = ETSI->MessageText[i];
399
400 #ifdef DEBUG
401     fprintf(stdout, "   UDH header available (length %i",off);
402 #endif
403     
404     SMS->UDHType = GSM_UnknownUDH;
405
406     i=-1;
407     while (true) {
408       i++;
409       if (UDHHeaders[i].UDHType==GSM_NoUDH) break;
410       tmp=UDHHeaders[i].Length;
411       if (tmp==SMS->UDH[0]) { //if length is the same
412
413         if (tmp==0x05) tmp=tmp-2;/*two last bytes can be different for such UDH*/
414         if (tmp==0x0b) tmp=tmp-3;/*three last bytes can be different for such UDH*/
415
416         UDHOK=true;
417         for (w=0;w<tmp;w++) {
418           if (UDHHeaders[i].Text[w]!=SMS->UDH[w+1]) {
419             UDHOK=false;
420             break;
421           }
422         }
423         if (UDHOK) {
424           SMS->UDHType=UDHHeaders[i].UDHType;
425           break;
426         }
427       }
428     }
429
430 #ifdef DEBUG
431     switch (SMS->UDHType) {
432       case GSM_ConcatenatedMessages:
433         fprintf(stdout,_(", concatenated (linked) message %d/%d"),SMS->UDH[5],SMS->UDH[4]);break;
434       case GSM_DisableVoice:
435         fprintf(stdout,_(", disables voice indicator"));break;
436       case GSM_EnableVoice:
437         fprintf(stdout,_(", enables voice indicator"));break;
438       case GSM_DisableFax:
439         fprintf(stdout,_(", disables fax indicator"));break;
440       case GSM_EnableFax:
441         fprintf(stdout,_(", enables fax indicator"));break;
442       case GSM_DisableEmail:
443         fprintf(stdout,_(", disables email indicator"));break;
444       case GSM_EnableEmail:
445         fprintf(stdout,_(", enables email indicator"));break;
446       case GSM_VoidSMS:
447         fprintf(stdout,_(", void SMS"));break;
448       case GSM_WAPBookmarkUDH:
449         fprintf(stdout,_(", WAP Bookmark"));break;
450       case GSM_WAPBookmarkUDHLong:
451         fprintf(stdout,_(", WAP Bookmark, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
452       case GSM_WAPSettingsUDH:
453         fprintf(stdout,_(", WAP Settings, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
454       case GSM_RingtoneUDH:
455         fprintf(stdout,_(", ringtone"));break;
456       case GSM_OpLogo:
457         fprintf(stdout,_(", GSM Operator Logo"));break;
458       case GSM_CallerIDLogo:
459         fprintf(stdout,_(", Caller Logo"));break;
460       case GSM_ProfileUDH:
461         fprintf(stdout,_(", Profile SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
462       case GSM_CalendarNoteUDH:
463         fprintf(stdout,_(", Calendar note SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
464       case GSM_CalendarNoteUDH2:
465         fprintf(stdout,_(", Calendar note SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
466       case GSM_PhonebookUDH:
467         fprintf(stdout,_(", Phonebook Entry, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
468       default:
469         fprintf(stdout,_(", UNKNOWN"));break;
470     }
471                
472     fprintf(stdout, ")\n");
473
474     hexdump(off,SMS->UDH);
475 #endif
476   }
477
478   SMS->Coding = GSM_Coding_Default;
479
480   /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
481   if ((ETSI->TPDCS & 0xf4) == 0xf4) SMS->Coding=GSM_Coding_8bit;
482   if ((ETSI->TPDCS & 0x08) == 0x08) SMS->Coding=GSM_Coding_Unicode;
483
484   switch (SMS->Coding) {
485     case GSM_Coding_Default:
486       w=(7-off)%7;
487       if (w<0) w=(14-off)%14;
488   
489       SMS->Length=ETSI->TPUDL - (off*8 + w) / 7;
490
491       tmp=GSM_UnpackEightBitsToSeven(w,ETSI->TPUDL-off, SMS->Length, ETSI->MessageText+off, output);
492
493 #ifdef DEBUG
494       fprintf(stdout, "   7 bit SMS, body is (length %i): ",SMS->Length);
495 #endif /* DEBUG */
496
497       DecodeDefault (SMS->MessageText, output, SMS->Length);
498
499 #ifdef DEBUG
500       fprintf(stdout, "%s\n",SMS->MessageText);   
501 #endif
502
503       break;
504     case GSM_Coding_8bit:
505       SMS->Length=ETSI->TPUDL - off;
506
507       memcpy(SMS->MessageText,ETSI->MessageText+off,SMS->Length);
508
509 #ifdef DEBUG
510       fprintf(stdout, "   8 bit SMS, body is (length %i)\n",SMS->Length);
511       hexdump(SMS->Length,SMS->MessageText);
512 #endif /* DEBUG */
513
514       break;
515     case GSM_Coding_Unicode:
516       SMS->Length=(ETSI->TPUDL - off) / 2;
517
518 #ifdef DEBUG
519       fprintf(stdout, "   7 bit SMS, body is (length %i), Unicode coding: ",SMS->Length);
520       for (i=0; i<SMS->Length;i++) {
521         fprintf(stdout, "[%02x %02x]", ETSI->MessageText[off+i*2] , ETSI->MessageText[off+i*2+1]);
522       }
523       fprintf(stdout, "\n");              
524 #endif /* DEBUG */
525
526       /* here we decode "special" chars */
527       for (i=0; i<SMS->Length;i++) {
528         if (ETSI->MessageText[off+i*2]  ==0x00 &&
529             ETSI->MessageText[off+i*2+1]==0x01)
530           ETSI->MessageText[off+i*2+1]='~'; //enables/disables blinking
531         if (ETSI->MessageText[off+i*2]  ==0x00 &&
532             ETSI->MessageText[off+i*2+1]==0x00)
533           ETSI->MessageText[off+i*2+1]='`'; //hides rest ot contents
534       }
535
536       DecodeUnicode (SMS->MessageText, ETSI->MessageText+off, SMS->Length);
537
538       break;
539   }
540
541   return GE_NONE;
542 }
543
544 GSM_Error GSM_DecodeETSISMSStatusReportData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
545 {
546   /* See GSM 03.40 section 9.2.3.11 (TP-Service-Centre-Time-Stamp) */
547 #ifdef DEBUG
548   fprintf(stdout, _("   SMSC response date: "));
549 #endif
550   GSM_DecodeSMSDateTime(&SMS->SMSCTime, ETSI->SMSCDateTime);
551     
552   if (ETSI->TPStatus < 0x03) {
553     strcpy(SMS->MessageText,_("Delivered"));
554
555 #ifdef DEBUG
556     /* more detailed reason only for debug */
557     /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
558     switch (ETSI->TPStatus) {
559       case 0x00: fprintf(stdout, _("   SM received by the SME"));break;
560       case 0x01: fprintf(stdout, _("   SM forwarded by the SC to the SME but the SC is unable to confirm delivery"));break;
561       case 0x02: fprintf(stdout, _("   SM replaced by the SC"));break;
562     }
563 #endif /* DEBUG */
564
565     SMS->Length = 10;
566       
567   } else if (ETSI->TPStatus & 0x40) {
568
569     strcpy(SMS->MessageText,_("Failed"));
570
571 #ifdef DEBUG
572     /* more detailed reason only for debug */
573     if (ETSI->TPStatus & 0x20) {
574
575       /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
576       fprintf(stdout, _("   Temporary error, SC is not making any more transfer attempts\n"));
577       switch (ETSI->TPStatus) {
578         case 0x60: fprintf(stdout, _("   Congestion"));break;
579         case 0x61: fprintf(stdout, _("   SME busy"));break;
580         case 0x62: fprintf(stdout, _("   No response from SME"));break;
581         case 0x63: fprintf(stdout, _("   Service rejected"));break;
582         case 0x64: fprintf(stdout, _("   Quality of service not available"));break;
583         case 0x65: fprintf(stdout, _("   Error in SME"));break;
584         default  : fprintf(stdout, _("   Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
585       }
586
587     } else {
588
589       /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
590       fprintf(stdout, _("   Permanent error, SC is not making any more transfer attempts\n"));
591       switch (ETSI->TPStatus) {
592         case 0x40: fprintf(stdout, _("   Remote procedure error"));break;
593         case 0x41: fprintf(stdout, _("   Incompatibile destination"));break;
594         case 0x42: fprintf(stdout, _("   Connection rejected by SME"));break;
595         case 0x43: fprintf(stdout, _("   Not obtainable"));break;
596         case 0x44: fprintf(stdout, _("   Quality of service not available"));break;
597         case 0x45: fprintf(stdout, _("   No internetworking available"));break;
598         case 0x46: fprintf(stdout, _("   SM Validity Period Expired"));break;
599         case 0x47: fprintf(stdout, _("   SM deleted by originating SME"));break;
600         case 0x48: fprintf(stdout, _("   SM Deleted by SC Administration"));break;
601         case 0x49: fprintf(stdout, _("   SM does not exist"));break;
602         default  : fprintf(stdout, _("   Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
603       }
604     }
605 #endif /* DEBUG */
606       
607       SMS->Length = 6;
608   } else if (ETSI->TPStatus & 0x20) {
609     strcpy(SMS->MessageText,_("Pending"));
610
611 #ifdef DEBUG
612     /* more detailed reason only for debug */
613     /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
614     fprintf(stdout, _("   Temporary error, SC still trying to transfer SM\n"));
615     switch (ETSI->TPStatus) {
616       case 0x20: fprintf(stdout, _("   Congestion"));break;
617       case 0x21: fprintf(stdout, _("   SME busy"));break;
618       case 0x22: fprintf(stdout, _("   No response from SME"));break;
619       case 0x23: fprintf(stdout, _("   Service rejected"));break;
620       case 0x24: fprintf(stdout, _("   Quality of service not aviable"));break;
621       case 0x25: fprintf(stdout, _("   Error in SME"));break;
622       default  : fprintf(stdout, _("   Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
623     }
624 #endif /* DEBUG */
625     SMS->Length = 7;
626   } else {
627     strcpy(SMS->MessageText,_("Unknown"));
628
629 #ifdef DEBUG
630     /* more detailed reason only for debug */
631     fprintf(stdout, _("   Reserved/Specific to SC: %x"),ETSI->TPStatus);
632 #endif /* DEBUG */
633     SMS->Length = 8;
634   }
635
636 #ifdef DEBUG
637   fprintf(stdout, _("\n"));
638 #endif /* DEBUG */
639
640   return GE_NONE;
641 }
642
643 GSM_Error GSM_EncodeETSISMSSubmitHeader(GSM_SMSMessage *SMS,GSM_ETSISMSMessage *ETSI)
644 {
645   GSM_Error error;
646
647   /* First of all we should get SMSC number */
648   if (SMS->MessageCenter.No) {
649     error = GSM->GetSMSCenter(&SMS->MessageCenter);
650     if (error != GE_NONE) return error;
651     SMS->MessageCenter.No = 0;
652   }
653
654 #ifdef DEBUG
655   fprintf(stdout, _("Packing SMS to \"%s\" via message center \"%s\"\n"), SMS->Destination, SMS->MessageCenter.Number);
656 #endif /* DEBUG */
657
658   ETSI->SMSCNumber[0]=GSM_PackSemiOctetNumber(SMS->MessageCenter.Number, ETSI->SMSCNumber+1, false);
659
660   /* GSM 03.40 section 9.2.3.17 (TP-Reply-Path) */
661   if (SMS->ReplyViaSameSMSC) ETSI->firstbyte |= 128;
662   
663   /* When save to Outbox with SMS Class, "Edit" is not displayed in phone menu
664      and can forward it to another phone with set class (for example, 0=Flash) */
665   /* Message Class*/
666   /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
667   if (SMS->Class>=0 && SMS->Class<5) ETSI->TPDCS |= (240+SMS->Class);  
668
669   /* When is not set for SMS saved for Inbox, phone displays "Message" instead
670      of number and doesn't display "Details" info */
671   ETSI->Number[0] = GSM_PackSemiOctetNumber(SMS->Destination, ETSI->Number+1, true);
672
673   return GE_NONE;
674 }
675
676 GSM_Error GSM_DecodeETSISMSHeader(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
677 {
678 #ifdef DEBUG
679   fprintf(stdout, _("   SMS center number: %s"), GSM_UnpackSemiOctetNumber(ETSI->SMSCNumber,false));
680   if (SMS->folder==0 && (ETSI->firstbyte & 128)!=0) //GST_INBOX
681     fprintf(stdout, _(" (centre set for reply)"));
682 #endif
683
684   strcpy(SMS->MessageCenter.Number, GSM_UnpackSemiOctetNumber(ETSI->SMSCNumber,false));
685
686   SMS->ReplyViaSameSMSC=false;
687   if ((ETSI->firstbyte & 128)!=0) SMS->ReplyViaSameSMSC=true;
688
689 #ifdef DEBUG      
690   fprintf(stdout, _("\n   Remote number (recipient or sender): %s\n"), GSM_UnpackSemiOctetNumber(ETSI->Number,true));
691 #endif
692
693   strcpy(SMS->Sender, GSM_UnpackSemiOctetNumber(ETSI->Number,true));
694
695   return GE_NONE;
696 }
697
698 /* FIXME: we should allow for all validity formats */
699 GSM_Error GSM_EncodeETSISMSSubmitValidity(GSM_SMSMessage *SMS,GSM_ETSISMSMessage *ETSI)
700 {
701   /* GSM 03.40 section 9.2.3.3 (TP-Validity-Period-Format) */
702   /* Bits 4 and 3: 10. TP-VP field present and integer represent (relative) */
703   ETSI->firstbyte |= 0x10;
704
705   /* GSM 03.40 section 9.2.3.12 (TP-Validity Period) */
706   /* FIXME: error-checking for correct Validity - it should not be bigger then
707      63 weeks and smaller then 5minutes. We should also test intervals because
708      the SMS->Validity to TP-VP is not continuos. I think that the simplest
709      solution will be an array of correct values. We should parse it and if we
710      find the closest TP-VP value we should use it. Or is it good to take
711      closest smaller TP-VP as we do now? I think it is :-) */
712
713   /* 5 minutes intervals up to 12 hours = 720 minutes */
714   if (SMS->Validity <= 720)
715     ETSI->TPVP = (unsigned char) (SMS->Validity/5)-1;
716
717   /* 30 minutes intervals up to 1 day */
718   else if ((SMS->Validity > 720) && (SMS->Validity <= 1440))
719     ETSI->TPVP = (unsigned char) ((SMS->Validity-720)/30)+143;
720
721   /* 1 day intervals up to 30 days */
722   else if ((SMS->Validity > 1440) && (SMS->Validity <= 43200))
723     ETSI->TPVP = (unsigned char) (SMS->Validity/1440)+166;
724
725   /* 1 week intervals up to 63 weeks */
726   else if ((SMS->Validity > 43200) && (SMS->Validity <= 635040))
727     ETSI->TPVP = (unsigned char) (SMS->Validity/10080)+192;
728
729   return GE_NONE;
730 }
731
732 /* FIXME: do we need more than SMS_Submit and SMS_Deliver ? */
733 GSM_Error GSM_EncodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI, SMS_MessageType PDU, int *length)
734 {
735   int size=0;
736   GSM_Error error;
737
738   ETSI->firstbyte=0;
739   ETSI->TPPID=0;
740   ETSI->TPDCS=0;
741   ETSI->TPUDL=0;
742   ETSI->TPStatus=0;
743   ETSI->TPVP=0;
744
745   switch (PDU) {
746     case SMS_Submit:
747
748       /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
749       /* Bits 1 and 0: 01. SMS-Submit */
750       ETSI->firstbyte |= 0x01;
751
752       /* GSM 03.40 section 9.2.3.5 (TP-Status-Raport-Request) */
753       /* Mask for request for delivery report from SMSC */
754       if (SMS->Type == GST_DR) ETSI->firstbyte |= 32;
755
756       error=GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
757       if (error!=GE_NONE) return error;
758       error=GSM_EncodeETSISMSSubmitValidity(SMS, ETSI);
759       if (error!=GE_NONE) return error;
760       size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
761
762       break;
763     case SMS_Deliver:
764
765       /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
766       /* Bits 1 and 0: 00. SMS-Deliver */
767       ETSI->firstbyte |= 0x00;
768
769       error=GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
770       if (error!=GE_NONE) return error;
771       GSM_EncodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
772       size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
773
774       break;
775     default:
776       break;
777   }
778
779   /* size is the length of the data in octets including udh */
780   *length=size;
781
782   return GE_NONE;
783 }
784
785 /* This function decodes parts of SMS coded according to GSM 03.40 
786    (given in GSM_ETSISMSMessage) to GSM_SMSMessage */
787 GSM_Error GSM_DecodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
788 {
789   SMS_MessageType PDU=SMS_Deliver;
790
791   /* See GSM 03.40 section 9.2.3.1 */
792   if ((ETSI->firstbyte & 0x03) == 0x01) PDU=SMS_Submit;
793   if ((ETSI->firstbyte & 0x03) == 0x02) PDU=SMS_Status_Report;
794
795   GSM_DecodeETSISMSHeader(SMS, ETSI);
796
797   switch (PDU) {
798     case SMS_Submit:
799 #ifdef DEBUG
800       fprintf(stdout, _("   SMS submit "));
801 #endif
802       SMS->SMSData=false; 
803       GSM_DecodeETSISMSSubmitData(SMS,ETSI);
804       break;
805     case SMS_Deliver:
806 #ifdef DEBUG
807       fprintf(stdout, _("   SMS deliver "));
808       fprintf(stdout, _("   Date: "));
809 #endif
810       SMS->SMSData=true; 
811       GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
812       GSM_DecodeETSISMSSubmitData(SMS,ETSI);
813       break;
814     case SMS_Status_Report:
815 #ifdef DEBUG
816       fprintf(stdout, _("   SMS status report "));
817       fprintf(stdout, _("   Date: "));
818 #endif
819       SMS->SMSData=true; 
820       GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
821       GSM_DecodeETSISMSStatusReportData(SMS,ETSI);
822       break;
823     default:
824       break;
825   }
826
827   SMS->MessageText[SMS->Length]=0;
828
829   return GE_NONE;
830 }
831 #endif /* UCLINUX */
832
833 static void GSM_SetDefaultSMSData (GSM_SMSMessage *SMS)
834 {
835   struct tm *now;
836   time_t nowh;
837   GSM_DateTime Date;
838
839   /* Default settings for SMS message:
840   - no delivery report
841   - Class Message 1
842   - no compression
843   - SMSC no. 1
844   - validity 3 days */
845
846   SMS->folder = GST_OUTBOX;
847   SMS->Type = GST_SMS;
848   SMS->Class = -1;
849   SMS->Compression = false;
850   SMS->MessageCenter.No = 1;
851   SMS->Validity = 4320; /* 4320 minutes == 72 hours */
852   SMS->ReplyViaSameSMSC = false;
853   SMS->UDHType = GSM_NoUDH;
854   SMS->Coding=GSM_Coding_Default;
855   strcpy(SMS->Destination,"");
856
857   /* This part is required to save SMS */    
858
859   SMS->Status = GSS_NOTSENTREAD;
860   SMS->Location = 0;
861
862   nowh=time(NULL);
863   now=localtime(&nowh);
864
865   Date.Year = now->tm_year;
866   Date.Month = now->tm_mon+1;
867   Date.Day = now->tm_mday;
868   Date.Hour = now->tm_hour;
869   Date.Minute = now->tm_min;
870   Date.Second = now->tm_sec;
871
872   /* I have 100 (for 2000) Year now :-) */
873   if (Date.Year>99 && Date.Year<1900) {
874     Date.Year=Date.Year+1900;
875   }
876
877   /* We need to have only two last digits of year */
878   if (Date.Year>1900)
879   {
880     if (Date.Year<2000) Date.Year = Date.Year-1900;
881                    else Date.Year = Date.Year-2000;
882   }
883
884   SMS->Time.Year=Date.Year;
885   SMS->Time.Month=Date.Month;
886   SMS->Time.Day=Date.Day;
887   SMS->Time.Hour=Date.Hour;
888   SMS->Time.Minute=Date.Minute;
889   SMS->Time.Second=Date.Second;
890
891   SMS->Name[0]=0;
892 }
893
894 /* This function encodes the UserDataHeader as described in:
895    - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
896    - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
897 */
898 #ifdef UCLINUX
899 static
900 #endif /* UCLINUX */
901 GSM_Error EncodeUDHHeader(char *text, GSM_UDH UDHType)
902 {
903         int i=0;
904
905         if (UDHType!=GSM_NoUDH) {
906           while (true) {
907             if (UDHHeaders[i].UDHType==GSM_NoUDH) {
908 #ifdef DEBUG
909                 fprintf(stderr,_("Not supported User Data Header type\n"));
910 #endif
911                 break;
912             }
913             if (UDHHeaders[i].UDHType==UDHType) {
914                 text[0] = UDHHeaders[i].Length; // UDH Length
915                 memcpy(text+1, UDHHeaders[i].Text, UDHHeaders[i].Length);
916                 break;
917             }
918             i++;
919           }
920         }
921         return GE_NONE;
922 }
923
924 static int GSM_MakeSinglePartSMS2(GSM_SMSMessage *SMS,
925     unsigned char *MessageBuffer,int cur, GSM_UDH UDHType, GSM_Coding_Type Coding){
926
927   int j;
928   int current,smsudhlength;
929
930   GSM_SetDefaultSMSData(SMS);
931
932   EncodeUDHHeader(SMS->UDH, UDHType);
933   SMS->UDHType=UDHType;
934
935   switch (UDHType) {
936     case GSM_EnableVoice:
937     case GSM_DisableVoice:
938     case GSM_EnableEmail:
939     case GSM_DisableEmail:
940     case GSM_EnableFax:
941     case GSM_DisableFax:
942       SMS->Class=1;
943       SMS->Coding=Coding;
944       break;
945     case GSM_NoUDH:
946     case GSM_ConcatenatedMessages:
947     case GSM_VoidSMS:
948     case GSM_HangSMS:
949     case GSM_BugSMS:
950     case GSM_PhonebookUDH:
951     case GSM_CalendarNoteUDH: //class=1?
952       SMS->Class=-1;
953       SMS->Coding=Coding;
954       break;
955     case GSM_OpLogo:
956     case GSM_CallerIDLogo:
957     case GSM_RingtoneUDH:
958     case GSM_WAPBookmarkUDH:
959     case GSM_WAPBookmarkUDHLong:
960     case GSM_WAPSettingsUDH:
961     case GSM_ProfileUDH:
962       SMS->Class=1;
963       SMS->Coding=GSM_Coding_8bit;
964       break;
965     default:
966       fprintf(stderr,_("Error in makesinglepartsms !\n\n\n"));
967   }
968
969   current=cur;
970
971   smsudhlength=0;
972   if (UDHType!=GSM_NoUDH)
973     smsudhlength=SMS->UDH[0]+1;
974
975   j=0;
976   switch (SMS->Coding) {
977     case GSM_Coding_8bit:
978       j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength);     //max=140
979       break;
980     case GSM_Coding_Default:
981       j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)*8/7; //max=160
982       break;
983     case GSM_Coding_Unicode:
984       j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)/2;   //max=70
985       break;
986   }
987   if (current>j) current=j;
988
989   memcpy(SMS->MessageText,MessageBuffer,current);    
990   SMS->MessageText[current]=0;
991   SMS->Length=current;
992
993   return current;
994 }
995
996 void GSM_MakeMultiPartSMS2(GSM_MultiSMSMessage *SMS,
997     unsigned char *MessageBuffer,int MessageLength, GSM_UDH UDHType, GSM_Coding_Type Coding){
998
999   int i=0,j,pos=0,current=0;
1000
1001   for (i=0;i<4;i++) {  
1002     if (pos==MessageLength) break;
1003
1004     current=MessageLength-pos;
1005
1006     pos=pos+GSM_MakeSinglePartSMS2(&SMS->SMS[i],MessageBuffer+pos,current,UDHType,Coding);
1007   }
1008
1009   for (j=0;j<i;j++)
1010   {
1011     switch (UDHType) {
1012       case GSM_ProfileUDH:
1013       case GSM_WAPBookmarkUDHLong:
1014       case GSM_WAPSettingsUDH:
1015       case GSM_CalendarNoteUDH:
1016       case GSM_CalendarNoteUDH2:
1017       case GSM_PhonebookUDH:
1018         SMS->SMS[j].UDH[10]=i;
1019         SMS->SMS[j].UDH[11]=j+1;
1020         break;
1021       case GSM_ConcatenatedMessages:
1022         SMS->SMS[j].UDH[4]=i;
1023         SMS->SMS[j].UDH[5]=j+1;
1024         break;
1025     default:
1026       break;
1027     }
1028   }
1029
1030   SMS->number=i;
1031 }