From 88b0d33b8b0b1f6ae320cfc863ca6a47fa8fec22 Mon Sep 17 00:00:00 2001 From: zautrix Date: Sat, 07 Aug 2004 17:24:40 +0000 Subject: Initial revision --- (limited to 'gammu/emb/common/service/sms/gsmsms.c') diff --git a/gammu/emb/common/service/sms/gsmsms.c b/gammu/emb/common/service/sms/gsmsms.c new file mode 100644 index 0000000..9920835 --- a/dev/null +++ b/gammu/emb/common/service/sms/gsmsms.c @@ -0,0 +1,663 @@ +/* (c) 2001-2004 by Marcin Wiacek */ +/* based on some work from Pawel Kot, others and Gnokii */ + +#include +#include +#include + +#include "../../gsmcomon.h" +#include "../../misc/coding/coding.h" +#include "../gsmcal.h" +#include "../gsmpbk.h" +#include "../gsmlogo.h" +#include "../gsmring.h" +#include "../gsmdata.h" +#include "../gsmnet.h" +#include "gsmsms.h" + +/* User data headers */ +static GSM_UDHHeader UDHHeaders[] = { + /* See GSM 03.40 section 9.2.3.24.1 + * 1 byte 0x00 + * 1 byte 0x03 + * 1 byte 0x01: unique ID for message series + * 1 byte 0x00: how many SMS in sequence + * 1 byte 0x00: number of current SMS in sequence */ + { UDH_ConcatenatedMessages, 0x05, "\x00\x03\x01\x00\x00",2,-1,4,3}, + + /* See GSM 03.40 section 9.2.3.24.2 for voice, fax and email messages */ + { UDH_DisableVoice, 0x04, "\x01\x02\x00\x00",-1,-1,-1,-1}, + { UDH_DisableFax, 0x04, "\x01\x02\x01\x00",-1,-1,-1,-1}, + { UDH_DisableEmail, 0x04, "\x01\x02\x02\x00",-1,-1,-1,-1}, + { UDH_EnableVoice, 0x04, "\x01\x02\x00\x01",-1,-1,-1,-1}, + { UDH_EnableFax, 0x04, "\x01\x02\x01\x01",-1,-1,-1,-1}, + { UDH_EnableEmail, 0x04, "\x01\x02\x02\x01",-1,-1,-1,-1}, + + /* When send such SMS to some phones, they don't display anything, + * only beep and enable vibra/light + */ + { UDH_VoidSMS, 0x08, "\x01\x02\x02\x01\x01\x02\x02\x00",-1,-1,-1,-1}, + + /* Nokia Smart Messaging (short version) UDH + * General format : + * 1 byte 0x05 : IEI application port addressing scheme, 16 bit address + * 1 byte 0x04 : IEI length + * 2 bytes : destination address : high & low byte + * 2 bytes 0x00 0x00 : originator address : high & low byte */ + { UDH_NokiaRingtone, 0x06, "\x05\x04\x15\x81\x00\x00",-1,-1,-1,-1}, + { UDH_NokiaOperatorLogo, 0x06, "\x05\x04\x15\x82\x00\x00",-1,-1,-1,-1}, + { UDH_NokiaCallerLogo, 0x06, "\x05\x04\x15\x83\x00\x00",-1,-1,-1,-1}, + { UDH_NokiaWAP, 0x06, "\x05\x04\xc3\x4f\x00\x00",-1,-1,-1,-1}, + + /* Nokia Smart Messaging (long version) UDH and other + * General format: + * 1 byte 0x05 : IEI application port addressing scheme, 16 bit address + * 1 byte 0x04 : IEI length + * 2 bytes 0x00 0x00 : destination address : high & low byte + * 2 bytes 0x00 0x00 : originator address : high & low byte + * 1 byte 0x00 : SAR + * 1 byte 0x03 : SAR length + * 1 byte : diagram reference number (unique ID for message series) + * 1 byte : number of all SMS + * 1 byte : number of current SMS */ + { UDH_NokiaCalendarLong, 0x0b, "\x05\x04\x00\xe4\x00\x00\x00\x03\xc7\x00\x00",8,-1,10,9}, + { UDH_MMSIndicatorLong, 0x0b, "\x05\x04\x0b\x84\x23\xf0\x00\x03\xe5\x00\x00",8,-1,10,9}, + { UDH_NokiaRingtoneLong, 0x0b, "\x05\x04\x15\x81\x00\x00\x00\x03\x01\x00\x00",8,-1,10,9}, + { UDH_NokiaOperatorLogoLong, 0x0b, "\x05\x04\x15\x82\x00\x00\x00\x03\x02\x00\x00",8,-1,10,9}, + { UDH_NokiaProfileLong, 0x0b, "\x05\x04\x15\x8a\x00\x00\x00\x03\xce\x00\x00",8,-1,10,9}, + { UDH_NokiaPhonebookLong, 0x0b, "\x05\x04\x23\xf4\x00\x00\x00\x03\x01\x00\x00",8,-1,10,9}, + { UDH_NokiaWAPLong, 0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x7f\x00\x00",8,-1,10,9}, + + { UDH_ConcatenatedMessages16bit,0x06, "\x08\x04\x00\x00\x00\x00",-1,2,5,4}, + + { UDH_NoUDH, 0x00, "",-1,-1,-1,-1} +}; + +/* --------------------------- Unpacking SMS ------------------------------- */ + +/* See GSM 03.40 section 9.2.3.11 */ +static GSM_Error GSM_DecodeSMSDateTime(GSM_DateTime *DT, unsigned char *req) +{ + DT->Year = DecodeWithBCDAlphabet(req[0]); + if (DT->Year<90) DT->Year=DT->Year+2000; else DT->Year=DT->Year+1990; + DT->Month = DecodeWithBCDAlphabet(req[1]); + DT->Day = DecodeWithBCDAlphabet(req[2]); + DT->Hour = DecodeWithBCDAlphabet(req[3]); + DT->Minute = DecodeWithBCDAlphabet(req[4]); + DT->Second = DecodeWithBCDAlphabet(req[5]); + + /* Base for timezone is GMT. It's in quarters */ + DT->Timezone=(10*(req[6]&0x07)+(req[6]>>4))/4; + + if (req[6]&0x08) DT->Timezone = -DT->Timezone; + + dbgprintf("Decoding date & time: "); + dbgprintf("%s %4d/%02d/%02d ", DayOfWeek(DT->Year, DT->Month, DT->Day), + DT->Year, DT->Month, DT->Day); + dbgprintf("%02d:%02d:%02d %02d00\n", DT->Hour, DT->Minute, DT->Second, DT->Timezone); + + return ERR_NONE; +} + +void GSM_DecodeUDHHeader(GSM_UDHHeader *UDH) +{ + int i, tmp, w; + bool UDHOK; + + UDH->Type = UDH_UserUDH; + UDH->ID8bit = -1; + UDH->ID16bit = -1; + UDH->PartNumber = -1; + UDH->AllParts = -1; + + i=-1; + while (true) { + i++; + if (UDHHeaders[i].Type==UDH_NoUDH) break; + + tmp=UDHHeaders[i].Length; + /* if length is the same */ + if (tmp==UDH->Text[0]) { + + if (tmp==0x05) tmp=tmp-3;/*three last bytes can be different for such UDH*/ + if (tmp==0x0b) tmp=tmp-3;/*three last bytes can be different for such UDH*/ + if (tmp==0x06 && UDH->Text[1] == 0x08) tmp=tmp-4; + + UDHOK=true; + for (w=0;wText[w+1]) { + UDHOK=false; + break; + } + } + if (UDHOK) { + UDH->Type=UDHHeaders[i].Type; + + if (UDHHeaders[i].ID8bit !=-1) UDH->ID8bit = UDH->Text[UDHHeaders[i].ID8bit+1]; + if (UDHHeaders[i].ID16bit !=-1) UDH->ID16bit = UDH->Text[UDHHeaders[i].ID16bit+1]*256+UDH->Text[UDHHeaders[i].ID16bit+2]; + if (UDHHeaders[i].PartNumber !=-1) UDH->PartNumber = UDH->Text[UDHHeaders[i].PartNumber+1]; + if (UDHHeaders[i].AllParts !=-1) UDH->AllParts = UDH->Text[UDHHeaders[i].AllParts+1]; + break; + } + } + } + +#ifdef DEBUG + dbgprintf("Type of UDH: "); + switch (UDH->Type) { + case UDH_ConcatenatedMessages : dbgprintf("Concatenated (linked) message"); break; + case UDH_ConcatenatedMessages16bit : dbgprintf("Concatenated (linked) message"); break; + case UDH_DisableVoice : dbgprintf("Disables voice indicator"); break; + case UDH_EnableVoice : dbgprintf("Enables voice indicator"); break; + case UDH_DisableFax : dbgprintf("Disables fax indicator"); break; + case UDH_EnableFax : dbgprintf("Enables fax indicator"); break; + case UDH_DisableEmail : dbgprintf("Disables email indicator"); break; + case UDH_EnableEmail : dbgprintf("Enables email indicator"); break; + case UDH_VoidSMS : dbgprintf("Void SMS"); break; + case UDH_NokiaWAP : dbgprintf("Nokia WAP Bookmark"); break; + case UDH_NokiaOperatorLogoLong : dbgprintf("Nokia operator logo"); break; + case UDH_NokiaWAPLong : dbgprintf("Nokia WAP Bookmark or WAP/MMS Settings"); break; + case UDH_NokiaRingtone : dbgprintf("Nokia ringtone"); break; + case UDH_NokiaRingtoneLong : dbgprintf("Nokia ringtone"); break; + case UDH_NokiaOperatorLogo : dbgprintf("Nokia GSM operator logo"); break; + case UDH_NokiaCallerLogo : dbgprintf("Nokia caller logo"); break; + case UDH_NokiaProfileLong : dbgprintf("Nokia profile"); break; + case UDH_NokiaCalendarLong : dbgprintf("Nokia calendar note"); break; + case UDH_NokiaPhonebookLong : dbgprintf("Nokia phonebook entry"); break; + case UDH_UserUDH : dbgprintf("User UDH"); break; + case UDH_MMSIndicatorLong : dbgprintf("MMS indicator"); break; + case UDH_NoUDH: break; + } + if (UDH->ID8bit != -1) dbgprintf(", ID 8 bit %i",UDH->ID8bit); + if (UDH->ID16bit != -1) dbgprintf(", ID 16 bit %i",UDH->ID16bit); + if (UDH->PartNumber != -1 && UDH->AllParts != -1) { + dbgprintf(", part %i of %i",UDH->PartNumber,UDH->AllParts); + } + dbgprintf("\n"); + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, di.dl, UDH->Text, UDH->Length); +#endif +} + +GSM_Error GSM_DecodeSMSFrameText(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout) +{ + int off=0; // length of the User Data Header + int w,i,tmp=0; + unsigned char output[161]; + + SMS->UDH.Length = 0; + /* UDH header available */ + if (buffer[Layout.firstbyte] & 64) { + /* Length of UDH header */ + off = (buffer[Layout.Text] + 1); + SMS->UDH.Length = off; + dbgprintf("UDH header available (length %i)\n",off); + + /* Copy UDH header into SMS->UDH */ + for (i = 0; i < off; i++) SMS->UDH.Text[i] = buffer[Layout.Text + i]; + + GSM_DecodeUDHHeader(&SMS->UDH); + } + + /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */ + if ((buffer[Layout.TPDCS] & 0xf4) == 0xf4) SMS->Coding=SMS_Coding_8bit; + if ((buffer[Layout.TPDCS] & 0x08) == 0x08) SMS->Coding=SMS_Coding_Unicode; + + switch (SMS->Coding) { + case SMS_Coding_Default: + i = 0; + do { + i+=7; + w=(i-off)%i; + } while (w<0); + SMS->Length=buffer[Layout.TPUDL] - (off*8 + w) / 7; + tmp=GSM_UnpackEightBitsToSeven(w, buffer[Layout.TPUDL]-off, SMS->Length, buffer+(Layout.Text+off), output); + dbgprintf("7 bit SMS, length %i\n",SMS->Length); + DecodeDefault (SMS->Text, output, SMS->Length, true, NULL); + dbgprintf("%s\n",DecodeUnicodeString(SMS->Text)); + break; + case SMS_Coding_8bit: + SMS->Length=buffer[Layout.TPUDL] - off; + memcpy(SMS->Text,buffer+(Layout.Text+off),SMS->Length); +#ifdef DEBUG + dbgprintf("8 bit SMS, length %i\n",SMS->Length); + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, di.dl, SMS->Text, SMS->Length); +#endif + break; + case SMS_Coding_Unicode: + SMS->Length=(buffer[Layout.TPUDL] - off) / 2; + DecodeUnicodeSpecialNOKIAChars(SMS->Text,buffer+(Layout.Text+off), SMS->Length); +#ifdef DEBUG + dbgprintf("Unicode SMS, length %i\n",SMS->Length); + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, di.dl, buffer+(Layout.Text+off), SMS->Length*2); + dbgprintf("%s\n",DecodeUnicodeString(SMS->Text)); +#endif + break; + } + + return ERR_NONE; +} + +GSM_Error GSM_DecodeSMSFrameStatusReportData(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout) +{ + SMS->DeliveryStatus = buffer[Layout.TPStatus]; + + if (buffer[Layout.TPStatus] < 0x03) { + EncodeUnicode(SMS->Text,"Delivered",9); + SMS->Length = 9; + } else if (buffer[Layout.TPStatus] & 0x40) { + EncodeUnicode(SMS->Text,"Failed",6); + SMS->Length = 6; + } else if (buffer[Layout.TPStatus] & 0x20) { + EncodeUnicode(SMS->Text,"Pending",7); + SMS->Length = 7; + } else { + EncodeUnicode(SMS->Text,"Unknown",7); + SMS->Length = 7; + } + +#ifdef DEBUG + /* See GSM 03.40 section 9.2.3.15 (TP-Status) */ + if (buffer[Layout.TPStatus] & 0x40) { + if (buffer[Layout.TPStatus] & 0x20) { + /* 0x60, 0x61, ... */ + dbgprintf("Temporary error, SC is not making any more transfer attempts\n"); + } else { + /* 0x40, 0x41, ... */ + dbgprintf("Permanent error, SC is not making any more transfer attempts\n"); + } + } else if (buffer[Layout.TPStatus] & 0x20) { + /* 0x20, 0x21, ... */ + dbgprintf("Temporary error, SC still trying to transfer SM\n"); + } + switch (buffer[Layout.TPStatus]) { + case 0x00: dbgprintf("SM received by the SME"); break; + case 0x01: dbgprintf("SM forwarded by the SC to the SME but the SC is unable to confirm delivery");break; + case 0x02: dbgprintf("SM replaced by the SC"); break; + case 0x20: dbgprintf("Congestion"); break; + case 0x21: dbgprintf("SME busy"); break; + case 0x22: dbgprintf("No response from SME"); break; + case 0x23: dbgprintf("Service rejected"); break; + case 0x24: dbgprintf("Quality of service not available"); break; + case 0x25: dbgprintf("Error in SME"); break; + case 0x40: dbgprintf("Remote procedure error"); break; + case 0x41: dbgprintf("Incompatibile destination"); break; + case 0x42: dbgprintf("Connection rejected by SME"); break; + case 0x43: dbgprintf("Not obtainable"); break; + case 0x44: dbgprintf("Quality of service not available"); break; + case 0x45: dbgprintf("No internetworking available"); break; + case 0x46: dbgprintf("SM Validity Period Expired"); break; + case 0x47: dbgprintf("SM deleted by originating SME"); break; + case 0x48: dbgprintf("SM Deleted by SC Administration"); break; + case 0x49: dbgprintf("SM does not exist"); break; + case 0x60: dbgprintf("Congestion"); break; + case 0x61: dbgprintf("SME busy"); break; + case 0x62: dbgprintf("No response from SME"); break; + case 0x63: dbgprintf("Service rejected"); break; + case 0x64: dbgprintf("Quality of service not available"); break; + case 0x65: dbgprintf("Error in SME"); break; + default : dbgprintf("Reserved/Specific to SC: %x",buffer[Layout.TPStatus]); break; + } + dbgprintf("\n"); +#endif + + return ERR_NONE; +} + +GSM_Error GSM_DecodeSMSFrame(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout) +{ + GSM_DateTime zerodt = {0,0,0,0,0,0,0}; +#ifdef DEBUG + if (Layout.firstbyte == 255) { + dbgprintf("ERROR: firstbyte in SMS layout not set\n"); + return ERR_UNKNOWN; + } + if (Layout.TPDCS != 255) dbgprintf("TPDCS : %02x %i\n",buffer[Layout.TPDCS] ,buffer[Layout.TPDCS]); + if (Layout.TPMR != 255) dbgprintf("TPMR : %02x %i\n",buffer[Layout.TPMR] ,buffer[Layout.TPMR]); + if (Layout.TPPID != 255) dbgprintf("TPPID : %02x %i\n",buffer[Layout.TPPID] ,buffer[Layout.TPPID]); + if (Layout.TPUDL != 255) dbgprintf("TPUDL : %02x %i\n",buffer[Layout.TPUDL] ,buffer[Layout.TPUDL]); + if (Layout.firstbyte != 255) dbgprintf("FirstByte : %02x %i\n",buffer[Layout.firstbyte],buffer[Layout.firstbyte]); + if (Layout.Text != 255) dbgprintf("Text : %02x %i\n",buffer[Layout.Text] ,buffer[Layout.Text]); +#endif + + SMS->UDH.Type = UDH_NoUDH; + SMS->Coding = SMS_Coding_Default; + SMS->Length = 0; + SMS->SMSC.Location = 0; + SMS->SMSC.DefaultNumber[0] = 0; + SMS->SMSC.DefaultNumber[1] = 0; + SMS->SMSC.Number[0] = 0; + SMS->SMSC.Number[1] = 0; + SMS->SMSC.Name[0] = 0; + SMS->SMSC.Name[1] = 0; + SMS->SMSC.Validity.Format = SMS_Validity_NotAvailable; + SMS->SMSC.Format = SMS_FORMAT_Text; + SMS->Number[0] = 0; + SMS->Number[1] = 0; + SMS->Name[0] = 0; + SMS->Name[1] = 0; + SMS->ReplyViaSameSMSC = false; + if (Layout.SMSCNumber!=255) { + GSM_UnpackSemiOctetNumber(SMS->SMSC.Number,buffer+Layout.SMSCNumber,false); + dbgprintf("SMS center number : \"%s\"\n",DecodeUnicodeString(SMS->SMSC.Number)); + } + if ((buffer[Layout.firstbyte] & 0x80)!=0) SMS->ReplyViaSameSMSC=true; +#ifdef DEBUG + if (SMS->ReplyViaSameSMSC) dbgprintf("SMS centre set for reply\n"); +#endif + if (Layout.Number!=255) { + GSM_UnpackSemiOctetNumber(SMS->Number,buffer+Layout.Number,true); + dbgprintf("Remote number : \"%s\"\n",DecodeUnicodeString(SMS->Number)); + } + if (Layout.Text != 255 && Layout.TPDCS!=255 && Layout.TPUDL!=255) { + GSM_DecodeSMSFrameText(SMS, buffer, Layout); + } + if (Layout.DateTime != 255) { + GSM_DecodeSMSDateTime(&SMS->DateTime,buffer+(Layout.DateTime)); + } else { + SMS->DateTime = zerodt; + } + if (Layout.SMSCTime != 255 && Layout.TPStatus != 255) { + /* GSM 03.40 section 9.2.3.11 (TP-Service-Centre-Time-Stamp) */ + dbgprintf("SMSC response date: "); + GSM_DecodeSMSDateTime(&SMS->SMSCTime, buffer+(Layout.SMSCTime)); + GSM_DecodeSMSFrameStatusReportData(SMS,buffer,Layout); + } else { + SMS->SMSCTime = zerodt; + } + SMS->Class = -1; + if (Layout.TPDCS != 255) { + if ((buffer[Layout.TPDCS] & 0xF3)==0xF0) SMS->Class = 0; + if ((buffer[Layout.TPDCS] & 0xF3)==0xF1) SMS->Class = 1; + if ((buffer[Layout.TPDCS] & 0xF3)==0xF2) SMS->Class = 2; + if ((buffer[Layout.TPDCS] & 0xF3)==0xF3) SMS->Class = 3; + } + dbgprintf("SMS class: %i\n",SMS->Class); + + SMS->MessageReference = 0; + if (Layout.TPMR != 255) SMS->MessageReference = buffer[Layout.TPMR]; + + SMS->ReplaceMessage = 0; + if (Layout.TPPID != 255) { + if (buffer[Layout.TPPID] > 0x40 && buffer[Layout.TPPID] < 0x48) { + SMS->ReplaceMessage = buffer[Layout.TPPID] - 0x40; + } + } + SMS->RejectDuplicates = false; + if ((buffer[Layout.firstbyte] & 0x04)==0x04) SMS->RejectDuplicates = true; + + return ERR_NONE; +} + +/* ----------------------------- Packing SMS ------------------------------- */ + +/* See GSM 03.40 section 9.2.3.11 */ +static GSM_Error GSM_EncodeSMSDateTime(GSM_DateTime *DT, unsigned char *req) +{ + int Year; + + dbgprintf("Encoding SMS datetime: %02i/%02i/%04i %02i:%02i:%02i\n", + DT->Day,DT->Month,DT->Year,DT->Hour,DT->Minute,DT->Second); + + /* We need to have only two last digits of year */ + if (DT->Year>1900) { + if (DT->Year<2000) Year = DT->Year-1900; + else Year = DT->Year-2000; + } else Year = DT->Year; + + req[0]=EncodeWithBCDAlphabet(Year); + req[1]=EncodeWithBCDAlphabet(DT->Month); + req[2]=EncodeWithBCDAlphabet(DT->Day); + req[3]=EncodeWithBCDAlphabet(DT->Hour); + req[4]=EncodeWithBCDAlphabet(DT->Minute); + req[5]=EncodeWithBCDAlphabet(DT->Second); + + /* FIXME: do it */ + req[6]=0; /* TimeZone = +-0 */ + + return ERR_NONE; +} + +static int GSM_EncodeSMSFrameText(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout) +{ + int off = 0; // length of the User Data Header + int size = 0, size2 = 0, w,p; + char buff[200]; + + if (SMS->UDH.Type!=UDH_NoUDH) { + buffer[Layout.firstbyte] |= 0x40; /* GSM 03.40 section 9.2.3.23 (TP-User-Data-Header-Indicator) */ + off = 1 + SMS->UDH.Text[0]; /* off - length of the User Data Header */ + memcpy(buffer+Layout.Text, SMS->UDH.Text, off); /* we copy the udh */ +#ifdef DEBUG + dbgprintf("UDH, length %i\n",off); + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, di.dl, SMS->UDH.Text, off); +#endif + } + switch (SMS->Coding) { + case SMS_Coding_8bit: + /* the mask for the 8-bit data */ + /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) + * and GSM 03.38 section 4 */ + buffer[Layout.TPDCS] |= 0xf4; + memcpy(buffer+(Layout.Text+off), SMS->Text, SMS->Length); + size2 = size = SMS->Length+off; +#ifdef DEBUG + dbgprintf("8 bit SMS, length %i\n",SMS->Length); + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, di.dl, SMS->Text, SMS->Length); +#endif + break; + case SMS_Coding_Default: + p = 0; + do { + p+=7; + w=(p-off)%p; + } while (w<0); + p = UnicodeLength(SMS->Text); + EncodeDefault(buff, SMS->Text, &p, true, NULL); + size = GSM_PackSevenBitsToEight(w, buff, buffer+(Layout.Text+off), p); + size += off; + size2 = (off*8 + w) / 7 + p; + dbgprintf("7 bit SMS, length %i, %i\n",size,size2); + dbgprintf("%s\n",DecodeUnicodeString(SMS->Text)); + if (size > GSM_MAX_8BIT_SMS_LENGTH) { + size = 0; size2 = 0; + } + break; + case SMS_Coding_Unicode: + /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) + * and GSM 03.38 section 4 */ + buffer[Layout.TPDCS] |= 0x08; + EncodeUnicodeSpecialNOKIAChars(buffer+(Layout.Text+off), SMS->Text, UnicodeLength(SMS->Text)); + size=size2=UnicodeLength(buffer+(Layout.Text+off))*2+off; +#ifdef DEBUG + dbgprintf("Unicode SMS, length %i\n",(size2-off)/2); + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, di.dl, buffer+(Layout.Text+off), size2-off); + dbgprintf("%s\n",DecodeUnicodeString(buffer+(Layout.Text+off))); +#endif + break; + } + + /* GSM 03.40 section 9.2.3.16 (TP-User-Data-Length) + * SMS->Length is: + - integer representation of the number od octets within the + user data when TP-User-Data is coded using 8 bit data + - the sum of the number of septets in UDH including any padding + and the number of septets in TP-User-Data in other case + */ + buffer[Layout.TPUDL] = size2; + return size; +} + +GSM_Error GSM_EncodeSMSFrame(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout, int *length, bool clear) +{ + int i; + + if (clear) { + /* Cleaning up to the SMS text */ + for (i=0;iPDU) { + case SMS_Submit: + buffer[Layout.firstbyte] |= 0x01; + break; + /* SMS_Status_Report when Submit sms should have delivery report */ + /* We DON'T CREATE FRAME FOR REAL SMS_STATUS_REPORT */ + case SMS_Status_Report: + buffer[Layout.firstbyte] |= 0x01; + /* GSM 03.40 section 9.2.3.5 (TP-Status-Raport-Request) */ + /* Set when want delivery report from SMSC */ + buffer[Layout.firstbyte] |= 0x20; + break; + case SMS_Deliver: + buffer[Layout.firstbyte] |= 0x00; + } + + /* GSM 03.40 section 9.2.3.17 (TP-Reply-Path) */ + if (SMS->ReplyViaSameSMSC) buffer[Layout.firstbyte] |= 0x80; + + if (Layout.Number!=255) { + buffer[Layout.Number] = GSM_PackSemiOctetNumber(SMS->Number,buffer+(Layout.Number+1),true); + dbgprintf("Recipient number \"%s\"\n",DecodeUnicodeString(SMS->Number)); + } + if (Layout.SMSCNumber!=255) { + buffer[Layout.SMSCNumber]=GSM_PackSemiOctetNumber(SMS->SMSC.Number,buffer+(Layout.SMSCNumber+1), false); + dbgprintf("SMSC number \"%s\"\n",DecodeUnicodeString(SMS->SMSC.Number)); + } + + /* Message Class*/ + /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */ + if (Layout.TPDCS != 255) { + if (SMS->Class>=0 && SMS->Class<5) buffer[Layout.TPDCS] |= (240+SMS->Class); + dbgprintf("SMS class %i\n",SMS->Class); + } + + if (Layout.TPVP != 255) { + /* GSM 03.40 section 9.2.3.3 (TP-Validity-Period-Format) */ + /* Bits 4 and 3: 10. TP-VP field present and integer represent (relative) */ + buffer[Layout.firstbyte] |= 0x10; + buffer[Layout.TPVP]=((unsigned char)SMS->SMSC.Validity.Relative); + dbgprintf("SMS validity %02x\n",SMS->SMSC.Validity.Relative); + } + + if (Layout.DateTime != 255) { + GSM_EncodeSMSDateTime(&SMS->DateTime, buffer+Layout.DateTime); + } + + if (Layout.TPMR != 255) { + dbgprintf("TPMR: %02x %i\n",SMS->MessageReference,SMS->MessageReference); + buffer[Layout.TPMR] = SMS->MessageReference; + } + + if (SMS->RejectDuplicates) { + /* GSM 03.40 section 9.2.3.25 (TP Reject Duplicates) */ + buffer[Layout.firstbyte] |= 0x04; + } + + if (Layout.TPPID != 255) { + buffer[Layout.TPPID] = 0; + if (SMS->ReplaceMessage > 0 && SMS->ReplaceMessage < 8) { + buffer[Layout.TPPID] = 0x40 + SMS->ReplaceMessage; + } + } + + /* size is the length of the data in octets including UDH */ + *length=GSM_EncodeSMSFrameText(SMS,buffer,Layout); +// if (*length == 0) return GE_UNKNOWN; + *length += Layout.Text; + + return ERR_NONE; +} + +/* ----------------- Some help functions ----------------------------------- */ + +void GSM_SetDefaultSMSData(GSM_SMSMessage *SMS) +{ + SMS->Class = -1; + SMS->SMSC.Location = 1; + SMS->SMSC.Format = SMS_FORMAT_Text; + SMS->SMSC.Validity.Format = SMS_Validity_RelativeFormat; + SMS->SMSC.Validity.Relative = SMS_VALID_Max_Time; + SMS->ReplyViaSameSMSC = false; + SMS->UDH.Type = UDH_NoUDH; + SMS->UDH.Length = 0; + SMS->UDH.Text[0] = 0; + SMS->UDH.ID8bit = 0; + SMS->UDH.ID16bit = 0; + SMS->UDH.PartNumber = 0; + SMS->UDH.AllParts = 0; + SMS->Coding = SMS_Coding_Default; + SMS->Text[0] = 0; + SMS->Text[1] = 0; + SMS->PDU = SMS_Submit; + SMS->RejectDuplicates = false; + SMS->MessageReference = 0; + SMS->ReplaceMessage = 0; + SMS->Length = 0; + + /* This part is required to save SMS */ + SMS->State = SMS_UnSent; + SMS->Location = 0; + SMS->Folder = 0x02; /*Outbox*/ + GSM_GetCurrentDateTime (&SMS->DateTime); + SMS->Name[0] = 0; + SMS->Name[1] = 0; +} + +/** + * GSM 03.40 section 9.2.3.24 + */ +void GSM_EncodeUDHHeader(GSM_UDHHeader *UDH) +{ + int i=0; + + if (UDH->Type == UDH_NoUDH) { + UDH->Length = 0; + return; + } + if (UDH->Type == UDH_UserUDH) { + UDH->Length = UDH->Text[0] + 1; + return; + } + while (true) { + if (UDHHeaders[i].Type==UDH_NoUDH) { + dbgprintf("Not supported UDH type\n"); + break; + } + if (UDHHeaders[i].Type!=UDH->Type) { + i++; + continue; + } + /* UDH Length */ + UDH->Text[0] = UDHHeaders[i].Length; + memcpy(UDH->Text+1, UDHHeaders[i].Text, UDHHeaders[i].Length); + UDH->Length = UDH->Text[0] + 1; + + if (UDHHeaders[i].ID8bit != -1) { + UDH->Text[UDHHeaders[i].ID8bit+1] = UDH->ID8bit % 256; + } else { + UDH->ID8bit = -1; + } + if (UDHHeaders[i].ID16bit != -1) { + UDH->Text[UDHHeaders[i].ID16bit+1] = UDH->ID16bit / 256; + UDH->Text[UDHHeaders[i].ID16bit+2] = UDH->ID16bit % 256; + } else { + UDH->ID16bit = -1; + } + if (UDHHeaders[i].PartNumber != -1) { + UDH->Text[UDHHeaders[i].PartNumber+1] = UDH->PartNumber; + } else { + UDH->PartNumber = -1; + } + if (UDHHeaders[i].AllParts != -1) { + UDH->Text[UDHHeaders[i].AllParts+1] = UDH->AllParts; + } else { + UDH->AllParts = -1; + } + break; + } +} + +/* How should editor hadle tabs in this file? Add editor commands here. + * vim: noexpandtab sw=8 ts=8 sts=8: + */ -- cgit v0.9.0.2