7 A Linux/Unix toolset and driver for Nokia mobile phones.
9 Copyright (C) 2001 Pawe³ Kot <pkot@linuxnews.pl>
11 Released under the terms of the GNU GPL, see file COPYING for more details.
13 Library for parsing and creating Short Messages (SMS).
16 Revision 1.1.1.8 2002/04/03 00:08:04 short
17 Found in "gnokii-working" directory, some November-patches version
19 Revision 1.1 2001/11/08 16:23:21 pkot
20 New version of libsms. Not functional yet, but it reasonably stable API.
22 Revision 1.1 2001/07/09 23:06:26 pkot
23 Moved sms.* files from my hard disk to CVS
29 #include "gsm-common.h"
30 #include "gsm-encoding.h"
37 /* Number of header specific octets in SMS header (before data) */
38 static unsigned short DataOffset[] = {
40 3, /* SMS Deliver Report */
42 3, /* SMS Submit Report */
44 3 /* SMS Status Report */
47 /* User data headers */
48 static struct udh_data headers[] = {
50 { 0x05, "\x00\x03\x01\x00\x00" }, /* Concatenated messages */
51 { 0x06, "\x05\x04\x15\x82\x00\x00" }, /* Operator logos */
52 { 0x06, "\x05\x04\x15\x82\x00\x00" }, /* Caller logos */
53 { 0x06, "\x05\x04\x15\x81\x00\x00" }, /* Ringtones */
54 { 0x04, "\x03\x01\x00\x00" }, /* Voice Messages */
55 { 0x04, "\x03\x01\x01\x00" }, /* Fax Messages */
56 { 0x04, "\x03\x01\x02\x00" }, /* Email Messages */
65 static char *GetBCDNumber(u8 *Number)
67 static char Buffer[20] = "";
69 /* This is the length of BCD coded number */
77 Unpack7BitCharacters(0, length, length, Number+2, Buffer);
81 if (Number[1] == SMS_International)
85 for (count = 0; count < length - 1; count++) {
86 Digit = Number[count+2] & 0x0f;
87 if (Digit < 10) sprintf(Buffer, "%s%d", Buffer, Digit);
88 Digit = Number[count+2] >> 4;
89 if (Digit < 10) sprintf(Buffer, "%s%d", Buffer, Digit);
97 char *GetBCDNumber(u8 *Number, int maxdigits, int maxbytes)
99 static char Buffer[22] = "";
100 int length = Number[0]; /* This is the length of BCD coded number */
101 int bytes = 0, digits = 0;
104 dprintf("%i\n", length);
106 if (Number[1] == 0x91) {
107 sprintf(Buffer, "+");
112 while ((Digit < 10) && (bytes < length) && (digits < maxdigits)) {
113 Digit = Number[bytes + 2] & 0x0f;
115 sprintf(Buffer, "%s%d", Buffer, Digit);
117 Digit = Number[bytes + 2] >> 4;
119 sprintf(Buffer, "%s%d", Buffer, Digit);
130 static char *PrintDateTime(u8 *Number)
132 static char Buffer[23] = "";
134 memset(Buffer, 0, 23);
135 sprintf(Buffer, "%d%d-", Number[0] & 0x0f, Number[0] >> 4);
136 sprintf(Buffer, "%s%d%d-", Buffer, Number[1] & 0x0f, Number[1] >> 4);
137 sprintf(Buffer, "%s%d%d ", Buffer, Number[2] & 0x0f, Number[2] >> 4);
138 sprintf(Buffer, "%s%d%d:", Buffer, Number[3] & 0x0f, Number[3] >> 4);
139 sprintf(Buffer, "%s%d%d:", Buffer, Number[4] & 0x0f, Number[4] >> 4);
140 sprintf(Buffer, "%s%d%d", Buffer, Number[5] & 0x0f, Number[5] >> 4);
141 if (Number[6] & 0x08)
142 sprintf(Buffer, "%s-", Buffer);
144 sprintf(Buffer, "%s+", Buffer);
145 sprintf(Buffer, "%s%02d00", Buffer, (10 * (Number[6] & 0x07) + (Number[6] >> 4)) / 4);
150 static SMS_DateTime *UnpackDateTime(u8 *Number, SMS_DateTime *dt)
152 dt->Year = 10 * (Number[0] & 0x0f) + (Number[0] >> 4);
153 dt->Month = 10 * (Number[1] & 0x0f) + (Number[1] >> 4);
154 dt->Day = 10 * (Number[2] & 0x0f) + (Number[2] >> 4);
155 dt->Hour = 10 * (Number[3] & 0x0f) + (Number[3] >> 4);
156 dt->Minute = 10 * (Number[4] & 0x0f) + (Number[4] >> 4);
157 dt->Second = 10 * (Number[5] & 0x0f) + (Number[5] >> 4);
158 dt->Timezone = (10 * (Number[6] & 0x07) + (Number[6] >> 4)) / 4;
159 if (Number[6] & 0x08) dt->Timezone = -dt->Timezone;
168 /* This function encodes the UserDataHeader as described in:
169 - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
170 - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
172 static GSM_Error EncodeUDH(SMS_UDHInfo UDHi, char *UDH)
180 case SMS_VoiceMessage:
182 case SMS_EmailMessage:
183 UDH[pos+4] = UDHi.u.SpecialSMSMessageIndication.MessageCount;
184 if (UDHi.u.SpecialSMSMessageIndication.Store) UDH[pos+3] |= 0x80;
185 case SMS_ConcatenatedMessages:
187 case SMS_CallerIDLogo:
189 UDH[0] += headers[UDHi.Type].length;
190 memcpy(UDH+pos+1, headers[UDHi.Type].header, headers[UDHi.Type].length);
193 fprintf(stderr, _("Not supported User Data Header type\n"));
199 static GSM_Error EncodeSMSSubmitHeader(GSM_SMSMessage *SMS, char *frame)
201 GSM_Error error = GE_NONE;
207 if (SMS->ReplyViaSameSMSC) frame[13] |= 0x80;
209 /* User Data Header */
210 for (i = 0; SMS->UDH[i].Type != SMS_NoUDH; i++) {
212 error = EncodeUDH(SMS->UDH[i], frame);
213 if (error != GE_NONE) return error;
216 /* Status (Delivery) Report Request */
217 if (SMS->Report) frame[13] |= 0x20;
219 /* Validity Period Format: mask - 0x00, 0x10, 0x08, 0x18 */
220 frame[13] |= ((SMS->Validity.VPF & 0x03) << 3);
222 /* Reject Duplicates */
223 if (SMS->RejectDuplicates) frame[13] |= 0x04;
225 /* Message Type is already set */
227 /* Message Reference */
228 /* Can we set this? */
230 /* Protocol Identifier */
231 /* FIXME: allow to change this in better way.
232 currently only 0x5f == `Return Call Message' is used */
233 frame[16] = SMS->PID;
235 /* Data Coding Scheme */
236 switch (SMS->DCS.Type) {
237 case SMS_GeneralDataCoding:
238 if (SMS->DCS.u.General.Compressed) frame[17] |= 0x20;
239 if (SMS->DCS.u.General.Class) frame[17] |= (0x10 | (SMS->DCS.u.General.Class - 1));
240 frame[17] |= ((SMS->DCS.u.General.Alphabet & 0x03) << 2);
242 case SMS_MessageWaiting:
243 if (SMS->DCS.u.MessageWaiting.Discard) frame[17] |= 0xc0;
244 else if (SMS->DCS.u.MessageWaiting.Alphabet == SMS_UCS2) frame[17] |= 0xe0;
245 else frame[17] |= 0xd0;
246 if (SMS->DCS.u.MessageWaiting.Active) frame[17] |= 0x80;
247 frame[17] |= (SMS->DCS.u.MessageWaiting.Type & 0x03);
250 fprintf(stderr, _("Wrong Data Coding Scheme (DCS) format\n"));
251 return GE_SMSWRONGFORMAT;
254 /* User Data Length */
255 /* Will be filled later. */
257 /* Destination Address */
259 /* Validity Period */
260 switch (SMS->Validity.VPF) {
261 case SMS_EnhancedFormat:
263 case SMS_RelativeFormat:
265 case SMS_AbsoluteFormat:
274 static GSM_Error EncodeSMSDeliverHeader()
279 static GSM_Error EncodeSMSHeader(GSM_SMSMessage *SMS, char *frame)
280 /* We can create either SMS DELIVER (for saving in Inbox) or SMS SUBMIT
281 (for sending or saving in Outbox) message */
284 frame[12] |= (SMS->Type >> 1);
286 case SMS_Submit: /* we send SMS or save it in Outbox */
287 return EncodeSMSSubmitHeader(SMS, frame);
288 case SMS_Deliver: /* we save SMS in Inbox */
289 return EncodeSMSSubmitHeader(SMS, frame);
290 default: /* we don't create other formats of SMS */
291 return GE_SMSWRONGFORMAT;
295 /* This function encodes SMS as described in:
296 - GSM 03.40 version 6.1.0 Release 1997, section 9
298 GSM_Error EncodePDUSMS(GSM_SMSMessage *SMS, char *frame)
300 GSM_Error error = GE_NONE;
303 /* Short Message Data info element id */
305 /* Length of Short Message Data */
307 /* Short Message Reference value */
308 if (SMS->Number) frame[3] = SMS->Number;
310 /* Short Message status */
311 if (SMS->Status) frame[1] = SMS->Status;
316 if (SMS->MessageCenter.Number) {
317 // error = GSM->GetSMSCenter(&SMS->MessageCenter);
319 if (error != GE_NONE)
321 strcpy(SMS->MessageCenter.Number, "0");
323 dprintf("Sending SMS to %s via message center %s\n", SMS->Sender, SMS->MessageCenter.Number);
326 // EncodeUDH(SMS, frame);
327 // if (error != GE_NONE) return error;
329 /* User Data Header - if present */
330 for (i = 0; SMS->UDH[i].Type != SMS_NoUDH; i++) {
331 error = EncodeUDH(SMS->UDH[i], frame);
332 if (error != GE_NONE) return error;
336 // EncodeData(&(SMS->MessageText), &(SMS->DCS), frame);
341 /* This function does simple SMS encoding - no PDU coding */
342 GSM_Error EncodeTextSMS()
351 static GSM_Error SMSStatus(unsigned char status, GSM_SMSMessage *sms)
354 strcpy(sms->MessageText, _("Delivered"));
357 dprintf(_("SM received by the SME"));
360 dprintf(_("SM forwarded by the SC to the SME but the SC is unable to confirm delivery"));
363 dprintf(_("SM replaced by the SC"));
367 } else if (status & 0x40) {
369 strcpy(sms->MessageText, _("Failed"));
371 /* more detailed reason only for debug */
374 dprintf(_("Temporary error, SC is not making any more transfer attempts\n"));
378 dprintf(_("Congestion"));
381 dprintf(_("SME busy"));
384 dprintf(_("No response from SME"));
387 dprintf(_("Service rejected"));
390 dprintf(_("Quality of service not aviable"));
393 dprintf(_("Error in SME"));
396 dprintf(_("Reserved/Specific to SC: %x"), status);
400 dprintf(_("Permanent error, SC is not making any more transfer attempts\n"));
403 dprintf(_("Remote procedure error"));
406 dprintf(_("Incompatibile destination"));
409 dprintf(_("Connection rejected by SME"));
412 dprintf(_("Not obtainable"));
415 dprintf(_("Quality of service not aviable"));
418 dprintf(_("No internetworking available"));
421 dprintf(_("SM Validity Period Expired"));
424 dprintf(_("SM deleted by originating SME"));
427 dprintf(_("SM Deleted by SC Administration"));
430 dprintf(_("SM does not exist"));
433 dprintf(_("Reserved/Specific to SC: %x"), status);
438 } else if (status & 0x20) {
439 strcpy(sms->MessageText, _("Pending"));
441 /* more detailed reason only for debug */
442 dprintf(_("Temporary error, SC still trying to transfer SM\n"));
445 dprintf(_("Congestion"));
448 dprintf(_("SME busy"));
451 dprintf(_("No response from SME"));
454 dprintf(_("Service rejected"));
457 dprintf(_("Quality of service not aviable"));
460 dprintf(_("Error in SME"));
463 dprintf(_("Reserved/Specific to SC: %x"), status);
468 strcpy(sms->MessageText, _("Unknown"));
470 /* more detailed reason only for debug */
471 dprintf(_("Reserved/Specific to SC: %x"), status);
477 static GSM_Error DecodeData(char *message, char *output, int length, SMS_DataCodingScheme dcs, int udhlen, int size)
480 if ((dcs.Type & 0x08) == 0x08) {
481 length = (length - udhlen)/2;
482 DecodeUnicode(output, message, length);
485 if ((dcs.Type & 0xf4) == 0xf4) {
486 memcpy(output, message, length);
489 length = length - (udhlen * 8 + ((7-udhlen)%7)) / 7;
490 Unpack7BitCharacters((7-udhlen)%7, size, length, message, output);
492 DecodeAscii(output, output, length);
494 dprintf("%s\n", output);
498 /* This function decodes UDH as described in:
499 - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
500 - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
502 static GSM_Error DecodeUDH(char *SMSMessage, SMS_UDHInfo **UDHi, GSM_SMSMessage *sms)
504 unsigned char length, pos, nr;
506 length = SMSMessage[0];
510 unsigned char udh_length;
512 udh_length = SMSMessage[pos+1];
513 switch (SMSMessage[pos]) {
514 case 0x00: // Concatenated short messages
515 dprintf("Concat UDH length: %d\n", udh_length);
516 UDHi[nr]->Type = SMS_ConcatenatedMessages;
517 UDHi[nr]->u.ConcatenatedShortMessage.ReferenceNumber = SMSMessage[pos + 3];
518 UDHi[nr]->u.ConcatenatedShortMessage.MaximumNumber = SMSMessage[pos + 4];
519 UDHi[nr]->u.ConcatenatedShortMessage.CurrentNumber = SMSMessage[pos + 5];
521 case 0x01: // Special SMS Message Indication
522 switch (SMSMessage[pos + 3] & 0x03) {
524 UDHi[nr]->Type = SMS_VoiceMessage;
527 UDHi[nr]->Type = SMS_FaxMessage;
530 UDHi[nr]->Type = SMS_EmailMessage;
533 UDHi[nr]->Type = SMS_UnknownUDH;
536 UDHi[nr]->u.SpecialSMSMessageIndication.Store = (SMSMessage[pos + 3] & 0x80) >> 7;
537 UDHi[nr]->u.SpecialSMSMessageIndication.MessageCount = SMSMessage[pos + 4];
539 case 0x04: // Application port addression scheme, 8 bit address
541 case 0x05: // Application port addression scheme, 16 bit address
543 case 0x06: // SMSC Control Parameters
545 case 0x07: // UDH Source Indicator
550 length -= (udh_length + 2);
551 pos += (udh_length + 2);
559 static GSM_Error DecodeSMSSubmit()
564 static GSM_Error DecodeSMSDeliver()
569 static GSM_Error DecodeSMSHeader(unsigned char *message, GSM_SMSMessage *sms)
571 /* Short Message Type */
572 switch (sms->Type = message[7]) {
574 dprintf("Mobile Terminated message:\n");
576 case SMS_Delivery_Report:
577 dprintf("Delivery Report:\n");
578 UnpackDateTime(message + 39 + DataOffset[sms->Type], &(sms->Time));
579 dprintf("\tDelivery date: %s\n", PrintDateTime(message + 39 + DataOffset[sms->Type]));
582 dprintf("Mobile Originated message:\n");
585 dprintf("Not supported message:\n");
588 /* Short Message status */
589 sms->Status = message[4];
590 dprintf("\tStatus: ");
591 switch (sms->Status) {
605 dprintf("UNKNOWN\n");
609 /* Short Message location in memory */
610 sms->Number = message[6];
611 dprintf("\tLocation: %d\n", sms->Number);
613 /* Short Message Center */
614 strcpy(sms->MessageCenter.Number, GetBCDNumber(message));
615 dprintf("\tSMS center number: %s\n", sms->MessageCenter.Number);
616 sms->ReplyViaSameSMSC = false;
617 if (sms->RemoteNumber.number[0] == 0 && (message[12] & 0x80)) {
618 sms->ReplyViaSameSMSC = true;
622 message[20+DataOffset[sms->Type]] = ((message[20+DataOffset[sms->Type]])+1)/2+1;
623 dprintf("\tRemote number (recipient or sender): %s\n", GSM_GetBCDNumber(message + 20 + DataOffset[sms->Type]));
624 strcpy(sms->RemoteNumber.number, GetBCDNumber(message + 20 + DataOffset[sms->Type]));
626 UnpackDateTime(message + 24 + DataOffset[sms->Type], &(sms->Time));
627 dprintf("\tDate: %s\n", PrintDateTime(message + 24 + DataOffset[sms->Type]));
629 /* Data Coding Scheme */
630 if (sms->Type != SMS_Delivery_Report)
631 sms->DCS.Type = message[18 + DataOffset[sms->Type]];
635 /* User Data Header */
636 if (message[20] & 0x40) /* UDH header available */
637 DecodeUDH(message + 31 + DataOffset[sms->Type], (SMS_UDHInfo **)sms->UDH, sms);
644 /* This function decodes SMS as described in:
645 - GSM 03.40 version 6.1.0 Release 1997, section 9
647 GSM_Error DecodePDUSMS(unsigned char *message, GSM_SMSMessage *SMS, int MessageLength)
651 DecodeSMSHeader(message, SMS);
652 for (i = 0; i < SMS->UDH_No; i++) {
653 udhlen += headers[SMS->UDH[i].Type].length;
655 if (SMS->Type == SMS_Delivery_Report) {
656 SMSStatus(message[22], SMS);
658 int size = MessageLength -
659 39 - /* Header Length */
660 DataOffset[SMS->Type] - /* offset */
661 udhlen - /* UDH Length */
663 DecodeData(message + 39 + DataOffset[SMS->Type] + udhlen,
664 (unsigned char *)&(SMS->MessageText),
665 SMS->Length, SMS->DCS,
672 /* This function does simple SMS decoding - no PDU coding */
673 GSM_Error DecodeTextSMS(unsigned char *message, GSM_SMSMessage *sms)
677 unsigned char output[161];
679 sms->EightBit = false;
681 if (w < 0) w = (14 - off) % 14;
682 sms->Length = message[9 + 11 + offset] - (off * 8 + w) / 7;
685 if (w < 0) w = (14 - off) % 14;
686 tmp = Unpack7BitCharacters(w, length-31-9-offset, sms->Length, message+9+31+offset, output);
687 dprintf(" 7 bit SMS, body is (length %i): ",sms->Length);
688 for (i = 0; i < tmp; i++) {
689 dprintf("%c", DecodeWithDefaultAlphabet(output[i]));
690 sms->MessageText[i] = DecodeWithDefaultAlphabet(output[i]);