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