5 A Linux/Unix toolset and driver for Nokia mobile phones.
7 Released under the terms of the GNU GPL, see file COPYING for more details.
18 #include "gsm-coding.h"
20 /* User data headers */
21 GSM_UDHHeader UDHHeaders[] = {
22 { GSM_ConcatenatedMessages, 0x05, "\x00\x03\x01\x00\x00" },
23 /* See GSM 03.40 section 9.2.3.24.1 */
24 { GSM_DisableVoice, 0x04, "\x01\x02\x00\x00" },
25 { GSM_DisableFax, 0x04, "\x01\x02\x01\x00" },
26 { GSM_DisableEmail, 0x04, "\x01\x02\x02\x00" },
27 { GSM_EnableVoice, 0x04, "\x01\x02\x00\x01" },
28 { GSM_EnableFax, 0x04, "\x01\x02\x01\x01" },
29 { GSM_EnableEmail, 0x04, "\x01\x02\x02\x01" },
30 /* See GSM 03.40 section 9.2.3.24.2 for voice, fax and email messages */
31 { GSM_VoidSMS, 0x08, "\x01\x02\x02\x01\x01\x02\x02\x00" },
32 /* When send such SMS to some phones, they don't display anything,
33 only beep and enable vibra/light */
34 { GSM_BugSMS, 0x09, "\x01\x02\x02\x01\x01\x02\x02\x00\xc0" },
36 /* Short Smart Messaging UDH */
38 /* 1 byte 0x05 : IEI application port addressing scheme, 16 bit address */
39 /* 1 byte 0x04 : IEI length */
40 /* 2 bytes : destination address: high & low byte */
41 /* 2 bytes 0x00 0x00: originator address: high & low byte */
42 { GSM_RingtoneUDH, 0x06, "\x05\x04\x15\x81\x00\x00" },
43 { GSM_OpLogo, 0x06, "\x05\x04\x15\x82\x00\x00" },
44 { GSM_CallerIDLogo, 0x06, "\x05\x04\x15\x83\x00\x00" },
45 { GSM_WAPBookmarkUDH, 0x06, "\x05\x04\xc3\x4f\x00\x00" },
47 /* Long Smart Messaging UDH */
49 /* 1 byte 0x05 : IEI application port addressing scheme, 16 bit address */
50 /* 1 byte 0x04 : IEI length */
51 /* 2 bytes 0x00 0x00: destination address: high & low byte */
52 /* 2 bytes 0x00 0x00: originator address: high & low byte */
53 /* 1 byte 0x00 : null byte for end first part ? */
54 /* 1 byte 0x03 : length for rest ? */
56 /* 1 byte : number of all SMS */
57 /* 1 byte : number of current SMS */
58 { GSM_CalendarNoteUDH, 0x0b, "\x05\x04\x00\xe4\x00\x00\x00\x03\xc7\x00\x00" }, //from 3310 ?
59 { GSM_CalendarNoteUDH2, 0x0b, "\x05\x04\x23\xf5\x00\x00\x00\x03\x01\x00\x00" }, //from 6210 or 9210 Note: last 0x01 changes according to note type
60 { GSM_ProfileUDH, 0x0b, "\x05\x04\x15\x8a\x00\x00\x00\x03\xce\x00\x00" },
61 { GSM_WAPBookmarkUDH, 0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x01\x00\x00" },//note last 0x01 can change
62 { GSM_WAPSettingsUDH, 0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x7f\x00\x00" },
63 { GSM_PhonebookUDH, 0x0b, "\x05\x04\x23\xf4\x00\x00\x00\x03\x01\x00\x00" },
65 { GSM_NoUDH, 0x00, "" }
68 #define ByteMask ((1 << Bits) - 1)
70 int GSM_PackSevenBitsToEight(int offset, unsigned char *input, unsigned char *output)
72 unsigned char *OUT = output; /* Current pointer to the output buffer */
73 unsigned char *IN = input; /* Current pointer to the input buffer */
74 int Bits; /* Number of bits directly copied to
76 Bits = (7 + offset) % 8;
78 /* If we don't begin with 0th bit, we will write only a part of the
85 while ((IN - input) < strlen(input)) {
86 unsigned char Byte = EncodeWithDefaultAlphabet(*IN);
88 *OUT = Byte >> (7 - Bits);
89 /* If we don't write at 0th bit of the octet, we should write
90 a second part of the previous octet */
92 *(OUT-1) |= (Byte & ((1 << (7-Bits)) - 1)) << (Bits+1);
96 if (Bits == -1) Bits = 7;
101 return (OUT - output);
104 int GSM_UnpackEightBitsToSeven(int offset, int in_length, int out_length,
105 unsigned char *input, unsigned char *output)
107 unsigned char *OUT = output; /* Current pointer to the output buffer */
108 unsigned char *IN = input; /* Current pointer to the input buffer */
109 unsigned char Rest = 0x00;
112 Bits = offset ? offset : 7;
114 while ((IN - input) < in_length) {
116 *OUT = ((*IN & ByteMask) << (7 - Bits)) | Rest;
119 /* If we don't start from 0th bit, we shouldn't go to the
120 next char. Under *OUT we have now 0 and under Rest -
121 _first_ part of the char. */
122 if ((IN != input) || (Bits == 7)) OUT++;
125 if ((OUT - output) >= out_length) break;
127 /* After reading 7 octets we have read 7 full characters but
128 we have 7 bits as well. This is the next character */
142 /* This function implements packing of numbers (SMS Center number and
143 destination number) for SMS sending function. */
144 /* See GSM 03.40 9.1.1:
145 1 byte - length of number given in semioctets or bytes (when given in bytes,
146 includes one byte for byte with number format.
147 Returned by function (set semioctet to true, if want result
149 1 byte - format of number. See GSM_NumberType; in gsm-common.h. Returned
151 n bytes - 2n or 2n-1 semioctets with number. For some types of numbers
152 in the most significant bits of the last byte with 0x0f
153 (1111 binary) are filled if the number is represented
154 with odd number of digits. Returned in u8 *Output. */
155 /* 1 semioctet = 4 bits = half of byte */
156 int GSM_PackSemiOctetNumber(u8 *Number, u8 *Output, bool semioctet) {
158 u8 *IN=Number; /* Pointer to the input number */
159 u8 *OUT=Output; /* Pointer to the output */
161 unsigned char format=GNT_UNKNOWN; // format of number used by us
163 /* Checking for format number */
165 if (length==0 && *IN=='+')
166 format=GNT_INTERNATIONAL; // first byte is '+'. It can be international
168 if (*IN>'9' || *IN<'0') {
169 format=GNT_ALPHANUMERIC; //char is not number. It must be alphanumeric
175 /* Now we return to first byte */
176 for (j=0;j<length;j++) IN--;
178 /* The first byte in the Semi-octet representation of the address field is
179 the Type-of-Address. This field is described in the official GSM
180 specification 03.40 version 5.3.0, section 9.1.2.5, page 33.*/
183 /* The next field is the number. See GSM 03.40 section 9.1.2 */
185 case GNT_ALPHANUMERIC:
186 length=GSM_PackSevenBitsToEight(0, IN, OUT)*2;
189 case GNT_INTERNATIONAL:
191 EncodeBCD (OUT, IN+1, length, true);
195 EncodeBCD (OUT, IN, length, true);
202 /* Convert number of semioctets to number of chars */
203 if (length % 2) length++;
204 return length / 2 + 1;
208 char *GSM_UnpackSemiOctetNumber(u8 *Number, bool semioctet) {
210 static char Buffer[20]="";
211 int length=Number[0];
214 /* Convert number of semioctets to number of chars */
215 if (length % 2) length++;
216 length=length / 2 + 1;
219 length--; //without leading byte with format of number
222 case GNT_ALPHANUMERIC:
223 GSM_UnpackEightBitsToSeven(0, length, length, Number+2, Buffer);
227 case GNT_INTERNATIONAL:
229 DecodeBCD (Buffer+1, Number+2, length);
233 DecodeBCD (Buffer, Number+2, length);
240 /* See GSM 03.40 section 9.2.3.11 */
241 GSM_Error GSM_EncodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
244 fprintf(stdout,_("Date & time in saved SMS: %02i/%02i/%04i %02i:%02i:%02i\n"),
245 DT->Day,DT->Month,DT->Year,DT->Hour,DT->Minute,DT->Second);
248 req[0]=EncodeWithBCDAlphabet(DT->Year);
249 req[1]=EncodeWithBCDAlphabet(DT->Month);
250 req[2]=EncodeWithBCDAlphabet(DT->Day);
251 req[3]=EncodeWithBCDAlphabet(DT->Hour);
252 req[4]=EncodeWithBCDAlphabet(DT->Minute);
253 req[5]=EncodeWithBCDAlphabet(DT->Second);
256 req[6]=0; /* TimeZone = +-0 */
261 /* See GSM 03.40 section 9.2.3.11 */
262 GSM_Error GSM_DecodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
264 DT->Year = DecodeWithBCDAlphabet(req[0]);
265 DT->Month = DecodeWithBCDAlphabet(req[1]);
266 DT->Day = DecodeWithBCDAlphabet(req[2]);
267 DT->Hour = DecodeWithBCDAlphabet(req[3]);
268 DT->Minute = DecodeWithBCDAlphabet(req[4]);
269 DT->Second = DecodeWithBCDAlphabet(req[5]);
271 DT->Timezone=(10*(req[6]&0x07)+(req[6]>>4))/4;
272 if (req[6]&0x08) DT->Timezone = -DT->Timezone;
275 fprintf(stdout, _("%d%d-"), req[0]&0xf, req[0]>>4);
276 fprintf(stdout, _("%d%d-"), req[1]&0xf, req[1]>>4);
277 fprintf(stdout, _("%d%d "), req[2]&0xf, req[2]>>4);
278 fprintf(stdout, _("%d%d:"), req[3]&0xf, req[3]>>4);
279 fprintf(stdout, _("%d%d:"), req[4]&0xf, req[4]>>4);
280 fprintf(stdout, _("%d%d "), req[5]&0xf, req[5]>>4);
283 if (req[6] & 0x08) fprintf(stdout, "-");
284 else fprintf(stdout, "+");
286 fprintf(stdout, _("%02d00"), (10*(req[6]&0x07)+(req[6]>>4))/4);
289 fprintf(stdout, "\n");
295 int GSM_EncodeETSISMSSubmitData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
297 int off,size=0,size2=0,w,i;
299 /* off - length of the user data header */
303 /* GSM 03.40 section 9.2.3.23 (TP-User-Data-Header-Indicator) */
304 ETSI->firstbyte |= 0x40;
306 /* off - length of the user data header */
307 off = 1 + SMS->UDH[0];
309 /* we copy the udh */
310 memcpy(ETSI->MessageText, SMS->UDH, off);
312 // if (SMS->UDHType==GSM_HangSMS) ETSI->TPDCS=0x08; //Such SMS hangs phone (5110, 5.07),
313 //when saved to Outbox atry try to read it
314 /*from phone's menu*/
317 switch (SMS->Coding) {
318 /* When save SMS to SIM and it's 8 bit SMS,
319 "Edit" is not displayed in phone menu and
320 "Message cannot be displayed here" instead of message text */
321 case GSM_Coding_8bit:
323 /* the mask for the 8-bit data */
324 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
327 memcpy(ETSI->MessageText + off, SMS->MessageText, SMS->Length);
329 size2 = size = SMS->Length+off;
333 case GSM_Coding_Default:
336 if (w<0) w=(14-off)%14;
338 size = GSM_PackSevenBitsToEight(w, SMS->MessageText, ETSI->MessageText + off);
340 size2 = (off*8 + w) / 7 + strlen(SMS->MessageText);
344 case GSM_Coding_Unicode:
346 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
350 fprintf(stdout,_("SMS Length is %i\n"),strlen(SMS->MessageText));
353 EncodeUnicode (ETSI->MessageText+off,SMS->MessageText,strlen(SMS->MessageText));
354 /* here we code "special" chars */
355 for (i=0;i<strlen(SMS->MessageText);i++) {
356 if (SMS->MessageText[i]=='~') ETSI->MessageText[off+1+i*2]=1; //enables/disables blinking
357 if (SMS->MessageText[i]=='`') ETSI->MessageText[off+1+i*2]=0; //hides rest ot contents
360 size=size2=strlen(SMS->MessageText)*2+off;
365 /* FIXME: support for compression */
366 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
367 /* See also GSM 03.42 */
368 /* if (SMS->Compression) ETSI->TPDCS |= 0x20; */
371 - integer representation of the number od octets within the user data when UD is coded using 8bit data
372 - the sum of the number of septets in UDH including any padding and number of septets in UD in other case */
373 /* GSM 03.40 section 9.2.3.16 (TP-User-Data-Length) */
379 GSM_Error GSM_DecodeETSISMSSubmitData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
382 unsigned char output[161];
385 /* off - length of the user data header */
388 SMS->UDHType = GSM_NoUDH;
390 if (ETSI->firstbyte & 64) { /* UDH header available */
392 off = (ETSI->MessageText[0] + 1); /* Length of UDH header */
394 /* Copy UDH header into SMS->UDH */
395 for (i = 0; i < off; i++) SMS->UDH[i] = ETSI->MessageText[i];
398 fprintf(stdout, " UDH header available (length %i",off);
401 SMS->UDHType = GSM_UnknownUDH;
406 if (UDHHeaders[i].UDHType==GSM_NoUDH) break;
407 tmp=UDHHeaders[i].Length;
408 if (tmp==SMS->UDH[0]) { //if length is the same
410 if (tmp==0x05) tmp=tmp-2;/*two last bytes can be different for such UDH*/
411 if (tmp==0x0b) tmp=tmp-3;/*three last bytes can be different for such UDH*/
414 for (w=0;w<tmp;w++) {
415 if (UDHHeaders[i].Text[w]!=SMS->UDH[w+1]) {
421 SMS->UDHType=UDHHeaders[i].UDHType;
428 switch (SMS->UDHType) {
429 case GSM_ConcatenatedMessages:
430 fprintf(stdout,_(", concatenated (linked) message %d/%d"),SMS->UDH[5],SMS->UDH[4]);break;
431 case GSM_DisableVoice:
432 fprintf(stdout,_(", disables voice indicator"));break;
433 case GSM_EnableVoice:
434 fprintf(stdout,_(", enables voice indicator"));break;
436 fprintf(stdout,_(", disables fax indicator"));break;
438 fprintf(stdout,_(", enables fax indicator"));break;
439 case GSM_DisableEmail:
440 fprintf(stdout,_(", disables email indicator"));break;
441 case GSM_EnableEmail:
442 fprintf(stdout,_(", enables email indicator"));break;
444 fprintf(stdout,_(", void SMS"));break;
445 case GSM_WAPBookmarkUDH:
446 fprintf(stdout,_(", WAP Bookmark"));break;
447 case GSM_WAPBookmarkUDHLong:
448 fprintf(stdout,_(", WAP Bookmark, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
449 case GSM_WAPSettingsUDH:
450 fprintf(stdout,_(", WAP Settings, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
451 case GSM_RingtoneUDH:
452 fprintf(stdout,_(", ringtone"));break;
454 fprintf(stdout,_(", GSM Operator Logo"));break;
455 case GSM_CallerIDLogo:
456 fprintf(stdout,_(", Caller Logo"));break;
458 fprintf(stdout,_(", Profile SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
459 case GSM_CalendarNoteUDH:
460 fprintf(stdout,_(", Calendar note SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
461 case GSM_CalendarNoteUDH2:
462 fprintf(stdout,_(", Calendar note SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
463 case GSM_PhonebookUDH:
464 fprintf(stdout,_(", Phonebook Entry, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
466 fprintf(stdout,_(", UNKNOWN"));break;
469 fprintf(stdout, ")\n");
471 hexdump(off,SMS->UDH);
475 SMS->Coding = GSM_Coding_Default;
477 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
478 if ((ETSI->TPDCS & 0xf4) == 0xf4) SMS->Coding=GSM_Coding_8bit;
479 if ((ETSI->TPDCS & 0x08) == 0x08) SMS->Coding=GSM_Coding_Unicode;
481 switch (SMS->Coding) {
482 case GSM_Coding_Default:
484 if (w<0) w=(14-off)%14;
486 SMS->Length=ETSI->TPUDL - (off*8 + w) / 7;
488 tmp=GSM_UnpackEightBitsToSeven(w,ETSI->TPUDL-off, SMS->Length, ETSI->MessageText+off, output);
491 fprintf(stdout, " 7 bit SMS, body is (length %i): ",SMS->Length);
494 DecodeDefault (SMS->MessageText, output, SMS->Length);
497 fprintf(stdout, "%s\n",SMS->MessageText);
501 case GSM_Coding_8bit:
502 SMS->Length=ETSI->TPUDL - off;
504 memcpy(SMS->MessageText,ETSI->MessageText+off,SMS->Length);
507 fprintf(stdout, " 8 bit SMS, body is (length %i)\n",SMS->Length);
508 hexdump(SMS->Length,SMS->MessageText);
512 case GSM_Coding_Unicode:
513 SMS->Length=(ETSI->TPUDL - off) / 2;
516 fprintf(stdout, " 7 bit SMS, body is (length %i), Unicode coding: ",SMS->Length);
517 for (i=0; i<SMS->Length;i++) {
518 fprintf(stdout, "[%02x %02x]", ETSI->MessageText[off+i*2] , ETSI->MessageText[off+i*2+1]);
520 fprintf(stdout, "\n");
523 /* here we decode "special" chars */
524 for (i=0; i<SMS->Length;i++) {
525 if (ETSI->MessageText[off+i*2] ==0x00 &&
526 ETSI->MessageText[off+i*2+1]==0x01)
527 ETSI->MessageText[off+i*2+1]='~'; //enables/disables blinking
528 if (ETSI->MessageText[off+i*2] ==0x00 &&
529 ETSI->MessageText[off+i*2+1]==0x00)
530 ETSI->MessageText[off+i*2+1]='`'; //hides rest ot contents
533 DecodeUnicode (SMS->MessageText, ETSI->MessageText+off, SMS->Length);
541 GSM_Error GSM_DecodeETSISMSStatusReportData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
543 /* See GSM 03.40 section 9.2.3.11 (TP-Service-Centre-Time-Stamp) */
545 fprintf(stdout, _(" SMSC response date: "));
547 GSM_DecodeSMSDateTime(&SMS->SMSCTime, ETSI->SMSCDateTime);
549 if (ETSI->TPStatus < 0x03) {
550 strcpy(SMS->MessageText,_("Delivered"));
553 /* more detailed reason only for debug */
554 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
555 switch (ETSI->TPStatus) {
556 case 0x00: fprintf(stdout, _(" SM received by the SME"));break;
557 case 0x01: fprintf(stdout, _(" SM forwarded by the SC to the SME but the SC is unable to confirm delivery"));break;
558 case 0x02: fprintf(stdout, _(" SM replaced by the SC"));break;
564 } else if (ETSI->TPStatus & 0x40) {
566 strcpy(SMS->MessageText,_("Failed"));
569 /* more detailed reason only for debug */
570 if (ETSI->TPStatus & 0x20) {
572 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
573 fprintf(stdout, _(" Temporary error, SC is not making any more transfer attempts\n"));
574 switch (ETSI->TPStatus) {
575 case 0x60: fprintf(stdout, _(" Congestion"));break;
576 case 0x61: fprintf(stdout, _(" SME busy"));break;
577 case 0x62: fprintf(stdout, _(" No response from SME"));break;
578 case 0x63: fprintf(stdout, _(" Service rejected"));break;
579 case 0x64: fprintf(stdout, _(" Quality of service not available"));break;
580 case 0x65: fprintf(stdout, _(" Error in SME"));break;
581 default : fprintf(stdout, _(" Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
586 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
587 fprintf(stdout, _(" Permanent error, SC is not making any more transfer attempts\n"));
588 switch (ETSI->TPStatus) {
589 case 0x40: fprintf(stdout, _(" Remote procedure error"));break;
590 case 0x41: fprintf(stdout, _(" Incompatibile destination"));break;
591 case 0x42: fprintf(stdout, _(" Connection rejected by SME"));break;
592 case 0x43: fprintf(stdout, _(" Not obtainable"));break;
593 case 0x44: fprintf(stdout, _(" Quality of service not available"));break;
594 case 0x45: fprintf(stdout, _(" No internetworking available"));break;
595 case 0x46: fprintf(stdout, _(" SM Validity Period Expired"));break;
596 case 0x47: fprintf(stdout, _(" SM deleted by originating SME"));break;
597 case 0x48: fprintf(stdout, _(" SM Deleted by SC Administration"));break;
598 case 0x49: fprintf(stdout, _(" SM does not exist"));break;
599 default : fprintf(stdout, _(" Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
605 } else if (ETSI->TPStatus & 0x20) {
606 strcpy(SMS->MessageText,_("Pending"));
609 /* more detailed reason only for debug */
610 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
611 fprintf(stdout, _(" Temporary error, SC still trying to transfer SM\n"));
612 switch (ETSI->TPStatus) {
613 case 0x20: fprintf(stdout, _(" Congestion"));break;
614 case 0x21: fprintf(stdout, _(" SME busy"));break;
615 case 0x22: fprintf(stdout, _(" No response from SME"));break;
616 case 0x23: fprintf(stdout, _(" Service rejected"));break;
617 case 0x24: fprintf(stdout, _(" Quality of service not aviable"));break;
618 case 0x25: fprintf(stdout, _(" Error in SME"));break;
619 default : fprintf(stdout, _(" Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
624 strcpy(SMS->MessageText,_("Unknown"));
627 /* more detailed reason only for debug */
628 fprintf(stdout, _(" Reserved/Specific to SC: %x"),ETSI->TPStatus);
634 fprintf(stdout, _("\n"));
640 GSM_Error GSM_EncodeETSISMSSubmitHeader(GSM_SMSMessage *SMS,GSM_ETSISMSMessage *ETSI)
644 /* First of all we should get SMSC number */
645 if (SMS->MessageCenter.No) {
646 error = GSM->GetSMSCenter(&SMS->MessageCenter);
647 if (error != GE_NONE) return error;
648 SMS->MessageCenter.No = 0;
652 fprintf(stdout, _("Packing SMS to \"%s\" via message center \"%s\"\n"), SMS->Destination, SMS->MessageCenter.Number);
655 ETSI->SMSCNumber[0]=GSM_PackSemiOctetNumber(SMS->MessageCenter.Number, ETSI->SMSCNumber+1, false);
657 /* GSM 03.40 section 9.2.3.17 (TP-Reply-Path) */
658 if (SMS->ReplyViaSameSMSC) ETSI->firstbyte |= 128;
660 /* When save to Outbox with SMS Class, "Edit" is not displayed in phone menu
661 and can forward it to another phone with set class (for example, 0=Flash) */
663 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
664 if (SMS->Class>=0 && SMS->Class<5) ETSI->TPDCS |= (240+SMS->Class);
666 /* When is not set for SMS saved for Inbox, phone displays "Message" instead
667 of number and doesn't display "Details" info */
668 ETSI->Number[0] = GSM_PackSemiOctetNumber(SMS->Destination, ETSI->Number+1, true);
673 GSM_Error GSM_DecodeETSISMSHeader(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
676 fprintf(stdout, _(" SMS center number: %s"), GSM_UnpackSemiOctetNumber(ETSI->SMSCNumber,false));
677 if (SMS->folder==0 && (ETSI->firstbyte & 128)!=0) //GST_INBOX
678 fprintf(stdout, _(" (centre set for reply)"));
681 strcpy(SMS->MessageCenter.Number, GSM_UnpackSemiOctetNumber(ETSI->SMSCNumber,false));
683 SMS->ReplyViaSameSMSC=false;
684 if ((ETSI->firstbyte & 128)!=0) SMS->ReplyViaSameSMSC=true;
687 fprintf(stdout, _("\n Remote number (recipient or sender): %s\n"), GSM_UnpackSemiOctetNumber(ETSI->Number,true));
690 strcpy(SMS->Sender, GSM_UnpackSemiOctetNumber(ETSI->Number,true));
695 /* FIXME: we should allow for all validity formats */
696 GSM_Error GSM_EncodeETSISMSSubmitValidity(GSM_SMSMessage *SMS,GSM_ETSISMSMessage *ETSI)
698 /* GSM 03.40 section 9.2.3.3 (TP-Validity-Period-Format) */
699 /* Bits 4 and 3: 10. TP-VP field present and integer represent (relative) */
700 ETSI->firstbyte |= 0x10;
702 /* GSM 03.40 section 9.2.3.12 (TP-Validity Period) */
703 /* FIXME: error-checking for correct Validity - it should not be bigger then
704 63 weeks and smaller then 5minutes. We should also test intervals because
705 the SMS->Validity to TP-VP is not continuos. I think that the simplest
706 solution will be an array of correct values. We should parse it and if we
707 find the closest TP-VP value we should use it. Or is it good to take
708 closest smaller TP-VP as we do now? I think it is :-) */
710 /* 5 minutes intervals up to 12 hours = 720 minutes */
711 if (SMS->Validity <= 720)
712 ETSI->TPVP = (unsigned char) (SMS->Validity/5)-1;
714 /* 30 minutes intervals up to 1 day */
715 else if ((SMS->Validity > 720) && (SMS->Validity <= 1440))
716 ETSI->TPVP = (unsigned char) ((SMS->Validity-720)/30)+143;
718 /* 1 day intervals up to 30 days */
719 else if ((SMS->Validity > 1440) && (SMS->Validity <= 43200))
720 ETSI->TPVP = (unsigned char) (SMS->Validity/1440)+166;
722 /* 1 week intervals up to 63 weeks */
723 else if ((SMS->Validity > 43200) && (SMS->Validity <= 635040))
724 ETSI->TPVP = (unsigned char) (SMS->Validity/10080)+192;
729 /* FIXME: do we need more than SMS_Submit and SMS_Deliver ? */
730 GSM_Error GSM_EncodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI, SMS_MessageType PDU, int *length)
744 /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
745 /* Bits 1 and 0: 01. SMS-Submit */
746 ETSI->firstbyte |= 0x01;
748 /* GSM 03.40 section 9.2.3.5 (TP-Status-Raport-Request) */
749 /* Mask for request for delivery report from SMSC */
750 if (SMS->Type == GST_DR) ETSI->firstbyte |= 32;
752 GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
753 GSM_EncodeETSISMSSubmitValidity(SMS, ETSI);
754 size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
759 /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
760 /* Bits 1 and 0: 00. SMS-Deliver */
761 ETSI->firstbyte |= 0x00;
763 GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
764 GSM_EncodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
765 size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
772 /* size is the length of the data in octets including udh */
778 /* This function decodes parts of SMS coded according to GSM 03.40
779 (given in GSM_ETSISMSMessage) to GSM_SMSMessage */
780 GSM_Error GSM_DecodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
782 SMS_MessageType PDU=SMS_Deliver;
784 /* See GSM 03.40 section 9.2.3.1 */
785 if ((ETSI->firstbyte & 0x03) == 0x01) PDU=SMS_Submit;
786 if ((ETSI->firstbyte & 0x03) == 0x02) PDU=SMS_Status_Report;
788 GSM_DecodeETSISMSHeader(SMS, ETSI);
793 fprintf(stdout, _(" SMS submit "));
796 GSM_DecodeETSISMSSubmitData(SMS,ETSI);
800 fprintf(stdout, _(" SMS deliver "));
801 fprintf(stdout, _(" Date: "));
804 GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
805 GSM_DecodeETSISMSSubmitData(SMS,ETSI);
807 case SMS_Status_Report:
809 fprintf(stdout, _(" SMS status report "));
810 fprintf(stdout, _(" Date: "));
813 GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
814 GSM_DecodeETSISMSStatusReportData(SMS,ETSI);
820 SMS->MessageText[SMS->Length]=0;
825 void GSM_SetDefaultSMSData (GSM_SMSMessage *SMS)
831 /* Default settings for SMS message:
838 SMS->folder = GST_OUTBOX;
841 SMS->Compression = false;
842 SMS->MessageCenter.No = 1;
843 SMS->Validity = 4320; /* 4320 minutes == 72 hours */
844 SMS->ReplyViaSameSMSC = false;
845 SMS->UDHType = GSM_NoUDH;
846 SMS->Coding=GSM_Coding_Default;
847 strcpy(SMS->Destination,"");
849 /* This part is required to save SMS */
851 SMS->Status = GSS_NOTSENTREAD;
855 now=localtime(&nowh);
857 Date.Year = now->tm_year;
858 Date.Month = now->tm_mon+1;
859 Date.Day = now->tm_mday;
860 Date.Hour = now->tm_hour;
861 Date.Minute = now->tm_min;
862 Date.Second = now->tm_sec;
864 /* I have 100 (for 2000) Year now :-) */
865 if (Date.Year>99 && Date.Year<1900) {
866 Date.Year=Date.Year+1900;
869 /* We need to have only two last digits of year */
872 if (Date.Year<2000) Date.Year = Date.Year-1900;
873 else Date.Year = Date.Year-2000;
876 SMS->Time.Year=Date.Year;
877 SMS->Time.Month=Date.Month;
878 SMS->Time.Day=Date.Day;
879 SMS->Time.Hour=Date.Hour;
880 SMS->Time.Minute=Date.Minute;
881 SMS->Time.Second=Date.Second;
886 /* This function encodes the UserDataHeader as described in:
887 - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
888 - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
890 GSM_Error EncodeUDHHeader(char *text, GSM_UDH UDHType)
894 if (UDHType!=GSM_NoUDH) {
896 if (UDHHeaders[i].UDHType==GSM_NoUDH) {
898 fprintf(stderr,_("Not supported User Data Header type\n"));
902 if (UDHHeaders[i].UDHType==UDHType) {
903 text[0] = UDHHeaders[i].Length; // UDH Length
904 memcpy(text+1, UDHHeaders[i].Text, UDHHeaders[i].Length);
913 int GSM_MakeSinglePartSMS2(GSM_SMSMessage *SMS,
914 unsigned char *MessageBuffer,int cur, GSM_UDH UDHType, GSM_Coding_Type Coding){
917 int current,smsudhlength;
919 GSM_SetDefaultSMSData(SMS);
921 EncodeUDHHeader(SMS->UDH, UDHType);
922 SMS->UDHType=UDHType;
925 case GSM_EnableVoice:
926 case GSM_DisableVoice:
927 case GSM_EnableEmail:
928 case GSM_DisableEmail:
935 case GSM_ConcatenatedMessages:
939 case GSM_PhonebookUDH:
940 case GSM_CalendarNoteUDH: //class=1?
945 case GSM_CallerIDLogo:
946 case GSM_RingtoneUDH:
947 case GSM_WAPBookmarkUDH:
948 case GSM_WAPBookmarkUDHLong:
949 case GSM_WAPSettingsUDH:
952 SMS->Coding=GSM_Coding_8bit;
955 fprintf(stderr,_("Error in makesinglepartsms !\n\n\n"));
961 if (UDHType!=GSM_NoUDH)
962 smsudhlength=SMS->UDH[0]+1;
965 switch (SMS->Coding) {
966 case GSM_Coding_8bit:
967 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength); //max=140
969 case GSM_Coding_Default:
970 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)*8/7; //max=160
972 case GSM_Coding_Unicode:
973 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)/2; //max=70
976 if (current>j) current=j;
978 memcpy(SMS->MessageText,MessageBuffer,current);
979 SMS->MessageText[current]=0;
985 void GSM_MakeMultiPartSMS2(GSM_MultiSMSMessage *SMS,
986 unsigned char *MessageBuffer,int MessageLength, GSM_UDH UDHType, GSM_Coding_Type Coding){
988 int i=0,j,pos=0,current=0;
991 if (pos==MessageLength) break;
993 current=MessageLength-pos;
995 pos=pos+GSM_MakeSinglePartSMS2(&SMS->SMS[i],MessageBuffer+pos,current,UDHType,Coding);
1001 case GSM_ProfileUDH:
1002 case GSM_WAPBookmarkUDHLong:
1003 case GSM_WAPSettingsUDH:
1004 case GSM_CalendarNoteUDH:
1005 case GSM_CalendarNoteUDH2:
1006 case GSM_PhonebookUDH:
1007 SMS->SMS[j].UDH[10]=i;
1008 SMS->SMS[j].UDH[11]=j+1;
1010 case GSM_ConcatenatedMessages:
1011 SMS->SMS[j].UDH[4]=i;
1012 SMS->SMS[j].UDH[5]=j+1;