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.
20 #include "gsm-coding.h"
22 /* User data headers */
23 static GSM_UDHHeader UDHHeaders[] = {
24 { GSM_ConcatenatedMessages, 0x05, "\x00\x03\x01\x00\x00" },
25 /* See GSM 03.40 section 9.2.3.24.1 */
26 { GSM_DisableVoice, 0x04, "\x01\x02\x00\x00" },
27 { GSM_DisableFax, 0x04, "\x01\x02\x01\x00" },
28 { GSM_DisableEmail, 0x04, "\x01\x02\x02\x00" },
29 { GSM_EnableVoice, 0x04, "\x01\x02\x00\x01" },
30 { GSM_EnableFax, 0x04, "\x01\x02\x01\x01" },
31 { GSM_EnableEmail, 0x04, "\x01\x02\x02\x01" },
32 /* See GSM 03.40 section 9.2.3.24.2 for voice, fax and email messages */
33 { GSM_VoidSMS, 0x08, "\x01\x02\x02\x01\x01\x02\x02\x00" },
34 /* When send such SMS to some phones, they don't display anything,
35 only beep and enable vibra/light */
36 { GSM_BugSMS, 0x09, "\x01\x02\x02\x01\x01\x02\x02\x00\xc0" },
38 /* Short Smart Messaging UDH */
40 /* 1 byte 0x05 : IEI application port addressing scheme, 16 bit address */
41 /* 1 byte 0x04 : IEI length */
42 /* 2 bytes : destination address: high & low byte */
43 /* 2 bytes 0x00 0x00: originator address: high & low byte */
44 { GSM_RingtoneUDH, 0x06, "\x05\x04\x15\x81\x00\x00" },
45 { GSM_OpLogo, 0x06, "\x05\x04\x15\x82\x00\x00" },
46 { GSM_CallerIDLogo, 0x06, "\x05\x04\x15\x83\x00\x00" },
47 { GSM_WAPBookmarkUDH, 0x06, "\x05\x04\xc3\x4f\x00\x00" },
49 /* Long Smart Messaging UDH */
51 /* 1 byte 0x05 : IEI application port addressing scheme, 16 bit address */
52 /* 1 byte 0x04 : IEI length */
53 /* 2 bytes 0x00 0x00: destination address: high & low byte */
54 /* 2 bytes 0x00 0x00: originator address: high & low byte */
55 /* 1 byte 0x00 : null byte for end first part ? */
56 /* 1 byte 0x03 : length for rest ? */
58 /* 1 byte : number of all SMS */
59 /* 1 byte : number of current SMS */
60 { GSM_CalendarNoteUDH, 0x0b, "\x05\x04\x00\xe4\x00\x00\x00\x03\xc7\x00\x00" }, //from 3310 ?
61 { 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
62 { GSM_ProfileUDH, 0x0b, "\x05\x04\x15\x8a\x00\x00\x00\x03\xce\x00\x00" },
63 { GSM_WAPBookmarkUDH, 0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x01\x00\x00" },//note last 0x01 can change
64 { GSM_WAPSettingsUDH, 0x0b, "\x05\x04\xc3\x4f\x00\x00\x00\x03\x7f\x00\x00" },
65 { GSM_PhonebookUDH, 0x0b, "\x05\x04\x23\xf4\x00\x00\x00\x03\x01\x00\x00" },
67 { GSM_NoUDH, 0x00, "" }
70 #define ByteMask ((1 << Bits) - 1)
73 static int GSM_PackSevenBitsToEight(int offset, unsigned char *input, unsigned char *output)
75 unsigned char *OUT = output; /* Current pointer to the output buffer */
76 unsigned char *IN = input; /* Current pointer to the input buffer */
77 int Bits; /* Number of bits directly copied to
79 Bits = (7 + offset) % 8;
81 /* If we don't begin with 0th bit, we will write only a part of the
88 while ((IN - input) < strlen(input)) {
89 unsigned char Byte = EncodeWithDefaultAlphabet(*IN);
91 *OUT = Byte >> (7 - Bits);
92 /* If we don't write at 0th bit of the octet, we should write
93 a second part of the previous octet */
95 *(OUT-1) |= (Byte & ((1 << (7-Bits)) - 1)) << (Bits+1);
99 if (Bits == -1) Bits = 7;
104 return (OUT - output);
107 int GSM_UnpackEightBitsToSeven(int offset, int in_length, int out_length,
108 unsigned char *input, unsigned char *output)
110 unsigned char *OUT = output; /* Current pointer to the output buffer */
111 unsigned char *IN = input; /* Current pointer to the input buffer */
112 unsigned char Rest = 0x00;
115 Bits = offset ? offset : 7;
117 while ((IN - input) < in_length) {
119 *OUT = ((*IN & ByteMask) << (7 - Bits)) | Rest;
122 /* If we don't start from 0th bit, we shouldn't go to the
123 next char. Under *OUT we have now 0 and under Rest -
124 _first_ part of the char. */
125 if ((IN != input) || (Bits == 7)) OUT++;
128 if ((OUT - output) >= out_length) break;
130 /* After reading 7 octets we have read 7 full characters but
131 we have 7 bits as well. This is the next character */
145 /* This function implements packing of numbers (SMS Center number and
146 destination number) for SMS sending function. */
147 /* See GSM 03.40 9.1.1:
148 1 byte - length of number given in semioctets or bytes (when given in bytes,
149 includes one byte for byte with number format.
150 Returned by function (set semioctet to true, if want result
152 1 byte - format of number. See GSM_NumberType; in gsm-common.h. Returned
154 n bytes - 2n or 2n-1 semioctets with number. For some types of numbers
155 in the most significant bits of the last byte with 0x0f
156 (1111 binary) are filled if the number is represented
157 with odd number of digits. Returned in u8 *Output. */
158 /* 1 semioctet = 4 bits = half of byte */
159 int GSM_PackSemiOctetNumber(u8 *Number, u8 *Output, bool semioctet) {
161 u8 *IN=Number; /* Pointer to the input number */
162 u8 *OUT=Output; /* Pointer to the output */
164 unsigned char format=GNT_UNKNOWN; // format of number used by us
166 /* Checking for format number */
168 if (length==0 && *IN=='+')
169 format=GNT_INTERNATIONAL; // first byte is '+'. It can be international
171 if (*IN>'9' || *IN<'0') {
172 format=GNT_ALPHANUMERIC; //char is not number. It must be alphanumeric
178 /* Now we return to first byte */
179 for (j=0;j<length;j++) IN--;
181 /* The first byte in the Semi-octet representation of the address field is
182 the Type-of-Address. This field is described in the official GSM
183 specification 03.40 version 5.3.0, section 9.1.2.5, page 33.*/
186 /* The next field is the number. See GSM 03.40 section 9.1.2 */
188 case GNT_ALPHANUMERIC:
189 length=GSM_PackSevenBitsToEight(0, IN, OUT)*2;
192 case GNT_INTERNATIONAL:
194 EncodeBCD (OUT, IN+1, length, true);
198 EncodeBCD (OUT, IN, length, true);
205 /* Convert number of semioctets to number of chars */
206 if (length % 2) length++;
207 return length / 2 + 1;
211 char *GSM_UnpackSemiOctetNumber(u8 *Number, bool semioctet) {
213 static char Buffer[20]="";
214 int length=Number[0];
217 /* Convert number of semioctets to number of chars */
218 if (length % 2) length++;
219 length=length / 2 + 1;
222 length--; //without leading byte with format of number
225 case GNT_ALPHANUMERIC:
226 GSM_UnpackEightBitsToSeven(0, length, length, Number+2, Buffer);
230 case GNT_INTERNATIONAL:
232 DecodeBCD (Buffer+1, Number+2, length);
236 DecodeBCD (Buffer, Number+2, length);
243 /* See GSM 03.40 section 9.2.3.11 */
244 GSM_Error GSM_EncodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
247 fprintf(stdout,_("Date & time in saved SMS: %02i/%02i/%04i %02i:%02i:%02i\n"),
248 DT->Day,DT->Month,DT->Year,DT->Hour,DT->Minute,DT->Second);
251 req[0]=EncodeWithBCDAlphabet(DT->Year);
252 req[1]=EncodeWithBCDAlphabet(DT->Month);
253 req[2]=EncodeWithBCDAlphabet(DT->Day);
254 req[3]=EncodeWithBCDAlphabet(DT->Hour);
255 req[4]=EncodeWithBCDAlphabet(DT->Minute);
256 req[5]=EncodeWithBCDAlphabet(DT->Second);
259 req[6]=0; /* TimeZone = +-0 */
264 /* See GSM 03.40 section 9.2.3.11 */
265 static GSM_Error GSM_DecodeSMSDateTime(GSM_DateTime *DT, unsigned char *req)
267 DT->Year = DecodeWithBCDAlphabet(req[0]);
268 DT->Month = DecodeWithBCDAlphabet(req[1]);
269 DT->Day = DecodeWithBCDAlphabet(req[2]);
270 DT->Hour = DecodeWithBCDAlphabet(req[3]);
271 DT->Minute = DecodeWithBCDAlphabet(req[4]);
272 DT->Second = DecodeWithBCDAlphabet(req[5]);
274 DT->Timezone=(10*(req[6]&0x07)+(req[6]>>4))/4;
275 if (req[6]&0x08) DT->Timezone = -DT->Timezone;
278 fprintf(stdout, _("%d%d-"), req[0]&0xf, req[0]>>4);
279 fprintf(stdout, _("%d%d-"), req[1]&0xf, req[1]>>4);
280 fprintf(stdout, _("%d%d "), req[2]&0xf, req[2]>>4);
281 fprintf(stdout, _("%d%d:"), req[3]&0xf, req[3]>>4);
282 fprintf(stdout, _("%d%d:"), req[4]&0xf, req[4]>>4);
283 fprintf(stdout, _("%d%d "), req[5]&0xf, req[5]>>4);
286 if (req[6] & 0x08) fprintf(stdout, "-");
287 else fprintf(stdout, "+");
289 fprintf(stdout, _("%02d00"), (10*(req[6]&0x07)+(req[6]>>4))/4);
292 fprintf(stdout, "\n");
298 static int GSM_EncodeETSISMSSubmitData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
300 int off,size=0,size2=0,w,i;
302 /* off - length of the user data header */
306 /* GSM 03.40 section 9.2.3.23 (TP-User-Data-Header-Indicator) */
307 ETSI->firstbyte |= 0x40;
309 /* off - length of the user data header */
310 off = 1 + SMS->UDH[0];
312 /* we copy the udh */
313 memcpy(ETSI->MessageText, SMS->UDH, off);
315 // if (SMS->UDHType==GSM_HangSMS) ETSI->TPDCS=0x08; //Such SMS hangs phone (5110, 5.07),
316 //when saved to Outbox atry try to read it
317 /*from phone's menu*/
320 switch (SMS->Coding) {
321 /* When save SMS to SIM and it's 8 bit SMS,
322 "Edit" is not displayed in phone menu and
323 "Message cannot be displayed here" instead of message text */
324 case GSM_Coding_8bit:
326 /* the mask for the 8-bit data */
327 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
330 memcpy(ETSI->MessageText + off, SMS->MessageText, SMS->Length);
332 size2 = size = SMS->Length+off;
336 case GSM_Coding_Default:
339 if (w<0) w=(14-off)%14;
341 size = GSM_PackSevenBitsToEight(w, SMS->MessageText, ETSI->MessageText + off);
343 size2 = (off*8 + w) / 7 + strlen(SMS->MessageText);
347 case GSM_Coding_Unicode:
349 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
353 fprintf(stdout,_("SMS Length is %ld\n"),(long)strlen(SMS->MessageText));
356 EncodeUnicode (ETSI->MessageText+off,SMS->MessageText,strlen(SMS->MessageText));
357 /* here we code "special" chars */
358 for (i=0;i<strlen(SMS->MessageText);i++) {
359 if (SMS->MessageText[i]=='~') ETSI->MessageText[off+1+i*2]=1; //enables/disables blinking
360 if (SMS->MessageText[i]=='`') ETSI->MessageText[off+1+i*2]=0; //hides rest ot contents
363 size=size2=strlen(SMS->MessageText)*2+off;
368 /* FIXME: support for compression */
369 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
370 /* See also GSM 03.42 */
371 /* if (SMS->Compression) ETSI->TPDCS |= 0x20; */
374 - integer representation of the number od octets within the user data when UD is coded using 8bit data
375 - the sum of the number of septets in UDH including any padding and number of septets in UD in other case */
376 /* GSM 03.40 section 9.2.3.16 (TP-User-Data-Length) */
382 GSM_Error GSM_DecodeETSISMSSubmitData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
385 unsigned char output[161];
388 /* off - length of the user data header */
391 SMS->UDHType = GSM_NoUDH;
393 if (ETSI->firstbyte & 64) { /* UDH header available */
395 off = (ETSI->MessageText[0] + 1); /* Length of UDH header */
397 /* Copy UDH header into SMS->UDH */
398 for (i = 0; i < off; i++) SMS->UDH[i] = ETSI->MessageText[i];
401 fprintf(stdout, " UDH header available (length %i",off);
404 SMS->UDHType = GSM_UnknownUDH;
409 if (UDHHeaders[i].UDHType==GSM_NoUDH) break;
410 tmp=UDHHeaders[i].Length;
411 if (tmp==SMS->UDH[0]) { //if length is the same
413 if (tmp==0x05) tmp=tmp-2;/*two last bytes can be different for such UDH*/
414 if (tmp==0x0b) tmp=tmp-3;/*three last bytes can be different for such UDH*/
417 for (w=0;w<tmp;w++) {
418 if (UDHHeaders[i].Text[w]!=SMS->UDH[w+1]) {
424 SMS->UDHType=UDHHeaders[i].UDHType;
431 switch (SMS->UDHType) {
432 case GSM_ConcatenatedMessages:
433 fprintf(stdout,_(", concatenated (linked) message %d/%d"),SMS->UDH[5],SMS->UDH[4]);break;
434 case GSM_DisableVoice:
435 fprintf(stdout,_(", disables voice indicator"));break;
436 case GSM_EnableVoice:
437 fprintf(stdout,_(", enables voice indicator"));break;
439 fprintf(stdout,_(", disables fax indicator"));break;
441 fprintf(stdout,_(", enables fax indicator"));break;
442 case GSM_DisableEmail:
443 fprintf(stdout,_(", disables email indicator"));break;
444 case GSM_EnableEmail:
445 fprintf(stdout,_(", enables email indicator"));break;
447 fprintf(stdout,_(", void SMS"));break;
448 case GSM_WAPBookmarkUDH:
449 fprintf(stdout,_(", WAP Bookmark"));break;
450 case GSM_WAPBookmarkUDHLong:
451 fprintf(stdout,_(", WAP Bookmark, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
452 case GSM_WAPSettingsUDH:
453 fprintf(stdout,_(", WAP Settings, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
454 case GSM_RingtoneUDH:
455 fprintf(stdout,_(", ringtone"));break;
457 fprintf(stdout,_(", GSM Operator Logo"));break;
458 case GSM_CallerIDLogo:
459 fprintf(stdout,_(", Caller Logo"));break;
461 fprintf(stdout,_(", Profile SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
462 case GSM_CalendarNoteUDH:
463 fprintf(stdout,_(", Calendar note SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
464 case GSM_CalendarNoteUDH2:
465 fprintf(stdout,_(", Calendar note SMS, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
466 case GSM_PhonebookUDH:
467 fprintf(stdout,_(", Phonebook Entry, part %i/%i"),SMS->UDH[11],SMS->UDH[10]);break;
469 fprintf(stdout,_(", UNKNOWN"));break;
472 fprintf(stdout, ")\n");
474 hexdump(off,SMS->UDH);
478 SMS->Coding = GSM_Coding_Default;
480 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
481 if ((ETSI->TPDCS & 0xf4) == 0xf4) SMS->Coding=GSM_Coding_8bit;
482 if ((ETSI->TPDCS & 0x08) == 0x08) SMS->Coding=GSM_Coding_Unicode;
484 switch (SMS->Coding) {
485 case GSM_Coding_Default:
487 if (w<0) w=(14-off)%14;
489 SMS->Length=ETSI->TPUDL - (off*8 + w) / 7;
491 tmp=GSM_UnpackEightBitsToSeven(w,ETSI->TPUDL-off, SMS->Length, ETSI->MessageText+off, output);
494 fprintf(stdout, " 7 bit SMS, body is (length %i): ",SMS->Length);
497 DecodeDefault (SMS->MessageText, output, SMS->Length);
500 fprintf(stdout, "%s\n",SMS->MessageText);
504 case GSM_Coding_8bit:
505 SMS->Length=ETSI->TPUDL - off;
507 memcpy(SMS->MessageText,ETSI->MessageText+off,SMS->Length);
510 fprintf(stdout, " 8 bit SMS, body is (length %i)\n",SMS->Length);
511 hexdump(SMS->Length,SMS->MessageText);
515 case GSM_Coding_Unicode:
516 SMS->Length=(ETSI->TPUDL - off) / 2;
519 fprintf(stdout, " 7 bit SMS, body is (length %i), Unicode coding: ",SMS->Length);
520 for (i=0; i<SMS->Length;i++) {
521 fprintf(stdout, "[%02x %02x]", ETSI->MessageText[off+i*2] , ETSI->MessageText[off+i*2+1]);
523 fprintf(stdout, "\n");
526 /* here we decode "special" chars */
527 for (i=0; i<SMS->Length;i++) {
528 if (ETSI->MessageText[off+i*2] ==0x00 &&
529 ETSI->MessageText[off+i*2+1]==0x01)
530 ETSI->MessageText[off+i*2+1]='~'; //enables/disables blinking
531 if (ETSI->MessageText[off+i*2] ==0x00 &&
532 ETSI->MessageText[off+i*2+1]==0x00)
533 ETSI->MessageText[off+i*2+1]='`'; //hides rest ot contents
536 DecodeUnicode (SMS->MessageText, ETSI->MessageText+off, SMS->Length);
544 GSM_Error GSM_DecodeETSISMSStatusReportData(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
546 /* See GSM 03.40 section 9.2.3.11 (TP-Service-Centre-Time-Stamp) */
548 fprintf(stdout, _(" SMSC response date: "));
550 GSM_DecodeSMSDateTime(&SMS->SMSCTime, ETSI->SMSCDateTime);
552 if (ETSI->TPStatus < 0x03) {
553 strcpy(SMS->MessageText,_("Delivered"));
556 /* more detailed reason only for debug */
557 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
558 switch (ETSI->TPStatus) {
559 case 0x00: fprintf(stdout, _(" SM received by the SME"));break;
560 case 0x01: fprintf(stdout, _(" SM forwarded by the SC to the SME but the SC is unable to confirm delivery"));break;
561 case 0x02: fprintf(stdout, _(" SM replaced by the SC"));break;
567 } else if (ETSI->TPStatus & 0x40) {
569 strcpy(SMS->MessageText,_("Failed"));
572 /* more detailed reason only for debug */
573 if (ETSI->TPStatus & 0x20) {
575 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
576 fprintf(stdout, _(" Temporary error, SC is not making any more transfer attempts\n"));
577 switch (ETSI->TPStatus) {
578 case 0x60: fprintf(stdout, _(" Congestion"));break;
579 case 0x61: fprintf(stdout, _(" SME busy"));break;
580 case 0x62: fprintf(stdout, _(" No response from SME"));break;
581 case 0x63: fprintf(stdout, _(" Service rejected"));break;
582 case 0x64: fprintf(stdout, _(" Quality of service not available"));break;
583 case 0x65: fprintf(stdout, _(" Error in SME"));break;
584 default : fprintf(stdout, _(" Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
589 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
590 fprintf(stdout, _(" Permanent error, SC is not making any more transfer attempts\n"));
591 switch (ETSI->TPStatus) {
592 case 0x40: fprintf(stdout, _(" Remote procedure error"));break;
593 case 0x41: fprintf(stdout, _(" Incompatibile destination"));break;
594 case 0x42: fprintf(stdout, _(" Connection rejected by SME"));break;
595 case 0x43: fprintf(stdout, _(" Not obtainable"));break;
596 case 0x44: fprintf(stdout, _(" Quality of service not available"));break;
597 case 0x45: fprintf(stdout, _(" No internetworking available"));break;
598 case 0x46: fprintf(stdout, _(" SM Validity Period Expired"));break;
599 case 0x47: fprintf(stdout, _(" SM deleted by originating SME"));break;
600 case 0x48: fprintf(stdout, _(" SM Deleted by SC Administration"));break;
601 case 0x49: fprintf(stdout, _(" SM does not exist"));break;
602 default : fprintf(stdout, _(" Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
608 } else if (ETSI->TPStatus & 0x20) {
609 strcpy(SMS->MessageText,_("Pending"));
612 /* more detailed reason only for debug */
613 /* See GSM 03.40 section 9.2.3.15 (TP-Status) */
614 fprintf(stdout, _(" Temporary error, SC still trying to transfer SM\n"));
615 switch (ETSI->TPStatus) {
616 case 0x20: fprintf(stdout, _(" Congestion"));break;
617 case 0x21: fprintf(stdout, _(" SME busy"));break;
618 case 0x22: fprintf(stdout, _(" No response from SME"));break;
619 case 0x23: fprintf(stdout, _(" Service rejected"));break;
620 case 0x24: fprintf(stdout, _(" Quality of service not aviable"));break;
621 case 0x25: fprintf(stdout, _(" Error in SME"));break;
622 default : fprintf(stdout, _(" Reserved/Specific to SC: %x"),ETSI->TPStatus);break;
627 strcpy(SMS->MessageText,_("Unknown"));
630 /* more detailed reason only for debug */
631 fprintf(stdout, _(" Reserved/Specific to SC: %x"),ETSI->TPStatus);
637 fprintf(stdout, _("\n"));
643 GSM_Error GSM_EncodeETSISMSSubmitHeader(GSM_SMSMessage *SMS,GSM_ETSISMSMessage *ETSI)
647 /* First of all we should get SMSC number */
648 if (SMS->MessageCenter.No) {
649 error = GSM->GetSMSCenter(&SMS->MessageCenter);
650 if (error != GE_NONE) return error;
651 SMS->MessageCenter.No = 0;
655 fprintf(stdout, _("Packing SMS to \"%s\" via message center \"%s\"\n"), SMS->Destination, SMS->MessageCenter.Number);
658 ETSI->SMSCNumber[0]=GSM_PackSemiOctetNumber(SMS->MessageCenter.Number, ETSI->SMSCNumber+1, false);
660 /* GSM 03.40 section 9.2.3.17 (TP-Reply-Path) */
661 if (SMS->ReplyViaSameSMSC) ETSI->firstbyte |= 128;
663 /* When save to Outbox with SMS Class, "Edit" is not displayed in phone menu
664 and can forward it to another phone with set class (for example, 0=Flash) */
666 /* GSM 03.40 section 9.2.3.10 (TP-Data-Coding-Scheme) and GSM 03.38 section 4 */
667 if (SMS->Class>=0 && SMS->Class<5) ETSI->TPDCS |= (240+SMS->Class);
669 /* When is not set for SMS saved for Inbox, phone displays "Message" instead
670 of number and doesn't display "Details" info */
671 ETSI->Number[0] = GSM_PackSemiOctetNumber(SMS->Destination, ETSI->Number+1, true);
676 GSM_Error GSM_DecodeETSISMSHeader(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
679 fprintf(stdout, _(" SMS center number: %s"), GSM_UnpackSemiOctetNumber(ETSI->SMSCNumber,false));
680 if (SMS->folder==0 && (ETSI->firstbyte & 128)!=0) //GST_INBOX
681 fprintf(stdout, _(" (centre set for reply)"));
684 strcpy(SMS->MessageCenter.Number, GSM_UnpackSemiOctetNumber(ETSI->SMSCNumber,false));
686 SMS->ReplyViaSameSMSC=false;
687 if ((ETSI->firstbyte & 128)!=0) SMS->ReplyViaSameSMSC=true;
690 fprintf(stdout, _("\n Remote number (recipient or sender): %s\n"), GSM_UnpackSemiOctetNumber(ETSI->Number,true));
693 strcpy(SMS->Sender, GSM_UnpackSemiOctetNumber(ETSI->Number,true));
698 /* FIXME: we should allow for all validity formats */
699 GSM_Error GSM_EncodeETSISMSSubmitValidity(GSM_SMSMessage *SMS,GSM_ETSISMSMessage *ETSI)
701 /* GSM 03.40 section 9.2.3.3 (TP-Validity-Period-Format) */
702 /* Bits 4 and 3: 10. TP-VP field present and integer represent (relative) */
703 ETSI->firstbyte |= 0x10;
705 /* GSM 03.40 section 9.2.3.12 (TP-Validity Period) */
706 /* FIXME: error-checking for correct Validity - it should not be bigger then
707 63 weeks and smaller then 5minutes. We should also test intervals because
708 the SMS->Validity to TP-VP is not continuos. I think that the simplest
709 solution will be an array of correct values. We should parse it and if we
710 find the closest TP-VP value we should use it. Or is it good to take
711 closest smaller TP-VP as we do now? I think it is :-) */
713 /* 5 minutes intervals up to 12 hours = 720 minutes */
714 if (SMS->Validity <= 720)
715 ETSI->TPVP = (unsigned char) (SMS->Validity/5)-1;
717 /* 30 minutes intervals up to 1 day */
718 else if ((SMS->Validity > 720) && (SMS->Validity <= 1440))
719 ETSI->TPVP = (unsigned char) ((SMS->Validity-720)/30)+143;
721 /* 1 day intervals up to 30 days */
722 else if ((SMS->Validity > 1440) && (SMS->Validity <= 43200))
723 ETSI->TPVP = (unsigned char) (SMS->Validity/1440)+166;
725 /* 1 week intervals up to 63 weeks */
726 else if ((SMS->Validity > 43200) && (SMS->Validity <= 635040))
727 ETSI->TPVP = (unsigned char) (SMS->Validity/10080)+192;
732 /* FIXME: do we need more than SMS_Submit and SMS_Deliver ? */
733 GSM_Error GSM_EncodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI, SMS_MessageType PDU, int *length)
748 /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
749 /* Bits 1 and 0: 01. SMS-Submit */
750 ETSI->firstbyte |= 0x01;
752 /* GSM 03.40 section 9.2.3.5 (TP-Status-Raport-Request) */
753 /* Mask for request for delivery report from SMSC */
754 if (SMS->Type == GST_DR) ETSI->firstbyte |= 32;
756 error=GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
757 if (error!=GE_NONE) return error;
758 error=GSM_EncodeETSISMSSubmitValidity(SMS, ETSI);
759 if (error!=GE_NONE) return error;
760 size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
765 /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
766 /* Bits 1 and 0: 00. SMS-Deliver */
767 ETSI->firstbyte |= 0x00;
769 error=GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
770 if (error!=GE_NONE) return error;
771 GSM_EncodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
772 size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
779 /* size is the length of the data in octets including udh */
785 /* This function decodes parts of SMS coded according to GSM 03.40
786 (given in GSM_ETSISMSMessage) to GSM_SMSMessage */
787 GSM_Error GSM_DecodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
789 SMS_MessageType PDU=SMS_Deliver;
791 /* See GSM 03.40 section 9.2.3.1 */
792 if ((ETSI->firstbyte & 0x03) == 0x01) PDU=SMS_Submit;
793 if ((ETSI->firstbyte & 0x03) == 0x02) PDU=SMS_Status_Report;
795 GSM_DecodeETSISMSHeader(SMS, ETSI);
800 fprintf(stdout, _(" SMS submit "));
803 GSM_DecodeETSISMSSubmitData(SMS,ETSI);
807 fprintf(stdout, _(" SMS deliver "));
808 fprintf(stdout, _(" Date: "));
811 GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
812 GSM_DecodeETSISMSSubmitData(SMS,ETSI);
814 case SMS_Status_Report:
816 fprintf(stdout, _(" SMS status report "));
817 fprintf(stdout, _(" Date: "));
820 GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
821 GSM_DecodeETSISMSStatusReportData(SMS,ETSI);
827 SMS->MessageText[SMS->Length]=0;
833 static void GSM_SetDefaultSMSData (GSM_SMSMessage *SMS)
839 /* Default settings for SMS message:
846 SMS->folder = GST_OUTBOX;
849 SMS->Compression = false;
850 SMS->MessageCenter.No = 1;
851 SMS->Validity = 4320; /* 4320 minutes == 72 hours */
852 SMS->ReplyViaSameSMSC = false;
853 SMS->UDHType = GSM_NoUDH;
854 SMS->Coding=GSM_Coding_Default;
855 strcpy(SMS->Destination,"");
857 /* This part is required to save SMS */
859 SMS->Status = GSS_NOTSENTREAD;
863 now=localtime(&nowh);
865 Date.Year = now->tm_year;
866 Date.Month = now->tm_mon+1;
867 Date.Day = now->tm_mday;
868 Date.Hour = now->tm_hour;
869 Date.Minute = now->tm_min;
870 Date.Second = now->tm_sec;
872 /* I have 100 (for 2000) Year now :-) */
873 if (Date.Year>99 && Date.Year<1900) {
874 Date.Year=Date.Year+1900;
877 /* We need to have only two last digits of year */
880 if (Date.Year<2000) Date.Year = Date.Year-1900;
881 else Date.Year = Date.Year-2000;
884 SMS->Time.Year=Date.Year;
885 SMS->Time.Month=Date.Month;
886 SMS->Time.Day=Date.Day;
887 SMS->Time.Hour=Date.Hour;
888 SMS->Time.Minute=Date.Minute;
889 SMS->Time.Second=Date.Second;
894 /* This function encodes the UserDataHeader as described in:
895 - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
896 - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
901 GSM_Error EncodeUDHHeader(char *text, GSM_UDH UDHType)
905 if (UDHType!=GSM_NoUDH) {
907 if (UDHHeaders[i].UDHType==GSM_NoUDH) {
909 fprintf(stderr,_("Not supported User Data Header type\n"));
913 if (UDHHeaders[i].UDHType==UDHType) {
914 text[0] = UDHHeaders[i].Length; // UDH Length
915 memcpy(text+1, UDHHeaders[i].Text, UDHHeaders[i].Length);
924 static int GSM_MakeSinglePartSMS2(GSM_SMSMessage *SMS,
925 unsigned char *MessageBuffer,int cur, GSM_UDH UDHType, GSM_Coding_Type Coding){
928 int current,smsudhlength;
930 GSM_SetDefaultSMSData(SMS);
932 EncodeUDHHeader(SMS->UDH, UDHType);
933 SMS->UDHType=UDHType;
936 case GSM_EnableVoice:
937 case GSM_DisableVoice:
938 case GSM_EnableEmail:
939 case GSM_DisableEmail:
946 case GSM_ConcatenatedMessages:
950 case GSM_PhonebookUDH:
951 case GSM_CalendarNoteUDH: //class=1?
956 case GSM_CallerIDLogo:
957 case GSM_RingtoneUDH:
958 case GSM_WAPBookmarkUDH:
959 case GSM_WAPBookmarkUDHLong:
960 case GSM_WAPSettingsUDH:
963 SMS->Coding=GSM_Coding_8bit;
966 fprintf(stderr,_("Error in makesinglepartsms !\n\n\n"));
972 if (UDHType!=GSM_NoUDH)
973 smsudhlength=SMS->UDH[0]+1;
976 switch (SMS->Coding) {
977 case GSM_Coding_8bit:
978 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength); //max=140
980 case GSM_Coding_Default:
981 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)*8/7; //max=160
983 case GSM_Coding_Unicode:
984 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)/2; //max=70
987 if (current>j) current=j;
989 memcpy(SMS->MessageText,MessageBuffer,current);
990 SMS->MessageText[current]=0;
996 void GSM_MakeMultiPartSMS2(GSM_MultiSMSMessage *SMS,
997 unsigned char *MessageBuffer,int MessageLength, GSM_UDH UDHType, GSM_Coding_Type Coding){
999 int i=0,j,pos=0,current=0;
1002 if (pos==MessageLength) break;
1004 current=MessageLength-pos;
1006 pos=pos+GSM_MakeSinglePartSMS2(&SMS->SMS[i],MessageBuffer+pos,current,UDHType,Coding);
1012 case GSM_ProfileUDH:
1013 case GSM_WAPBookmarkUDHLong:
1014 case GSM_WAPSettingsUDH:
1015 case GSM_CalendarNoteUDH:
1016 case GSM_CalendarNoteUDH2:
1017 case GSM_PhonebookUDH:
1018 SMS->SMS[j].UDH[10]=i;
1019 SMS->SMS[j].UDH[11]=j+1;
1021 case GSM_ConcatenatedMessages:
1022 SMS->SMS[j].UDH[4]=i;
1023 SMS->SMS[j].UDH[5]=j+1;