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