Imported gammu 0.90.7
[gammu.git] / common / service / sms / gsmsms.c
blobd5c1b6b7938bcdde8e4b55805cd329c02859ef5a
2 #include <ctype.h>
3 #include <string.h>
4 #include <time.h>
6 #include "../../gsmcomon.h"
7 #include "../../misc/coding/coding.h"
8 #include "../gsmcal.h"
9 #include "../gsmpbk.h"
10 #include "../gsmlogo.h"
11 #include "../gsmring.h"
12 #include "../gsmwap.h"
13 #include "../gsmnet.h"
14 #include "gsmsms.h"
16 /* User data headers */
17 static GSM_UDHHeader UDHHeaders[] = {
18 /* See GSM 03.40 section 9.2.3.24.1
19 * 1 byte 0x00
20 * 1 byte 0x03
21 * 1 byte 0x01: unique ID for message series
22 * 1 byte 0x00: how many SMS in sequence
23 * 1 byte 0x00: number of current SMS in sequence */
24 { UDH_ConcatenatedMessages, 0x05, "\x00\x03\x01\x00\x00",2,-1,4,3},
26 /* See GSM 03.40 section 9.2.3.24.2 for voice, fax and email messages */
27 { UDH_DisableVoice, 0x04, "\x01\x02\x00\x00",-1,-1,-1,-1},
28 { UDH_DisableFax, 0x04, "\x01\x02\x01\x00",-1,-1,-1,-1},
29 { UDH_DisableEmail, 0x04, "\x01\x02\x02\x00",-1,-1,-1,-1},
30 { UDH_EnableVoice, 0x04, "\x01\x02\x00\x01",-1,-1,-1,-1},
31 { UDH_EnableFax, 0x04, "\x01\x02\x01\x01",-1,-1,-1,-1},
32 { UDH_EnableEmail, 0x04, "\x01\x02\x02\x01",-1,-1,-1,-1},
34 /* When send such SMS to some phones, they don't display anything,
35 * only beep and enable vibra/light
37 { UDH_VoidSMS, 0x08, "\x01\x02\x02\x01\x01\x02\x02\x00",-1,-1,-1,-1},
39 /* Nokia Smart Messaging (short version) UDH
40 * General format :
41 * 1 byte 0x05 : IEI application port addressing scheme, 16 bit address
42 * 1 byte 0x04 : IEI length
43 * 2 bytes : destination address : high & low byte
44 * 2 bytes 0x00 0x00 : originator address : high & low byte */
45 { UDH_NokiaRingtone, 0x06, "\x05\x04\x15\x81\x00\x00",-1,-1,-1,-1},
46 { UDH_NokiaOperatorLogo, 0x06, "\x05\x04\x15\x82\x00\x00",-1,-1,-1,-1},
47 { UDH_NokiaCallerLogo, 0x06, "\x05\x04\x15\x83\x00\x00",-1,-1,-1,-1},
48 { UDH_NokiaWAP, 0x06, "\x05\x04\xc3\x4f\x00\x00",-1,-1,-1,-1},
50 /* Nokia Smart Messaging (long version) UDH and other
51 * General format:
52 * 1 byte 0x05 : IEI application port addressing scheme, 16 bit address
53 * 1 byte 0x04 : IEI length
54 * 2 bytes 0x00 0x00 : destination address : high & low byte
55 * 2 bytes 0x00 0x00 : originator address : high & low byte
56 * 1 byte 0x00 : SAR
57 * 1 byte 0x03 : SAR length
58 * 1 byte : diagram reference number (unique ID for message series)
59 * 1 byte : number of all SMS
60 * 1 byte : number of current SMS */
61 { UDH_NokiaCalendarLong, 0x0b, "\x05\x04\x00\xe4\x00\x00\x00\x03\xc7\x00\x00",8,-1,10,9},
62 { UDH_MMSIndicatorLong, 0x0b, "\x05\x04\x0b\x84\x23\xf0\x00\x03\xe5\x00\x00",8,-1,10,9},
63 { UDH_NokiaRingtoneLong, 0x0b, "\x05\x04\x15\x81\x00\x00\x00\x03\x01\x00\x00",8,-1,10,9},
64 { UDH_NokiaOperatorLogoLong, 0x0b, "\x05\x04\x15\x82\x00\x00\x00\x03\x02\x00\x00",8,-1,10,9},
65 { UDH_NokiaProfileLong, 0x0b, "\x05\x04\x15\x8a\x00\x00\x00\x03\xce\x00\x00",8,-1,10,9},
66 { UDH_NokiaPhonebookLong, 0x0b, "\x05\x04\x23\xf4\x00\x00\x00\x03\x01\x00\x00",8,-1,10,9},
67 { UDH_NokiaWAPLong, 0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x7f\x00\x00",8,-1,10,9},
69 { UDH_ConcatenatedMessages16bit,0x06, "\x08\x04\x00\x00\x00\x00",-1,2,5,4},
71 { UDH_NoUDH, 0x00, "",-1,-1,-1,-1}
74 /* --------------------------- Unpacking SMS ------------------------------- */
76 /* See GSM 03.40 section 9.2.3.11 */
77 static GSM_Error GSM_DecodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
79 DT->Year = DecodeWithBCDAlphabet(req[0]);
80 if (DT->Year<90) DT->Year=DT->Year+2000; else DT->Year=DT->Year+1990;
81 DT->Month = DecodeWithBCDAlphabet(req[1]);
82 DT->Day = DecodeWithBCDAlphabet(req[2]);
83 DT->Hour = DecodeWithBCDAlphabet(req[3]);
84 DT->Minute = DecodeWithBCDAlphabet(req[4]);
85 DT->Second = DecodeWithBCDAlphabet(req[5]);
87 /* Base for timezone is GMT. It's in quarters */
88 DT->Timezone=(10*(req[6]&0x07)+(req[6]>>4))/4;
90 if (req[6]&0x08) DT->Timezone = -DT->Timezone;
92 dbgprintf("Decoding date & time: ");
93 dbgprintf("%s %4d/%02d/%02d ", DayOfWeek(DT->Year, DT->Month, DT->Day),
94 DT->Year, DT->Month, DT->Day);
95 dbgprintf("%02d:%02d:%02d %02d00\n", DT->Hour, DT->Minute, DT->Second, DT->Timezone);
97 return GE_NONE;
100 void GSM_DecodeUDHHeader(GSM_UDHHeader *UDH)
102 int i, tmp, w;
103 bool UDHOK;
105 UDH->Type = UDH_UserUDH;
106 UDH->ID8bit = -1;
107 UDH->ID16bit = -1;
108 UDH->PartNumber = -1;
109 UDH->AllParts = -1;
111 i=-1;
112 while (true) {
113 i++;
114 if (UDHHeaders[i].Type==UDH_NoUDH) break;
116 tmp=UDHHeaders[i].Length;
117 /* if length is the same */
118 if (tmp==UDH->Text[0]) {
120 if (tmp==0x05) tmp=tmp-3;/*three last bytes can be different for such UDH*/
121 if (tmp==0x0b) tmp=tmp-3;/*three last bytes can be different for such UDH*/
122 if (tmp==0x06 && UDH->Text[1] == 0x08) tmp=tmp-4;
124 UDHOK=true;
125 for (w=0;w<tmp;w++) {
126 if (UDHHeaders[i].Text[w]!=UDH->Text[w+1]) {
127 UDHOK=false;
128 break;
131 if (UDHOK) {
132 UDH->Type=UDHHeaders[i].Type;
134 if (UDHHeaders[i].ID8bit !=-1) UDH->ID8bit = UDH->Text[UDHHeaders[i].ID8bit+1];
135 if (UDHHeaders[i].ID16bit !=-1) UDH->ID16bit = UDH->Text[UDHHeaders[i].ID16bit+1]*256+UDH->Text[UDHHeaders[i].ID16bit+2];
136 if (UDHHeaders[i].PartNumber !=-1) UDH->PartNumber = UDH->Text[UDHHeaders[i].PartNumber+1];
137 if (UDHHeaders[i].AllParts !=-1) UDH->AllParts = UDH->Text[UDHHeaders[i].AllParts+1];
138 break;
143 #ifdef DEBUG
144 dbgprintf("Type of UDH: ");
145 switch (UDH->Type) {
146 case UDH_ConcatenatedMessages : dbgprintf("Concatenated (linked) message"); break;
147 case UDH_ConcatenatedMessages16bit : dbgprintf("Concatenated (linked) message"); break;
148 case UDH_DisableVoice : dbgprintf("Disables voice indicator"); break;
149 case UDH_EnableVoice : dbgprintf("Enables voice indicator"); break;
150 case UDH_DisableFax : dbgprintf("Disables fax indicator"); break;
151 case UDH_EnableFax : dbgprintf("Enables fax indicator"); break;
152 case UDH_DisableEmail : dbgprintf("Disables email indicator"); break;
153 case UDH_EnableEmail : dbgprintf("Enables email indicator"); break;
154 case UDH_VoidSMS : dbgprintf("Void SMS"); break;
155 case UDH_NokiaWAP : dbgprintf("Nokia WAP Bookmark"); break;
156 case UDH_NokiaOperatorLogoLong : dbgprintf("Nokia operator logo"); break;
157 case UDH_NokiaWAPLong : dbgprintf("Nokia WAP Bookmark or WAP/MMS Settings"); break;
158 case UDH_NokiaRingtone : dbgprintf("Nokia ringtone"); break;
159 case UDH_NokiaRingtoneLong : dbgprintf("Nokia ringtone"); break;
160 case UDH_NokiaOperatorLogo : dbgprintf("Nokia GSM operator logo"); break;
161 case UDH_NokiaCallerLogo : dbgprintf("Nokia caller logo"); break;
162 case UDH_NokiaProfileLong : dbgprintf("Nokia profile"); break;
163 case UDH_NokiaCalendarLong : dbgprintf("Nokia calendar note"); break;
164 case UDH_NokiaPhonebookLong : dbgprintf("Nokia phonebook entry"); break;
165 case UDH_UserUDH : dbgprintf("User UDH"); break;
166 case UDH_MMSIndicatorLong : dbgprintf("MMS indicator"); break;
167 case UDH_NoUDH: break;
169 if (UDH->ID8bit != -1) dbgprintf(", ID 8 bit %i",UDH->ID8bit);
170 if (UDH->ID16bit != -1) dbgprintf(", ID 16 bit %i",UDH->ID16bit);
171 if (UDH->PartNumber != -1 && UDH->AllParts != -1) {
172 dbgprintf(", part %i of %i",UDH->PartNumber,UDH->AllParts);
174 dbgprintf("\n");
175 if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, UDH->Text, UDH->Length);
176 #endif
179 GSM_Error GSM_DecodeSMSFrameText(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout)
181 int off=0; /* off - length of the user data header */
182 int w,i,tmp=0;
183 unsigned char output[161];
185 SMS->UDH.Length = 0;
186 /* UDH header available */
187 if (buffer[Layout.firstbyte] & 64) {
188 /* Length of UDH header */
189 off = (buffer[Layout.Text] + 1);
190 SMS->UDH.Length = off;
191 dbgprintf("UDH header available (length %i)\n",off);
193 /* Copy UDH header into SMS->UDH */
194 for (i = 0; i < off; i++) SMS->UDH.Text[i] = buffer[Layout.Text + i];
196 GSM_DecodeUDHHeader(&SMS->UDH);
199 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
200 if ((buffer[Layout.TPDCS] & 0xf4) == 0xf4) SMS->Coding=GSM_Coding_8bit;
201 if ((buffer[Layout.TPDCS] & 0x08) == 0x08) SMS->Coding=GSM_Coding_Unicode;
203 switch (SMS->Coding) {
204 case GSM_Coding_Default:
205 i = 0;
206 do {
207 i+=7;
208 w=(i-off)%i;
209 } while (w<0);
210 SMS->Length=buffer[Layout.TPUDL] - (off*8 + w) / 7;
211 tmp=GSM_UnpackEightBitsToSeven(w, buffer[Layout.TPUDL]-off, SMS->Length, buffer+(Layout.Text+off), output);
212 dbgprintf("7 bit SMS, length %i\n",SMS->Length);
213 DecodeDefault (SMS->Text, output, SMS->Length, true, NULL);
214 dbgprintf("%s\n",DecodeUnicodeString(SMS->Text));
215 break;
216 case GSM_Coding_8bit:
217 SMS->Length=buffer[Layout.TPUDL] - off;
218 memcpy(SMS->Text,buffer+(Layout.Text+off),SMS->Length);
219 #ifdef DEBUG
220 dbgprintf("8 bit SMS, length %i\n",SMS->Length);
221 if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, SMS->Text, SMS->Length);
222 #endif
223 break;
224 case GSM_Coding_Unicode:
225 SMS->Length=(buffer[Layout.TPUDL] - off) / 2;
226 DecodeUnicodeSpecialNOKIAChars(SMS->Text,buffer+(Layout.Text+off), SMS->Length);
227 #ifdef DEBUG
228 dbgprintf("Unicode SMS, length %i\n",SMS->Length);
229 if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df,buffer+(Layout.Text+off), SMS->Length*2);
230 dbgprintf("%s\n",DecodeUnicodeString(SMS->Text));
231 #endif
232 break;
235 return GE_NONE;
238 GSM_Error GSM_DecodeSMSFrameStatusReportData(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout)
240 SMS->DeliveryStatus = buffer[Layout.TPStatus];
242 if (buffer[Layout.TPStatus] < 0x03) {
243 EncodeUnicode(SMS->Text,"Delivered",9);
244 SMS->Length = 9;
245 } else if (buffer[Layout.TPStatus] & 0x40) {
246 EncodeUnicode(SMS->Text,"Failed",6);
247 SMS->Length = 6;
248 } else if (buffer[Layout.TPStatus] & 0x20) {
249 EncodeUnicode(SMS->Text,"Pending",7);
250 SMS->Length = 7;
251 } else {
252 EncodeUnicode(SMS->Text,"Unknown",7);
253 SMS->Length = 7;
256 #ifdef DEBUG
257 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
258 if (buffer[Layout.TPStatus] & 0x40) {
259 if (buffer[Layout.TPStatus] & 0x20) {
260 /* 0x60, 0x61, ... */
261 dbgprintf("Temporary error, SC is not making any more transfer attempts\n");
262 } else {
263 /* 0x40, 0x41, ... */
264 dbgprintf("Permanent error, SC is not making any more transfer attempts\n");
266 } else if (buffer[Layout.TPStatus] & 0x20) {
267 /* 0x20, 0x21, ... */
268 dbgprintf("Temporary error, SC still trying to transfer SM\n");
270 switch (buffer[Layout.TPStatus]) {
271 case 0x00: dbgprintf("SM received by the SME"); break;
272 case 0x01: dbgprintf("SM forwarded by the SC to the SME but the SC is unable to confirm delivery");break;
273 case 0x02: dbgprintf("SM replaced by the SC"); break;
274 case 0x20: dbgprintf("Congestion"); break;
275 case 0x21: dbgprintf("SME busy"); break;
276 case 0x22: dbgprintf("No response from SME"); break;
277 case 0x23: dbgprintf("Service rejected"); break;
278 case 0x24: dbgprintf("Quality of service not available"); break;
279 case 0x25: dbgprintf("Error in SME"); break;
280 case 0x40: dbgprintf("Remote procedure error"); break;
281 case 0x41: dbgprintf("Incompatibile destination"); break;
282 case 0x42: dbgprintf("Connection rejected by SME"); break;
283 case 0x43: dbgprintf("Not obtainable"); break;
284 case 0x44: dbgprintf("Quality of service not available"); break;
285 case 0x45: dbgprintf("No internetworking available"); break;
286 case 0x46: dbgprintf("SM Validity Period Expired"); break;
287 case 0x47: dbgprintf("SM deleted by originating SME"); break;
288 case 0x48: dbgprintf("SM Deleted by SC Administration"); break;
289 case 0x49: dbgprintf("SM does not exist"); break;
290 case 0x60: dbgprintf("Congestion"); break;
291 case 0x61: dbgprintf("SME busy"); break;
292 case 0x62: dbgprintf("No response from SME"); break;
293 case 0x63: dbgprintf("Service rejected"); break;
294 case 0x64: dbgprintf("Quality of service not available"); break;
295 case 0x65: dbgprintf("Error in SME"); break;
296 default : dbgprintf("Reserved/Specific to SC: %x",buffer[Layout.TPStatus]); break;
298 dbgprintf("\n");
299 #endif /* DEBUG */
301 return GE_NONE;
304 GSM_Error GSM_DecodeSMSFrame(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout)
306 #ifdef DEBUG
307 if (Layout.firstbyte == 255) {
308 dbgprintf("ERROR: firstbyte in SMS layout not set\n");
309 return GE_UNKNOWN;
311 if (Layout.TPDCS != 255) dbgprintf("TPDCS : %02x %i\n",buffer[Layout.TPDCS] ,buffer[Layout.TPDCS]);
312 if (Layout.TPMR != 255) dbgprintf("TPMR : %02x %i\n",buffer[Layout.TPMR] ,buffer[Layout.TPMR]);
313 if (Layout.TPPID != 255) dbgprintf("TPPID : %02x %i\n",buffer[Layout.TPPID] ,buffer[Layout.TPPID]);
314 if (Layout.TPUDL != 255) dbgprintf("TPUDL : %02x %i\n",buffer[Layout.TPUDL] ,buffer[Layout.TPUDL]);
315 if (Layout.firstbyte != 255) dbgprintf("FirstByte : %02x %i\n",buffer[Layout.firstbyte],buffer[Layout.firstbyte]);
316 if (Layout.Text != 255) dbgprintf("Text : %02x %i\n",buffer[Layout.Text] ,buffer[Layout.Text]);
317 #endif
319 SMS->UDH.Type = UDH_NoUDH;
320 SMS->Coding = GSM_Coding_Default;
321 SMS->Length = 0;
322 SMS->SMSC.Number[0] = 0;
323 SMS->SMSC.Number[1] = 0;
324 SMS->Number[0] = 0;
325 SMS->Number[1] = 0;
326 SMS->Name[0] = 0;
327 SMS->Name[1] = 0;
328 SMS->ReplyViaSameSMSC = false;
329 if (Layout.SMSCNumber!=255) {
330 GSM_UnpackSemiOctetNumber(SMS->SMSC.Number,buffer+Layout.SMSCNumber,false);
331 dbgprintf("SMS center number : \"%s\"\n",DecodeUnicodeString(SMS->SMSC.Number));
333 if ((buffer[Layout.firstbyte] & 0x80)!=0) SMS->ReplyViaSameSMSC=true;
334 #ifdef DEBUG
335 if (SMS->ReplyViaSameSMSC) dbgprintf("SMS centre set for reply\n");
336 #endif
337 if (Layout.Number!=255) {
338 GSM_UnpackSemiOctetNumber(SMS->Number,buffer+Layout.Number,true);
339 dbgprintf("Remote number : \"%s\"\n",DecodeUnicodeString(SMS->Number));
341 if (Layout.Text != 255 && Layout.TPDCS!=255 && Layout.TPUDL!=255) {
342 GSM_DecodeSMSFrameText(SMS, buffer, Layout);
344 if (Layout.DateTime != 255) {
345 GSM_DecodeSMSDateTime(&SMS->DateTime,buffer+(Layout.DateTime));
347 if (Layout.SMSCTime != 255 && Layout.TPStatus != 255) {
348 /* See GSM 03.40 section 9.2.3.11 (TP-Service-Centre-Time-Stamp) */
349 dbgprintf("SMSC response date: ");
350 GSM_DecodeSMSDateTime(&SMS->SMSCTime, buffer+(Layout.SMSCTime));
351 GSM_DecodeSMSFrameStatusReportData(SMS,buffer,Layout);
353 SMS->Class = -1;
354 if (Layout.TPDCS != 255) {
355 if ((buffer[Layout.TPDCS] & 0xF3)==0xF0) SMS->Class = 0;
356 if ((buffer[Layout.TPDCS] & 0xF3)==0xF1) SMS->Class = 1;
357 if ((buffer[Layout.TPDCS] & 0xF3)==0xF2) SMS->Class = 2;
358 if ((buffer[Layout.TPDCS] & 0xF3)==0xF3) SMS->Class = 3;
360 dbgprintf("SMS class: %i\n",SMS->Class);
361 SMS->MessageReference = 0;
362 if (Layout.TPMR != 255) {
363 SMS->MessageReference = buffer[Layout.TPMR];
365 SMS->ReplaceMessage = 0;
366 if (Layout.TPPID != 255) {
367 if (buffer[Layout.TPPID] > 0x40 && buffer[Layout.TPPID] < 0x48) {
368 SMS->ReplaceMessage = buffer[Layout.TPPID] - 0x40;
371 SMS->RejectDuplicates = false;
372 if ((buffer[Layout.firstbyte] & 0x04)==0x04) SMS->RejectDuplicates = true;
374 return GE_NONE;
377 /* ----------------------------- Packing SMS ------------------------------- */
379 /* See GSM 03.40 section 9.2.3.11 */
380 static GSM_Error GSM_EncodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
382 int Year;
384 dbgprintf("Encoding SMS datetime: %02i/%02i/%04i %02i:%02i:%02i\n",
385 DT->Day,DT->Month,DT->Year,DT->Hour,DT->Minute,DT->Second);
387 /* We need to have only two last digits of year */
388 if (DT->Year>1900)
390 if (DT->Year<2000) Year = DT->Year-1900;
391 else Year = DT->Year-2000;
392 } else Year = DT->Year;
394 req[0]=EncodeWithBCDAlphabet(Year);
395 req[1]=EncodeWithBCDAlphabet(DT->Month);
396 req[2]=EncodeWithBCDAlphabet(DT->Day);
397 req[3]=EncodeWithBCDAlphabet(DT->Hour);
398 req[4]=EncodeWithBCDAlphabet(DT->Minute);
399 req[5]=EncodeWithBCDAlphabet(DT->Second);
401 /* FIXME: do it */
402 req[6]=0; /* TimeZone = +-0 */
404 return GE_NONE;
407 static int GSM_EncodeSMSFrameText(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout)
409 int off = 0; /* off - length of the user data header */
410 int size = 0, size2 = 0, w,p;
411 char buff[200];
413 if (SMS->UDH.Type!=UDH_NoUDH) {
414 buffer[Layout.firstbyte] |= 0x40; /* GSM 03.40 section 9.2.3.23 (TP-User-Data-Header-Indicator) */
415 off = 1 + SMS->UDH.Text[0]; /* off - length of the user data header */
416 memcpy(buffer+Layout.Text, SMS->UDH.Text, off); /* we copy the udh */
417 #ifdef DEBUG
418 dbgprintf("UDH, length %i\n",off);
419 if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df, SMS->UDH.Text, off);
420 #endif
422 switch (SMS->Coding) {
423 case GSM_Coding_8bit:
424 /* the mask for the 8-bit data */
425 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme)
426 * and GSM 03.38 section 4 */
427 buffer[Layout.TPDCS] |= 0xf4;
428 memcpy(buffer+(Layout.Text+off), SMS->Text, SMS->Length);
429 size2 = size = SMS->Length+off;
430 #ifdef DEBUG
431 dbgprintf("8 bit SMS, length %i\n",SMS->Length);
432 if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df,SMS->Text,SMS->Length);
433 #endif
434 break;
435 case GSM_Coding_Default:
436 p = 0;
437 do {
438 p+=7;
439 w=(p-off)%p;
440 } while (w<0);
441 p = UnicodeLength(SMS->Text);
442 EncodeDefault(buff, SMS->Text, &p, true, NULL);
443 size = GSM_PackSevenBitsToEight(w, buff, buffer+(Layout.Text+off), p);
444 size += off;
445 size2 = (off*8 + w) / 7 + p;
446 dbgprintf("7 bit SMS, length %i, %i\n",size,size2);
447 dbgprintf("%s\n",DecodeUnicodeString(SMS->Text));
448 if (size > GSM_MAX_8BIT_SMS_LENGTH) {
449 size = 0; size2 = 0;
451 break;
452 case GSM_Coding_Unicode:
453 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme)
454 * and GSM 03.38 section 4 */
455 buffer[Layout.TPDCS] |= 0x08;
456 EncodeUnicodeSpecialNOKIAChars(buffer+(Layout.Text+off), SMS->Text, UnicodeLength(SMS->Text));
457 size=size2=UnicodeLength(buffer+(Layout.Text+off))*2+off;
458 #ifdef DEBUG
459 dbgprintf("Unicode SMS, length %i\n",(size2-off)/2);
460 if (di.dl == DL_TEXTALL || di.dl == DL_TEXTALLDATE) DumpMessage(di.df,buffer+(Layout.Text+off), size2-off);
461 dbgprintf("%s\n",DecodeUnicodeString(buffer+(Layout.Text+off)));
462 #endif
463 break;
466 /* SMS->Length is:
467 - integer representation of the number od octets within the user data
468 when UD is coded using 8bit data
469 - the sum of the number of septets in UDH including any padding
470 and number of septets in UD in other case */
471 /* GSM 03.40 section 9.2.3.16 (TP-User-Data-Length) */
472 buffer[Layout.TPUDL] = size2;
473 return size;
476 GSM_Error GSM_EncodeSMSFrame(GSM_SMSMessage *SMS, unsigned char *buffer, GSM_SMSMessageLayout Layout, int *length, bool clear)
478 int i;
480 if (clear) {
481 /* Cleaning up to the SMS text */
482 for (i=0;i<Layout.Text;i++) buffer[i] = 0;
485 /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
486 switch (SMS->PDU) {
487 case SMS_Submit:
488 buffer[Layout.firstbyte] |= 0x01;
489 break;
490 /* SMS_Status_Report when Submit sms should have delivery report */
491 /* We DON'T CREATE FRAME FOR REAL SMS_STATUS_REPORT */
492 case SMS_Status_Report:
493 buffer[Layout.firstbyte] |= 0x01;
494 /* GSM 03.40 section 9.2.3.5 (TP-Status-Raport-Request) */
495 /* Mask when want delivery report from SMSC */
496 buffer[Layout.firstbyte] |= 0x20;
497 break;
498 case SMS_Deliver:
499 buffer[Layout.firstbyte] |= 0x00;
502 /* GSM 03.40 section 9.2.3.17 (TP-Reply-Path) */
503 if (SMS->ReplyViaSameSMSC) buffer[Layout.firstbyte] |= 0x80;
505 if (Layout.Number!=255) {
506 buffer[Layout.Number] = GSM_PackSemiOctetNumber(SMS->Number,buffer+(Layout.Number+1),true);
507 dbgprintf("Recipient number \"%s\"\n",DecodeUnicodeString(SMS->Number));
509 if (Layout.SMSCNumber!=255) {
510 buffer[Layout.SMSCNumber]=GSM_PackSemiOctetNumber(SMS->SMSC.Number,buffer+(Layout.SMSCNumber+1), false);
511 dbgprintf("SMSC number \"%s\"\n",DecodeUnicodeString(SMS->SMSC.Number));
514 /* Message Class*/
515 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
516 if (Layout.TPDCS != 255) {
517 if (SMS->Class>=0 && SMS->Class<5) buffer[Layout.TPDCS] |= (240+SMS->Class);
520 if (Layout.TPVP != 255) {
521 /* GSM 03.40 section 9.2.3.3 (TP-Validity-Period-Format) */
522 /* Bits 4 and 3: 10. TP-VP field present and integer represent (relative) */
523 buffer[Layout.firstbyte] |= 0x10;
524 buffer[Layout.TPVP]=((unsigned char)SMS->SMSC.Validity.Relative);
525 dbgprintf("SMS validity %02x\n",SMS->SMSC.Validity.Relative);
528 if (Layout.DateTime != 255) {
529 GSM_EncodeSMSDateTime(&SMS->DateTime, buffer+Layout.DateTime);
532 if (Layout.TPMR != 255) {
533 buffer[Layout.TPMR] = SMS->MessageReference;
536 if (SMS->RejectDuplicates) {
537 /* GSM 03.40 section 9.2.3.25 (TP Reject Duplicates) */
538 buffer[Layout.firstbyte] |= 0x04;
541 if (Layout.TPPID != 255) {
542 buffer[Layout.TPPID] = 0;
543 if (SMS->ReplaceMessage > 0 && SMS->ReplaceMessage < 8) {
544 buffer[Layout.TPPID] = 0x40 + SMS->ReplaceMessage;
548 /* size is the length of the data in octets including udh */
549 *length=GSM_EncodeSMSFrameText(SMS,buffer,Layout);
550 // if (*length == 0) return GE_UNKNOWN;
551 *length += Layout.Text;
553 return GE_NONE;
556 /* ----------------- Some help functions ----------------------------------- */
558 void GSM_SetDefaultSMSData(GSM_SMSMessage *SMS)
560 SMS->Class = -1;
561 SMS->SMSC.Location = 1;
562 SMS->SMSC.Validity.VPF = GSM_RelativeFormat;
563 SMS->SMSC.Validity.Relative = GSMV_Max_Time;
564 SMS->ReplyViaSameSMSC = false;
565 SMS->UDH.Type = UDH_NoUDH;
566 SMS->UDH.Length = 0;
567 SMS->UDH.Text[0] = 0;
568 SMS->UDH.ID8bit = 0;
569 SMS->UDH.ID16bit = 0;
570 SMS->UDH.PartNumber = 0;
571 SMS->UDH.AllParts = 0;
572 SMS->Coding = GSM_Coding_Default;
573 SMS->Text[0] = 0;
574 SMS->Text[1] = 0;
575 SMS->PDU = SMS_Submit;
576 SMS->RejectDuplicates = false;
577 SMS->MessageReference = 0;
578 SMS->ReplaceMessage = 0;
579 SMS->Length = 0;
581 /* This part is required to save SMS */
582 SMS->State = GSM_UnSent;
583 SMS->Location = 0;
584 SMS->Folder = 0x02; /*Outbox*/
585 GSM_GetCurrentDateTime (&SMS->DateTime);
586 SMS->Name[0] = 0;
587 SMS->Name[1] = 0;
591 * GSM 03.40 section 9.2.3.24
593 void GSM_EncodeUDHHeader(GSM_UDHHeader *UDH)
595 int i=0;
597 switch (UDH->Type) {
598 case UDH_NoUDH:
599 UDH->Length = 0;
600 break;
601 case UDH_UserUDH:
602 UDH->Length = UDH->Text[0] + 1;
603 break;
604 default:
605 while (true) {
606 if (UDHHeaders[i].Type==UDH_NoUDH) {
607 dbgprintf("Not supported UDH type\n");
608 break;
610 if (UDHHeaders[i].Type==UDH->Type) {
611 /* UDH Length */
612 UDH->Text[0] = UDHHeaders[i].Length;
613 memcpy(UDH->Text+1, UDHHeaders[i].Text, UDHHeaders[i].Length);
614 UDH->Length = UDH->Text[0] + 1;
616 if (UDHHeaders[i].ID8bit != -1) {
617 UDH->Text[UDHHeaders[i].ID8bit+1] = UDH->ID8bit % 256;
618 } else {
619 UDH->ID8bit = -1;
621 if (UDHHeaders[i].ID16bit != -1) {
622 UDH->Text[UDHHeaders[i].ID16bit+1] = UDH->ID16bit / 256;
623 UDH->Text[UDHHeaders[i].ID16bit+2] = UDH->ID16bit % 256;
624 } else {
625 UDH->ID16bit = -1;
627 if (UDHHeaders[i].PartNumber != -1) {
628 UDH->Text[UDHHeaders[i].PartNumber+1] = UDH->PartNumber;
629 } else {
630 UDH->PartNumber = -1;
632 if (UDHHeaders[i].AllParts != -1) {
633 UDH->Text[UDHHeaders[i].AllParts+1] = UDH->AllParts;
634 } else {
635 UDH->AllParts = -1;
637 break;
639 i++;
644 /* How should editor hadle tabs in this file? Add editor commands here.
645 * vim: noexpandtab sw=8 ts=8 sts=8: