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