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, tmp);
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)
747 /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
748 /* Bits 1 and 0: 01. SMS-Submit */
749 ETSI->firstbyte |= 0x01;
751 /* GSM 03.40 section 9.2.3.5 (TP-Status-Raport-Request) */
752 /* Mask for request for delivery report from SMSC */
753 if (SMS->Type == GST_DR) ETSI->firstbyte |= 32;
755 GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
756 GSM_EncodeETSISMSSubmitValidity(SMS, ETSI);
757 size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
762 /* GSM 03.40 section 9.2.3.1 (TP-Message-Type-Indicator) */
763 /* Bits 1 and 0: 00. SMS-Deliver */
764 ETSI->firstbyte |= 0x00;
766 GSM_EncodeETSISMSSubmitHeader(SMS, ETSI);
767 GSM_EncodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
768 size=GSM_EncodeETSISMSSubmitData(SMS, ETSI);
775 /* size is the length of the data in octets including udh */
781 /* This function decodes parts of SMS coded according to GSM 03.40
782 (given in GSM_ETSISMSMessage) to GSM_SMSMessage */
783 GSM_Error GSM_DecodeETSISMS(GSM_SMSMessage *SMS, GSM_ETSISMSMessage *ETSI)
785 SMS_MessageType PDU=SMS_Deliver;
787 /* See GSM 03.40 section 9.2.3.1 */
788 if ((ETSI->firstbyte & 0x03) == 0x01) PDU=SMS_Submit;
789 if ((ETSI->firstbyte & 0x03) == 0x02) PDU=SMS_Status_Report;
791 GSM_DecodeETSISMSHeader(SMS, ETSI);
796 fprintf(stdout, _(" SMS submit "));
799 GSM_DecodeETSISMSSubmitData(SMS,ETSI);
803 fprintf(stdout, _(" SMS deliver "));
804 fprintf(stdout, _(" Date: "));
807 GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
808 GSM_DecodeETSISMSSubmitData(SMS,ETSI);
810 case SMS_Status_Report:
812 fprintf(stdout, _(" SMS status report "));
813 fprintf(stdout, _(" Date: "));
816 GSM_DecodeSMSDateTime(&SMS->Time, ETSI->DeliveryDateTime);
817 GSM_DecodeETSISMSStatusReportData(SMS,ETSI);
823 SMS->MessageText[SMS->Length]=0;
829 static void GSM_SetDefaultSMSData (GSM_SMSMessage *SMS)
835 /* Default settings for SMS message:
842 SMS->folder = GST_OUTBOX;
845 SMS->Compression = false;
846 SMS->MessageCenter.No = 1;
847 SMS->Validity = 4320; /* 4320 minutes == 72 hours */
848 SMS->ReplyViaSameSMSC = false;
849 SMS->UDHType = GSM_NoUDH;
850 SMS->Coding=GSM_Coding_Default;
851 strcpy(SMS->Destination,"");
853 /* This part is required to save SMS */
855 SMS->Status = GSS_NOTSENTREAD;
859 now=localtime(&nowh);
861 Date.Year = now->tm_year;
862 Date.Month = now->tm_mon+1;
863 Date.Day = now->tm_mday;
864 Date.Hour = now->tm_hour;
865 Date.Minute = now->tm_min;
866 Date.Second = now->tm_sec;
868 /* I have 100 (for 2000) Year now :-) */
869 if (Date.Year>99 && Date.Year<1900) {
870 Date.Year=Date.Year+1900;
873 /* We need to have only two last digits of year */
876 if (Date.Year<2000) Date.Year = Date.Year-1900;
877 else Date.Year = Date.Year-2000;
880 SMS->Time.Year=Date.Year;
881 SMS->Time.Month=Date.Month;
882 SMS->Time.Day=Date.Day;
883 SMS->Time.Hour=Date.Hour;
884 SMS->Time.Minute=Date.Minute;
885 SMS->Time.Second=Date.Second;
890 /* This function encodes the UserDataHeader as described in:
891 - GSM 03.40 version 6.1.0 Release 1997, section 9.2.3.24
892 - Smart Messaging Specification, Revision 1.0.0, September 15, 1997
897 GSM_Error EncodeUDHHeader(char *text, GSM_UDH UDHType)
901 if (UDHType!=GSM_NoUDH) {
903 if (UDHHeaders[i].UDHType==GSM_NoUDH) {
905 fprintf(stderr,_("Not supported User Data Header type\n"));
909 if (UDHHeaders[i].UDHType==UDHType) {
910 text[0] = UDHHeaders[i].Length; // UDH Length
911 memcpy(text+1, UDHHeaders[i].Text, UDHHeaders[i].Length);
920 static int GSM_MakeSinglePartSMS2(GSM_SMSMessage *SMS,
921 unsigned char *MessageBuffer,int cur, GSM_UDH UDHType, GSM_Coding_Type Coding){
924 int current,smsudhlength;
926 GSM_SetDefaultSMSData(SMS);
928 EncodeUDHHeader(SMS->UDH, UDHType);
929 SMS->UDHType=UDHType;
932 case GSM_EnableVoice:
933 case GSM_DisableVoice:
934 case GSM_EnableEmail:
935 case GSM_DisableEmail:
942 case GSM_ConcatenatedMessages:
946 case GSM_PhonebookUDH:
947 case GSM_CalendarNoteUDH: //class=1?
952 case GSM_CallerIDLogo:
953 case GSM_RingtoneUDH:
954 case GSM_WAPBookmarkUDH:
955 case GSM_WAPBookmarkUDHLong:
956 case GSM_WAPSettingsUDH:
959 SMS->Coding=GSM_Coding_8bit;
962 fprintf(stderr,_("Error in makesinglepartsms !\n\n\n"));
968 if (UDHType!=GSM_NoUDH)
969 smsudhlength=SMS->UDH[0]+1;
972 switch (SMS->Coding) {
973 case GSM_Coding_8bit:
974 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength); //max=140
976 case GSM_Coding_Default:
977 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)*8/7; //max=160
979 case GSM_Coding_Unicode:
980 j=(GSM_MAX_SMS_8_BIT_LENGTH-smsudhlength)/2; //max=70
983 if (current>j) current=j;
985 memcpy(SMS->MessageText,MessageBuffer,current);
986 SMS->MessageText[current]=0;
992 void GSM_MakeMultiPartSMS2(GSM_MultiSMSMessage *SMS,
993 unsigned char *MessageBuffer,int MessageLength, GSM_UDH UDHType, GSM_Coding_Type Coding){
995 int i=0,j,pos=0,current=0;
998 if (pos==MessageLength) break;
1000 current=MessageLength-pos;
1002 pos=pos+GSM_MakeSinglePartSMS2(&SMS->SMS[i],MessageBuffer+pos,current,UDHType,Coding);
1008 case GSM_ProfileUDH:
1009 case GSM_WAPBookmarkUDHLong:
1010 case GSM_WAPSettingsUDH:
1011 case GSM_CalendarNoteUDH:
1012 case GSM_CalendarNoteUDH2:
1013 case GSM_PhonebookUDH:
1014 SMS->SMS[j].UDH[10]=i;
1015 SMS->SMS[j].UDH[11]=j+1;
1017 case GSM_ConcatenatedMessages:
1018 SMS->SMS[j].UDH[4]=i;
1019 SMS->SMS[j].UDH[5]=j+1;