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') diff --git a/gammu/emb/common/service/sms/gsmems.c b/gammu/emb/common/service/sms/gsmems.c new file mode 100644 index 0000000..a7e20f4 --- a/dev/null +++ b/gammu/emb/common/service/sms/gsmems.c @@ -0,0 +1,765 @@ +/* (c) 2002-2004 by Marcin Wiacek */ + +#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" +#include "gsmmulti.h" + +/* EMS Developers' Guidelines from www.sonyericsson.com + * docs from Alcatel + */ +GSM_Error GSM_EncodeEMSMultiPartSMS(GSM_MultiPartSMSInfo *Info, + GSM_MultiSMSMessage *SMS, + GSM_UDH UDHType) +{ + unsigned char Buffer[GSM_MAX_SMS_LENGTH*2*MAX_MULTI_SMS]; + int i,UsedText,j,Length,Width,Height,z,x,y; + unsigned int Len; + int Used,FreeText,FreeBytes,Width2,CopiedText,CopiedSMSText; + unsigned char UDHID; + GSM_Bitmap Bitmap,Bitmap2; + GSM_Ringtone Ring; + GSM_Coding_Type Coding = SMS_Coding_Default; + GSM_Phone_Bitmap_Types BitmapType; + MultiPartSMSEntry *Entry; + bool start; + GSM_DateTime Date; + +#ifdef DEBUG + if (UDHType != UDH_NoUDH) dbgprintf("linked EMS\n"); +#endif + + if (Info->UnicodeCoding) Coding = SMS_Coding_Unicode; + + /* Cleaning on the start */ + for (i=0;iSMS[i]); + SMS->SMS[i].UDH.Type = UDHType; + GSM_EncodeUDHHeader(&SMS->SMS[i].UDH); + SMS->SMS[i].Coding = Coding; + } + + /* Packing */ + for (i=0;iEntriesNum;i++) { + Entry = &Info->Entries[i]; + + switch (Entry->ID) { + case SMS_ConcatenatedTextLong: + case SMS_ConcatenatedTextLong16bit: + Len = 0; + while(1) { + if (Entry->Left || Entry->Right || + Entry->Center || Entry->Large || + Entry->Small || Entry->Bold || + Entry->Italic || Entry->Underlined || + Entry->Strikethrough) { + Buffer[0] = 0x0A; /* ID for text format */ + Buffer[1] = 0x03; /* length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + Buffer[3] = 0x00; /* how many chars */ + Buffer[4] = 0x00; /* formatting bits */ + if (Entry->Left) { + } else if (Entry->Right) { Buffer[4] |= 1; + } else if (Entry->Center) { Buffer[4] |= 2; + } else Buffer[4] |= 3; + if (Entry->Large) { Buffer[4] |= 4; + } else if (Entry->Small) { Buffer[4] |= 8;} + if (Entry->Bold) Buffer[4] |= 16; + if (Entry->Italic) Buffer[4] |= 32; + if (Entry->Underlined) Buffer[4] |= 64; + if (Entry->Strikethrough) Buffer[4] |= 128; + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,5,true,&UsedText,&CopiedText,&CopiedSMSText); + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], &UsedText, &FreeText, &FreeBytes); + if (FreeText == 0) continue; + } + GSM_AddSMS_Text_UDH(SMS,Coding,Entry->Buffer+Len*2,UnicodeLength(Entry->Buffer) - Len,false,&UsedText,&CopiedText,&CopiedSMSText); + if (Entry->Left || Entry->Right || + Entry->Center || Entry->Large || + Entry->Small || Entry->Bold || + Entry->Italic || Entry->Underlined || + Entry->Strikethrough) { + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-3] = UsedText; + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-2] = CopiedSMSText; + } + Len += CopiedText; + if (Len == UnicodeLength(Entry->Buffer)) break; + dbgprintf("%i %i\n",Len,UnicodeLength(Entry->Buffer)); + } + break; + case SMS_EMSPredefinedSound: + case SMS_EMSPredefinedAnimation: + if (Entry->ID == SMS_EMSPredefinedSound) { + Buffer[0] = 0x0B; /* ID for def.sound */ + } else { + Buffer[0] = 0x0D; /* ID for def.animation */ + } + Buffer[1] = 0x02; /* Length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + Buffer[3] = Entry->Number; /* Number of anim. */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-2] = UsedText; + break; + case SMS_EMSSonyEricssonSound: + case SMS_EMSSound10: + case SMS_EMSSound12: + if (Entry->Protected) { + Buffer[0] = 0x17; /* ID for ODI */ + Buffer[1] = 2; /* Length of rest */ + Buffer[2] = 1; /* Number of protected objects */ + Buffer[3] = 1; /* 1=Protected,0=Not protected */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + } + + Length = 128; /* 128 bytes is maximal length from specs */ + switch (Entry->ID) { + case SMS_EMSSound10: + Entry->RingtoneNotes = GSM_EncodeEMSSound(*Entry->Ringtone, Buffer+3, &Length, 1.0, true); + break; + case SMS_EMSSound12: + Entry->RingtoneNotes = GSM_EncodeEMSSound(*Entry->Ringtone, Buffer+3, &Length, 1.2, true); + break; + case SMS_EMSSonyEricssonSound: + Entry->RingtoneNotes = GSM_EncodeEMSSound(*Entry->Ringtone, Buffer+3, &Length, 0, true); + break; + default: + break; + } + + Buffer[0] = 0x0C; /* ID for EMS sound */ + Buffer[1] = Length+1; /* Length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,Length+3,true,&UsedText,&CopiedText,&CopiedSMSText); + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-Length-1] = UsedText; + break; + case SMS_EMSSonyEricssonSoundLong: + case SMS_EMSSound10Long: + case SMS_EMSSound12Long: + Ring = *Entry->Ringtone; + + /* First check if we can use classic format */ + Length = 128; /* 128 bytes is maximal length from specs */ + switch (Entry->ID) { + case SMS_EMSSound10Long: + Entry->RingtoneNotes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 1.0, true); + break; + case SMS_EMSSound12Long: + Entry->RingtoneNotes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 1.2, true); + break; + case SMS_EMSSonyEricssonSoundLong: + Entry->RingtoneNotes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 0, true); + break; + default: + break; + } + if (Entry->RingtoneNotes == Ring.NoteTone.NrCommands) { + if (Entry->Protected) { + Buffer[0] = 0x17; /* ID for ODI */ + Buffer[1] = 2; /* Length of rest */ + Buffer[2] = 1; /* Number of protected objects */ + Buffer[3] = 1; /* 1=Protected,0=Not protected */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + } + + Buffer[0] = 0x0C; /* ID for EMS sound */ + Buffer[1] = Length+1; /* Length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,Length+3,true,&UsedText,&CopiedText,&CopiedSMSText); + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-Length-1] = UsedText; + break; + } + + /* Find free place in first SMS */ + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], &UsedText, &FreeText, &FreeBytes); + Length = FreeBytes - 3; + if (Entry->Protected) Length = Length - 4; + if (Length < 0) Length = 128; + if (Length > 128) Length = 128; + + Ring = *Entry->Ringtone; + + /* Checking number of SMS */ + Used = 0; + FreeBytes = 0; + start = true; + while (1) { + if (FreeBytes != 0) { + z = 0; + for (j=FreeBytes;jID) { + case SMS_EMSSound10Long: + FreeBytes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 1.0, start); + break; + case SMS_EMSSound12Long: + FreeBytes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 1.2, start); + break; + case SMS_EMSSonyEricssonSoundLong: + FreeBytes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 0, start); + break; + default: + break; + } + start = false; + Used++; + } + dbgprintf("Used SMS: %i\n",Used); + + if (Entry->Protected) { + Buffer[0] = 0x17; /* ID for ODI */ + Buffer[1] = 2; /* Length of rest */ + Buffer[2] = Used+1; /* Number of protected objects */ + Buffer[3] = 1; /* 1=Protected,0=Not protected */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + } + + /* Save UPI UDH */ + Buffer[0] = 0x13; /* ID for UPI */ + Buffer[1] = 1; /* Length of rest */ + Buffer[2] = Used; /* Number of used parts */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,3,true,&UsedText,&CopiedText,&CopiedSMSText); + + /* Find free place in first SMS */ + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], &UsedText, &FreeText, &FreeBytes); + Length = FreeBytes - 3; + if (Length < 0) Length = 128; + if (Length > 128) Length = 128; + + Ring = *Entry->Ringtone; + + /* Saving */ + FreeBytes = 0; + start = true; + while (1) { + if (FreeBytes != 0) { + z = 0; + for (j=FreeBytes;jID) { + case SMS_EMSSound10Long: + FreeBytes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 1.0, start); + break; + case SMS_EMSSound12Long: + FreeBytes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 1.2, start); + break; + case SMS_EMSSonyEricssonSoundLong: + FreeBytes = GSM_EncodeEMSSound(Ring, Buffer+3, &Length, 0, start); + break; + default: + break; + } + Buffer[0] = 0x0C; /* ID for EMS sound */ + Buffer[1] = Length+1; /* Length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,Length+3,true,&UsedText,&CopiedText,&CopiedSMSText); + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-Length-1] = UsedText; + start = false; + } + + Entry->RingtoneNotes = Entry->Ringtone->NoteTone.NrCommands; + + break; + case SMS_EMSAnimation: + if (Entry->Protected) { + Buffer[0] = 0x17; /* ID for ODI */ + Buffer[1] = 2; /* Length of rest */ + Buffer[2] = 1; /* Number of protected objects */ + Buffer[3] = 1; /* 1=Protected,0=Not protected */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + } + + if (Entry->Bitmap->Bitmap[0].BitmapWidth > 8 || Entry->Bitmap->Bitmap[0].BitmapHeight > 8) { + BitmapType = GSM_EMSMediumPicture; /* Bitmap 16x16 */ + Buffer[0] = 0x0E; /* ID for 16x16 animation */ + } else { + BitmapType = GSM_EMSSmallPicture; /* Bitmap 8x8 */ + Buffer[0] = 0x0F; /* ID for 8x8 animation */ + } + Length = PHONE_GetBitmapSize(BitmapType,0,0); + + Buffer[1] = Length*Entry->Bitmap->Number + 1; /* Length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + for (j=0;jBitmap->Number;j++) { + PHONE_EncodeBitmap(BitmapType, Buffer+3+j*Length, &Entry->Bitmap->Bitmap[j]); + } + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,3+Length*Entry->Bitmap->Number,true,&UsedText,&CopiedText,&CopiedSMSText); + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-1-Length*Entry->Bitmap->Number] = UsedText; + break; + case SMS_EMSFixedBitmap: + if (Entry->Protected) { + Buffer[0] = 0x17; /* ID for ODI */ + Buffer[1] = 2; /* Length of rest */ + Buffer[2] = 1; /* Number of protected objects */ + Buffer[3] = 1; /* 1=Protected,0=Not protected */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + } + + if (Entry->Bitmap->Bitmap[0].BitmapWidth > 16 || Entry->Bitmap->Bitmap[0].BitmapHeight > 16) { + BitmapType = GSM_EMSBigPicture; /* Bitmap 32x32 */ + Buffer[0] = 0x10; /* ID for EMS bitmap */ + } else { + BitmapType = GSM_EMSMediumPicture; /* Bitmap 16x16 */ + Buffer[0] = 0x11; /* ID for EMS bitmap */ + } + Length = PHONE_GetBitmapSize(BitmapType,0,0); + PHONE_GetBitmapWidthHeight(BitmapType, &Width, &Height); + + Buffer[1] = Length + 1; /* Length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + PHONE_EncodeBitmap(BitmapType,Buffer+3, &Entry->Bitmap->Bitmap[0]); + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,3+Length,true,&UsedText,&CopiedText,&CopiedSMSText); + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-1-Length] = UsedText; + break; + case SMS_EMSVariableBitmapLong: + BitmapType = GSM_EMSVariablePicture; + Width = Entry->Bitmap->Bitmap[0].BitmapWidth; + Height = Entry->Bitmap->Bitmap[0].BitmapHeight; + Bitmap = Entry->Bitmap->Bitmap[0]; + + /* First check if we can use classical format */ + while (1) { + /* Width should be multiply of 8 */ + while (Width % 8 != 0) Width--; + + /* specs */ + if (Width <= 96 && Height <= 128) break; + + Height--; + } + Length = PHONE_GetBitmapSize(BitmapType,Width,Height); + if (Length <= 128) { + if (Entry->Protected) { + Buffer[0] = 0x17; /* ID for ODI */ + Buffer[1] = 2; /* Length of rest */ + Buffer[2] = 1; /* Number of protected objects */ + Buffer[3] = 1; /* 1=Protected,0=Not protected */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + } + + Buffer[0] = 0x12; /* ID for EMS bitmap */ + Buffer[1] = Length + 3; /* Length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + Buffer[3] = Width/8; /* Bitmap width/8 */ + Buffer[4] = Height; /* Bitmap height */ + + GSM_ResizeBitmap(&Bitmap, &Entry->Bitmap->Bitmap[0], Width, Height); +#ifdef DEBUG + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) GSM_PrintBitmap(di.df,&Bitmap); +#endif + PHONE_EncodeBitmap(BitmapType,Buffer+5, &Bitmap); + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,5+Length,true,&UsedText,&CopiedText,&CopiedSMSText); + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-3-Length] = UsedText; + break; + } + + /* Find free place in first SMS */ + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], &UsedText, &FreeText, &FreeBytes); + Used = 0; + Length = FreeBytes - 3; + if (Entry->Protected) Length = Length - 4; + if (Length < 0) Length = 128; + if (Length > 128) Length = 128; + + /* Checking number of SMS */ + FreeBytes = 0; + while (FreeBytes != Width) { + Width2 = 8; + while (FreeBytes + Width2 != Width) { + if (PHONE_GetBitmapSize(BitmapType,Width2+8,Height) > Length) break; + + Width2 = Width2 + 8; + } + FreeBytes = FreeBytes + Width2; + Length = 128; + Used ++; + } + dbgprintf("Used SMS: %i\n",Used); + + if (Entry->Protected) { + Buffer[0] = 0x17; /* ID for ODI */ + Buffer[1] = 2; /* Length of rest */ + Buffer[2] = Used+1; /* Number of protected objects */ + Buffer[3] = 1; /* 1=Protected,0=Not protected */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + } + + /* Save UPI UDH */ + Buffer[0] = 0x13; /* ID for UPI */ + Buffer[1] = 1; /* Length of rest */ + Buffer[2] = Used; /* Number of used parts */ + + /* Find free place in first SMS */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,3,true,&UsedText,&CopiedText,&CopiedSMSText); + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], &UsedText, &FreeText, &FreeBytes); + Length = FreeBytes - 3; + if (Length < 0) Length = 128; + if (Length > 128) Length = 128; + +#ifdef DEBUG + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) GSM_PrintBitmap(di.df,&Bitmap); +#endif + + /* Saving SMS */ + FreeBytes = 0; + while (FreeBytes != Width) { + Width2 = 8; + while (FreeBytes + Width2 != Width) { + if (PHONE_GetBitmapSize(BitmapType,Width2+8,Height) > Length) break; + + Width2 = Width2 + 8; + } + + /* Copying part of bitmap to new structure */ + Bitmap2.BitmapWidth = Width2; + Bitmap2.BitmapHeight = Height; + GSM_ClearBitmap(&Bitmap2); + for (x=0;xSMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-3-Length] = UsedText; + + FreeBytes = FreeBytes + Width2; + Length = 128; + } + break; + case SMS_EMSVariableBitmap: + if (Entry->Protected) { + Buffer[0] = 0x17; /* ID for ODI */ + Buffer[1] = 2; /* Length of rest */ + Buffer[2] = 1; /* Number of protected objects */ + Buffer[3] = 1; /* 1=Protected,0=Not protected */ + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,4,true,&UsedText,&CopiedText,&CopiedSMSText); + } + + BitmapType = GSM_EMSVariablePicture; + Width = Entry->Bitmap->Bitmap[0].BitmapWidth; + Height = Entry->Bitmap->Bitmap[0].BitmapHeight; + + while (1) { + /* Width should be multiply of 8 */ + while (Width % 8 != 0) Width--; + + /* specs */ + if (PHONE_GetBitmapSize(BitmapType,Width,Height) <= 128) break; + + Height--; + } + + Length = PHONE_GetBitmapSize(BitmapType,Width,Height); + + Buffer[0] = 0x12; /* ID for EMS bitmap */ + Buffer[1] = Length + 3; /* Length of rest */ + Buffer[2] = 0x00; /* Position in EMS msg */ + Buffer[3] = Width/8; /* Bitmap width/8 */ + Buffer[4] = Height; /* Bitmap height */ + + GSM_ResizeBitmap(&Bitmap, &Entry->Bitmap->Bitmap[0], Width, Height); +#ifdef DEBUG + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) GSM_PrintBitmap(di.df,&Bitmap); +#endif + PHONE_EncodeBitmap(BitmapType,Buffer+5, &Bitmap); + GSM_AddSMS_Text_UDH(SMS,Coding,Buffer,5+Length,true,&UsedText,&CopiedText,&CopiedSMSText); + SMS->SMS[SMS->Number].UDH.Text[SMS->SMS[SMS->Number].UDH.Length-3-Length] = UsedText; + break; + default: + break; + } + } + + SMS->Number++; + + if (UDHType == UDH_ConcatenatedMessages) { + UDHID = GSM_MakeSMSIDFromTime(); + for (i=0;iNumber;i++) { + SMS->SMS[i].UDH.Text[2+1] = UDHID; + SMS->SMS[i].UDH.Text[3+1] = SMS->Number; + SMS->SMS[i].UDH.Text[4+1] = i+1; + } + } + if (UDHType == UDH_ConcatenatedMessages16bit) { + UDHID = GSM_MakeSMSIDFromTime(); + GSM_GetCurrentDateTime (&Date); + for (i=0;iNumber;i++) { + SMS->SMS[i].UDH.Text[2+1] = Date.Hour; + SMS->SMS[i].UDH.Text[3+1] = UDHID; + SMS->SMS[i].UDH.Text[4+1] = SMS->Number; + SMS->SMS[i].UDH.Text[5+1] = i+1; + } + } + +#ifdef DEBUG + dbgprintf("SMS number is %i\n",SMS->Number); + for (i=0;iNumber;i++) { + dbgprintf("UDH length %i\n",SMS->SMS[i].UDH.Length); + DumpMessage(di.df, di.dl, SMS->SMS[i].UDH.Text, SMS->SMS[i].UDH.Length); + dbgprintf("SMS length %i\n",UnicodeLength(SMS->SMS[i].Text)*2); + DumpMessage(di.df, di.dl, SMS->SMS[i].Text, UnicodeLength(SMS->SMS[i].Text)*2); + } +#endif + return ERR_NONE; +} + +static bool AddEMSText(GSM_SMSMessage *SMS, GSM_MultiPartSMSInfo *Info, int *Pos, int Len) +{ + int BufferLen; + + if (Len==0) return true; + + if (Info->Entries[Info->EntriesNum].ID!=SMS_ConcatenatedTextLong && + Info->Entries[Info->EntriesNum].ID!=0) { + (Info->EntriesNum)++; + } + BufferLen = UnicodeLength(Info->Entries[Info->EntriesNum].Buffer)*2; + switch (SMS->Coding) { + case SMS_Coding_8bit: +// memcpy(Info->Entries[Info->EntriesNum].Buffer+BufferLen,SMS->Text+(*Pos),Len); +// BufferLen+=Len; +// (*Pos)+=Len; + break; + case SMS_Coding_Unicode: + case SMS_Coding_Default: + Info->Entries[Info->EntriesNum].Buffer = realloc(Info->Entries[Info->EntriesNum].Buffer, BufferLen + (Len * 2) + 2); + if (Info->Entries[Info->EntriesNum].Buffer == NULL) return false; + memcpy(Info->Entries[Info->EntriesNum].Buffer + BufferLen, SMS->Text + (*Pos) *2, Len * 2); + BufferLen += Len * 2; + break; + } + (*Pos)+=Len; + Info->Entries[Info->EntriesNum].Buffer[BufferLen] = 0; + Info->Entries[Info->EntriesNum].Buffer[BufferLen+1] = 0; + Info->Entries[Info->EntriesNum].ID = SMS_ConcatenatedTextLong; + return true; +} + +bool GSM_DecodeEMSMultiPartSMS(GSM_MultiPartSMSInfo *Info, + GSM_MultiSMSMessage *SMS) +{ + int i, w, Pos, z, UPI = 1, width, height; + bool RetVal = false, NewPicture = true; + GSM_Phone_Bitmap_Types BitmapType; + GSM_Bitmap Bitmap,Bitmap2; + + for (i=0;iEntries[i].ID = 0; + } + + for (i=0;iNumber;i++) { + Pos = 0; + w = 1; + while (w < SMS->SMS[i].UDH.Length) { + if (Info->EntriesNum + 1 == MAX_MULTI_SMS) { + dbgprintf("Couldn't parse SMS, contains too many EMS parts!\n"); + return false; + } + switch(SMS->SMS[i].UDH.Text[w]) { + case 0x00: + dbgprintf("UDH part - linked SMS with 8 bit ID\n"); + break; + case 0x08: + dbgprintf("UDH part - linked SMS with 16 bit ID\n"); + break; +// case 0x0A: +// dbgprintf("UDH part - EMS text formatting\n"); +// break; + case 0x0B: + dbgprintf("UDH part - default EMS sound\n"); + if (SMS->SMS[i].UDH.Text[w+2] > Pos) { + z = Pos; + if (!AddEMSText(&SMS->SMS[i], Info, &Pos, SMS->SMS[i].UDH.Text[w+2]-z)) return false; + } + if (Info->Entries[Info->EntriesNum].ID != 0) (Info->EntriesNum)++; + Info->Entries[Info->EntriesNum].Number = SMS->SMS[i].UDH.Text[w+3]; + Info->Entries[Info->EntriesNum].ID = SMS_EMSPredefinedSound; + RetVal = true; + break; +// case 0x0C: +// dbgprintf("UDH part - EMS sound\n"); +// break; + case 0x0D: + dbgprintf("UDH part - default EMS animation\n"); + if (SMS->SMS[i].UDH.Text[w+2] > Pos) { + z = Pos; + if (!AddEMSText(&SMS->SMS[i], Info, &Pos, SMS->SMS[i].UDH.Text[w+2]-z)) return false; + } + if (Info->Entries[Info->EntriesNum].ID != 0) (Info->EntriesNum)++; + Info->Entries[Info->EntriesNum].Number = SMS->SMS[i].UDH.Text[w+3]; + Info->Entries[Info->EntriesNum].ID = SMS_EMSPredefinedAnimation; + RetVal = true; + break; + case 0x0E: + case 0x0F: + if (SMS->SMS[i].UDH.Text[w] == 0x0E) { + dbgprintf("UDH part - EMS 16x16 animation\n"); + BitmapType = GSM_EMSMediumPicture; + } else { + dbgprintf("UDH part - EMS 8x8 animation\n"); + BitmapType = GSM_EMSSmallPicture; + } + dbgprintf("Position - %i\n",SMS->SMS[i].UDH.Text[w+2]); + if (SMS->SMS[i].UDH.Text[w+2] > Pos) { + z = Pos; + if (!AddEMSText(&SMS->SMS[i], Info, &Pos, SMS->SMS[i].UDH.Text[w+2]-z)) return false; + } + (Info->EntriesNum)++; + Info->Entries[Info->EntriesNum].ID = SMS_EMSAnimation; + Info->Entries[Info->EntriesNum].Bitmap = (GSM_MultiBitmap *)malloc(sizeof(GSM_MultiBitmap)); + if (Info->Entries[Info->EntriesNum].Bitmap == NULL) return false; + Info->Entries[Info->EntriesNum].Bitmap->Number = 0; + for (z=0;z<((SMS->SMS[i].UDH.Text[w+1]-1)/PHONE_GetBitmapSize(BitmapType,0,0));z++) { + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[z].Type = GSM_PictureImage; + PHONE_DecodeBitmap(BitmapType, + SMS->SMS[i].UDH.Text + w + 3 + PHONE_GetBitmapSize(BitmapType,0,0) * z, + &Info->Entries[Info->EntriesNum].Bitmap->Bitmap[z]); + Info->Entries[Info->EntriesNum].Bitmap->Number++; + } + RetVal = true; + break; + case 0x10: + case 0x11: + if (SMS->SMS[i].UDH.Text[w] == 0x10) { + dbgprintf("UDH part - EMS 32x32 picture\n"); + BitmapType = GSM_EMSBigPicture; + } else { + dbgprintf("UDH part - EMS 16x16 picture\n"); + BitmapType = GSM_EMSMediumPicture; + } + dbgprintf("Position - %i\n",SMS->SMS[i].UDH.Text[w+2]); + if (SMS->SMS[i].UDH.Text[w+2] > Pos) { + z = Pos; + if (!AddEMSText(&SMS->SMS[i], Info, &Pos, SMS->SMS[i].UDH.Text[w+2]-z)) return false; + } + if (Info->Entries[Info->EntriesNum].ID != 0) (Info->EntriesNum)++; + Info->Entries[Info->EntriesNum].Bitmap = (GSM_MultiBitmap *)malloc(sizeof(GSM_MultiBitmap)); + if (Info->Entries[Info->EntriesNum].Bitmap == NULL) return false; + PHONE_DecodeBitmap(BitmapType, + SMS->SMS[i].UDH.Text + w + 3, + &Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0]); + Info->Entries[Info->EntriesNum].Bitmap->Number = 1; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].Text[0] = 0; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].Text[1] = 0; + Info->Entries[Info->EntriesNum].ID = SMS_EMSFixedBitmap; + RetVal = true; + break; + case 0x12: + dbgprintf("UDH part - EMS variable width bitmap\n"); + if (SMS->SMS[i].UDH.Text[w+2] > Pos) { + z = Pos; + if (!AddEMSText(&SMS->SMS[i], Info, &Pos, SMS->SMS[i].UDH.Text[w+2]-z)) return false; + } + if (NewPicture) { + (Info->EntriesNum)++; + Info->Entries[Info->EntriesNum].Bitmap->Number = 0; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].BitmapWidth = 0; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].BitmapHeight = 0; + } + Bitmap.BitmapWidth = SMS->SMS[i].UDH.Text[w+3]*8; + Bitmap.BitmapHeight = SMS->SMS[i].UDH.Text[w+4]; + Info->Entries[Info->EntriesNum].Bitmap = (GSM_MultiBitmap *)malloc(sizeof(GSM_MultiBitmap)); + if (Info->Entries[Info->EntriesNum].Bitmap == NULL) return false; + if (NewPicture) { + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].BitmapWidth = Bitmap.BitmapWidth; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].BitmapHeight = Bitmap.BitmapHeight; + PHONE_DecodeBitmap(GSM_EMSVariablePicture, + SMS->SMS[i].UDH.Text + w + 5, + &Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0]); + } else { + PHONE_DecodeBitmap(GSM_EMSVariablePicture, + SMS->SMS[i].UDH.Text + w + 5, + &Bitmap); + Bitmap2 = Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0]; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].BitmapWidth = Bitmap.BitmapWidth+Bitmap2.BitmapWidth; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].BitmapHeight = Bitmap2.BitmapHeight; + for (width=0;widthEntries[Info->EntriesNum].Bitmap->Bitmap[0],width,height); + } else { + GSM_ClearPointBitmap(&Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0],width,height); + } + } + } + for (width=0;widthEntries[Info->EntriesNum].Bitmap->Bitmap[0],width+Bitmap2.BitmapWidth,height); + } else { + GSM_ClearPointBitmap(&Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0],width+Bitmap2.BitmapWidth,height); + } + } + } + } + if (UPI == 1) { + Info->Entries[Info->EntriesNum].Bitmap->Number = 1; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].Text[0] = 0; + Info->Entries[Info->EntriesNum].Bitmap->Bitmap[0].Text[1] = 0; + Info->Entries[Info->EntriesNum].ID = SMS_EMSVariableBitmap; + RetVal = true; + NewPicture = true; + dbgprintf("New variable picture\n"); + } else { + NewPicture = false; + UPI--; + } + break; + case 0x13: + dbgprintf("UDH part - UPI\n"); + dbgprintf("Value %i\n",SMS->SMS[i].UDH.Text[w+2]); + UPI = SMS->SMS[i].UDH.Text[w+2]; + break; + case 0x17: + dbgprintf("UDH part - Object Distribution Indicator (Media Rights Protecting) ignored now\n"); + break; + default: + dbgprintf("UDH part - block %02x\n",SMS->SMS[i].UDH.Text[w]); + Info->Unknown = true; + } /* switch */ + w=w+SMS->SMS[i].UDH.Text[w+1]+2; + } /* while */ + if (!AddEMSText(&SMS->SMS[i], Info, &Pos, SMS->SMS[i].Length-Pos)) return false; + RetVal = true; + } + if (RetVal) (Info->EntriesNum)++; + return RetVal; +} + +/* How should editor hadle tabs in this file? Add editor commands here. + * vim: noexpandtab sw=8 ts=8 sts=8: + */ diff --git a/gammu/emb/common/service/sms/gsmems.h b/gammu/emb/common/service/sms/gsmems.h new file mode 100644 index 0000000..6701d28 --- a/dev/null +++ b/gammu/emb/common/service/sms/gsmems.h @@ -0,0 +1,20 @@ +/* (c) 2002-2003 by Marcin Wiacek */ + +#ifndef __gsm_ems_h +#define __gsm_ems_h + +#include "../../gsmcomon.h" +#include "gsmmulti.h" + +GSM_Error GSM_EncodeEMSMultiPartSMS(GSM_MultiPartSMSInfo *Info, + GSM_MultiSMSMessage *SMS, + GSM_UDH UDHType); + +bool GSM_DecodeEMSMultiPartSMS(GSM_MultiPartSMSInfo *Info, + GSM_MultiSMSMessage *SMS); + +#endif + +/* How should editor hadle tabs in this file? Add editor commands here. + * vim: noexpandtab sw=8 ts=8 sts=8: + */ diff --git a/gammu/emb/common/service/sms/gsmmulti.c b/gammu/emb/common/service/sms/gsmmulti.c new file mode 100644 index 0000000..6c1cdcd --- a/dev/null +++ b/gammu/emb/common/service/sms/gsmmulti.c @@ -0,0 +1,1148 @@ +/* (c) 2002-2004 by Marcin Wiacek */ + +#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" +#include "gsmmulti.h" +#include "gsmems.h" + +/* ----------------- Splitting SMS into parts ------------------------------ */ + +unsigned char GSM_MakeSMSIDFromTime(void) +{ + GSM_DateTime Date; + unsigned char retval; + + GSM_GetCurrentDateTime (&Date); + retval = Date.Second; + switch (Date.Minute/10) { + case 2: case 7: retval = retval + 60; break; + case 4: case 8: retval = retval + 120; break; + case 9: case 5: case 0: retval = retval + 180; break; + } + retval += Date.Minute/10; + return retval; +} + +void GSM_Find_Free_Used_SMS2(GSM_Coding_Type Coding,GSM_SMSMessage SMS, int *UsedText, int *FreeText, int *FreeBytes) +{ + int UsedBytes; + + switch (Coding) { + case SMS_Coding_Default: + FindDefaultAlphabetLen(SMS.Text,&UsedBytes,UsedText,500); + UsedBytes = *UsedText * 7 / 8; + if (UsedBytes * 8 / 7 != *UsedText) UsedBytes++; + *FreeBytes = GSM_MAX_8BIT_SMS_LENGTH - SMS.UDH.Length - UsedBytes; + *FreeText = (GSM_MAX_8BIT_SMS_LENGTH - SMS.UDH.Length) * 8 / 7 - *UsedText; + break; + case SMS_Coding_Unicode: + *UsedText = UnicodeLength(SMS.Text); + UsedBytes = *UsedText * 2; + *FreeBytes = GSM_MAX_8BIT_SMS_LENGTH - SMS.UDH.Length - UsedBytes; + *FreeText = *FreeBytes / 2; + break; + case SMS_Coding_8bit: + *UsedText = UsedBytes = SMS.Length; + *FreeBytes = GSM_MAX_8BIT_SMS_LENGTH - SMS.UDH.Length - UsedBytes; + *FreeText = *FreeBytes; + break; + } + dbgprintf("UDH len %i, UsedBytes %i, FreeText %i, UsedText %i, FreeBytes %i\n",SMS.UDH.Length,UsedBytes,*FreeText,*UsedText,*FreeBytes); +} + +GSM_Error GSM_AddSMS_Text_UDH(GSM_MultiSMSMessage *SMS, + GSM_Coding_Type Coding, + char *Buffer, + int BufferLen, + bool UDH, + int *UsedText, + int *CopiedText, + int *CopiedSMSText) +{ + int FreeText,FreeBytes,Copy,i,j; + + dbgprintf("Checking used\n"); + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], UsedText, &FreeText, &FreeBytes); + + if (UDH) { + dbgprintf("Adding UDH\n"); + if (FreeBytes - BufferLen <= 0) { + dbgprintf("Going to the new SMS\n"); + SMS->Number++; + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], UsedText, &FreeText, &FreeBytes); + } + if (SMS->SMS[SMS->Number].UDH.Length == 0) { + SMS->SMS[SMS->Number].UDH.Length = 1; + SMS->SMS[SMS->Number].UDH.Text[0] = 0x00; + } + memcpy(SMS->SMS[SMS->Number].UDH.Text+SMS->SMS[SMS->Number].UDH.Length,Buffer,BufferLen); + SMS->SMS[SMS->Number].UDH.Length += BufferLen; + SMS->SMS[SMS->Number].UDH.Text[0] += BufferLen; + SMS->SMS[SMS->Number].UDH.Type = UDH_UserUDH; + dbgprintf("UDH added %i\n",BufferLen); + } else { + dbgprintf("Adding text\n"); + if (FreeText == 0) { + dbgprintf("Going to the new SMS\n"); + SMS->Number++; + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], UsedText, &FreeText, &FreeBytes); + } + + Copy = FreeText; + dbgprintf("copy %i\n",Copy); + if (BufferLen < Copy) Copy = BufferLen; + dbgprintf("copy %i\n",Copy); + + switch (Coding) { + case SMS_Coding_Default: + FindDefaultAlphabetLen(Buffer,&i,&j,FreeText); + dbgprintf("def length %i %i\n",i,j); + SMS->SMS[SMS->Number].Text[UnicodeLength(SMS->SMS[SMS->Number].Text)*2+i*2] = 0; + SMS->SMS[SMS->Number].Text[UnicodeLength(SMS->SMS[SMS->Number].Text)*2+i*2+1] = 0; + memcpy(SMS->SMS[SMS->Number].Text+UnicodeLength(SMS->SMS[SMS->Number].Text)*2,Buffer,i*2); + *CopiedText = i; + *CopiedSMSText = j; + SMS->SMS[SMS->Number].Length += i; + break; + case SMS_Coding_Unicode: + SMS->SMS[SMS->Number].Text[UnicodeLength(SMS->SMS[SMS->Number].Text)*2+Copy*2] = 0; + SMS->SMS[SMS->Number].Text[UnicodeLength(SMS->SMS[SMS->Number].Text)*2+Copy*2+1] = 0; + memcpy(SMS->SMS[SMS->Number].Text+UnicodeLength(SMS->SMS[SMS->Number].Text)*2,Buffer,Copy*2); + *CopiedText = *CopiedSMSText = Copy; + SMS->SMS[SMS->Number].Length += Copy; + break; + case SMS_Coding_8bit: + memcpy(SMS->SMS[SMS->Number].Text+SMS->SMS[SMS->Number].Length,Buffer,Copy); + SMS->SMS[SMS->Number].Length += Copy; + *CopiedText = *CopiedSMSText = Copy; + break; + } + dbgprintf("Text added\n"); + } + + dbgprintf("Checking on the end\n"); + GSM_Find_Free_Used_SMS2(Coding,SMS->SMS[SMS->Number], UsedText, &FreeText, &FreeBytes); + + return ERR_NONE; +} + +void GSM_MakeMultiPartSMS(GSM_MultiSMSMessage *SMS, + unsigned char *MessageBuffer, + int MessageLength, + GSM_UDH UDHType, + GSM_Coding_Type Coding, + int Class, + unsigned char ReplaceMessage) +{ + int j,Len,UsedText,CopiedText,CopiedSMSText; + unsigned char UDHID; + GSM_DateTime Date; + + Len = 0; + while(1) { + GSM_SetDefaultSMSData(&SMS->SMS[SMS->Number]); + SMS->SMS[SMS->Number].Class = Class; + SMS->SMS[SMS->Number].Coding = Coding; + + SMS->SMS[SMS->Number].UDH.Type = UDHType; + GSM_EncodeUDHHeader(&SMS->SMS[SMS->Number].UDH); + + if (Coding == SMS_Coding_8bit) { + GSM_AddSMS_Text_UDH(SMS,Coding,MessageBuffer+Len,MessageLength - Len,false,&UsedText,&CopiedText,&CopiedSMSText); + } else { + GSM_AddSMS_Text_UDH(SMS,Coding,MessageBuffer+Len*2,MessageLength - Len,false,&UsedText,&CopiedText,&CopiedSMSText); + } + Len += CopiedText; + dbgprintf("%i %i\n",Len,MessageLength); + if (Len == MessageLength) break; + if (SMS->Number == MAX_MULTI_SMS) break; + SMS->Number++; + } + + SMS->Number++; + + UDHID = GSM_MakeSMSIDFromTime(); + GSM_GetCurrentDateTime (&Date); + for (j=0;jNumber;j++) { + SMS->SMS[j].UDH.Type = UDHType; + SMS->SMS[j].UDH.ID8bit = UDHID; + SMS->SMS[j].UDH.ID16bit = UDHID + 256 * Date.Hour; + SMS->SMS[j].UDH.PartNumber = j+1; + SMS->SMS[j].UDH.AllParts = SMS->Number; + GSM_EncodeUDHHeader(&SMS->SMS[j].UDH); + } + if (SMS->Number == 1) SMS->SMS[0].ReplaceMessage = ReplaceMessage; +} + +/* Calculates number of SMS and number of left chars in SMS */ +void GSM_SMSCounter(int MessageLength, + unsigned char *MessageBuffer, + GSM_UDH UDHType, + GSM_Coding_Type Coding, + int *SMSNum, + int *CharsLeft) +{ + int UsedText,FreeBytes; + GSM_MultiSMSMessage MultiSMS; + + MultiSMS.Number = 0; + GSM_MakeMultiPartSMS(&MultiSMS,MessageBuffer,MessageLength,UDHType,Coding,-1,false); + GSM_Find_Free_Used_SMS2(Coding,MultiSMS.SMS[MultiSMS.Number-1], &UsedText, CharsLeft, &FreeBytes); + *SMSNum = MultiSMS.Number; +} + +/* Nokia Smart Messaging 3.0 */ +static void GSM_EncodeSMS30MultiPartSMS(GSM_MultiPartSMSInfo *Info, + char *Buffer, int *Length) +{ + int len; + + /*SM version. Here 3.0*/ + Buffer[(*Length)++] = 0x30; + + if (Info->Entries[0].ID == SMS_NokiaProfileLong) { + if (Info->Entries[0].Buffer != NULL) { + if (Info->Entries[0].Buffer[0]!=0x00 || Info->Entries[0].Buffer[1]!=0x00) { + Buffer[(*Length)++] = SM30_PROFILENAME; + Buffer[(*Length)++] = 0x00; + Buffer[(*Length)++] = 2*UnicodeLength(Info->Entries[0].Buffer); + CopyUnicodeString(Buffer+(*Length),Info->Entries[0].Buffer); + *Length = *Length + 2*UnicodeLength(Info->Entries[0].Buffer); + } + } + if (Info->Entries[0].Ringtone != NULL) { + Buffer[(*Length)++] = SM30_RINGTONE; + /* Length for this part later will be changed */ + Buffer[(*Length)++] = 0x01; + Buffer[(*Length)++] = 0x00; + /* Smart Messaging 3.0 says: 16*9=144 bytes, + * but on 3310 4.02 it was possible to save about 196 chars + * (without cutting) */ + len = 196; + Info->Entries[0].RingtoneNotes=GSM_EncodeNokiaRTTLRingtone(*Info->Entries[0].Ringtone,Buffer+(*Length),&len); + Buffer[(*Length)-2] = len / 256; + Buffer[(*Length)-1] = len % 256; + *Length = *Length + len; + } + } + if (Info->Entries[0].Bitmap != NULL) { + if (Info->Entries[0].ID == SMS_NokiaPictureImageLong) { + Buffer[(*Length)++] = SM30_OTA; + } else { + Buffer[(*Length)++] = SM30_SCREENSAVER; + } + Buffer[(*Length)++] = 0x01; + Buffer[(*Length)++] = 0x00; + NOKIA_CopyBitmap(GSM_NokiaPictureImage, &Info->Entries[0].Bitmap->Bitmap[0], Buffer, Length); + if (Info->Entries[0].Bitmap->Bitmap[0].Text[0]!=0 || Info->Entries[0].Bitmap->Bitmap[0].Text[1]!=0) { + if (Info->UnicodeCoding) { + Buffer[(*Length)++] = SM30_UNICODETEXT; + /* Length for text part */ + Buffer[(*Length)++] = 0x00; + Buffer[(*Length)++] = UnicodeLength(Info->Entries[0].Bitmap->Bitmap[0].Text)*2; + memcpy(Buffer+(*Length),Info->Entries[0].Bitmap->Bitmap[0].Text,UnicodeLength(Info->Entries[0].Bitmap->Bitmap[0].Text)*2); + *Length = *Length + UnicodeLength(Info->Entries[0].Bitmap->Bitmap[0].Text)*2; + } else { + /*ID for ISO-8859-1 text*/ + Buffer[(*Length)++] = SM30_ISOTEXT; + Buffer[(*Length)++] = 0x00; + Buffer[(*Length)++] = UnicodeLength(Info->Entries[0].Bitmap->Bitmap[0].Text); + memcpy(Buffer+(*Length),DecodeUnicodeString(Info->Entries[0].Bitmap->Bitmap[0].Text),UnicodeLength(Info->Entries[0].Bitmap->Bitmap[0].Text)); + *Length = *Length +UnicodeLength(Info->Entries[0].Bitmap->Bitmap[0].Text); + } + } + } +} + +/* Alcatel docs from www.alcatel.com/wap/ahead */ +GSM_Error GSM_EncodeAlcatelMultiPartSMS(GSM_MultiSMSMessage *SMS, + unsigned char *Data, + int Len, + unsigned char *Name, + int Type) +{ + unsigned char buff[100],UDHID; + int p,i; + GSM_UDHHeader MyUDH; + + for (i=0;iSMS[i]); + SMS->SMS[i].UDH.Type = UDH_UserUDH; + SMS->SMS[i].UDH.Text[1] = 0x80; /* Alcatel */ + p = UnicodeLength(Name); + EncodeDefault(buff, Name, &p, true, NULL); + SMS->SMS[i].UDH.Text[2] = GSM_PackSevenBitsToEight(0, buff, SMS->SMS[i].UDH.Text+3, p) + 4; + SMS->SMS[i].UDH.Text[3] = GSM_PackSevenBitsToEight(0, buff, SMS->SMS[i].UDH.Text+3, p); + SMS->SMS[i].UDH.Text[4] = Type; + SMS->SMS[i].UDH.Text[5] = Len / 256; + SMS->SMS[i].UDH.Text[6] = Len % 256; + SMS->SMS[i].UDH.Text[0] = 6 + SMS->SMS[i].UDH.Text[3]; + SMS->SMS[i].UDH.Length = SMS->SMS[i].UDH.Text[0] + 1; + + if (Len > 140 - SMS->SMS[i].UDH.Length) { + MyUDH.Type = UDH_ConcatenatedMessages; + GSM_EncodeUDHHeader(&MyUDH); + + memcpy(SMS->SMS[i].UDH.Text+SMS->SMS[i].UDH.Length,MyUDH.Text+1,MyUDH.Length-1); + SMS->SMS[i].UDH.Text[0] += MyUDH.Length-1; + SMS->SMS[i].UDH.Length += MyUDH.Length-1; + } + + SMS->SMS[i].Coding = SMS_Coding_8bit; + SMS->SMS[i].Class = 1; + } + + p = 0; + while (p != Len) { + i = 140-SMS->SMS[SMS->Number].UDH.Length; + if (Len - p < i) i = Len - p; + memcpy(SMS->SMS[SMS->Number].Text,Data+p,i); + p += i; + SMS->SMS[SMS->Number].Length = i; + SMS->Number++; + + } + + /* Linked sms UDH */ + if (SMS->Number != 1) { + UDHID = GSM_MakeSMSIDFromTime(); + for (i=0;iNumber;i++) { + SMS->SMS[i].UDH.Text[SMS->SMS[i].UDH.Length-3] = UDHID; + SMS->SMS[i].UDH.Text[SMS->SMS[i].UDH.Length-2] = SMS->Number; + SMS->SMS[i].UDH.Text[SMS->SMS[i].UDH.Length-1] = i+1; + } + } + + return ERR_NONE; +} + +/* Alcatel docs from www.alcatel.com/wap/ahead and other */ +GSM_Error GSM_EncodeMultiPartSMS(GSM_MultiPartSMSInfo *Info, + GSM_MultiSMSMessage *SMS) +{ + unsigned char Buffer[GSM_MAX_SMS_LENGTH*2*MAX_MULTI_SMS]; + unsigned char Buffer2[GSM_MAX_SMS_LENGTH*2*MAX_MULTI_SMS]; + int Length = 0,smslen,i, Class = -1, j,p; + GSM_Error error; + GSM_Coding_Type Coding = SMS_Coding_8bit; + GSM_UDH UDH = UDH_NoUDH; + GSM_UDHHeader UDHHeader; + bool EMS = false; + int textnum = 0; + + SMS->Number = 0; + + if (Info->Entries[0].ID == SMS_AlcatelSMSTemplateName) { + Buffer[Length++] = 0x00; //number of elements + for (i=1;iEntriesNum;i++) { + switch (Info->Entries[i].ID) { + case SMS_EMSSound10: + case SMS_EMSSound12: + case SMS_EMSSonyEricssonSound: + case SMS_EMSSound10Long: + case SMS_EMSSound12Long: + case SMS_EMSSonyEricssonSoundLong: + case SMS_EMSVariableBitmap: + case SMS_EMSAnimation: + case SMS_EMSVariableBitmapLong: + break; + case SMS_EMSPredefinedSound: + Buffer[0]++; + Buffer[Length++] = 0x01; //type of data + Buffer[Length++] = 1 % 256; //len + Buffer[Length++] = 1 / 256; //len + Buffer[Length++] = Info->Entries[i].Number; + break; + case SMS_EMSPredefinedAnimation: + Buffer[0]++; + Buffer[Length++] = 0x02; //type of data + Buffer[Length++] = 1 % 256; //len + Buffer[Length++] = 1 / 256; //len + Buffer[Length++] = Info->Entries[i].Number; + break; + case SMS_ConcatenatedTextLong: + Buffer[0]++; + p = UnicodeLength(Info->Entries[i].Buffer); + EncodeDefault(Buffer2, Info->Entries[i].Buffer, &p, true, NULL); + Buffer[Length++] = 0x00; //type of data + Length = Length + 2; + smslen = GSM_PackSevenBitsToEight(0, Buffer2, Buffer+Length, p); + Buffer[Length-2] = smslen % 256; //len + Buffer[Length-1] = smslen / 256; //len + Length = Length + smslen; + break; + default: + return ERR_UNKNOWN; + } + } + Buffer[0] = Buffer[0] * 2; + return GSM_EncodeAlcatelMultiPartSMS(SMS,Buffer,Length,Info->Entries[0].Buffer,ALCATELTDD_SMSTEMPLATE); + } + + for (i=0;iEntriesNum;i++) { + switch (Info->Entries[i].ID) { + case SMS_EMSPredefinedAnimation: + case SMS_EMSPredefinedSound: + case SMS_EMSSound10: + case SMS_EMSSound12: + case SMS_EMSSonyEricssonSound: + case SMS_EMSSound10Long: + case SMS_EMSSound12Long: + case SMS_EMSSonyEricssonSoundLong: + case SMS_EMSFixedBitmap: + case SMS_EMSVariableBitmap: + case SMS_EMSAnimation: + case SMS_EMSVariableBitmapLong: + EMS = true; + break; + case SMS_ConcatenatedTextLong: + case SMS_ConcatenatedTextLong16bit: + + /* This covers situation, when somebody will call function + * with two or more SMS_Concatenated.... entries only. + * It will be still only linked sms, but functions below + * will pack only first entry according to own limits. + * We redirect to EMS functions, because they are more generic + * here and will handle it correctly and produce linked sms + * from all entries + */ + textnum ++; + if (textnum > 1) EMS = true; + + if (Info->Entries[i].Left || Info->Entries[i].Right || + Info->Entries[i].Center || Info->Entries[i].Large || + Info->Entries[i].Small || Info->Entries[i].Bold || + Info->Entries[i].Italic || Info->Entries[i].Underlined || + Info->Entries[i].Strikethrough) { + EMS = true; + } + default: + break; + } + if (EMS) break; + } + if (EMS) { + error=GSM_EncodeEMSMultiPartSMS(Info,SMS,UDH_NoUDH); + if (error != ERR_NONE) return error; + if (SMS->Number != 1) { + SMS->Number = 0; + for (i=0;iEntriesNum;i++) { + if (Info->Entries[i].ID == SMS_ConcatenatedTextLong16bit) { + return GSM_EncodeEMSMultiPartSMS(Info,SMS,UDH_ConcatenatedMessages); + } + } + return GSM_EncodeEMSMultiPartSMS(Info,SMS,UDH_ConcatenatedMessages16bit); + } + return error; + } + + if (Info->EntriesNum != 1) return ERR_UNKNOWN; + + switch (Info->Entries[0].ID) { + case SMS_AlcatelMonoBitmapLong: + Buffer[0] = Info->Entries[0].Bitmap->Bitmap[0].BitmapWidth; + Buffer[1] = Info->Entries[0].Bitmap->Bitmap[0].BitmapHeight; + PHONE_EncodeBitmap(GSM_AlcatelBMMIPicture, Buffer+2, &Info->Entries[0].Bitmap->Bitmap[0]); + Length = PHONE_GetBitmapSize(GSM_AlcatelBMMIPicture,Info->Entries[0].Bitmap->Bitmap[0].BitmapWidth,Info->Entries[0].Bitmap->Bitmap[0].BitmapHeight)+2; + return GSM_EncodeAlcatelMultiPartSMS(SMS,Buffer,Length,Info->Entries[0].Bitmap->Bitmap[0].Text,ALCATELTDD_PICTURE); + case SMS_AlcatelMonoAnimationLong: + /* Number of sequence words */ + Buffer[0] = (Info->Entries[0].Bitmap->Number+1) % 256; + Buffer[1] = (Info->Entries[0].Bitmap->Number+1) / 256; + /* Picture display time 1 second (1 = 100ms) */ + Buffer[2] = 10 % 256; + Buffer[3] = 10 / 256 + 0xF0; + + Length = 4; + j = 0; + + /* Offsets to bitmaps */ + for (i=0;iEntries[0].Bitmap->Number;i++) { + Buffer[Length++] = (4+j+Info->Entries[0].Bitmap->Number*2) % 256; + Buffer[Length++] = (4+j+Info->Entries[0].Bitmap->Number*2) / 256; + j += PHONE_GetBitmapSize(GSM_AlcatelBMMIPicture,Info->Entries[0].Bitmap->Bitmap[i].BitmapWidth,Info->Entries[0].Bitmap->Bitmap[i].BitmapHeight)+2; + } + + /* Bitmaps */ + for (i=0;iEntries[0].Bitmap->Number;i++) { + Buffer[Length++] = Info->Entries[0].Bitmap->Bitmap[i].BitmapWidth; + Buffer[Length++] = Info->Entries[0].Bitmap->Bitmap[i].BitmapHeight; + PHONE_EncodeBitmap(GSM_AlcatelBMMIPicture, Buffer+Length, &Info->Entries[0].Bitmap->Bitmap[i]); + Length += PHONE_GetBitmapSize(GSM_AlcatelBMMIPicture,Info->Entries[0].Bitmap->Bitmap[i].BitmapWidth,Info->Entries[0].Bitmap->Bitmap[i].BitmapHeight); + } + return GSM_EncodeAlcatelMultiPartSMS(SMS,Buffer,Length,Info->Entries[0].Bitmap->Bitmap[0].Text,ALCATELTDD_ANIMATION); + case SMS_MMSIndicatorLong: + Class = 1; + UDH = UDH_MMSIndicatorLong; + GSM_EncodeMMSIndicatorSMSText(Buffer,&Length,*Info->Entries[0].MMSIndicator); + break; + case SMS_NokiaRingtoneLong: + case SMS_NokiaRingtone: + UDH = UDH_NokiaRingtone; + Class = 1; + /* 7 = length of UDH_NokiaRingtone UDH header */ + Length = GSM_MAX_8BIT_SMS_LENGTH-7; + Info->Entries[0].RingtoneNotes = GSM_EncodeNokiaRTTLRingtone(*Info->Entries[0].Ringtone,Buffer,&Length); + if (Info->Entries[0].ID == SMS_NokiaRingtone) break; + if (Info->Entries[0].RingtoneNotes != Info->Entries[0].Ringtone->NoteTone.NrCommands) { + UDH = UDH_NokiaRingtoneLong; + Length = (GSM_MAX_8BIT_SMS_LENGTH-12)*3; + Info->Entries[0].RingtoneNotes = GSM_EncodeNokiaRTTLRingtone(*Info->Entries[0].Ringtone,Buffer,&Length); + } + break; + case SMS_NokiaOperatorLogoLong: + if (Info->Entries[0].Bitmap->Bitmap[0].BitmapWidth > 72 || Info->Entries[0].Bitmap->Bitmap[0].BitmapHeight > 14) { + UDH = UDH_NokiaOperatorLogoLong; + Class = 1; + NOKIA_EncodeNetworkCode(Buffer, Info->Entries[0].Bitmap->Bitmap[0].NetworkCode); + Length = Length + 3; + NOKIA_CopyBitmap(GSM_Nokia7110OperatorLogo, &Info->Entries[0].Bitmap->Bitmap[0], Buffer, &Length); + break; + } + case SMS_NokiaOperatorLogo: + UDH = UDH_NokiaOperatorLogo; + Class = 1; + NOKIA_EncodeNetworkCode(Buffer, Info->Entries[0].Bitmap->Bitmap[0].NetworkCode); + Length = Length + 3; + NOKIA_CopyBitmap(GSM_NokiaOperatorLogo, &Info->Entries[0].Bitmap->Bitmap[0], Buffer, &Length); + break; + case SMS_NokiaCallerLogo: + UDH = UDH_NokiaCallerLogo; + Class = 1; + NOKIA_CopyBitmap(GSM_NokiaCallerLogo, &Info->Entries[0].Bitmap->Bitmap[0], Buffer, &Length); + break; + case SMS_NokiaProfileLong: + case SMS_NokiaPictureImageLong: + case SMS_NokiaScreenSaverLong: + Class = 1; + UDH = UDH_NokiaProfileLong; + GSM_EncodeSMS30MultiPartSMS(Info,Buffer,&Length); + break; + case SMS_NokiaWAPBookmarkLong: + Class = 1; + NOKIA_EncodeWAPBookmarkSMSText(Buffer,&Length,Info->Entries[0].Bookmark); + /* 7 = length of UDH_NokiaWAP UDH header */ + if (Length>(GSM_MAX_8BIT_SMS_LENGTH-7)) { + UDH=UDH_NokiaWAPLong; + } else { + UDH=UDH_NokiaWAP; + } + break; + case SMS_NokiaWAPSettingsLong: + Class = 1; + UDH = UDH_NokiaWAPLong; + NOKIA_EncodeWAPMMSSettingsSMSText(Buffer,&Length,Info->Entries[0].Settings,false); + break; + case SMS_NokiaMMSSettingsLong: + Class = 1; + UDH = UDH_NokiaWAPLong; + NOKIA_EncodeWAPMMSSettingsSMSText(Buffer,&Length,Info->Entries[0].Settings,true); + break; + case SMS_NokiaVCARD10Long: + GSM_EncodeVCARD(Buffer,&Length,Info->Entries[0].Phonebook,true,Nokia_VCard10); + /* is 1 SMS ? 8 = length of ..SCKE2 */ + if (Length<=GSM_MAX_SMS_LENGTH-8) { + sprintf(Buffer,"//SCKE2 "); + Length = 8; + GSM_EncodeVCARD(Buffer,&Length,Info->Entries[0].Phonebook,true,Nokia_VCard10); + } else { + /* FIXME: It wasn't checked */ + UDH = UDH_NokiaPhonebookLong; + } + Coding = SMS_Coding_Default; + memcpy(Buffer2,Buffer,Length); + EncodeUnicode(Buffer,Buffer2,Length); + break; + case SMS_NokiaVCARD21Long: + GSM_EncodeVCARD(Buffer,&Length,Info->Entries[0].Phonebook,true,Nokia_VCard21); + /* Is 1 SMS ? 12 = length of ..SCKL23F4 */ + if (Length<=GSM_MAX_SMS_LENGTH-12) { + sprintf(Buffer,"//SCKL23F4%c%c",13,10); + Length = 12; + GSM_EncodeVCARD(Buffer,&Length,Info->Entries[0].Phonebook,true,Nokia_VCard21); + } else { + UDH = UDH_NokiaPhonebookLong; + /* Here can be also 8 bit coding */ + } + Coding = SMS_Coding_Default; + memcpy(Buffer2,Buffer,Length); + EncodeUnicode(Buffer,Buffer2,Length); + break; + case SMS_VCARD10Long: + GSM_EncodeVCARD(Buffer,&Length,Info->Entries[0].Phonebook,true,Nokia_VCard10); + if (Length>GSM_MAX_SMS_LENGTH) UDH = UDH_ConcatenatedMessages; + Coding = SMS_Coding_Default; + memcpy(Buffer2,Buffer,Length); + EncodeUnicode(Buffer,Buffer2,Length); + break; + case SMS_VCARD21Long: + GSM_EncodeVCARD(Buffer,&Length,Info->Entries[0].Phonebook,true,Nokia_VCard21); + if (Length>GSM_MAX_SMS_LENGTH) UDH = UDH_ConcatenatedMessages; + Coding = SMS_Coding_Default; + memcpy(Buffer2,Buffer,Length); + EncodeUnicode(Buffer,Buffer2,Length); + break; + case SMS_NokiaVCALENDAR10Long: + error=GSM_EncodeVCALENDAR(Buffer,&Length,Info->Entries[0].Calendar,true,Nokia_VCalendar); + if (error != ERR_NONE) return error; + /* Is 1 SMS ? 8 = length of ..SCKE4 */ + if (Length<=GSM_MAX_SMS_LENGTH-8) { + sprintf(Buffer,"//SCKE4 "); + Length = 8; + GSM_EncodeVCALENDAR(Buffer,&Length,Info->Entries[0].Calendar,true,Nokia_VCalendar); + } else { + UDH = UDH_NokiaCalendarLong; + /* can be here 8 bit coding ? */ + } + Coding = SMS_Coding_Default; + memcpy(Buffer2,Buffer,Length); + EncodeUnicode(Buffer,Buffer2,Length); + break; + case SMS_NokiaVTODOLong: + error=GSM_EncodeVTODO(Buffer,&Length,Info->Entries[0].ToDo,true,Nokia_VToDo); + if (error != ERR_NONE) return error; + UDH = UDH_NokiaCalendarLong; + Coding = SMS_Coding_Default; + memcpy(Buffer2,Buffer,Length); + EncodeUnicode(Buffer,Buffer2,Length); + break; + case SMS_DisableVoice: + case SMS_DisableFax: + case SMS_DisableEmail: + case SMS_EnableVoice: + case SMS_EnableFax: + case SMS_EnableEmail: + case SMS_VoidSMS: + case SMS_Text: + Class = Info->Class; + switch (Info->Entries[0].ID) { + case SMS_DisableVoice : UDH = UDH_DisableVoice; break; + case SMS_DisableFax : UDH = UDH_DisableFax; break; + case SMS_DisableEmail : UDH = UDH_DisableEmail; break; + case SMS_EnableVoice : UDH = UDH_EnableVoice; break; + case SMS_EnableFax : UDH = UDH_EnableFax; break; + case SMS_EnableEmail : UDH = UDH_EnableEmail; break; + case SMS_VoidSMS : UDH = UDH_VoidSMS; break; + case SMS_Text : UDH = UDH_NoUDH; break; + default : break; + } + UDHHeader.Type = UDH; + GSM_EncodeUDHHeader(&UDHHeader); + memcpy(Buffer,Info->Entries[0].Buffer,UnicodeLength(Info->Entries[0].Buffer)*2+2); + if (Info->UnicodeCoding) { + Coding = SMS_Coding_Unicode; + Length = UnicodeLength(Info->Entries[0].Buffer); + if (Length>(140-UDHHeader.Length)/2) Length = (140-UDHHeader.Length)/2; + } else { + Coding = SMS_Coding_Default; + FindDefaultAlphabetLen(Info->Entries[0].Buffer,&Length,&smslen,(GSM_MAX_8BIT_SMS_LENGTH-UDHHeader.Length)*8/7); + } + break; + case SMS_ConcatenatedAutoTextLong: + case SMS_ConcatenatedAutoTextLong16bit: + smslen = UnicodeLength(Info->Entries[0].Buffer); + memcpy(Buffer,Info->Entries[0].Buffer,smslen*2); + EncodeDefault(Buffer2, Buffer, &smslen, true, NULL); + DecodeDefault(Buffer, Buffer2, smslen, true, NULL); +#ifdef DEBUG + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) { + dbgprintf("Info->Entries[0].Buffer:\n"); + DumpMessage(di.df, di.dl, Info->Entries[0].Buffer, UnicodeLength(Info->Entries[0].Buffer)*2); + dbgprintf("Buffer:\n"); + DumpMessage(di.df, di.dl, Buffer, UnicodeLength(Buffer)*2); + } +#endif + Info->UnicodeCoding = false; + for (smslen=0;smslen<(int)(UnicodeLength(Info->Entries[0].Buffer)*2);smslen++) { + if (Info->Entries[0].Buffer[smslen] != Buffer[smslen]) { + Info->UnicodeCoding = true; + dbgprintf("Setting to Unicode %i\n",smslen); + break; + } + } + /* No break here - we go to the SMS_ConcatenatedTextLong */ + case SMS_ConcatenatedTextLong: + case SMS_ConcatenatedTextLong16bit: + Class = Info->Class; + memcpy(Buffer,Info->Entries[0].Buffer,UnicodeLength(Info->Entries[0].Buffer)*2+2); + UDH = UDH_NoUDH; + if (Info->UnicodeCoding) { + Coding = SMS_Coding_Unicode; + Length = UnicodeLength(Info->Entries[0].Buffer); + if (Info->Entries[0].ID == SMS_ConcatenatedTextLong16bit || + Info->Entries[0].ID == SMS_ConcatenatedAutoTextLong16bit) { + if (Length>70) UDH=UDH_ConcatenatedMessages16bit; + } else { + if (Length>70) UDH=UDH_ConcatenatedMessages; + } + } else { + Coding = SMS_Coding_Default; + FindDefaultAlphabetLen(Info->Entries[0].Buffer,&Length,&smslen,5000); + if (Info->Entries[0].ID == SMS_ConcatenatedTextLong16bit || + Info->Entries[0].ID == SMS_ConcatenatedAutoTextLong16bit) { + if (smslen>GSM_MAX_SMS_LENGTH) UDH=UDH_ConcatenatedMessages16bit; + } else { + if (smslen>GSM_MAX_SMS_LENGTH) UDH=UDH_ConcatenatedMessages; + } + } + default: + break; + } + GSM_MakeMultiPartSMS(SMS,Buffer,Length,UDH,Coding,Class,Info->ReplaceMessage); + return ERR_NONE; +} + +void GSM_ClearMultiPartSMSInfo(GSM_MultiPartSMSInfo *Info) +{ + int i; + + for (i=0;iEntries[i].Number = 0; + Info->Entries[i].Ringtone = NULL; + Info->Entries[i].Bitmap = NULL; + Info->Entries[i].Bookmark = NULL; + Info->Entries[i].Settings = NULL; + Info->Entries[i].MMSIndicator = NULL; + Info->Entries[i].Phonebook = NULL; + Info->Entries[i].Calendar = NULL; + Info->Entries[i].ToDo = NULL; + Info->Entries[i].Protected = false; + + Info->Entries[i].Buffer = NULL; + Info->Entries[i].Left = false; + Info->Entries[i].Right = false; + Info->Entries[i].Center = false; + Info->Entries[i].Large = false; + Info->Entries[i].Small = false; + Info->Entries[i].Bold = false; + Info->Entries[i].Italic = false; + Info->Entries[i].Underlined = false; + Info->Entries[i].Strikethrough = false; + + Info->Entries[i].RingtoneNotes = 0; + } + Info->Unknown = false; + Info->EntriesNum = 0; + Info->Class = -1; + Info->ReplaceMessage = 0; + Info->UnicodeCoding = false; +} + +void GSM_FreeMultiPartSMSInfo(GSM_MultiPartSMSInfo *Info) +{ + int i; + + for (i=0;iEntries[i].Ringtone); + Info->Entries[i].Ringtone = NULL; + free(Info->Entries[i].Bitmap); + Info->Entries[i].Bitmap = NULL; + free(Info->Entries[i].Bookmark); + Info->Entries[i].Bookmark = NULL; + free(Info->Entries[i].Settings); + Info->Entries[i].Settings = NULL; + free(Info->Entries[i].MMSIndicator); + Info->Entries[i].MMSIndicator = NULL; + free(Info->Entries[i].Phonebook); + Info->Entries[i].Phonebook = NULL; + free(Info->Entries[i].Calendar); + Info->Entries[i].Calendar = NULL; + free(Info->Entries[i].ToDo); + Info->Entries[i].ToDo = NULL; + free(Info->Entries[i].Buffer); + Info->Entries[i].Buffer = NULL; + } +} + +/* ----------------- Joining SMS from parts -------------------------------- */ + +bool GSM_DecodeMultiPartSMS(GSM_MultiPartSMSInfo *Info, + GSM_MultiSMSMessage *SMS, + bool ems) +{ + int i, Length = 0; + char Buffer[GSM_MAX_SMS_LENGTH*2*MAX_MULTI_SMS]; + bool emsexist = false; + + GSM_ClearMultiPartSMSInfo(Info); + if (ems) { + emsexist = true; + for (i=0;iNumber;i++) { + if (SMS->SMS[i].UDH.Type != UDH_ConcatenatedMessages && + SMS->SMS[i].UDH.Type != UDH_ConcatenatedMessages16bit && + SMS->SMS[i].UDH.Type != UDH_UserUDH) { + emsexist = false; + break; + } + } + } + + /* EMS decoding */ + if (emsexist) return GSM_DecodeEMSMultiPartSMS(Info,SMS); + + /* Smart Messaging decoding */ + if (SMS->SMS[0].UDH.Type == UDH_NokiaRingtone && SMS->Number == 1) { + Info->Entries[0].Ringtone = (GSM_Ringtone *)malloc(sizeof(GSM_Ringtone)); + if (Info->Entries[0].Ringtone == NULL) return false; + if (GSM_DecodeNokiaRTTLRingtone(Info->Entries[0].Ringtone, SMS->SMS[0].Text, SMS->SMS[0].Length)==ERR_NONE) { + Info->Entries[0].ID = SMS_NokiaRingtone; + Info->EntriesNum = 1; + return true; + } + } + if (SMS->SMS[0].UDH.Type == UDH_NokiaCallerLogo && SMS->Number == 1) { + Info->Entries[0].Bitmap = (GSM_MultiBitmap *)malloc(sizeof(GSM_MultiBitmap)); + if (Info->Entries[0].Bitmap == NULL) return false; + Info->Entries[0].Bitmap->Number = 1; + PHONE_DecodeBitmap(GSM_NokiaCallerLogo, SMS->SMS[0].Text+4, &Info->Entries[0].Bitmap->Bitmap[0]); +#ifdef DEBUG + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) GSM_PrintBitmap(di.df,&Info->Entries[0].Bitmap->Bitmap[0]); +#endif + Info->Entries[0].ID = SMS_NokiaCallerLogo; + Info->EntriesNum = 1; + return true; + } + if (SMS->SMS[0].UDH.Type == UDH_NokiaOperatorLogo && SMS->Number == 1) { + Info->Entries[0].Bitmap = (GSM_MultiBitmap *)malloc(sizeof(GSM_MultiBitmap)); + if (Info->Entries[0].Bitmap == NULL) return false; + Info->Entries[0].Bitmap->Number = 1; + PHONE_DecodeBitmap(GSM_NokiaOperatorLogo, SMS->SMS[0].Text+7, &Info->Entries[0].Bitmap->Bitmap[0]); + NOKIA_DecodeNetworkCode(SMS->SMS[0].Text, Info->Entries[0].Bitmap->Bitmap[0].NetworkCode); +#ifdef DEBUG + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) GSM_PrintBitmap(di.df,&Info->Entries[0].Bitmap->Bitmap[0]); +#endif + Info->Entries[0].ID = SMS_NokiaOperatorLogo; + Info->EntriesNum = 1; + return true; + } + if (SMS->SMS[0].UDH.Type == UDH_NokiaProfileLong) { + for (i=0;iNumber;i++) { + if (SMS->SMS[i].UDH.Type != UDH_NokiaProfileLong || + SMS->SMS[i].UDH.Text[11] != i+1 || + SMS->SMS[i].UDH.Text[10] != SMS->Number) { + return false; + } + memcpy(Buffer+Length,SMS->SMS[i].Text,SMS->SMS[i].Length); + Length = Length + SMS->SMS[i].Length; + } + Info->EntriesNum = 1; + Info->Entries[0].ID = SMS_NokiaPictureImageLong; + Info->Entries[0].Bitmap = (GSM_MultiBitmap *)malloc(sizeof(GSM_MultiBitmap)); + if (Info->Entries[0].Bitmap == NULL) return false; + Info->Entries[0].Bitmap->Number = 1; + Info->Entries[0].Bitmap->Bitmap[0].Text[0] = 0; + Info->Entries[0].Bitmap->Bitmap[0].Text[1] = 0; + i=1; + while (i!=Length) { + switch (Buffer[i]) { + case SM30_ISOTEXT: + dbgprintf("ISO 8859-2 text\n"); + Info->Unknown = true; + break; + case SM30_UNICODETEXT: + dbgprintf("Unicode text\n"); + memcpy(Info->Entries[0].Bitmap->Bitmap[0].Text,Buffer+i+3,Buffer[i+1]*256+Buffer[i+2]); + Info->Entries[0].Bitmap->Bitmap[0].Text[Buffer[i+1]*256 + Buffer[i+2]] = 0; + Info->Entries[0].Bitmap->Bitmap[0].Text[Buffer[i+1]*256 + Buffer[i+2]+ 1] = 0; + dbgprintf("Unicode Text \"%s\"\n",DecodeUnicodeString(Info->Entries[0].Bitmap->Bitmap[0].Text)); + break; + case SM30_OTA: + dbgprintf("OTA bitmap as Picture Image\n"); + PHONE_DecodeBitmap(GSM_NokiaPictureImage, Buffer + i + 7, &Info->Entries[0].Bitmap->Bitmap[0]); +#ifdef DEBUG + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) GSM_PrintBitmap(di.df,&Info->Entries[0].Bitmap->Bitmap[0]); +#endif + break; + case SM30_RINGTONE: + dbgprintf("RTTL ringtone\n"); + Info->Unknown = true; + break; + case SM30_PROFILENAME: + dbgprintf("Profile Name\n"); + Info->Entries[0].ID = SMS_NokiaProfileLong; + Info->Unknown = true; + break; + case SM30_SCREENSAVER: + dbgprintf("OTA bitmap as Screen Saver\n"); + PHONE_DecodeBitmap(GSM_NokiaPictureImage, Buffer + i + 7, &Info->Entries[0].Bitmap->Bitmap[0]); +#ifdef DEBUG + if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) GSM_PrintBitmap(di.df,&Info->Entries[0].Bitmap->Bitmap[0]); +#endif + Info->Entries[0].ID = SMS_NokiaScreenSaverLong; + break; + } + i = i + Buffer[i+1]*256 + Buffer[i+2] + 3; + dbgprintf("%i %i\n",i,Length); + } + return true; + } + + /* Linked sms */ + if (SMS->SMS[0].UDH.Type == UDH_ConcatenatedMessages || + SMS->SMS[0].UDH.Type == UDH_ConcatenatedMessages16bit) { + Info->EntriesNum = 1; + Info->Entries[0].ID = SMS_ConcatenatedTextLong; + if (SMS->SMS[0].UDH.Type == UDH_ConcatenatedMessages16bit) { + Info->Entries[0].ID = SMS_ConcatenatedTextLong16bit; + } + + for (i=0;iNumber;i++) { + switch (SMS->SMS[i].Coding) { + case SMS_Coding_8bit: + Info->Entries[0].Buffer = realloc(Info->Entries[0].Buffer, Length + SMS->SMS[i].Length + 2); + if (Info->Entries[0].Buffer == NULL) return false; + + memcpy(Info->Entries[0].Buffer + Length, SMS->SMS[i].Text, SMS->SMS[i].Length); + Length=Length+SMS->SMS[i].Length; + break; + case SMS_Coding_Unicode: + if (Info->Entries[0].ID == SMS_ConcatenatedTextLong) { + Info->Entries[0].ID = SMS_ConcatenatedAutoTextLong; + } + if (Info->Entries[0].ID == SMS_ConcatenatedTextLong16bit) { + Info->Entries[0].ID = SMS_ConcatenatedAutoTextLong16bit; + } + case SMS_Coding_Default: + Info->Entries[0].Buffer = realloc(Info->Entries[0].Buffer, Length + UnicodeLength(SMS->SMS[i].Text)*2 + 2); + if (Info->Entries[0].Buffer == NULL) return false; + + memcpy(Info->Entries[0].Buffer+Length,SMS->SMS[i].Text,UnicodeLength(SMS->SMS[i].Text)*2); + Length=Length+UnicodeLength(SMS->SMS[i].Text)*2; + break; + } + } + Info->Entries[0].Buffer[Length] = 0; + Info->Entries[0].Buffer[Length+1] = 0; + return true; + } + + return false; +} + +GSM_Error GSM_LinkSMS(GSM_MultiSMSMessage **INPUT, GSM_MultiSMSMessage **OUTPUT, bool ems) +{ + bool *INPUTSorted, copyit; + int i,OUTPUTNum,j,z,w; + + i = 0; + while (INPUT[i] != NULL) i++; + + INPUTSorted = calloc(i, sizeof(bool)); + if (INPUTSorted == NULL) return ERR_MOREMEMORY; + + OUTPUTNum = 0; + OUTPUT[0] = NULL; + + if (ems) { + i=0; + while (INPUT[i] != NULL) { + if (INPUT[i]->SMS[0].UDH.Type == UDH_UserUDH) { + w=1; + while (w < INPUT[i]->SMS[0].UDH.Length) { + switch(INPUT[i]->SMS[0].UDH.Text[w]) { + case 0x00: + dbgprintf("Adding ID to user UDH - linked SMS with 8 bit ID\n"); + INPUT[i]->SMS[0].UDH.ID8bit = INPUT[i]->SMS[0].UDH.Text[w+2]; + INPUT[i]->SMS[0].UDH.AllParts = INPUT[i]->SMS[0].UDH.Text[w+3]; + INPUT[i]->SMS[0].UDH.PartNumber = INPUT[i]->SMS[0].UDH.Text[w+4]; + break; + case 0x08: + dbgprintf("Adding ID to user UDH - linked SMS with 16 bit ID\n"); + INPUT[i]->SMS[0].UDH.ID16bit = INPUT[i]->SMS[0].UDH.Text[w+2]*256+INPUT[i]->SMS[0].UDH.Text[w+3]; + INPUT[i]->SMS[0].UDH.AllParts = INPUT[i]->SMS[0].UDH.Text[w+4]; + INPUT[i]->SMS[0].UDH.PartNumber = INPUT[i]->SMS[0].UDH.Text[w+5]; + break; + default: + dbgprintf("Block %02x\n",INPUT[i]->SMS[0].UDH.Text[w]); + } + dbgprintf("%i %i %i %i\n", + INPUT[i]->SMS[0].UDH.ID8bit, + INPUT[i]->SMS[0].UDH.ID16bit, + INPUT[i]->SMS[0].UDH.PartNumber, + INPUT[i]->SMS[0].UDH.AllParts); + w=w+INPUT[i]->SMS[0].UDH.Text[w+1]+2; + } + } + i++; + } + } + + i=0; + while (INPUT[i]!=NULL) { + /* If this one SMS was sorted earlier, do not touch */ + if (INPUTSorted[i]) { + i++; + continue; + } + copyit = false; + /* If we have: + * - linked sms returned by phone driver + * - sms without linking + * we copy it to OUTPUT + */ + if (INPUT[i]->Number != 1 || + INPUT[i]->SMS[0].UDH.Type == UDH_NoUDH || + INPUT[i]->SMS[0].UDH.PartNumber == -1) { + copyit = true; + } + /* If we have unknown UDH, we copy it to OUTPUT */ + if (INPUT[i]->SMS[0].UDH.Type == UDH_UserUDH) { + if (!ems) copyit = true; + if (ems && INPUT[i]->SMS[0].UDH.PartNumber == -1) copyit = true; + } + if (copyit) { + OUTPUT[OUTPUTNum] = malloc(sizeof(GSM_MultiSMSMessage)); + if (OUTPUT[OUTPUTNum] == NULL) { + free(INPUTSorted); + return ERR_MOREMEMORY; + } + OUTPUT[OUTPUTNum+1] = NULL; + + memcpy(OUTPUT[OUTPUTNum],INPUT[i],sizeof(GSM_MultiSMSMessage)); + INPUTSorted[i]=true; + OUTPUTNum++; + i = 0; + continue; + } + /* We have 1'st part of linked sms. It's single. + * We will try to find other parts + */ + if (INPUT[i]->SMS[0].UDH.PartNumber == 1) { + OUTPUT[OUTPUTNum] = malloc(sizeof(GSM_MultiSMSMessage)); + if (OUTPUT[OUTPUTNum] == NULL) { + free(INPUTSorted); + return ERR_MOREMEMORY; + } + OUTPUT[OUTPUTNum+1] = NULL; + + memcpy(&OUTPUT[OUTPUTNum]->SMS[0],&INPUT[i]->SMS[0],sizeof(GSM_SMSMessage)); + OUTPUT[OUTPUTNum]->Number = 1; + INPUTSorted[i] = true; + j = 1; + /* We're searching for other parts in sequence */ + while (j!=INPUT[i]->SMS[0].UDH.AllParts) { + z=0; + while(INPUT[z]!=NULL) { + /* This was sorted earlier or is not single */ + if (INPUTSorted[z] || INPUT[z]->Number != 1) { + z++; + continue; + } + if (ems && INPUT[i]->SMS[0].UDH.Type != UDH_ConcatenatedMessages && + INPUT[i]->SMS[0].UDH.Type != UDH_ConcatenatedMessages16bit && + INPUT[i]->SMS[0].UDH.Type != UDH_UserUDH && + INPUT[z]->SMS[0].UDH.Type != UDH_ConcatenatedMessages && + INPUT[z]->SMS[0].UDH.Type != UDH_ConcatenatedMessages16bit && + INPUT[z]->SMS[0].UDH.Type != UDH_UserUDH) { + if (INPUT[z]->SMS[0].UDH.Type != INPUT[i]->SMS[0].UDH.Type) { + z++; + continue; + } + } + if (!ems && INPUT[z]->SMS[0].UDH.Type != INPUT[i]->SMS[0].UDH.Type) { + z++; + continue; + } + dbgprintf("compare %i %i %i %i %i", + j+1, + INPUT[i]->SMS[0].UDH.ID8bit, + INPUT[i]->SMS[0].UDH.ID16bit, + INPUT[i]->SMS[0].UDH.PartNumber, + INPUT[i]->SMS[0].UDH.AllParts); + dbgprintf(" %i %i %i %i\n", + INPUT[z]->SMS[0].UDH.ID8bit, + INPUT[z]->SMS[0].UDH.ID16bit, + INPUT[z]->SMS[0].UDH.PartNumber, + INPUT[z]->SMS[0].UDH.AllParts); + if (INPUT[z]->SMS[0].UDH.ID8bit != INPUT[i]->SMS[0].UDH.ID8bit || + INPUT[z]->SMS[0].UDH.ID16bit != INPUT[i]->SMS[0].UDH.ID16bit || + INPUT[z]->SMS[0].UDH.AllParts != INPUT[i]->SMS[0].UDH.AllParts || + INPUT[z]->SMS[0].UDH.PartNumber != j+1) { + z++; + continue; + } + /* For SMS_Deliver compare also SMSC and Sender number */ + if (INPUT[z]->SMS[0].PDU == SMS_Deliver && + (strcmp(DecodeUnicodeString(INPUT[z]->SMS[0].SMSC.Number),DecodeUnicodeString(INPUT[i]->SMS[0].SMSC.Number)) || + strcmp(DecodeUnicodeString(INPUT[z]->SMS[0].Number),DecodeUnicodeString(INPUT[i]->SMS[0].Number)))) { + z++; + continue; + } + /* DCT4 Outbox: SMS Deliver. Empty number and SMSC. We compare dates */ + if (INPUT[z]->SMS[0].PDU == SMS_Deliver && + UnicodeLength(INPUT[z]->SMS[0].SMSC.Number)==0 && + UnicodeLength(INPUT[z]->SMS[0].Number)==0 && + (INPUT[z]->SMS[0].DateTime.Day != INPUT[i]->SMS[0].DateTime.Day || + INPUT[z]->SMS[0].DateTime.Month != INPUT[i]->SMS[0].DateTime.Month || + INPUT[z]->SMS[0].DateTime.Year != INPUT[i]->SMS[0].DateTime.Year || + INPUT[z]->SMS[0].DateTime.Hour != INPUT[i]->SMS[0].DateTime.Hour || + INPUT[z]->SMS[0].DateTime.Minute != INPUT[i]->SMS[0].DateTime.Minute || + INPUT[z]->SMS[0].DateTime.Second != INPUT[i]->SMS[0].DateTime.Second)) { + z++; + continue; + } + /* We found correct sms. Copy it */ + memcpy(&OUTPUT[OUTPUTNum]->SMS[j],&INPUT[z]->SMS[0],sizeof(GSM_SMSMessage)); + OUTPUT[OUTPUTNum]->Number++; + INPUTSorted[z]=true; + break; + } + /* Incomplete sequence */ + if (OUTPUT[OUTPUTNum]->Number==j) { + dbgprintf("Incomplete sequence\n"); + break; + } + j++; + } + OUTPUTNum++; + i = 0; + continue; + } + /* We have some next linked sms from sequence */ + if (INPUT[i]->SMS[0].UDH.PartNumber > 1) { + j = 0; + while (INPUT[j]!=NULL) { + if (INPUTSorted[j]) { + j++; + continue; + } + /* We have some not unassigned first sms from sequence. + * We can't touch other sms from sequences + */ + if (INPUT[j]->SMS[0].UDH.PartNumber == 1) break; + j++; + } + if (INPUT[j]==NULL) { + OUTPUT[OUTPUTNum] = malloc(sizeof(GSM_MultiSMSMessage)); + if (OUTPUT[OUTPUTNum] == NULL) { + free(INPUTSorted); + return ERR_MOREMEMORY; + } + OUTPUT[OUTPUTNum+1] = NULL; + + memcpy(OUTPUT[OUTPUTNum],INPUT[i],sizeof(GSM_MultiSMSMessage)); + INPUTSorted[i]=true; + OUTPUTNum++; + i = 0; + continue; + } else i++; + } + } + free(INPUTSorted); + return ERR_NONE; +} + +/* How should editor hadle tabs in this file? Add editor commands here. + * vim: noexpandtab sw=8 ts=8 sts=8: + */ diff --git a/gammu/emb/common/service/sms/gsmmulti.h b/gammu/emb/common/service/sms/gsmmulti.h new file mode 100644 index 0000000..c672261 --- a/dev/null +++ b/gammu/emb/common/service/sms/gsmmulti.h @@ -0,0 +1,271 @@ +/* (c) 2002-2004 by Marcin Wiacek */ + +#ifndef __gsm_multi_h +#define __gsm_multi_h + +#include "../../gsmcomon.h" +#include "../gsmlogo.h" +#include "../gsmcal.h" +#include "../gsmpbk.h" +#include "../gsmdata.h" +#include "../gsmring.h" +#include "gsmsms.h" + +/* ---------------------- multi SMS --------------------------------------- */ + +/* Identifiers for Smart Messaging 3.0 multipart SMS */ + +#define SM30_ISOTEXT 0 /* ISO 8859-1 text */ +#define SM30_UNICODETEXT 1 +#define SM30_OTA 2 +#define SM30_RINGTONE 3 +#define SM30_PROFILENAME 4 +/* ... */ +#define SM30_SCREENSAVER 6 + +/* Identifiers for Alcatel Terminal Data Download */ +#define ALCATELTDD_PICTURE 4 +#define ALCATELTDD_ANIMATION 5 +#define ALCATELTDD_SMSTEMPLATE 6 + +void GSM_SMSCounter(int MessageLength, + unsigned char *MessageBuffer, + GSM_UDH UDHType, + GSM_Coding_Type Coding, + int *SMSNum, + int *CharsLeft); + +#define MAX_MULTI_SMS 10 + +/** + * Multiple SMS messages, used for Smart Messaging 3.0/EMS. + */ +typedef struct { + /** + * Sender or recipient number. + */ + unsigned char Number; + /** + * Array of SMSes. + */ + GSM_SMSMessage SMS[MAX_MULTI_SMS]; +} GSM_MultiSMSMessage; + +GSM_Error GSM_AddSMS_Text_UDH(GSM_MultiSMSMessage *SMS, + GSM_Coding_Type Coding, + char *Buffer, + int BufferLen, + bool UDH, + int *UsedText, + int *CopiedText, + int *CopiedSMSText); + +void GSM_MakeMultiPartSMS(GSM_MultiSMSMessage *SMS, + unsigned char *MessageBuffer, + int MessageLength, + GSM_UDH UDHType, + GSM_Coding_Type Coding, + int Class, + unsigned char RejectDuplicates); + +void GSM_Find_Free_Used_SMS2(GSM_Coding_Type Coding,GSM_SMSMessage SMS, int *UsedText, int *FreeText, int *FreeBytes); + +unsigned char GSM_MakeSMSIDFromTime(void); + +/** + * ID during packing SMS for Smart Messaging 3.0, EMS and other + */ +typedef enum { + /** + * 1 text SMS. + */ + SMS_Text = 1, + /** + * Contacenated SMS, when longer than 1 SMS. + */ + SMS_ConcatenatedTextLong, + /** + * Contacenated SMS, auto Default/Unicode coding. + */ + SMS_ConcatenatedAutoTextLong, + SMS_ConcatenatedTextLong16bit, + SMS_ConcatenatedAutoTextLong16bit, + /** + * Nokia profile = Name, Ringtone, ScreenSaver + */ + SMS_NokiaProfileLong, + /** + * Nokia Picture Image + (text) + */ + SMS_NokiaPictureImageLong, + /** + * Nokia screen saver + (text) + */ + SMS_NokiaScreenSaverLong, + /** + * Nokia ringtone - old SM2.0 format, 1 SMS + */ + SMS_NokiaRingtone, + /** + * Nokia ringtone contacenated, when very long + */ + SMS_NokiaRingtoneLong, + /** + * Nokia 72x14 operator logo, 1 SMS + */ + SMS_NokiaOperatorLogo, + /** + * Nokia 72x14 op logo or 78x21 in 2 SMS + */ + SMS_NokiaOperatorLogoLong, + /** + * Nokia 72x14 caller logo, 1 SMS + */ + SMS_NokiaCallerLogo, + /** + * Nokia WAP bookmark in 1 or 2 SMS + */ + SMS_NokiaWAPBookmarkLong, + /** + * Nokia WAP settings in 2 SMS + */ + SMS_NokiaWAPSettingsLong, + /** + * Nokia MMS settings in 2 SMS + */ + SMS_NokiaMMSSettingsLong, + /** + * Nokia VCARD 1.0 - only name and default number + */ + SMS_NokiaVCARD10Long, + /** + * Nokia VCARD 2.1 - all numbers + text + */ + SMS_NokiaVCARD21Long, + /** + * Nokia VCALENDAR 1.0 - can be in few sms + */ + SMS_NokiaVCALENDAR10Long, + SMS_NokiaVTODOLong, + SMS_VCARD10Long, + SMS_VCARD21Long, + SMS_DisableVoice, + SMS_DisableFax, + SMS_DisableEmail, + SMS_EnableVoice, + SMS_EnableFax, + SMS_EnableEmail, + SMS_VoidSMS, + /** + * IMelody 1.0 + */ + SMS_EMSSound10, + /** + * IMelody 1.2 + */ + SMS_EMSSound12, + /** + * IMelody without header - SonyEricsson extension + */ + SMS_EMSSonyEricssonSound, + /** + * IMelody 1.0 with UPI. + */ + SMS_EMSSound10Long, + /*** + * IMelody 1.2 with UPI. + */ + SMS_EMSSound12Long, + /** + * IMelody without header with UPI. + */ + SMS_EMSSonyEricssonSoundLong, + SMS_EMSPredefinedSound, + SMS_EMSPredefinedAnimation, + SMS_EMSAnimation, + /** + * Fixed bitmap of size 16x16 or 32x32. + */ + SMS_EMSFixedBitmap, + SMS_EMSVariableBitmap, + SMS_EMSVariableBitmapLong, + SMS_MMSIndicatorLong, + /** + * Variable bitmap with black and white colors + */ + SMS_AlcatelMonoBitmapLong, + /** + * Variable animation with black and white colors + */ + SMS_AlcatelMonoAnimationLong, + SMS_AlcatelSMSTemplateName +} EncodeMultiPartSMSID; + +typedef struct { + EncodeMultiPartSMSID ID; + + int Number; + GSM_Ringtone *Ringtone; + GSM_MultiBitmap *Bitmap; + GSM_WAPBookmark *Bookmark; + GSM_WAPSettings *Settings; + GSM_MMSIndicator *MMSIndicator; + GSM_MemoryEntry *Phonebook; + GSM_CalendarEntry *Calendar; + GSM_ToDoEntry *ToDo; + bool Protected; + + unsigned char *Buffer; + bool Left; + bool Right; + bool Center; + bool Large; + bool Small; + bool Bold; + bool Italic; + bool Underlined; + bool Strikethrough; + + /* Return values */ + int RingtoneNotes; +} MultiPartSMSEntry; + +typedef struct { + MultiPartSMSEntry Entries[MAX_MULTI_SMS]; + int EntriesNum; + bool UnicodeCoding; + int Class; + unsigned char ReplaceMessage; + bool Unknown; +} GSM_MultiPartSMSInfo; + +/** + * Encodes multi part SMS from "readable" format. + */ +GSM_Error GSM_EncodeMultiPartSMS (GSM_MultiPartSMSInfo *Info, GSM_MultiSMSMessage *SMS); + +/** + * Decodes multi part SMS to "readable" format. + */ +bool GSM_DecodeMultiPartSMS (GSM_MultiPartSMSInfo *Info, GSM_MultiSMSMessage *SMS, bool ems); + +/** + * Clears @ref GSM_MultiPartSMSInfo to default values. + */ +void GSM_ClearMultiPartSMSInfo (GSM_MultiPartSMSInfo *Info); + +/** + * Frees any allocated structures inside @ref GSM_MultiPartSMSInfo. + */ +void GSM_FreeMultiPartSMSInfo (GSM_MultiPartSMSInfo *Info); + +/** + * Links SMS messages according to IDs. + */ +GSM_Error GSM_LinkSMS(GSM_MultiSMSMessage **INPUT, GSM_MultiSMSMessage **OUTPUT, bool ems); + +#endif + +/* How should editor hadle tabs in this file? Add editor commands here. + * vim: noexpandtab sw=8 ts=8 sts=8: + */ 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: + */ diff --git a/gammu/emb/common/service/sms/gsmsms.h b/gammu/emb/common/service/sms/gsmsms.h new file mode 100644 index 0000000..d87ff60 --- a/dev/null +++ b/gammu/emb/common/service/sms/gsmsms.h @@ -0,0 +1,492 @@ +/* (c) 2001-2004 by Marcin Wiacek */ +/* based on some work from Pawel Kot, others and Gnokii */ + +#ifndef __gsm_sms_h +#define __gsm_sms_h + +#include "../../gsmcomon.h" +#include "../gsmlogo.h" +#include "../gsmcal.h" +#include "../gsmpbk.h" +#include "../gsmdata.h" +#include "../gsmring.h" + +/* --------------------- Some general definitions ------------------------- */ + +#define GSM_MAX_UDH_LENGTH 140 +#define GSM_MAX_SMS_LENGTH 160 +#define GSM_MAX_8BIT_SMS_LENGTH 140 + +/* -------------------- Cell Broadcast ------------------------------------ */ + +/** + * Structure for Cell Broadcast messages. + */ +typedef struct { + /** + * Message text. + */ + char Text[300]; + /** + * Channel number. + */ + int Channel; +} GSM_CBMessage; + +/* ------------------------ SMS status ------------------------------------ */ + +/** + * Status of SMS memory. + */ +typedef struct { + /** + * Number of unread messages on SIM. + */ + int SIMUnRead; + /** + * Number of all saved messages (including unread) on SIM. + */ + int SIMUsed; + /** + * Number of all possible messages on SIM. + */ + int SIMSize; + /** + * Number of used templates (62xx/63xx/7110/etc.). + */ + int TemplatesUsed; + /** + * Number of unread messages in phone. + */ + int PhoneUnRead; + /** + * Number of all saved messages in phone. + */ + int PhoneUsed; + /** + * Number of all possible messages on phone. + */ + int PhoneSize; +} GSM_SMSMemoryStatus; + +/* --------------------- SMS Center --------------------------------------- */ + +/** + * Enum defines format of SMS messages. See GSM 03.40 section 9.2.3.9 + */ +typedef enum { + SMS_FORMAT_Pager = 1, + SMS_FORMAT_Fax, + SMS_FORMAT_Email, + SMS_FORMAT_Text + /* Some values not handled here */ +} GSM_SMSFormat; + +/** + * Enum defines some the most often used validity lengths for SMS messages + * for relative validity format. See GSM 03.40 section 9.2.3.12.1 - it gives + * more values + */ +typedef enum { + SMS_VALID_1_Hour = 0x0b, + SMS_VALID_6_Hours = 0x47, + SMS_VALID_1_Day = 0xa7, + SMS_VALID_3_Days = 0xa9, + SMS_VALID_1_Week = 0xad, + SMS_VALID_Max_Time = 0xff +} GSM_ValidityPeriod; + +/** + * Enum defines format of validity period for SMS messages. + * See GSM 03.40 section 9.2.3.12 + */ +typedef enum { + SMS_Validity_NotAvailable = 1, + SMS_Validity_RelativeFormat + /* Specification gives also other possibilities */ +} GSM_ValidityPeriodFormat; + +/** + * Structure for validity of SMS messages + */ +typedef struct { + GSM_ValidityPeriodFormat Format; + /** + * Value defines period for relative format + */ + GSM_ValidityPeriod Relative; +} GSM_SMSValidity; + +#define GSM_MAX_SMSC_NAME_LENGTH 30 + +/** + * Structure for SMSC (SMS Center) information. + */ +typedef struct { + /** + * Number of the SMSC on SIM + */ + int Location; + /** + * Name of the SMSC + */ + unsigned char Name[(GSM_MAX_SMSC_NAME_LENGTH+1)*2]; + /** + * SMSC phone number. + */ + unsigned char Number[(GSM_MAX_NUMBER_LENGTH+1)*2]; + /** + * Validity of SMS messages. + */ + GSM_SMSValidity Validity; + /** + * Format of sent SMS messages. + */ + GSM_SMSFormat Format; + /** + * Default recipient number. In old DCT3 ignored + */ + unsigned char DefaultNumber[(GSM_MAX_NUMBER_LENGTH+1)*2]; +} GSM_SMSC; + +/* --------------------- single SMS --------------------------------------- */ + +/** + * Status of SMS message. + */ +typedef enum { + SMS_Sent = 1, + SMS_UnSent, + SMS_Read, + SMS_UnRead +} GSM_SMS_State; + +/** + * Coding type of SMS. + */ +typedef enum { + /** + * Unicode + */ + SMS_Coding_Unicode = 1, + /** + * Default GSM aplhabet. + */ + SMS_Coding_Default, + /** + * 8-bit. + */ + SMS_Coding_8bit +} GSM_Coding_Type; + +/** + * Types of UDH (User Data Header). + */ +typedef enum { + UDH_NoUDH = 1, + /** + * Linked SMS. + */ + UDH_ConcatenatedMessages, + /** + * Linked SMS with 16 bit reference. + */ + UDH_ConcatenatedMessages16bit, + UDH_DisableVoice, + UDH_DisableFax, + UDH_DisableEmail, + UDH_EnableVoice, + UDH_EnableFax, + UDH_EnableEmail, + UDH_VoidSMS, + UDH_NokiaRingtone, + UDH_NokiaRingtoneLong, + UDH_NokiaOperatorLogo, + UDH_NokiaOperatorLogoLong, + UDH_NokiaCallerLogo, + UDH_NokiaWAP, + UDH_NokiaWAPLong, + UDH_NokiaCalendarLong, + UDH_NokiaProfileLong, + UDH_NokiaPhonebookLong, + UDH_UserUDH, + UDH_MMSIndicatorLong +} GSM_UDH; + +/** + * Structure for User Data Header. + */ +typedef struct { + /** + * UDH type. + */ + GSM_UDH Type; + /** + * UDH length. + */ + int Length; + /** + * UDH text. + */ + unsigned char Text[GSM_MAX_UDH_LENGTH]; + /** + * 8-bit ID, when required (-1 otherwise). + */ + int ID8bit; + /** + * 16-bit ID, when required (-1 otherwise). + */ + int ID16bit; + /** + * Number of current part. + */ + int PartNumber; + /** + * Total number of parts. + */ + int AllParts; +} GSM_UDHHeader; + +/** + * TP-Message-Type-Indicator. See GSM 03.40 section 9.2.3.1. + */ +typedef enum { + /** + * SMS in Inbox. + */ + SMS_Deliver = 1, + /** + * Delivery Report + */ + SMS_Status_Report, + /** + * SMS for sending or in Outbox + */ + SMS_Submit + /* specification gives more */ +} GSM_SMSMessageType; + +/** + * Maximal length of SMS name. + */ +#define GSM_MAX_SMS_NAME_LENGTH 40 + +/** + * SMS message data. + */ +typedef struct { + /** + * Message to be replaced. + */ + unsigned char ReplaceMessage; + /** + * Whether to reject duplicates. + */ + bool RejectDuplicates; + /** + * UDH (User Data Header) + */ + GSM_UDHHeader UDH; + /** + * Sender or recipient number. + */ + unsigned char Number[(GSM_MAX_NUMBER_LENGTH+1)*2]; + /** + * SMSC (SMS Center) + */ + GSM_SMSC SMSC; + /** + * For saved SMS: where exactly it's saved (SIM/phone) + */ + GSM_MemoryType Memory; + /** + * For saved SMS: location of SMS in memory. + */ + int Location; + /** + * For saved SMS: number of folder, where SMS is saved + */ + int Folder; + /** + * For saved SMS: whether SMS is really in Inbox. + */ + bool InboxFolder; + /** + * Length of the SMS message. + */ + int Length; + /** + * Status (read/unread/...) of SMS message. + */ + GSM_SMS_State State; + /** + * Name in Nokia with SMS memory (6210/7110, etc.) Ignored in other. + */ + unsigned char Name[(GSM_MAX_SMS_NAME_LENGTH+1)*2]; + /** + * Text for SMS. + */ + unsigned char Text[(GSM_MAX_SMS_LENGTH+1)*2]; + /** + * Type of message. + */ + GSM_SMSMessageType PDU; + /** + * Type of coding. + */ + GSM_Coding_Type Coding; + /** + * Date and time, when SMS was saved or sent + */ + GSM_DateTime DateTime; + /** + * Date of SMSC response in DeliveryReport messages. + */ + GSM_DateTime SMSCTime; + /** + * In delivery reports: status. + */ + unsigned char DeliveryStatus; + /** + * Indicates whether "Reply via same center" is set. + */ + bool ReplyViaSameSMSC; + /** + * SMS class. + */ + char Class; + /** + * Message reference. + */ + unsigned char MessageReference; +} GSM_SMSMessage; + +/* In layouts are saved locations for some SMS part. Below are listed + * specs, which describe them + */ +typedef struct { + /** + * TP-User-Data. GSM 03.40 section 9.2.3.24. + */ + unsigned char Text; + /** + * - In SMS-Deliver: TP-Originating-Address. GSM 03.40 section 9.2.3.7. + * - In SMS-Submit: TP-Destination-Address. GSM 03.40 section 9.2.3.8. + * - In SMS-Status-Report: TP-Recipient-Address. GSM 03.40 section 9.2.3.14. + */ + unsigned char Number; + /** + * SMSC number + */ + unsigned char SMSCNumber; + /** + * TP-Data-Coding-Scheme. GSM 03.40 section 9.2.3.10 + */ + unsigned char TPDCS; + /** + * - For SMS-Submit: TP-Validity-Period. GSM 03.40 section 9.2.3.12. + * - For SMS-Status-Report: TP-Discharge Time. GSM 03.40 section 9.2.3.13. + */ + unsigned char DateTime; + /** + * TP-Service-Centre-Time-Stamp in SMS-Status-Report. GSM 03.40 section 9.2.3.11. + */ + unsigned char SMSCTime; + /** + * TP-Status in SMS-Status-Report. GSM 03.40 section 9.2.3.15. + */ + unsigned char TPStatus; + /** + * TP-User-Data-Length. GSM 03.40 section 9.2.3.16. + */ + unsigned char TPUDL; + /** + * TP-Validity Period in SMS-Submit. GSM 03.40 section 9.2.3.12. + */ + unsigned char TPVP; + /** + * Byte contains in SMS-Deliver: + * - TP-Message-Type-Indicator (2 bits) GSM 03.40 section 9.2.3.1 + * - TP-More-Messages-To-Send (1 bit). GSM 03.40 section 9.2.3.2 + * - TP-Reply-Path (1 bit). GSM 03.40 section 9.2.3.17 + * - TP-User-Data-Header-Indicator (1 bit). GSM 03.40 section 9.2.3.23 + * - TP-Status-Report-Indicator (1 bit). GSM 03.40 section 9.2.3.4 + * + * Byte contains in SMS-Submit: + * - TP-Message-Type-Indicator (2 bits) GSM 03.40 section 9.2.3.1 + * - TP-Reject-Duplicates (1 bit). GSM 03.40 section + * - TP-Validity-Period-Format (2 bits).GSM 03.40 section 9.2.3.3 + * - TP-Reply-Path (1 bit). GSM 03.40 section 9.2.3.17 + * - TP-User-Data-Header-Indicator (1 bit). GSM 03.40 section 9.2.3.23 + * - TP-Status-Report-Request (1 bit). GSM 03.40 section 9.2.3.5 + */ + unsigned char firstbyte; + /** + * TP-Message Reference in SMS-Submit. GSM 03.40 section 9.2.3.6 + */ + unsigned char TPMR; + /** + * TP-Protocol-Identifier. GSM 03.40 section 9.2.3.9 + */ + unsigned char TPPID; +} GSM_SMSMessageLayout; + +GSM_Error GSM_DecodeSMSFrame(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout); +GSM_Error GSM_EncodeSMSFrame(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout, int *length, bool clear); + +GSM_Error GSM_DecodeSMSFrameStatusReportData (GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout); +GSM_Error GSM_DecodeSMSFrameText (GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout); + +void GSM_DecodeUDHHeader(GSM_UDHHeader *UDH); +void GSM_EncodeUDHHeader(GSM_UDHHeader *UDH); + +void GSM_SetDefaultSMSData(GSM_SMSMessage *SMS); + +/* ---------------------- SMS folders ------------------------------------- */ + +/** + * Number of possible SMS folders. + */ +#define GSM_MAX_SMS_FOLDERS 24 +/** + * Maximal length of SMS folder name. + */ +#define GSM_MAX_SMS_FOLDER_NAME_LEN 20 + +/** + * Information about SMS folder. + */ +typedef struct { + /** + * Whether it is really inbox. + */ + bool InboxFolder; + /** + * Where exactly it's saved + */ + GSM_MemoryType Memory; + /** + * Name for SMS folder. + */ + char Name[(GSM_MAX_SMS_FOLDER_NAME_LEN+1)*2]; +} GSM_OneSMSFolder; + +/** + * List of SMS folders. + */ +typedef struct { + /** + * Array of structures holding information about each folder. + */ + GSM_OneSMSFolder Folder[GSM_MAX_SMS_FOLDERS]; + /** + * Number of SMS folders. + */ + unsigned char Number; +} GSM_SMSFolders; + +#endif + +/* 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