58b21bce19a5e681ece539d2ef7a245d3f5f8788
[gnokii.git] / common / gsm-sms.c
1 /*
2
3   $Id$
4
5   G N O K I I
6
7   A Linux/Unix toolset and driver for Nokia mobile phones.
8
9   Copyright (C) 2001 Pawe¬≥ Kot <pkot@linuxnews.pl>
10
11   Released under the terms of the GNU GPL, see file COPYING for more details.
12
13   Library for parsing and creating Short Messages (SMS).
14
15   $Log$
16   Revision 1.1.1.1  2001/11/25 21:59:06  short
17   :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
18
19   Revision 1.13  2001/11/23 22:07:44  machek
20   Fix SMS receiving to work, again. Unfortunately, it is not possible to
21   reuse much of gsm-sms.c...
22
23   Revision 1.12  2001/11/22 17:56:53  pkot
24   smslib update. sms sending
25
26   Revision 1.11  2001/11/20 16:22:22  pkot
27   First attempt to read Picture Messages. They should appear when you enable DEBUG. Nokia seems to break own standards. :/ (Markus Plail)
28
29   Revision 1.10  2001/11/19 13:09:40  pkot
30   Begin work on sms sending
31
32   Revision 1.9  2001/11/18 00:54:32  pkot
33   Bugfixes. I18n of the user responses. UDH support in libsms. Business Card UDH Type
34
35   Revision 1.8  2001/11/17 20:19:29  pkot
36   smslib cleanups, fixes and debugging
37
38   Revision 1.7  2001/11/15 12:15:04  pkot
39   smslib updates. begin work on sms in 6100 series
40
41   Revision 1.6  2001/11/14 18:21:19  pkot
42   Fixing some problems with UDH and Unicode, but still doesn't work yet :-(
43
44   Revision 1.5  2001/11/14 14:26:18  pkot
45   Changed offset of DCS field to the right value in 6210/7110
46
47   Revision 1.4  2001/11/14 11:26:18  pkot
48   Getting SMS in 6210/7110 does finally work in some cases :)
49
50   Revision 1.3  2001/11/13 16:12:20  pkot
51   Preparing libsms to get to work. 6210/7110 SMS and SMS Folder updates
52
53   Revision 1.2  2001/11/09 14:25:04  pkot
54   DEBUG cleanups
55
56   Revision 1.1  2001/11/08 16:23:21  pkot
57   New version of libsms. Not functional yet, but it reasonably stable API.
58
59   Revision 1.1  2001/07/09 23:06:26  pkot
60   Moved sms.* files from my hard disk to CVS
61
62 */
63
64 #include <stdlib.h>
65 #include <string.h>
66
67 #include "gsm-common.h"
68 #include "gsm-encoding.h"
69 #include "gsm-bitmaps.h"
70
71 struct udh_data {
72         unsigned int length;
73         char *header;
74 };
75
76 /* Number of header specific octets in SMS header (before data) */
77 static unsigned short DataOffset[] = {
78         4, /* SMS Deliver */
79         3, /* SMS Deliver Report */
80         5, /* SMS Submit */
81         3, /* SMS Submit Report */
82         3, /* SMS Command */
83         3  /* SMS Status Report */
84 };
85
86 /* User data headers */
87 static struct udh_data headers[] = {
88         { 0x00, "" },
89         { 0x05, "\x00\x03\x01\x00\x00" },     /* Concatenated messages */
90         { 0x06, "\x05\x04\x15\x82\x00\x00" }, /* Operator logos */
91         { 0x06, "\x05\x04\x15\x83\x00\x00" }, /* Caller logos */
92         { 0x06, "\x05\x04\x15\x81\x00\x00" }, /* Ringtones */
93         { 0x04, "\x03\x01\x00\x00" },         /* Voice Messages */
94         { 0x04, "\x03\x01\x01\x00" },         /* Fax Messages */
95         { 0x04, "\x03\x01\x02\x00" },         /* Email Messages */
96         { 0x06, "\x05\x04\x23\xf4\x00\x00" }, /* Business Card */
97         { 0x00, "" }
98 };
99
100
101 /***
102  *** Util functions
103  ***/
104
105 /* This function implements packing of numbers (SMS Center number and
106    destination number) for SMS sending function. */
107 static int SemiOctetPack(char *Number, unsigned char *Output, SMS_NumberType type)
108 {
109         unsigned char *IN = Number;  /* Pointer to the input number */
110         unsigned char *OUT = Output; /* Pointer to the output */
111         int count = 0; /* This variable is used to notify us about count of already
112                           packed numbers. */
113
114         /* The first byte in the Semi-octet representation of the address field is
115            the Type-of-Address. This field is described in the official GSM
116            specification 03.40 version 6.1.0, section 9.1.2.5, page 33. We support
117            only international and unknown number. */
118         
119         *OUT++ = type;
120         if (type == SMS_International) IN++; /* Skip '+' */
121         if ((type == SMS_Unknown) && (*IN == '+')) IN++; /* Optional '+' in Unknown number type */
122
123         /* The next field is the number. It is in semi-octet representation - see
124            GSM scpecification 03.40 version 6.1.0, section 9.1.2.3, page 31. */
125         while (*IN) {
126                 if (count & 0x01) {
127                         *OUT = *OUT | ((*IN - '0') << 4);
128                         OUT++;
129                 }
130                 else
131                         *OUT = *IN - '0';
132                 count++; IN++;
133         }
134
135         /* We should also fill in the most significant bits of the last byte with
136            0x0f (1111 binary) if the number is represented with odd number of
137            digits. */
138         if (count & 0x01) {
139                 *OUT = *OUT | 0xf0;
140                 OUT++;
141         }
142
143         return (2 * (OUT - Output - 1) - (count % 2));
144 }
145
146 char *GetBCDNumber(u8 *Number)
147 {
148         static char Buffer[20] = "";
149         int length = Number[0]; /* This is the length of BCD coded number */
150         int count, Digit;
151
152         memset(Buffer, 0, 20);
153         switch (Number[1]) {
154         case SMS_Alphanumeric:
155                 Unpack7BitCharacters(0, length, length, Number+2, Buffer);
156                 Buffer[length] = 0;
157                 break;
158         case SMS_International:
159                 sprintf(Buffer, "+");
160         case SMS_Unknown:
161         case SMS_National:
162         case SMS_Network:
163         case SMS_Subscriber:
164         case SMS_Abbreviated:
165         default:
166                 for (count = 0; count < length - 1; count++) {
167                         Digit = Number[count+2] & 0x0f;
168                         if (Digit < 10) sprintf(Buffer, "%s%d", Buffer, Digit);
169                         Digit = Number[count+2] >> 4;
170                         if (Digit < 10) sprintf(Buffer, "%s%d", Buffer, Digit);
171                 }
172                 break;
173         }
174         return Buffer;
175 }
176
177 static char *PrintDateTime(u8 *Number) 
178 {
179         static char Buffer[23] = "";
180
181         memset(Buffer, 0, 23);
182         if (Number[0] < 70) sprintf(Buffer, "20");
183         else sprintf(Buffer, "19");
184         sprintf(Buffer, "%s%d%d-", Buffer, Number[0] & 0x0f, Number[0] >> 4);
185         sprintf(Buffer, "%s%d%d-", Buffer, Number[1] & 0x0f, Number[1] >> 4);
186         sprintf(Buffer, "%s%d%d ", Buffer, Number[2] & 0x0f, Number[2] >> 4);
187         sprintf(Buffer, "%s%d%d:", Buffer, Number[3] & 0x0f, Number[3] >> 4);
188         sprintf(Buffer, "%s%d%d:", Buffer, Number[4] & 0x0f, Number[4] >> 4);
189         sprintf(Buffer, "%s%d%d",  Buffer, Number[5] & 0x0f, Number[5] >> 4);
190         if (Number[6] & 0x08) 
191                 sprintf(Buffer, "%s-", Buffer);
192         else
193                 sprintf(Buffer, "%s+", Buffer);
194         sprintf(Buffer, "%s%02d00", Buffer, (10 * (Number[6] & 0x07) + (Number[6] >> 4)) / 4);
195
196         return Buffer;
197 }
198
199 SMS_DateTime *UnpackDateTime(u8 *Number, SMS_DateTime *dt)
200 {
201         dt->Year     =  10 * (Number[0] & 0x0f) + (Number[0] >> 4);
202         if (dt->Year < 70) dt->Year += 2000;
203         else dt->Year += 1900;
204         dt->Month    =  10 * (Number[1] & 0x0f) + (Number[1] >> 4);
205         dt->Day      =  10 * (Number[2] & 0x0f) + (Number[2] >> 4);
206         dt->Hour     =  10 * (Number[3] & 0x0f) + (Number[3] >> 4);
207         dt->Minute   =  10 * (Number[4] & 0x0f) + (Number[4] >> 4);
208         dt->Second   =  10 * (Number[5] & 0x0f) + (Number[5] >> 4);
209         dt->Timezone = (10 * (Number[6] & 0x07) + (Number[6] >> 4)) / 4;
210         if (Number[6] & 0x08) dt->Timezone = -dt->Timezone;
211
212         return dt;
213 }
214
215 /***
216  *** ENCODING SMS
217  ***/
218
219 static GSM_Error EncodeData(GSM_SMSMessage *SMS, char *dcs, char *message)
220 {
221         SMS_AlphabetType al;
222         unsigned short length = strlen(SMS->MessageText);
223
224         switch (SMS->DCS.Type) {
225         case SMS_GeneralDataCoding:
226                 switch (SMS->DCS.u.General.Class) {
227                 case 1: dcs[0] |= 0xf0; break;
228                 case 2: dcs[0] |= 0xf1; break;
229                 case 3: dcs[0] |= 0xf2; break;
230                 case 4: dcs[0] |= 0xf3; break;
231                 default: break;
232                 }
233                 if (SMS->DCS.u.General.Compressed) {
234                         /* Compression not supported yet */
235                         /* dcs[0] |= 0x20; */
236                 }
237                 al = SMS->DCS.u.General.Alphabet;
238                 break;
239         case SMS_MessageWaiting:
240                 al = SMS->DCS.u.MessageWaiting.Alphabet;
241                 if (SMS->DCS.u.MessageWaiting.Discard) dcs[0] |= 0xc0;
242                 else if (SMS->DCS.u.MessageWaiting.Alphabet == SMS_UCS2) dcs[0] |= 0xe0;
243                 else dcs[0] |= 0xd0;
244
245                 if (SMS->DCS.u.MessageWaiting.Active) dcs[0] |= 0x08;
246                 dcs[0] |= (SMS->DCS.u.MessageWaiting.Type & 0x03);
247
248                 break;
249         default:
250                 return GE_SMSWRONGFORMAT;
251         }
252         switch (al) {
253         case SMS_DefaultAlphabet:
254                 Pack7BitCharacters((7 - (SMS->UDH_Length % 7)) % 7, SMS->MessageText, message);
255                 SMS->Length = 8 * SMS->UDH_Length + (7 - (SMS->UDH_Length % 7)) % 7 + length;
256                 break;
257         case SMS_8bit:
258                 dcs[0] |= 0xf4;
259                 memcpy(message, SMS->MessageText + 1, SMS->MessageText[0]);
260                 SMS->Length = SMS->UDH_Length + SMS->MessageText[0];
261                 break;
262         case SMS_UCS2:
263                 dcs[0] |= 0x08;
264                 EncodeUnicode(message, SMS->MessageText, length);
265                 SMS->Length = length;
266                 break;
267         default:
268                 return GE_SMSWRONGFORMAT;
269         }
270         return GE_NONE;
271 }
272
273 /* This function encodes the UserDataHeader as described in:
274    - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
275    - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
276 */
277 static GSM_Error EncodeUDH(SMS_UDHInfo UDHi, char *UDH)
278 {
279         unsigned char pos;
280
281         pos = UDH[0];
282         switch (UDHi.Type) {
283         case SMS_NoUDH:
284                 break;
285         case SMS_VoiceMessage:
286         case SMS_FaxMessage:
287         case SMS_EmailMessage:
288                 UDH[pos+4] = UDHi.u.SpecialSMSMessageIndication.MessageCount;
289                 if (UDHi.u.SpecialSMSMessageIndication.Store) UDH[pos+3] |= 0x80;
290         case SMS_ConcatenatedMessages:
291         case SMS_OpLogo:
292         case SMS_CallerIDLogo:
293         case SMS_Ringtone:
294                 UDH[0] += headers[UDHi.Type].length;
295                 memcpy(UDH+pos+1, headers[UDHi.Type].header, headers[UDHi.Type].length);
296                 break;
297         default:
298                 dprintf("Not supported User Data Header type\n");
299                 break;
300         }
301         return GE_NONE;
302 }
303
304 static GSM_Error EncodeSMSSubmitHeader(GSM_SMSMessage *SMS, char *frame)
305 {
306         GSM_Error error = GE_NONE;
307
308         /* Standard Header: */
309         memcpy(frame, "\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9\x00\x00\x00\x00\x00\x00", 24);
310
311         /* Reply Path */
312         if (SMS->ReplyViaSameSMSC) frame[0] |= 0x80;
313
314         /* User Data Header Indicator */
315         if (SMS->UDH_No) frame[0] |= 0x40;
316
317         /* Status (Delivery) Report Request */
318         if (SMS->ReportStatus) frame[0] |= 0x20;
319
320         /* Validity Period Format: mask - 0x00, 0x10, 0x08, 0x18 */
321         frame[0] |= ((SMS->Validity.VPF & 0x03) << 3);
322
323         /* Reject Duplicates */
324         if (SMS->RejectDuplicates) frame[0] |= 0x04;
325
326         /* Message Type is already set */
327
328         /* Message Reference */
329         /* Can we set this? */
330
331         /* Protocol Identifier */
332         /* FIXME: allow to change this in better way.
333            currently only 0x5f == `Return Call Message' is used */
334         if (SMS->PID) frame[3] = SMS->PID;
335
336         /* Data Coding Scheme */
337         switch (SMS->DCS.Type) {
338         case SMS_GeneralDataCoding:
339                 if (SMS->DCS.u.General.Compressed) frame[4] |= 0x20;
340                 if (SMS->DCS.u.General.Class) frame[4] |= (0x10 | (SMS->DCS.u.General.Class - 1));
341                 frame[4] |= ((SMS->DCS.u.General.Alphabet & 0x03) << 2);
342                 break;
343         case SMS_MessageWaiting:
344                 if (SMS->DCS.u.MessageWaiting.Discard) frame[4] |= 0xc0;
345                 else if (SMS->DCS.u.MessageWaiting.Alphabet == SMS_UCS2) frame[4] |= 0xe0;
346                 else frame[4] |= 0xd0;
347                 if (SMS->DCS.u.MessageWaiting.Active) frame[4] |= 0x80;
348                 frame[4] |= (SMS->DCS.u.MessageWaiting.Type & 0x03);
349                 break;
350         default:
351                 dprintf("Wrong Data Coding Scheme (DCS) format\n");
352                 return GE_SMSWRONGFORMAT;
353         }
354
355         /* Destination Address */
356         frame[5] = SemiOctetPack(SMS->RemoteNumber.number, frame + 6, SMS->RemoteNumber.type);
357
358         /* Validity Period */
359         switch (SMS->Validity.VPF) {
360         case SMS_EnhancedFormat:
361                 break;
362         case SMS_RelativeFormat:
363                 break;
364         case SMS_AbsoluteFormat:
365                 break;
366         default:
367                 break;
368         }
369
370         return error;
371 }
372
373 static GSM_Error EncodeSMSDeliverHeader()
374 {
375         return GE_NONE;
376 }
377
378 static GSM_Error EncodeSMSHeader(GSM_SMSMessage *SMS, char *frame)
379 /* We can create either SMS DELIVER (for saving in Inbox) or SMS SUBMIT
380    (for sending or saving in Outbox) message */
381 {
382         /* Set SMS type */
383         frame[12] |= (SMS->Type >> 1);
384         switch (SMS->Type) {
385         case SMS_Submit: /* we send SMS or save it in Outbox */
386                 return EncodeSMSSubmitHeader(SMS, frame);
387         case SMS_Deliver: /* we save SMS in Inbox */
388                 return EncodeSMSDeliverHeader(SMS, frame);
389         default: /* we don't create other formats of SMS */
390                 return GE_SMSWRONGFORMAT;
391         }
392 }
393
394 /* This function encodes SMS as described in:
395    - GSM 03.40 version 6.1.0 Release 1997, section 9
396 */
397 int EncodePDUSMS(GSM_SMSMessage *SMS, char *message)
398 {
399         GSM_Error error = GE_NONE;
400         int i;
401
402         dprintf("Sending SMS to %s via message center %s\n", SMS->RemoteNumber.number, SMS->MessageCenter.Number);
403
404         /* SMSC number */
405         dprintf("%d %s\n", SMS->MessageCenter.Type, SMS->MessageCenter.Number);
406         message[0] = SemiOctetPack(SMS->MessageCenter.Number, message + 1, SMS->MessageCenter.Type);
407         if (message[0] % 2) message[0]++;
408         message[0] = message[0] / 2 + 1;
409
410         /* Common Header */
411         error = EncodeSMSHeader(SMS, message + 12);
412         if (error != GE_NONE) return error;
413
414         /* User Data Header - if present */
415 //      for (i = 0; i < SMS->UDH_No; i++) {
416 //              error = EncodeUDH(SMS->UDH[i], message + 24);
417 //              if (error != GE_NONE) return error;
418 //      }
419         SMS->UDH_Length = 0;
420
421         /* User Data */
422         EncodeData(SMS, message + 14, message + 36 + SMS->UDH_Length);
423         message[16] = SMS->Length;
424         return SMS->Length + 35;
425 }
426
427 /* This function does simple SMS encoding - no PDU coding */
428 GSM_Error EncodeTextSMS()
429 {
430         return GE_NONE;
431 }
432
433 /***
434  *** DECODING SMS
435  ***/
436
437 static GSM_Error SMSStatus(unsigned char status, GSM_SMSMessage *SMS)
438 {
439         if (status < 0x03) {
440                 strcpy(SMS->MessageText, _("Delivered"));
441                 switch (status) {
442                 case 0x00:
443                         dprintf("SM received by the SME");
444                         break;
445                 case 0x01:
446                         dprintf("SM forwarded by the SC to the SME but the SC is unable to confirm delivery");
447                         break;
448                 case 0x02:
449                         dprintf("SM replaced by the SC");
450                         break;
451                 }
452                 SMS->Length = strlen(_("Delivered"));
453         } else if (status & 0x40) {
454
455                 strcpy(SMS->MessageText, _("Failed"));
456
457                 /* more detailed reason only for debug */
458
459                 if (status & 0x20) {
460                         dprintf("Temporary error, SC is not making any more transfer attempts\n");
461
462                         switch (status) {
463                         case 0x60:
464                                 dprintf("Congestion");
465                                 break;
466                         case 0x61:
467                                 dprintf("SME busy");
468                                 break;
469                         case 0x62:
470                                 dprintf("No response from SME");
471                                 break;
472                         case 0x63:
473                                 dprintf("Service rejected");
474                                 break;
475                         case 0x64:
476                                 dprintf("Quality of service not aviable");
477                                 break;
478                         case 0x65:
479                                 dprintf("Error in SME");
480                                 break;
481                         default:
482                                 dprintf("Reserved/Specific to SC: %x", status);
483                                 break;
484                         }
485                 } else {
486                         dprintf("Permanent error, SC is not making any more transfer attempts\n");
487                         switch (status) {
488                         case 0x40:
489                                 dprintf("Remote procedure error");
490                                 break;
491                         case 0x41:
492                                 dprintf("Incompatibile destination");
493                                 break;
494                         case 0x42:
495                                 dprintf("Connection rejected by SME");
496                                 break;
497                         case 0x43:
498                                 dprintf("Not obtainable");
499                                 break;
500                         case 0x44:
501                                 dprintf("Quality of service not aviable");
502                                 break;
503                         case 0x45:
504                                 dprintf("No internetworking available");
505                                 break;
506                         case 0x46:
507                                 dprintf("SM Validity Period Expired");
508                                 break;
509                         case 0x47:
510                                 dprintf("SM deleted by originating SME");
511                                 break;
512                         case 0x48:
513                                 dprintf("SM Deleted by SC Administration");
514                                 break;
515                         case 0x49:
516                                 dprintf("SM does not exist");
517                                 break;
518                         default:
519                                 dprintf("Reserved/Specific to SC: %x", status);
520                                 break;
521                         }
522                 }
523                 SMS->Length = strlen(_("Failed"));
524         } else if (status & 0x20) {
525                 strcpy(SMS->MessageText, _("Pending"));
526
527                 /* more detailed reason only for debug */
528                 dprintf("Temporary error, SC still trying to transfer SM\n");
529                 switch (status) {
530                 case 0x20:
531                         dprintf("Congestion");
532                         break;
533                 case 0x21:
534                         dprintf("SME busy");
535                         break;
536                 case 0x22:
537                         dprintf("No response from SME");
538                         break;
539                 case 0x23:
540                         dprintf("Service rejected");
541                         break;
542                 case 0x24:
543                         dprintf("Quality of service not aviable");
544                         break;
545                 case 0x25:
546                         dprintf("Error in SME");
547                         break;
548                 default:
549                         dprintf("Reserved/Specific to SC: %x", status);
550                         break;
551                 }
552                 SMS->Length = strlen(_("Pending"));
553         } else {
554                 strcpy(SMS->MessageText, _("Unknown"));
555
556                 /* more detailed reason only for debug */
557                 dprintf("Reserved/Specific to SC: %x", status);
558                 SMS->Length = strlen(_("Unknown"));
559         }
560         dprintf("\n");
561         return GE_NONE;
562 }
563
564 static GSM_Error DecodeData(char *message, char *output, int length, int size, int udhlen, SMS_DataCodingScheme dcs)
565 {
566         /* Unicode */
567         if ((dcs.Type & 0x08) == 0x08) {
568                 dprintf("Unicode message\n");
569                 length = (length - udhlen)/2;
570                 DecodeUnicode(output, message, length);
571         } else {
572                 /* 8bit SMS */
573                 if ((dcs.Type & 0xf4) == 0xf4) {
574                         dprintf("8bit message\n");
575                         memcpy(output, message, length);
576                 /* 7bit SMS */
577                 } else {
578                         dprintf("Default Alphabet\n");
579                         length = length - (udhlen * 8 + ((7-(udhlen%7))%7)) / 7;
580                         Unpack7BitCharacters((7-udhlen)%7, size, length, message, output);
581                         DecodeAscii(output, output, length);
582                 }
583         }
584         dprintf("%s\n", output);
585         return GE_NONE;
586 }
587
588 /* This function decodes UDH as described in:
589    - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
590    - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
591 */
592 static GSM_Error DecodeUDH(char *message, GSM_SMSMessage *SMS)
593 {
594         unsigned char length, pos, nr;
595
596         SMS->UDH_Length = length = message[0] + 1;
597         pos = 1;
598         nr = 0;
599         while (length > 1) {
600                 unsigned char udh_length;
601
602                 udh_length = message[pos+1];
603                 switch (message[pos]) {
604                 case 0x00: // Concatenated short messages
605                         dprintf("Concatenated messages\n");
606                         SMS->UDH[nr].Type = SMS_ConcatenatedMessages;
607                         SMS->UDH[nr].u.ConcatenatedShortMessage.ReferenceNumber = message[pos + 2];
608                         SMS->UDH[nr].u.ConcatenatedShortMessage.MaximumNumber   = message[pos + 3];
609                         SMS->UDH[nr].u.ConcatenatedShortMessage.CurrentNumber   = message[pos + 4];
610                         break;
611                 case 0x01: // Special SMS Message Indication
612                         switch (message[pos + 2] & 0x03) {
613                         case 0x00:
614                                 dprintf("Voice Message\n");
615                                 SMS->UDH[nr].Type = SMS_VoiceMessage;
616                                 break;
617                         case 0x01:
618                                 dprintf("Fax Message\n");
619                                 SMS->UDH[nr].Type = SMS_FaxMessage;
620                                 break;
621                         case 0x02:
622                                 dprintf("Email Message\n");
623                                 SMS->UDH[nr].Type = SMS_EmailMessage;
624                                 break;
625                         default:
626                                 dprintf("Unknown\n");
627                                 SMS->UDH[nr].Type = SMS_UnknownUDH;
628                                 break;
629                         }
630                         SMS->UDH[nr].u.SpecialSMSMessageIndication.Store = (message[pos + 2] & 0x80) >> 7;
631                         SMS->UDH[nr].u.SpecialSMSMessageIndication.MessageCount = message[pos + 3];
632                         break;
633                 case 0x04: // Application port addression scheme, 8 bit address
634                         break;
635                 case 0x05: // Application port addression scheme, 16 bit address
636                         switch (((0x00ff & message[pos + 2]) << 8) | (0x00ff & message[pos + 3])) {
637                         case 0x1581:
638                                 dprintf("Ringtone\n");
639                                 SMS->UDH[nr].Type = SMS_Ringtone;
640                                 break;
641                         case 0x1582:
642                                 dprintf("Operator Logo\n");
643                                 SMS->UDH[nr].Type = SMS_OpLogo;
644                                 break;
645                         case 0x1583:
646                                 dprintf("Caller Icon\n");
647                                 SMS->UDH[nr].Type = SMS_CallerIDLogo;
648                                 break;
649                         case 0x23f4:
650                                 dprintf("Business Card\n");
651                                 SMS->UDH[nr].Type = SMS_BusinessCard;
652                                 break;
653                         default:
654                                 dprintf("Unknown\n");
655                                 SMS->UDH[nr].Type = SMS_UnknownUDH;
656                                 break;
657                         }
658                         break;
659                 case 0x06: // SMSC Control Parameters
660                         break;
661                 case 0x07: // UDH Source Indicator
662                         break;
663                 default:
664                         break;
665                 }
666                 length -= (udh_length + 2);
667                 pos += (udh_length + 2);
668                 nr++;
669         }
670         SMS->UDH_No = nr;
671
672         return GE_NONE;
673 }
674
675 static GSM_Error DecodeSMSHeader(unsigned char *message, GSM_SMSMessage *SMS)
676 {
677         /* Short Message Type */
678         switch (SMS->Type = message[2]) {
679         case SMS_Deliver:
680                 dprintf("Mobile Terminated message:\n");
681                 break;
682         case SMS_Delivery_Report:
683                 dprintf("Delivery Report:\n");
684                 UnpackDateTime(message + 34 + DataOffset[SMS->Type], &(SMS->SMSCTime));
685                 dprintf("\tDelivery date: %s\n", PrintDateTime(message + 34 + DataOffset[SMS->Type]));
686                 break;
687         case SMS_Submit:
688                 dprintf("Mobile Originated message:\n");
689                 break;
690         default:
691                 dprintf("Not supported message type:\n");
692                 break;
693         }
694
695         /* Short Message location in memory */
696         SMS->Number = message[1];
697         dprintf("\tLocation: %d\n", SMS->Number);
698
699         /* Short Message Center */
700         strcpy(SMS->MessageCenter.Number, GetBCDNumber(message + 3));
701         dprintf("\tSMS center number: %s\n", SMS->MessageCenter.Number);
702         SMS->ReplyViaSameSMSC = false;
703         if (SMS->RemoteNumber.number[0] == 0 && (message[6] & 0x80)) {
704                 SMS->ReplyViaSameSMSC = true;
705         }
706
707         /* Remote number */
708         message[15+DataOffset[SMS->Type]] = ((message[15+DataOffset[SMS->Type]])+1)/2+1;
709         dprintf("\tRemote number (recipient or sender): %s\n", GetBCDNumber(message + 15 + DataOffset[SMS->Type]));
710         strcpy(SMS->RemoteNumber.number, GetBCDNumber(message + 15 + DataOffset[SMS->Type]));
711
712         UnpackDateTime(message + 27 + DataOffset[SMS->Type], &(SMS->Time));
713         dprintf("\tDate: %s\n", PrintDateTime(message + 27 + DataOffset[SMS->Type]));
714
715         /* Message length */
716         SMS->Length = message[14+DataOffset[SMS->Type]];
717
718         /* Data Coding Scheme */
719         if (SMS->Type != SMS_Delivery_Report && SMS->Type != SMS_Picture)
720                 SMS->DCS.Type = message[13 + DataOffset[SMS->Type]];
721         else
722                 SMS->DCS.Type = 0;
723
724         /* User Data Header */
725         if (message[15] & 0x40) { /* UDH header available */
726                 dprintf("UDH found\n");
727                 DecodeUDH(message + 34 + DataOffset[SMS->Type], SMS);
728         } else {                    /* No UDH */
729                 dprintf("No UDH\n");
730                 SMS->UDH_No = 0;
731         }
732
733         return GE_NONE;
734 }
735
736 /* This function decodes SMS as described in:
737    - GSM 03.40 version 6.1.0 Release 1997, section 9
738 */
739 GSM_Error DecodePDUSMS(unsigned char *message, GSM_SMSMessage *SMS, int MessageLength)
740 {
741         int size;
742         GSM_Bitmap bitmap;
743
744         DecodeSMSHeader(message, SMS);
745         switch (SMS->Type) {
746         case SMS_Delivery_Report:
747                 SMSStatus(message[17], SMS);
748                 break;
749         case SMS_Picture:
750                 dprintf("Picture!!!\n");
751                 GSM_ReadSMSBitmap(SMS_Picture, message + 41, NULL, &bitmap);
752                 GSM_PrintBitmap(&bitmap);
753                 size = MessageLength - 45 - bitmap.size;
754                 SMS->Length = message[45 + bitmap.size];
755                 printf("%d %d %d\n", SMS->Length, bitmap.size, size);
756                 DecodeData(message + 46 + bitmap.size,
757                            (unsigned char *)&(SMS->MessageText),
758                            SMS->Length, size, 0, SMS->DCS);
759                 SMS->MessageText[SMS->Length] = 0;
760                 break;
761         default:
762                 size = MessageLength -
763                            34 -                    /* Header Length */
764                            DataOffset[SMS->Type] - /* offset */
765                            SMS->UDH_Length -       /* UDH Length */
766                            5;                      /* checksum */
767                 DecodeData(message + 34 + DataOffset[SMS->Type] + SMS->UDH_Length,
768                            (unsigned char *)&(SMS->MessageText),
769                            SMS->Length, size, SMS->UDH_Length, SMS->DCS);
770                 /* Just in case */
771                 SMS->MessageText[SMS->Length] = 0;
772                 break;
773         }
774         
775         return GE_NONE;
776 }
777
778 /* This function does simple SMS decoding - no PDU coding */
779 GSM_Error DecodeTextSMS(unsigned char *message, GSM_SMSMessage *SMS)
780 {
781         return GE_NONE;
782 }