7 A Linux/Unix toolset and driver for Nokia mobile phones.
9 Copyright (C) 1999, 2000 Hugh Blemings & Pavel JanÃk ml.
11 Released under the terms of the GNU GPL, see file COPYING for more details.
14 Revision 1.1.1.2 2002/04/03 00:07:55 short
15 Found in "gnokii-working" directory, some November-patches version
17 Revision 1.6 2001/09/14 12:15:28 pkot
18 Cleanups from 0.3.3 (part1)
20 Revision 1.5 2001/03/22 16:17:05 chris
21 Tidy-ups and fixed gnokii/Makefile and gnokii/ChangeLog which I somehow corrupted.
23 Revision 1.4 2001/03/21 23:36:04 chris
24 Added the statemachine
25 This will break gnokii --identify and --monitor except for 6210/7110
27 Revision 1.3 2001/02/28 21:26:51 machek
28 Added StrToMemoryType utility function
30 Revision 1.2 2001/02/03 23:56:15 chris
31 Start of work on irda support (now we just need fbus-irda.c!)
32 Proper unicode support in 7110 code (from pkot)
34 Revision 1.1 2001/01/12 14:28:39 pkot
35 Forgot to add this file. ;-)
40 #include <gsm-common.h>
42 #include <stdlib.h> /* for rand() */
45 /* Coding functions */
46 #define NUMBER_OF_7_BIT_ALPHABET_ELEMENTS 128
47 static unsigned char GSM_DefaultAlphabet[NUMBER_OF_7_BIT_ALPHABET_ELEMENTS] = {
49 /* ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet */
50 /* Characters in hex position 10, [12 to 1a] and 24 are not present on
51 latin1 charset, so we cannot reproduce on the screen, however they are
52 greek symbol not present even on my Nokia */
54 '@', 0xa3, '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec,
55 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5,
56 '?', '_', '?', '?', '?', '?', '?', '?',
57 '?', '?', '?', '?', 0xc6, 0xe6, 0xdf, 0xc9,
58 ' ', '!', '\"', '#', 0xa4, '%', '&', '\'',
59 '(', ')', '*', '+', ',', '-', '.', '/',
60 '0', '1', '2', '3', '4', '5', '6', '7',
61 '8', '9', ':', ';', '<', '=', '>', '?',
62 0xa1, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
63 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
64 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
65 'X', 'Y', 'Z', 0xc4, 0xd6, 0xd1, 0xdc, 0xa7,
66 0xbf, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
67 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
68 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
69 'x', 'y', 'z', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0
72 unsigned char EncodeWithDefaultAlphabet(unsigned char value)
76 if (value == '?') return 0x3f;
78 for (i = 0; i < NUMBER_OF_7_BIT_ALPHABET_ELEMENTS; i++)
79 if (GSM_DefaultAlphabet[i] == value)
85 unsigned char DecodeWithDefaultAlphabet(unsigned char value)
87 return GSM_DefaultAlphabet[value];
90 wchar_t EncodeWithUnicodeAlphabet(unsigned char value)
94 if (mbtowc(&retval, &value, 1) == -1) return '?';
98 unsigned char DecodeWithUnicodeAlphabet(wchar_t value)
100 unsigned char retval;
102 if (wctomb(&retval, value) == -1) return '?';
107 GSM_Error Unimplemented(void)
109 return GE_NOTIMPLEMENTED;
112 GSM_MemoryType StrToMemoryType(const char *s)
114 #define X(a) if (!strcmp(s, #a)) return GMT_##a;
129 /* This very small function is just to make it */
130 /* easier to clear the data struct every time one is created */
131 inline void GSM_DataClear(GSM_Data *data)
133 memset(data, 0, sizeof(GSM_Data));
135 /* TP-Validity Period handling */
137 /* TP-Validity Period handling */
138 unsigned char SMS_Validity_to_VP(int Validity)
140 /* FIXME: error-checking for correct Validity - it should not be bigger then
141 63 weeks and smaller then 5minutes. We should also test intervals because
142 the SMS->Validity to TP-VP is not continuos. I think that the simplest
143 solution will be an array of correct values. We should parse it and if we
144 find the closest TP-VP value we should use it. Or is it good to take
145 closest smaller TP-VP as we do now? I think it is :-) */
147 /* 5 minutes intervals up to 12 hours = 720 minutes */
149 return((unsigned char) (Validity/5)-1);
151 /* 30 minutes intervals up to 1 day */
152 else if ((Validity > 720) && (Validity <= 1440))
153 return((unsigned char) ((Validity-720)/30)+143);
155 /* 1 day intervals up to 30 days */
156 else if ((Validity > 1440) && (Validity <= 43200))
157 return((unsigned char) (Validity/1440)+166);
159 /* 1 week intervals up to 63 weeks */
160 else if ((Validity > 43200) && (Validity <= 635040))
161 return((unsigned char) (Validity/10080)+192);
163 /* Validity too big, strip it to the highest possible value */
164 return((unsigned char)0xFF);
167 int SMS_VP_to_Validity(GSM_SMSMessageValidity Validity)
169 if (Validity<=GSMV_1_Hour)
171 if (Validity<=GSMV_6_Hours)
173 if (Validity<=GSMV_24_Hours)
175 if (Validity<=GSMV_72_Hours)
177 if (Validity<=GSMV_1_Week)
179 if (Validity<=GSMV_Max_Time)
181 /* Validity too big, strip it to the highest possible value */
185 /* This function implements packing of numbers (SMS Center number and
186 * destination number) for SMS sending function.
187 * RETURN: strlen(Number+(*Number=='+')), that means the number of INPUT
188 * digits (without '+' marker) which is equal to the number of written
189 * digit nibbles (without possible trailing 0xF filler)
191 int SemiOctetPack(char *Number, unsigned char *Output)
193 unsigned char *IN = Number; /* Pointer to the input number */
194 unsigned char *OUT = Output; /* Pointer to the output */
195 int count = 0; /* This variable is used to notify us about count of already
198 /* The first byte in the Semi-octet representation of the address field is
199 the Type-of-Address. This field is described in the official GSM
200 specification 03.40 version 5.3.0, section 9.1.2.5, page 33. We support
201 only international and unknown number. */
204 *OUT++ = GNT_INTERNATIONAL; /* International number */
206 } else *OUT++ = GNT_UNKNOWN; /* Unknown number */
208 /* The next field is the number. It is in semi-octet representation - see
209 GSM scpecification 03.40 version 5.3.0, section 9.1.2.3, page 31. */
213 *OUT = *OUT | ((*IN - '0') << 4);
221 /* We should also fill in the most significant bits of the last byte with
222 0x0f (1111 binary) if the number is represented with odd number of
228 return (2 * (OUT - Output - 1) - 1);
231 return (2 * (OUT - Output - 1));
234 /* This function implements unpacking of numbers (SMS Center number and
235 * destination number) for SMS receiving function.
236 * INPUT: Number of NIBBLES which is
237 * equal to the number of written OUTPUT characters (without optional '+'
238 * marker), may be +1 as it is hard to predict possible trailing 0xF filler
239 * RETURN: Number with skipped number or NULL for error
241 char *SemiOctetUnpack(char *dest,size_t destlen,u8 *Number,size_t nibbles)
248 return(NULL); /* buffer overflow */
252 if (*s++ == GNT_INTERNATIONAL)
255 for (i=0;i<nibbles;i++) {
256 if (d +1/*current digit*/ +1/*terminating '\0'*/ >dest+destlen)
257 return(NULL); /* buffer overflow */
258 /* Here we advance "s" during first nibble read as when we finish
259 * now we must return the pointer AFTER it
262 Digit= (*s++ ) & 0x0F;
264 Digit=((s[-1])>>4) & 0x0F;
265 if (i+1==nibbles && Digit==0xF)
266 break; /* nibbles+1 is allowed */
268 return(NULL); /* digit=A..F */
276 /* When you are so poor that you even don't have your own buffer...
277 * RETURN: local static buffer address or NULL for error
278 * Note: address of Number with skipped number is lost!
279 * Note: be sure that this function is called only once before processing the result!
281 char *SemiOctetUnpack_static(u8 *Number,size_t nibbles)
283 static char Buffer[GNOKII_MAX(
284 GNOKII_MAX(GSM_MAX_SMS_CENTER_LENGTH,GSM_MAX_SENDER_LENGTH)
285 ,GSM_MAX_DESTINATION_LENGTH)
289 if (!(r=SemiOctetUnpack(Buffer,sizeof(Buffer),Number,nibbles)))
294 unsigned char GSM_Default_Alphabet[] = {
296 /* ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet */
297 /* Characters in hex position 10, [12 to 1a] and 24 are not present on
298 latin1 charset, so we cannot reproduce on the screen, however they are
299 greek symbol not present even on my Nokia */
301 '@', 0xa3, '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec,
302 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5,
303 '?', '_', '?', '?', '?', '?', '?', '?',
304 '?', '?', '?', '?', 0xc6, 0xe6, 0xdf, 0xc9,
305 ' ', '!', '\"', '#', 0xa4, '%', '&', '\'',
306 '(', ')', '*', '+', ',', '-', '.', '/',
307 '0', '1', '2', '3', '4', '5', '6', '7',
308 '8', '9', ':', ';', '<', '=', '>', '?',
309 0xa1, 'A', 'B', 'C', 'D', 'E', 'F', 'G',
310 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
311 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
312 'X', 'Y', 'Z', 0xc4, 0xd6, 0xd1, 0xdc, 0xa7,
313 0xbf, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
314 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
315 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
316 'x', 'y', 'z', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0
319 static unsigned char GetAlphabetValue(unsigned char value)
323 if (value == '?') return 0x3f;
325 for (i = 0 ; i < 128 ; i++)
326 if (GSM_Default_Alphabet[i] == value)
329 return 0x3f; /* '?' */
332 /* Function packs the '\0'-terminated 7-bit "input" string to the "output" 8-bit stream.
333 * UsedBits specifies the number of bottom bits already used in (*output) byte.
334 * RETURNS: Number of bytes written, at least partially
336 int PackSevenBitsToEight(int UsedBits, unsigned char *input, unsigned char *output)
338 unsigned char *OUT = output; /* Current pointer to the output buffer */
339 unsigned char *IN = input; /* Current pointer to the input buffer */
341 UsedBits %= 8; /* just sanity, (0<=x<8) assert would be appropriate */
344 unsigned char Byte = GetAlphabetValue(*IN);
346 /* UsedBits specifies the number of already allocated bits in OUT[-1]
347 * byte. If there were no bits used, we just don't advance the destination
348 * pointer and we gain the full 8 bits of free space in the current byte.
355 /* Write upper (UsedBits) of Byte to the bottom of (*OUT) */
356 *OUT = Byte >> (8 - UsedBits);
357 /* If we don't write at 0th bit of the octet, we should write
358 * a second part of the previous octet
359 * Write lower (8-UsedBits) to the top of OUT[-1]
362 OUT[-1] = (OUT[-1] & ((1<<UsedBits)-1)) | (Byte << UsedBits);
371 return (OUT - output);
374 int UnpackEightBitsToSeven(int offset, int in_length, int out_length,
375 unsigned char *input, unsigned char *output)
377 unsigned char *OUT = output; /* Current pointer to the output buffer */
378 unsigned char *IN = input; /* Current pointer to the input buffer */
379 unsigned char Rest = 0x00;
381 #define ByteMask ((1 << Bits) - 1)
383 Bits = offset ? offset : 7;
385 while ((IN - input) < in_length) {
387 *OUT = ((*IN & ByteMask) << (7 - Bits)) | Rest;
390 /* If we don't start from 0th bit, we shouldn't go to the
391 next char. Under *OUT we have now 0 and under Rest -
392 _first_ part of the char. */
393 if ((IN != input) || (Bits == 7)) OUT++;
396 if ((OUT - output) >= out_length) break;
398 /* After reading 7 octets we have read 7 full characters but
399 we have 7 bits as well. This is the next character */
413 GSM_Error SMS_SetupUDH(GSM_SMSMessage *sms,GSM_UDH UDHType)
415 unsigned short srcport,dstport;
417 sms->UDHPresent=true;
419 case GSM_RingtoneUDH: /* FALLTHRU */
420 case GSM_OpLogo: /* FALLTHRU */
421 case GSM_CallerIDLogo:
422 sms->UDH[1+0]=0x05; /* logos (16-bit ports) */
423 sms->UDH[1+1]=4; /* sizeof(dstport)+sizeof(srcport) */
424 sms->UDH[0]=2+sms->UDH[1+1]; /* total UserDataHeader size */
426 case GSM_RingtoneUDH: dstport=0x1581; break;
427 case GSM_OpLogo: dstport=0x1582; break;
428 case GSM_CallerIDLogo: dstport=0x1583; break;
429 default: dstport=0; /* double shut up GCC */
431 /* I have tested on Nokia 9000i on GSM_RingtoneUDH that it will silently
432 * ignore Multi-SMS incoming ringtone with srcport==0 .
433 * Despite this Single-SMS incoming ringtone is accepted even with srcport==0 !
434 * When both ports were equal to 0x1581 everything was fine.
437 sms->UDH[1+2+0+0]=dstport>>8;
438 sms->UDH[1+2+0+1]=dstport>>0;
439 sms->UDH[1+2+2+0]=srcport>>8;
440 sms->UDH[1+2+2+1]=srcport>>0;
445 sms->UDHPresent=false;
446 return(GE_NOTIMPLEMENTED); /* error */
449 GSM_UDH SMS_DetectUDH(GSM_SMSMessage *sms)
451 if (!sms->UDHPresent)
453 if (sms->UDH[0]< 1/*type*/ +1/*sub-length*/)
454 return(GSM_NoUDH); /* error */
455 switch (sms->UDH[1]) {
457 case 0x00: /* concatenated messages (8-bit reference) */
458 dprintf(_("Concatenated message!!!\n"));
460 return(GSM_NoUDH); /* error */
461 return(GSM_ConcatenatedMessages);
464 case 0x08: /* concatenated messages (16-bit reference) */
465 dprintf(_("Concatenated message!!!\n"));
467 return(GSM_NoUDH); /* error */
468 return(GSM_ConcatenatedMessages);
471 case 0x05: /* logos (16-bit ports) */
473 return(GSM_NoUDH); /* error */
474 switch ((sms->UDH[3]<<8) | (sms->UDH[4]<<0)) {
478 return(GSM_CallerIDLogo);
484 return(GSM_NoUDH); /* error - not recognized */
487 /* RETURNS: Total number of SMSes, sms->MessageTextLength will contain cargoitems
489 static int SMS_Deconcatenate_calcparts(GSM_SMSMessage *sms,size_t *cargoitemsp,size_t buflen,size_t messagetextpreitems)
491 int bits=(sms->EightBit ? 8 : 7);
492 int UDHbytes=(!sms->UDHPresent ? 0 : 1+sms->UDH[0]);
493 int UDHitems=(UDHbytes*8 +bits-1/*round-up*/)/bits;
494 int lengthitems=(sms->EightBit ? GSM_MAX_SMS_8BIT_LENGTH : GSM_MAX_SMS_LENGTH);
495 int cargoitems=lengthitems-UDHitems-messagetextpreitems;
498 *cargoitemsp=cargoitems;
500 return((buflen +cargoitems-1/*round-up*/)/cargoitems);
503 /* RETURNS: Whether we should send the prepared (*sms)
505 bool SMS_Deconcatenate(GSM_Deconcatenate_state *state,GSM_SMSMessage *sms,char *buf,size_t buflen,bool useudh)
507 size_t offset_start,offset_end,messagetextpreitems;
509 #define CONCAT_TEXT_HEADER "%2d/%-2d: "
510 /* This length has to match 7bit-to-8bit length of single concat UDH
511 * due to broken assumptions at least in xgnokii_sms.c
512 * =round-up((UDHlength+IEItype+IEIlength+reference+total+sequence)*8/7)
514 #define CONCAT_TEXT_HEADER_LENGTH 7
520 if ((state->singleshot=(1>=SMS_Deconcatenate_calcparts(sms,NULL,buflen,0)))) {
521 memcpy(sms->MessageText,buf,buflen);
522 sms->MessageText[sms->MessageTextLength=buflen]='\0';
523 return(true); /* send it */
526 state->sequencep=NULL;
528 if (state->singleshot)
529 return(false); /* done */
531 if (!state->sequence && useudh) {
533 if (!sms->UDHPresent) {
534 sms->UDHPresent=true;
537 if (1/*sizeof(UDH[0])*/ +sms->UDH[0]/*current UDH len*/ +6/*concatenation UDH len*/
539 return(false); /* error - UDH buffer overflow - shouldn't happen */
540 d=(sms->UDH+1)+sms->UDH[0];
542 *d++=0x00; /* concatenated messages (IEI) (8-bit reference number) */
543 *d++=0x03; /* IEI data length */
544 *d++=rand(); /* 8-bit reference number */
545 *d++=0; /* number of messages - not yet known, filled by d[-2] below! */
547 *d++=0; /* message sequence number - not yet filled */
549 sms->UDH[0]=d-(sms->UDH+1);
551 d[-2]=SMS_Deconcatenate_calcparts(sms,&state->cargoitems,buflen,0); /* UDH[0] must be already set */
553 if (!state->sequence && !useudh) {
554 gsprintf(sms->MessageText,sizeof(sms->MessageText),CONCAT_TEXT_HEADER,
555 0, SMS_Deconcatenate_calcparts(sms,&state->cargoitems,buflen,CONCAT_TEXT_HEADER_LENGTH));
558 /* It is important that "lastsequence" is not "u8" and so it will not overflow
559 * on the 255th part of SMS
564 *state->sequencep=state->sequence;
566 gsprintf(sms->MessageText,3,"%2d",state->sequence);
567 sms->MessageText[2]='/'; /* it may got overwritten by '\0' */
570 offset_start=(state->sequence-1)*state->cargoitems;
571 offset_end=GNOKII_MIN(buflen,(state->sequence-1+1)*state->cargoitems);
573 if (offset_start>=offset_end)
574 return(false); /* no more data available */
576 messagetextpreitems=(useudh ? 0 : CONCAT_TEXT_HEADER_LENGTH);
577 memcpy(sms->MessageText+messagetextpreitems,buf+offset_start,offset_end-offset_start);
578 sms->MessageText[sms->MessageTextLength=messagetextpreitems+offset_end-offset_start]='\0';
579 dprintf(_("Fragment %d: %d..%d (buflen=%d)\n"),state->sequence,offset_start,offset_end,buflen);
581 return(true); /* send it! */
584 static inline u8 SMS_CharFromHex(unsigned u)
587 return(u<'A'?u-('0'&0xDF):(u-('A'&0xDF))+0xA);
590 /* RETURN: end of "d" block or NULL if error
591 * Note: s==d on input is permitted
593 u8 *SMS_BlockFromHex(u8 *d,const char *s,size_t len)
596 if (!isxdigit(s[0]) || !isxdigit(s[1]))
598 *d++=(SMS_CharFromHex(s[0])<<4)|SMS_CharFromHex(s[1]);
603 return(NULL); /* error */
607 static inline char SMS_CharToHex(u8 x)
610 if (x<10) return(x +'0');
611 else return(x-10+'A');
614 /* RETURN: end of "d" block (no errors generated)
616 char *SMS_BlockToHex(char *d,const u8 *s,size_t len)
619 *d++=SMS_CharToHex(*s>>4U);
620 *d++=SMS_CharToHex(*s );