Not yet working stack checking (symbol STACKCHECK)
[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, tmp);
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
737   ETSI->firstbyte=0;
738   ETSI->TPPID=0;
739   ETSI->TPDCS=0;
740   ETSI->TPUDL=0;
741   ETSI->TPStatus=0;
742   ETSI->TPVP=0;
743
744   switch (PDU) {
745     case SMS_Submit:
746
747       /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
748       /* Bits 1 and 0: 01. SMS-Submit */
749       ETSI->firstbyte |= 0x01;
750
751       /* GSM 03.40 section 9.2.3.5 (TP-Status-Raport-Request) */
752       /* Mask for request for delivery report from SMSC */
753       if (SMS->Type == GST_DR) ETSI->firstbyte |= 32;
754
755       GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
756       GSM_EncodeETSISMSSubmitValidity(SMS, ETSI);
757       size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
758
759       break;
760     case SMS_Deliver:
761
762       /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
763       /* Bits 1 and 0: 00. SMS-Deliver */
764       ETSI->firstbyte |= 0x00;
765
766       GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
767       GSM_EncodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
768       size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
769
770       break;
771     default:
772       break;
773   }
774
775   /* size is the length of the data in octets including udh */
776   *length=size;
777
778   return GE_NONE;
779 }
780
781 /* This function decodes parts of SMS coded according to GSM 03.40 
782    (given in GSM_ETSISMSMessage) to GSM_SMSMessage */
783 GSM_Error GSM_DecodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
784 {
785   SMS_MessageType PDU=SMS_Deliver;
786
787   /* See GSM 03.40 section 9.2.3.1 */
788   if ((ETSI->firstbyte & 0x03) == 0x01) PDU=SMS_Submit;
789   if ((ETSI->firstbyte & 0x03) == 0x02) PDU=SMS_Status_Report;
790
791   GSM_DecodeETSISMSHeader(SMS, ETSI);
792
793   switch (PDU) {
794     case SMS_Submit:
795 #ifdef DEBUG
796       fprintf(stdout, _("   SMS submit "));
797 #endif
798       SMS->SMSData=false; 
799       GSM_DecodeETSISMSSubmitData(SMS,ETSI);
800       break;
801     case SMS_Deliver:
802 #ifdef DEBUG
803       fprintf(stdout, _("   SMS deliver "));
804       fprintf(stdout, _("   Date: "));
805 #endif
806       SMS->SMSData=true; 
807       GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
808       GSM_DecodeETSISMSSubmitData(SMS,ETSI);
809       break;
810     case SMS_Status_Report:
811 #ifdef DEBUG
812       fprintf(stdout, _("   SMS status report "));
813       fprintf(stdout, _("   Date: "));
814 #endif
815       SMS->SMSData=true; 
816       GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
817       GSM_DecodeETSISMSStatusReportData(SMS,ETSI);
818       break;
819     default:
820       break;
821   }
822
823   SMS->MessageText[SMS->Length]=0;
824
825   return GE_NONE;
826 }
827 #endif /* UCLINUX */
828
829 static void GSM_SetDefaultSMSData (GSM_SMSMessage *SMS)
830 {
831   struct tm *now;
832   time_t nowh;
833   GSM_DateTime Date;
834
835   /* Default settings for SMS message:
836   - no delivery report
837   - Class Message 1
838   - no compression
839   - SMSC no. 1
840   - validity 3 days */
841
842   SMS->folder = GST_OUTBOX;
843   SMS->Type = GST_SMS;
844   SMS->Class = -1;
845   SMS->Compression = false;
846   SMS->MessageCenter.No = 1;
847   SMS->Validity = 4320; /* 4320 minutes == 72 hours */
848   SMS->ReplyViaSameSMSC = false;
849   SMS->UDHType = GSM_NoUDH;
850   SMS->Coding=GSM_Coding_Default;
851   strcpy(SMS->Destination,"");
852
853   /* This part is required to save SMS */    
854
855   SMS->Status = GSS_NOTSENTREAD;
856   SMS->Location = 0;
857
858   nowh=time(NULL);
859   now=localtime(&nowh);
860
861   Date.Year = now->tm_year;
862   Date.Month = now->tm_mon+1;
863   Date.Day = now->tm_mday;
864   Date.Hour = now->tm_hour;
865   Date.Minute = now->tm_min;
866   Date.Second = now->tm_sec;
867
868   /* I have 100 (for 2000) Year now :-) */
869   if (Date.Year>99 && Date.Year<1900) {
870     Date.Year=Date.Year+1900;
871   }
872
873   /* We need to have only two last digits of year */
874   if (Date.Year>1900)
875   {
876     if (Date.Year<2000) Date.Year = Date.Year-1900;
877                    else Date.Year = Date.Year-2000;
878   }
879
880   SMS->Time.Year=Date.Year;
881   SMS->Time.Month=Date.Month;
882   SMS->Time.Day=Date.Day;
883   SMS->Time.Hour=Date.Hour;
884   SMS->Time.Minute=Date.Minute;
885   SMS->Time.Second=Date.Second;
886
887   SMS->Name[0]=0;
888 }
889
890 /* This function encodes the UserDataHeader as described in:
891    - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
892    - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
893 */
894 #ifdef UCLINUX
895 static
896 #endif /* UCLINUX */
897 GSM_Error EncodeUDHHeader(char *text, GSM_UDH UDHType)
898 {
899         int i=0;
900
901         if (UDHType!=GSM_NoUDH) {
902           while (true) {
903             if (UDHHeaders[i].UDHType==GSM_NoUDH) {
904 #ifdef DEBUG
905                 fprintf(stderr,_("Not supported User Data Header type\n"));
906 #endif
907                 break;
908             }
909             if (UDHHeaders[i].UDHType==UDHType) {
910                 text[0] = UDHHeaders[i].Length; // UDH Length
911                 memcpy(text+1, UDHHeaders[i].Text, UDHHeaders[i].Length);
912                 break;
913             }
914             i++;
915           }
916         }
917         return GE_NONE;
918 }
919
920 static int GSM_MakeSinglePartSMS2(GSM_SMSMessage *SMS,
921     unsigned char *MessageBuffer,int cur, GSM_UDH UDHType, GSM_Coding_Type Coding){
922
923   int j;
924   int current,smsudhlength;
925
926   GSM_SetDefaultSMSData(SMS);
927
928   EncodeUDHHeader(SMS->UDH, UDHType);
929   SMS->UDHType=UDHType;
930
931   switch (UDHType) {
932     case GSM_EnableVoice:
933     case GSM_DisableVoice:
934     case GSM_EnableEmail:
935     case GSM_DisableEmail:
936     case GSM_EnableFax:
937     case GSM_DisableFax:
938       SMS->Class=1;
939       SMS->Coding=Coding;
940       break;
941     case GSM_NoUDH:
942     case GSM_ConcatenatedMessages:
943     case GSM_VoidSMS:
944     case GSM_HangSMS:
945     case GSM_BugSMS:
946     case GSM_PhonebookUDH:
947     case GSM_CalendarNoteUDH: //class=1?
948       SMS->Class=-1;
949       SMS->Coding=Coding;
950       break;
951     case GSM_OpLogo:
952     case GSM_CallerIDLogo:
953     case GSM_RingtoneUDH:
954     case GSM_WAPBookmarkUDH:
955     case GSM_WAPBookmarkUDHLong:
956     case GSM_WAPSettingsUDH:
957     case GSM_ProfileUDH:
958       SMS->Class=1;
959       SMS->Coding=GSM_Coding_8bit;
960       break;
961     default:
962       fprintf(stderr,_("Error in makesinglepartsms !\n\n\n"));
963   }
964
965   current=cur;
966
967   smsudhlength=0;
968   if (UDHType!=GSM_NoUDH)
969     smsudhlength=SMS->UDH[0]+1;
970
971   j=0;
972   switch (SMS->Coding) {
973     case GSM_Coding_8bit:
974       j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength);     //max=140
975       break;
976     case GSM_Coding_Default:
977       j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)*8/7; //max=160
978       break;
979     case GSM_Coding_Unicode:
980       j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)/2;   //max=70
981       break;
982   }
983   if (current>j) current=j;
984
985   memcpy(SMS->MessageText,MessageBuffer,current);    
986   SMS->MessageText[current]=0;
987   SMS->Length=current;
988
989   return current;
990 }
991
992 void GSM_MakeMultiPartSMS2(GSM_MultiSMSMessage *SMS,
993     unsigned char *MessageBuffer,int MessageLength, GSM_UDH UDHType, GSM_Coding_Type Coding){
994
995   int i=0,j,pos=0,current=0;
996
997   for (i=0;i<4;i++) {  
998     if (pos==MessageLength) break;
999
1000     current=MessageLength-pos;
1001
1002     pos=pos+GSM_MakeSinglePartSMS2(&SMS->SMS[i],MessageBuffer+pos,current,UDHType,Coding);
1003   }
1004
1005   for (j=0;j<i;j++)
1006   {
1007     switch (UDHType) {
1008       case GSM_ProfileUDH:
1009       case GSM_WAPBookmarkUDHLong:
1010       case GSM_WAPSettingsUDH:
1011       case GSM_CalendarNoteUDH:
1012       case GSM_CalendarNoteUDH2:
1013       case GSM_PhonebookUDH:
1014         SMS->SMS[j].UDH[10]=i;
1015         SMS->SMS[j].UDH[11]=j+1;
1016         break;
1017       case GSM_ConcatenatedMessages:
1018         SMS->SMS[j].UDH[4]=i;
1019         SMS->SMS[j].UDH[5]=j+1;
1020         break;
1021     default:
1022       break;
1023     }
1024   }
1025
1026   SMS->number=i;
1027 }