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