summaryrefslogtreecommitdiffabout
path: root/gammu/emb/common/service/sms/gsmsms.c
Side-by-side diff
Diffstat (limited to 'gammu/emb/common/service/sms/gsmsms.c') (more/less context) (ignore whitespace changes)
-rw-r--r--gammu/emb/common/service/sms/gsmsms.c663
1 files changed, 663 insertions, 0 deletions
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 <ctype.h>
+#include <string.h>
+#include <time.h>
+
+#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;w<tmp;w++) {
+ if (UDHHeaders[i].Text[w]!=UDH->Text[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;i<Layout.Text;i++) buffer[i] = 0;
+ }
+
+ /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
+ switch (SMS->PDU) {
+ 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:
+ */