X-Git-Url: http://git.jankratochvil.net/?p=gnokii.git;a=blobdiff_plain;f=common%2Fgsm-common.c;h=72c1b8d299ddb6b9147c72098793440d25ee9a56;hp=54e63cb6bcb05cd517ab3151328eec53cf66f5b3;hb=49dd905279a8e62936e3713510ab0fd738e20ecb;hpb=2f2703c9133032c12671ca5c77ae626b8fb178d4 diff --git a/common/gsm-common.c b/common/gsm-common.c index 54e63cb..72c1b8d 100644 --- a/common/gsm-common.c +++ b/common/gsm-common.c @@ -11,14 +11,8 @@ Released under the terms of the GNU GPL, see file COPYING for more details. $Log$ - Revision 1.1.1.1 2001/11/25 21:58:59 short - :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001 - - Revision 1.8 2001/11/13 16:12:20 pkot - Preparing libsms to get to work. 6210/7110 SMS and SMS Folder updates - - Revision 1.7 2001/11/08 16:34:19 pkot - Updates to work with new libsms + Revision 1.1.1.2 2002/04/03 00:07:55 short + Found in "gnokii-working" directory, some November-patches version Revision 1.6 2001/09/14 12:15:28 pkot Cleanups from 0.3.3 (part1) @@ -43,8 +37,72 @@ */ +#include #include -#include "gsm-common.h" +#include /* for rand() */ +#include + +/* Coding functions */ +#define NUMBER_OF_7_BIT_ALPHABET_ELEMENTS 128 +static unsigned char GSM_DefaultAlphabet[NUMBER_OF_7_BIT_ALPHABET_ELEMENTS] = { + + /* ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet */ + /* Characters in hex position 10, [12 to 1a] and 24 are not present on + latin1 charset, so we cannot reproduce on the screen, however they are + greek symbol not present even on my Nokia */ + + '@', 0xa3, '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec, + 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5, + '?', '_', '?', '?', '?', '?', '?', '?', + '?', '?', '?', '?', 0xc6, 0xe6, 0xdf, 0xc9, + ' ', '!', '\"', '#', 0xa4, '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + 0xa1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', 0xc4, 0xd6, 0xd1, 0xdc, 0xa7, + 0xbf, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0 +}; + +unsigned char EncodeWithDefaultAlphabet(unsigned char value) +{ + unsigned char i; + + if (value == '?') return 0x3f; + + for (i = 0; i < NUMBER_OF_7_BIT_ALPHABET_ELEMENTS; i++) + if (GSM_DefaultAlphabet[i] == value) + return i; + + return '?'; +} + +unsigned char DecodeWithDefaultAlphabet(unsigned char value) +{ + return GSM_DefaultAlphabet[value]; +} + +wchar_t EncodeWithUnicodeAlphabet(unsigned char value) +{ + wchar_t retval; + + if (mbtowc(&retval, &value, 1) == -1) return '?'; + else return retval; +} + +unsigned char DecodeWithUnicodeAlphabet(wchar_t value) +{ + unsigned char retval; + + if (wctomb(&retval, value) == -1) return '?'; + else return retval; +} + GSM_Error Unimplemented(void) { @@ -64,30 +122,6 @@ GSM_MemoryType StrToMemoryType(const char *s) X(MC); X(LD); X(MT); - X(IN); - X(OU); - X(AR); - X(TE); - X(F1); - X(F2); - X(F3); - X(F4); - X(F5); - X(F6); - X(F7); - X(F8); - X(F9); - X(F10); - X(F11); - X(F12); - X(F13); - X(F14); - X(F15); - X(F16); - X(F17); - X(F18); - X(F19); - X(F20); return GMT_XX; #undef X } @@ -98,3 +132,493 @@ inline void GSM_DataClear(GSM_Data *data) { memset(data, 0, sizeof(GSM_Data)); } + /* TP-Validity Period handling */ + +/* TP-Validity Period handling */ +unsigned char SMS_Validity_to_VP(int Validity) +{ + /* FIXME: error-checking for correct Validity - it should not be bigger then + 63 weeks and smaller then 5minutes. We should also test intervals because + the SMS->Validity to TP-VP is not continuos. I think that the simplest + solution will be an array of correct values. We should parse it and if we + find the closest TP-VP value we should use it. Or is it good to take + closest smaller TP-VP as we do now? I think it is :-) */ + + /* 5 minutes intervals up to 12 hours = 720 minutes */ + if (Validity <= 720) + return((unsigned char) (Validity/5)-1); + + /* 30 minutes intervals up to 1 day */ + else if ((Validity > 720) && (Validity <= 1440)) + return((unsigned char) ((Validity-720)/30)+143); + + /* 1 day intervals up to 30 days */ + else if ((Validity > 1440) && (Validity <= 43200)) + return((unsigned char) (Validity/1440)+166); + + /* 1 week intervals up to 63 weeks */ + else if ((Validity > 43200) && (Validity <= 635040)) + return((unsigned char) (Validity/10080)+192); + + /* Validity too big, strip it to the highest possible value */ + return((unsigned char)0xFF); +} + +int SMS_VP_to_Validity(GSM_SMSMessageValidity Validity) +{ + if (Validity<=GSMV_1_Hour) + return(1*60); + if (Validity<=GSMV_6_Hours) + return(6*60); + if (Validity<=GSMV_24_Hours) + return(24*60); + if (Validity<=GSMV_72_Hours) + return(72*60); + if (Validity<=GSMV_1_Week) + return(7*24*60); + if (Validity<=GSMV_Max_Time) + return(63*7*24*60); + /* Validity too big, strip it to the highest possible value */ + return(63*7*24*60); +} + +/* This function implements packing of numbers (SMS Center number and + * destination number) for SMS sending function. + * RETURN: strlen(Number+(*Number=='+')), that means the number of INPUT + * digits (without '+' marker) which is equal to the number of written + * digit nibbles (without possible trailing 0xF filler) + */ +int SemiOctetPack(char *Number, unsigned char *Output) +{ + unsigned char *IN = Number; /* Pointer to the input number */ + unsigned char *OUT = Output; /* Pointer to the output */ + int count = 0; /* This variable is used to notify us about count of already + packed numbers. */ + + /* The first byte in the Semi-octet representation of the address field is + the Type-of-Address. This field is described in the official GSM + specification 03.40 version 5.3.0, section 9.1.2.5, page 33. We support + only international and unknown number. */ + + if (*IN == '+') { + *OUT++ = GNT_INTERNATIONAL; /* International number */ + IN++; + } else *OUT++ = GNT_UNKNOWN; /* Unknown number */ + + /* The next field is the number. It is in semi-octet representation - see + GSM scpecification 03.40 version 5.3.0, section 9.1.2.3, page 31. */ + + while (*IN) { + if (count & 0x01) { + *OUT = *OUT | ((*IN - '0') << 4); + OUT++; + } + else + *OUT = *IN - '0'; + count++; IN++; + } + + /* We should also fill in the most significant bits of the last byte with + 0x0f (1111 binary) if the number is represented with odd number of + digits. */ + + if (count & 0x01) { + *OUT=*OUT | 0xf0; + OUT++; + return (2 * (OUT - Output - 1) - 1); + } + + return (2 * (OUT - Output - 1)); +} + +/* This function implements unpacking of numbers (SMS Center number and + * destination number) for SMS receiving function. + * INPUT: Number of NIBBLES which is + * equal to the number of written OUTPUT characters (without optional '+' + * marker), may be +1 as it is hard to predict possible trailing 0xF filler + * RETURN: Number with skipped number or NULL for error + */ +char *SemiOctetUnpack(char *dest,size_t destlen,u8 *Number,size_t nibbles) +{ + int i; + char *d; + u8 *s,Digit; + + if (destlen<2) + return(NULL); /* buffer overflow */ + + d=dest; + s=Number; + if (*s++ == GNT_INTERNATIONAL) + *d++='+'; + + for (i=0;idest+destlen) + return(NULL); /* buffer overflow */ + /* Here we advance "s" during first nibble read as when we finish + * now we must return the pointer AFTER it + */ + if (!(i&1)) + Digit= (*s++ ) & 0x0F; + else + Digit=((s[-1])>>4) & 0x0F; + if (i+1==nibbles && Digit==0xF) + break; /* nibbles+1 is allowed */ + if (Digit>9) + return(NULL); /* digit=A..F */ + *d++=Digit+'0'; + } + *d='\0'; + + return(s); +} + +/* When you are so poor that you even don't have your own buffer... + * RETURN: local static buffer address or NULL for error + * Note: address of Number with skipped number is lost! + * Note: be sure that this function is called only once before processing the result! + */ +char *SemiOctetUnpack_static(u8 *Number,size_t nibbles) +{ + static char Buffer[GNOKII_MAX( + GNOKII_MAX(GSM_MAX_SMS_CENTER_LENGTH,GSM_MAX_SENDER_LENGTH) + ,GSM_MAX_DESTINATION_LENGTH) + ] = ""; + char *r; + + if (!(r=SemiOctetUnpack(Buffer,sizeof(Buffer),Number,nibbles))) + return(NULL); + return(Buffer); +} + +unsigned char GSM_Default_Alphabet[] = { + + /* ETSI GSM 03.38, version 6.0.1, section 6.2.1; Default alphabet */ + /* Characters in hex position 10, [12 to 1a] and 24 are not present on + latin1 charset, so we cannot reproduce on the screen, however they are + greek symbol not present even on my Nokia */ + + '@', 0xa3, '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec, + 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5, + '?', '_', '?', '?', '?', '?', '?', '?', + '?', '?', '?', '?', 0xc6, 0xe6, 0xdf, 0xc9, + ' ', '!', '\"', '#', 0xa4, '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + 0xa1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', 0xc4, 0xd6, 0xd1, 0xdc, 0xa7, + 0xbf, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', + 'x', 'y', 'z', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0 +}; + +static unsigned char GetAlphabetValue(unsigned char value) +{ + unsigned char i; + + if (value == '?') return 0x3f; + + for (i = 0 ; i < 128 ; i++) + if (GSM_Default_Alphabet[i] == value) + return i; + + return 0x3f; /* '?' */ +} + +/* Function packs the '\0'-terminated 7-bit "input" string to the "output" 8-bit stream. + * UsedBits specifies the number of bottom bits already used in (*output) byte. + * RETURNS: Number of bytes written, at least partially + */ +int PackSevenBitsToEight(int UsedBits, unsigned char *input, unsigned char *output) +{ + unsigned char *OUT = output; /* Current pointer to the output buffer */ + unsigned char *IN = input; /* Current pointer to the input buffer */ + + UsedBits %= 8; /* just sanity, (0<=x<8) assert would be appropriate */ + + while (*IN) { + unsigned char Byte = GetAlphabetValue(*IN); + + /* UsedBits specifies the number of already allocated bits in OUT[-1] + * byte. If there were no bits used, we just don't advance the destination + * pointer and we gain the full 8 bits of free space in the current byte. + */ + if (UsedBits == 0) + UsedBits = 8; + else + OUT++; + + /* Write upper (UsedBits) of Byte to the bottom of (*OUT) */ + *OUT = Byte >> (8 - UsedBits); + /* If we don't write at 0th bit of the octet, we should write + * a second part of the previous octet + * Write lower (8-UsedBits) to the top of OUT[-1] + */ + if (UsedBits != 8) + OUT[-1] = (OUT[-1] & ((1<> Bits; + + /* If we don't start from 0th bit, we shouldn't go to the + next char. Under *OUT we have now 0 and under Rest - + _first_ part of the char. */ + if ((IN != input) || (Bits == 7)) OUT++; + IN++; + + if ((OUT - output) >= out_length) break; + + /* After reading 7 octets we have read 7 full characters but + we have 7 bits as well. This is the next character */ + if (Bits == 1) { + *OUT = Rest; + OUT++; + Bits = 7; + Rest = 0x00; + } else { + Bits--; + } + } + + return OUT - output; +} + +GSM_Error SMS_SetupUDH(GSM_SMSMessage *sms,GSM_UDH UDHType) +{ +unsigned short srcport,dstport; + + sms->UDHPresent=true; + switch (UDHType) { + case GSM_RingtoneUDH: /* FALLTHRU */ + case GSM_OpLogo: /* FALLTHRU */ + case GSM_CallerIDLogo: + sms->UDH[1+0]=0x05; /* logos (16-bit ports) */ + sms->UDH[1+1]=4; /* sizeof(dstport)+sizeof(srcport) */ + sms->UDH[0]=2+sms->UDH[1+1]; /* total UserDataHeader size */ + switch (UDHType) { + case GSM_RingtoneUDH: dstport=0x1581; break; + case GSM_OpLogo: dstport=0x1582; break; + case GSM_CallerIDLogo: dstport=0x1583; break; + default: dstport=0; /* double shut up GCC */ + } + /* I have tested on Nokia 9000i on GSM_RingtoneUDH that it will silently + * ignore Multi-SMS incoming ringtone with srcport==0 . + * Despite this Single-SMS incoming ringtone is accepted even with srcport==0 ! + * When both ports were equal to 0x1581 everything was fine. + */ + srcport=dstport; + sms->UDH[1+2+0+0]=dstport>>8; + sms->UDH[1+2+0+1]=dstport>>0; + sms->UDH[1+2+2+0]=srcport>>8; + sms->UDH[1+2+2+1]=srcport>>0; + return(GE_NONE); + default: + break; + } + sms->UDHPresent=false; + return(GE_NOTIMPLEMENTED); /* error */ +} + +GSM_UDH SMS_DetectUDH(GSM_SMSMessage *sms) +{ + if (!sms->UDHPresent) + return(GSM_NoUDH); + if (sms->UDH[0]< 1/*type*/ +1/*sub-length*/) + return(GSM_NoUDH); /* error */ + switch (sms->UDH[1]) { + + case 0x00: /* concatenated messages (8-bit reference) */ + dprintf(_("Concatenated message!!!\n")); + if (sms->UDH[2]<3) + return(GSM_NoUDH); /* error */ + return(GSM_ConcatenatedMessages); + break; + + case 0x08: /* concatenated messages (16-bit reference) */ + dprintf(_("Concatenated message!!!\n")); + if (sms->UDH[2]<4) + return(GSM_NoUDH); /* error */ + return(GSM_ConcatenatedMessages); + break; + + case 0x05: /* logos (16-bit ports) */ + if (sms->UDH[2]<4) + return(GSM_NoUDH); /* error */ + switch ((sms->UDH[3]<<8) | (sms->UDH[4]<<0)) { + case 0x1582: + return(GSM_OpLogo); + case 0x1583: + return(GSM_CallerIDLogo); + } + break; + default: + break; + } + return(GSM_NoUDH); /* error - not recognized */ +} + +/* RETURNS: Total number of SMSes, sms->MessageTextLength will contain cargoitems + */ +static int SMS_Deconcatenate_calcparts(GSM_SMSMessage *sms,size_t *cargoitemsp,size_t buflen,size_t messagetextpreitems) +{ +int bits=(sms->EightBit ? 8 : 7); +int UDHbytes=(!sms->UDHPresent ? 0 : 1+sms->UDH[0]); +int UDHitems=(UDHbytes*8 +bits-1/*round-up*/)/bits; +int lengthitems=(sms->EightBit ? GSM_MAX_SMS_8BIT_LENGTH : GSM_MAX_SMS_LENGTH); +int cargoitems=lengthitems-UDHitems-messagetextpreitems; + + if (cargoitemsp) + *cargoitemsp=cargoitems; + + return((buflen +cargoitems-1/*round-up*/)/cargoitems); +} + +/* RETURNS: Whether we should send the prepared (*sms) + */ +bool SMS_Deconcatenate(GSM_Deconcatenate_state *state,GSM_SMSMessage *sms,char *buf,size_t buflen,bool useudh) +{ +size_t offset_start,offset_end,messagetextpreitems; + +#define CONCAT_TEXT_HEADER "%2d/%-2d: " +/* This length has to match 7bit-to-8bit length of single concat UDH + * due to broken assumptions at least in xgnokii_sms.c + * =round-up((UDHlength+IEItype+IEIlength+reference+total+sequence)*8/7) + */ +#define CONCAT_TEXT_HEADER_LENGTH 7 + + if (state->first) { + /* INIT */ + state->first=false; + + if ((state->singleshot=(1>=SMS_Deconcatenate_calcparts(sms,NULL,buflen,0)))) { + memcpy(sms->MessageText,buf,buflen); + sms->MessageText[sms->MessageTextLength=buflen]='\0'; + return(true); /* send it */ + } + state->sequence=0; + state->sequencep=NULL; + } + if (state->singleshot) + return(false); /* done */ + + if (!state->sequence && useudh) { +u8 *d; + if (!sms->UDHPresent) { + sms->UDHPresent=true; + sms->UDH[0]=0; + } + if (1/*sizeof(UDH[0])*/ +sms->UDH[0]/*current UDH len*/ +6/*concatenation UDH len*/ + > sizeof(sms->UDH)) + return(false); /* error - UDH buffer overflow - shouldn't happen */ + d=(sms->UDH+1)+sms->UDH[0]; + + *d++=0x00; /* concatenated messages (IEI) (8-bit reference number) */ + *d++=0x03; /* IEI data length */ + *d++=rand(); /* 8-bit reference number */ + *d++=0; /* number of messages - not yet known, filled by d[-2] below! */ + state->sequencep=d; + *d++=0; /* message sequence number - not yet filled */ + + sms->UDH[0]=d-(sms->UDH+1); + + d[-2]=SMS_Deconcatenate_calcparts(sms,&state->cargoitems,buflen,0); /* UDH[0] must be already set */ + } + if (!state->sequence && !useudh) { + gsprintf(sms->MessageText,sizeof(sms->MessageText),CONCAT_TEXT_HEADER, + 0, SMS_Deconcatenate_calcparts(sms,&state->cargoitems,buflen,CONCAT_TEXT_HEADER_LENGTH)); + } + + /* It is important that "lastsequence" is not "u8" and so it will not overflow + * on the 255th part of SMS + */ + state->sequence++; + + if (useudh) + *state->sequencep=state->sequence; + else { + gsprintf(sms->MessageText,3,"%2d",state->sequence); + sms->MessageText[2]='/'; /* it may got overwritten by '\0' */ + } + + offset_start=(state->sequence-1)*state->cargoitems; + offset_end=GNOKII_MIN(buflen,(state->sequence-1+1)*state->cargoitems); + + if (offset_start>=offset_end) + return(false); /* no more data available */ + + messagetextpreitems=(useudh ? 0 : CONCAT_TEXT_HEADER_LENGTH); + memcpy(sms->MessageText+messagetextpreitems,buf+offset_start,offset_end-offset_start); + sms->MessageText[sms->MessageTextLength=messagetextpreitems+offset_end-offset_start]='\0'; + dprintf(_("Fragment %d: %d..%d (buflen=%d)\n"),state->sequence,offset_start,offset_end,buflen); + + return(true); /* send it! */ +} + +static inline u8 SMS_CharFromHex(unsigned u) +{ + u&=0xDF; + return(u<'A'?u-('0'&0xDF):(u-('A'&0xDF))+0xA); +} + +/* RETURN: end of "d" block or NULL if error + * Note: s==d on input is permitted + */ +u8 *SMS_BlockFromHex(u8 *d,const char *s,size_t len) +{ + while (len>=2) { + if (!isxdigit(s[0]) || !isxdigit(s[1])) + break; /* error */ + *d++=(SMS_CharFromHex(s[0])<<4)|SMS_CharFromHex(s[1]); + s+=2; + len-=2; + } + if (len) + return(NULL); /* error */ + return(d); +} + +static inline char SMS_CharToHex(u8 x) +{ + x&=0x0F; + if (x<10) return(x +'0'); + else return(x-10+'A'); +} + +/* RETURN: end of "d" block (no errors generated) + */ +char *SMS_BlockToHex(char *d,const u8 *s,size_t len) +{ + while (len--) { + *d++=SMS_CharToHex(*s>>4U); + *d++=SMS_CharToHex(*s ); + s++; + } + return(d); +}