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)
*/
+#include <gsm-common.h>
#include <string.h>
-#include "gsm-common.h"
+#include <stdlib.h> /* for rand() */
+#include <ctype.h>
+
+/* 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)
{
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
}
{
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;i<nibbles;i++) {
+ if (d +1/*current digit*/ +1/*terminating '\0'*/ >dest+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<<UsedBits)-1)) | (Byte << UsedBits);
+
+ UsedBits--;
+
+ IN++;
+ }
+ if (UsedBits!=0)
+ OUT++;
+
+ return (OUT - output);
+}
+
+int UnpackEightBitsToSeven(int offset, int in_length, int out_length,
+ 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 */
+ unsigned char Rest = 0x00;
+ int Bits;
+#define ByteMask ((1 << Bits) - 1)
+
+ Bits = offset ? offset : 7;
+
+ while ((IN - input) < in_length) {
+
+ *OUT = ((*IN & ByteMask) << (7 - Bits)) | Rest;
+ Rest = *IN >> 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);
+}