/* (c) 2002-2004 by Marcin Wiacek, Michal Cihar and others */ /* based on some work from MyGnokii (www.mwiacek.com) */ /* based on some work from Gnokii (www.gnokii.org) * (C) 1999-2000 Hugh Blemings & Pavel Janik ml. (C) 2001-2004 Pawel Kot * GNU GPL version 2 or later */ /* Due to a problem in the source code management, the names of some of * the authors have unfortunately been lost. We do not mean to belittle * their efforts and hope they will contact us to see their names * properly added to the Copyright notice above. * Having published their contributions under the terms of the GNU * General Public License (GPL) [version 2], the Copyright of these * authors will remain respected by adhering to the license they chose * to publish their code under. */ #include #ifdef HAVE_WCHAR_H # include #endif #ifdef HAVE_WCTYPE_H # include #endif #include #include #include #include #include #include #ifdef WIN32 # define WIN32_LEAN_AND_MEAN # include #endif #include "../../debug.h" #include "coding.h" /* function changes #10 #13 chars to \n \r */ unsigned char *EncodeUnicodeSpecialChars(unsigned char *dest, const unsigned char *buffer) { int Pos=0, Pos2=0; while (buffer[Pos*2]!=0x00 || buffer[Pos*2+1]!=0x00) { if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == 10) { dest[Pos2*2] = 0x00; dest[Pos2*2+1] = '\\'; Pos2++; dest[Pos2*2] = 0x00; dest[Pos2*2+1] = 'n'; Pos2++; } else if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == 13) { dest[Pos2*2] = 0x00; dest[Pos2*2+1] = '\\'; Pos2++; dest[Pos2*2] = 0x00; dest[Pos2*2+1] = 'r'; Pos2++; } else if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == '\\') { dest[Pos2*2] = 0x00; dest[Pos2*2+1] = '\\'; Pos2++; dest[Pos2*2] = 0x00; dest[Pos2*2+1] = '\\'; Pos2++; } else if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == ';') { dest[Pos2*2] = 0x00; dest[Pos2*2+1] = '\\'; Pos2++; dest[Pos2*2] = 0x00; dest[Pos2*2+1] = ';'; Pos2++; } else if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == ',') { dest[Pos2*2] = 0x00; dest[Pos2*2+1] = '\\'; Pos2++; dest[Pos2*2] = 0x00; dest[Pos2*2+1] = ','; Pos2++; } else { dest[Pos2*2] = buffer[Pos*2]; dest[Pos2*2+1] = buffer[Pos*2+1]; Pos2++; } Pos++; } dest[Pos2*2] = 0; dest[Pos2*2+1] = 0; return dest; } /* function changes #10 #13 chars to \n \r */ char *EncodeSpecialChars(char *dest, const char *buffer) { int Pos=0, Pos2=0; while (buffer[Pos]!=0x00) { switch (buffer[Pos]) { case 10: dest[Pos2++] = '\\'; dest[Pos2++] = 'n'; break; case 13: dest[Pos2++] = '\\'; dest[Pos2++] = 'r'; break; case '\\': dest[Pos2++] = '\\'; dest[Pos2++] = '\\'; break; default: dest[Pos2++] = buffer[Pos]; } Pos++; } dest[Pos2] = 0; return dest; } unsigned char *DecodeUnicodeSpecialChars(unsigned char *dest, const unsigned char *buffer) { int Pos=0, Pos2=0, level=0; while (buffer[Pos*2]!=0x00 || buffer[Pos*2+1]!=0x00) { dest[Pos2*2] = buffer[Pos*2]; dest[Pos2*2+1] = buffer[Pos*2+1]; switch (level) { case 0: if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == '\\') { level = 1; } else { Pos2++; } break; case 1: if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == 'n') { dest[Pos2*2] = 0; dest[Pos2*2+1] = 10; } else if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == 'r') { dest[Pos2*2] = 0; dest[Pos2*2+1] = 13; } else if (buffer[Pos*2] == 0x00 && buffer[Pos*2+1] == '\\') { dest[Pos2*2] = 0; dest[Pos2*2+1] = '\\'; } Pos2++; level = 0; } Pos++; } dest[Pos2*2] = 0; dest[Pos2*2+1] = 0; return dest; } char *DecodeSpecialChars(char *dest, const char *buffer) { int Pos=0, Pos2=0, level=0; while (buffer[Pos]!=0x00) { dest[Pos2] = buffer[Pos]; switch (level) { case 0: if (buffer[Pos] == '\\') { level = 1; } else { Pos2++; } break; case 1: if (buffer[Pos] == 'n') dest[Pos2] = 10; if (buffer[Pos] == 'r') dest[Pos2] = 13; if (buffer[Pos] == '\\') dest[Pos2] = '\\'; Pos2++; level = 0; } Pos++; } dest[Pos2] = 0; return dest; } size_t UnicodeLength(const unsigned char *str) { size_t len = 0; if (str == NULL) return 0; while(str[len*2] != 0 || str[len*2+1] != 0) len++; return len; } /* Convert Unicode char saved in src to dest */ int EncodeWithUnicodeAlphabet(const unsigned char *src, gammu_char_t *dest) { int retval; wchar_t out = 0; retval = mbtowc(&out, src, MB_CUR_MAX); *dest = out; switch (retval) { case -1 : case 0 : return 1; default : return retval; } } /* Convert Unicode char saved in src to dest */ int DecodeWithUnicodeAlphabet(gammu_char_t src, unsigned char *dest) { int retval; switch (retval = wctomb(dest, src)) { case -1: *dest = '?'; return 1; default: return retval; } } void DecodeUnicode (const unsigned char *src, char *dest) { int i=0,o=0; gammu_char_t value, second; while (src[(2*i)+1]!=0x00 || src[2*i]!=0x00) { value = src[i * 2] * 256 + src[i * 2 + 1]; /* Decode UTF-16 */ if (value >= 0xD800 && value <= 0xDBFF) { second = src[(i + 1) * 2] * 256 + src[(i + 1) * 2 + 1]; if (second >= 0xDC00 && second <= 0xDFFF) { value = ((value - 0xD800) << 10) + (second - 0xDC00) + 0x010000; i++; } else if (second == 0) { /* Surrogate at the end of string */ value = 0xFFFD; /* REPLACEMENT CHARACTER */ } } o += DecodeWithUnicodeAlphabet(value, dest + o); i++; } dest[o]=0; } /* Decode Unicode string and return as function result */ char *DecodeUnicodeString (const unsigned char *src) { static char dest[500]; DecodeUnicode(src,dest); return dest; } /* Decode Unicode string to UTF8 or other console charset * and return as function result */ char *DecodeUnicodeConsole(const unsigned char *src) { static char dest[500]; if (GSM_global_debug.coding[0] != 0) { if (!strcmp(GSM_global_debug.coding,"utf8")) { EncodeUTF8(dest, src); } else { #ifdef WIN32 setlocale(LC_ALL, GSM_global_debug.coding); #endif DecodeUnicode(src,dest); } } else { #ifdef WIN32 setlocale(LC_ALL, ".OCP"); #endif DecodeUnicode(src,dest); #ifdef WIN32 setlocale(LC_ALL, ".ACP"); #endif } return dest; } /* Encode string to Unicode. Len is number of input chars */ void DecodeISO88591 (unsigned char *dest, const char *src, size_t len) { size_t i; for (i = 0; i < len; i++) { /* Hack for Euro sign */ if ((unsigned char)src[i] == 0x80) { dest[2 * i] = 0x20; dest[(2 * i) + 1] = 0xac; } else { dest[2 * i] = 0; dest[(2 * i) + 1] = src[i]; } } dest[2 * i] = 0; dest[(2 * i) + 1] = 0; } /** * Stores UTF16 char in output * * Returns 1 if additional output was used */ size_t StoreUTF16 (unsigned char *dest, gammu_char_t wc) { gammu_char_t tmp; if (wc > 0xffff) { wc = wc - 0x10000; tmp = 0xD800 | (wc >> 10); dest[0] = (tmp >> 8) & 0xff; dest[1] = tmp & 0xff; tmp = 0xDC00 | (wc & 0x3ff); dest[2] = (tmp >> 8) & 0xff; dest[3] = tmp & 0xff; return 1; } dest[0] = (wc >> 8) & 0xff; dest[1] = wc & 0xff; return 0; } /* Encode string to Unicode. Len is number of input chars */ void EncodeUnicode (unsigned char *dest, const char *src, size_t len) { size_t i_len = 0, o_len; gammu_char_t wc; for (o_len = 0; i_len < len; o_len++) { i_len += EncodeWithUnicodeAlphabet(&src[i_len], &wc); if (StoreUTF16(dest + o_len * 2, wc)) { o_len++; } } dest[o_len*2] = 0; dest[(o_len*2)+1] = 0; } unsigned char EncodeWithBCDAlphabet(int value) { div_t division; division=div(value,10); return ( ( (value-division.quot*10) & 0x0f) << 4) | (division.quot & 0xf); } int DecodeWithBCDAlphabet(unsigned char value) { return 10*(value & 0x0f)+(value >> 4); } void DecodeBCD (unsigned char *dest, const unsigned char *src, size_t len) { size_t i,current=0; int digit; for (i = 0; i < len; i++) { digit=src[i] & 0x0f; if (digit<10) dest[current++]=digit + '0'; digit=src[i] >> 4; if (digit<10) dest[current++]=digit + '0'; } dest[current]=0; } void EncodeBCD (unsigned char *dest, const unsigned char *src, size_t len, gboolean fill) { size_t i,current=0; for (i = 0; i < len; i++) { if (i & 0x01) { dest[current]=dest[current] | ((src[i]-'0') << 4); current++; } else { dest[current]=src[i]-'0'; } } /* When fill is set: if number consist of odd number of digits, we fill last bits in last byte with 0x0f */ if (fill && (len & 0x01)) dest[current]=dest[current] | 0xf0; } int DecodeWithHexBinAlphabet (unsigned char mychar) { if (mychar >= 'A' && mychar <= 'F') return mychar - 'A' + 10; if (mychar >= 'a' && mychar <= 'f') return mychar - 'a' + 10; if (mychar >= '0' && mychar <= '9') return mychar - '0'; return -1; } char EncodeWithHexBinAlphabet (int digit) { if (digit >= 0 && digit <= 9) return '0'+(digit); if (digit >=10 && digit <=15) return 'A'+(digit-10); return 0; } gboolean DecodeHexUnicode (unsigned char *dest, const char *src, size_t len) { size_t i, current = 0; int val0, val1, val2, val3; for (i = 0; i < len ; i += 4) { val0 = DecodeWithHexBinAlphabet(src[i + 0]); val1 = DecodeWithHexBinAlphabet(src[i + 1]); val2 = DecodeWithHexBinAlphabet(src[i + 2]); val3 = DecodeWithHexBinAlphabet(src[i + 3]); if (val0 < 0 || val1 < 0 || val2 < 0 || val3 < 0) { return FALSE; } dest[current++] = (val0 << 4) + val1; dest[current++] = (val2 << 4) + val3; } dest[current++] = 0; dest[current] = 0; return TRUE; } void EncodeHexUnicode (char *dest, const unsigned char *src, size_t len) { EncodeHexBin(dest, src, len * 2); } gboolean DecodeHexBin (unsigned char *dest, const unsigned char *src, size_t len) { size_t i,current=0; int low, high; for (i = 0; i < len/2 ; i++) { low = DecodeWithHexBinAlphabet(src[i*2+1]); high = DecodeWithHexBinAlphabet(src[i*2]); if (low < 0 || high < 0) return FALSE; dest[current++] = (high << 4) | low; } dest[current] = 0; return TRUE; } void EncodeHexBin (char *dest, const unsigned char *src, size_t len) { size_t i, outpos = 0; for (i = 0; i < len; i++) { dest[outpos++] = EncodeWithHexBinAlphabet(src[i] >> 4); dest[outpos++] = EncodeWithHexBinAlphabet(src[i] & 0xF); } dest[outpos] = 0; } /* ETSI GSM 03.38, section 6.2.1: Default alphabet for SMS messages */ static unsigned char GSM_DefaultAlphabetUnicode[128+1][2] = { {0x00,0x40},{0x00,0xa3},{0x00,0x24},{0x00,0xA5}, {0x00,0xE8},{0x00,0xE9},{0x00,0xF9},{0x00,0xEC},/*0x08*/ {0x00,0xF2},{0x00,0xC7},{0x00,'\n'},{0x00,0xD8}, {0x00,0xF8},{0x00,'\r'},{0x00,0xC5},{0x00,0xE5}, {0x03,0x94},{0x00,0x5f},{0x03,0xA6},{0x03,0x93}, {0x03,0x9B},{0x03,0xA9},{0x03,0xA0},{0x03,0xA8}, {0x03,0xA3},{0x03,0x98},{0x03,0x9E},{0x00,0xb9}, {0x00,0xC6},{0x00,0xE6},{0x00,0xDF},{0x00,0xC9},/*0x20*/ {0x00,' ' },{0x00,'!' },{0x00,'\"'},{0x00,'#' }, {0x00,0xA4},{0x00,'%' },{0x00,'&' },{0x00,'\''}, {0x00,'(' },{0x00,')' },{0x00,'*' },{0x00,'+' }, {0x00,',' },{0x00,'-' },{0x00,'.' },{0x00,'/' },/*0x30*/ {0x00,'0' },{0x00,'1' },{0x00,'2' },{0x00,'3' }, {0x00,'4' },{0x00,'5' },{0x00,'6' },{0x00,'7' }, {0x00,'8' },{0x00,'9' },{0x00,':' },{0x00,';' }, {0x00,'<' },{0x00,'=' },{0x00,'>' },{0x00,'?' },/*0x40*/ {0x00,0xA1},{0x00,'A' },{0x00,'B' },{0x00,'C' }, {0x00,'D' },{0x00,'E' },{0x00,'F' },{0x00,'G' }, {0x00,'H' },{0x00,'I' },{0x00,'J' },{0x00,'K' }, {0x00,'L' },{0x00,'M' },{0x00,'N' },{0x00,'O' }, {0x00,'P' },{0x00,'Q' },{0x00,'R' },{0x00,'S' }, {0x00,'T' },{0x00,'U' },{0x00,'V' },{0x00,'W' }, {0x00,'X' },{0x00,'Y' },{0x00,'Z' },{0x00,0xC4}, {0x00,0xD6},{0x00,0xD1},{0x00,0xDC},{0x00,0xA7}, {0x00,0xBF},{0x00,'a' },{0x00,'b' },{0x00,'c' }, {0x00,'d' },{0x00,'e' },{0x00,'f' },{0x00,'g' }, {0x00,'h' },{0x00,'i' },{0x00,'j' },{0x00,'k' }, {0x00,'l' },{0x00,'m' },{0x00,'n' },{0x00,'o' }, {0x00,'p' },{0x00,'q' },{0x00,'r' },{0x00,'s' }, {0x00,'t' },{0x00,'u' },{0x00,'v' },{0x00,'w' }, {0x00,'x' },{0x00,'y' },{0x00,'z' },{0x00,0xE4}, {0x00,0xF6},{0x00,0xF1},{0x00,0xFC},{0x00,0xE0}, {0x00,0x00} }; /* ETSI GSM 3.38 * Some sequences of 2 default alphabet chars (for example, * 0x1b, 0x65) are visible as one single additional char (for example, * 0x1b, 0x65 gives Euro char saved in Unicode as 0x20, 0xAC) * This table contains: * 1. two first char means second char from the sequence of chars from GSM default alphabet (first being 0x1b) * 2. two second is target (encoded) char saved in Unicode */ static unsigned char GSM_DefaultAlphabetCharsExtension[][3] = { {0x0a,0x00,0x0c}, /* \r */ {0x14,0x00,0x5e}, /* ^ */ {0x28,0x00,0x7b}, /* { */ {0x29,0x00,0x7d}, /* } */ {0x2f,0x00,0x5c}, /* \ */ {0x3c,0x00,0x5b}, /* [ */ {0x3d,0x00,0x7E}, /* ~ */ {0x3e,0x00,0x5d}, /* ] */ {0x40,0x00,0x7C}, /* | */ {0x65,0x20,0xAC}, /* Euro */ {0x00,0x00,0x00} }; void DecodeDefault (unsigned char *dest, const unsigned char *src, size_t len, gboolean UseExtensions, unsigned char *ExtraAlphabet) { size_t pos, current = 0, i; #ifdef DEBUG DumpMessageText(&GSM_global_debug, src, len); #endif for (pos = 0; pos < len; pos++) { if ((pos < (len - 1)) && UseExtensions && src[pos] == 0x1b) { for (i = 0; GSM_DefaultAlphabetCharsExtension[i][0] != 0x00; i++) { if (GSM_DefaultAlphabetCharsExtension[i][0] == src[pos + 1]) { dest[current++] = GSM_DefaultAlphabetCharsExtension[i][1]; dest[current++] = GSM_DefaultAlphabetCharsExtension[i][2]; pos++; break; } } /* Skip rest if we've found something */ if (GSM_DefaultAlphabetCharsExtension[i][0] != 0x00) { continue; } } if (ExtraAlphabet != NULL) { for (i = 0; ExtraAlphabet[i] != 0x00; i += 3) { if (ExtraAlphabet[i] == src[pos]) { dest[current++] = ExtraAlphabet[i + 1]; dest[current++] = ExtraAlphabet[i + 2]; break; } } /* Skip rest if we've found something */ if (ExtraAlphabet[i] != 0x00) { continue; } } dest[current++] = GSM_DefaultAlphabetUnicode[src[pos]][0]; dest[current++] = GSM_DefaultAlphabetUnicode[src[pos]][1]; } dest[current++]=0; dest[current]=0; #ifdef DEBUG DumpMessageText(&GSM_global_debug, dest, UnicodeLength(dest)*2); #endif } /* There are many national chars with "adds". In phone they're normally * changed to "plain" Latin chars. We have such functionality too. * This table is automatically created from convert.txt file (see * /docs/developers) using --makeconverttable. It contains such chars * to replace in order: * 1. original char (Unicode) 2. destination char (Unicode) */ static unsigned char ConvertTable[] = "\x00\xc0\x00\x41" \ "\x00\xe0\x00\x61" \ "\x00\xc1\x00\x41" \ "\x00\xe1\x00\x61" \ "\x00\xc2\x00\x41" \ "\x00\xe2\x00\x61" \ "\x00\xc3\x00\x41" \ "\x00\xe3\x00\x61" \ "\x1e\xa0\x00\x41" \ "\x1e\xa1\x00\x61" \ "\x1e\xa2\x00\x41" \ "\x1e\xa3\x00\x61" \ "\x1e\xa4\x00\x41" \ "\x1e\xa5\x00\x61" \ "\x1e\xa6\x00\x41" \ "\x1e\xa7\x00\x61" \ "\x1e\xa8\x00\x41" \ "\x1e\xa9\x00\x61" \ "\x1e\xaa\x00\x41" \ "\x1e\xab\x00\x61" \ "\x1e\xac\x00\x41" \ "\x1e\xad\x00\x61" \ "\x1e\xae\x00\x41" \ "\x1e\xaf\x00\x61" \ "\x1e\xb0\x00\x41" \ "\x1e\xb1\x00\x61" \ "\x1e\xb2\x00\x41" \ "\x1e\xb3\x00\x61" \ "\x1e\xb4\x00\x41" \ "\x1e\xb5\x00\x61" \ "\x1e\xb6\x00\x41" \ "\x1e\xb7\x00\x61" \ "\x01\xcd\x00\x41" \ "\x01\xce\x00\x61" \ "\x01\x00\x00\x41" \ "\x01\x01\x00\x61" \ "\x01\x02\x00\x41" \ "\x01\x03\x00\x61" \ "\x01\x04\x00\x41" \ "\x01\x05\x00\x61" \ "\x01\xfb\x00\x61" \ "\x01\x06\x00\x43" \ "\x01\x07\x00\x63" \ "\x01\x08\x00\x43" \ "\x01\x09\x00\x63" \ "\x01\x0a\x00\x43" \ "\x01\x0b\x00\x63" \ "\x01\x0c\x00\x43" \ "\x01\x0d\x00\x63" \ "\x00\xe7\x00\x63" \ "\x01\x0e\x00\x44" \ "\x01\x0f\x00\x64" \ "\x01\x10\x00\x44" \ "\x01\x11\x00\x64" \ "\x00\xc8\x00\x45" \ "\x00\xca\x00\x45" \ "\x00\xea\x00\x65" \ "\x00\xcb\x00\x45" \ "\x00\xeb\x00\x65" \ "\x1e\xb8\x00\x45" \ "\x1e\xb9\x00\x65" \ "\x1e\xba\x00\x45" \ "\x1e\xbb\x00\x65" \ "\x1e\xbc\x00\x45" \ "\x1e\xbd\x00\x65" \ "\x1e\xbe\x00\x45" \ "\x1e\xbf\x00\x65" \ "\x1e\xc0\x00\x45" \ "\x1e\xc1\x00\x65" \ "\x1e\xc2\x00\x45" \ "\x1e\xc3\x00\x65" \ "\x1e\xc4\x00\x45" \ "\x1e\xc5\x00\x65" \ "\x1e\xc6\x00\x45" \ "\x1e\xc7\x00\x65" \ "\x01\x12\x00\x45" \ "\x01\x13\x00\x65" \ "\x01\x14\x00\x45" \ "\x01\x15\x00\x65" \ "\x01\x16\x00\x45" \ "\x01\x17\x00\x65" \ "\x01\x18\x00\x45" \ "\x01\x19\x00\x65" \ "\x01\x1a\x00\x45" \ "\x01\x1b\x00\x65" \ "\x01\x1c\x00\x47" \ "\x01\x1d\x00\x67" \ "\x01\x1e\x00\x47" \ "\x01\x1f\x00\x67" \ "\x01\x20\x00\x47" \ "\x01\x21\x00\x67" \ "\x01\x22\x00\x47" \ "\x01\x23\x00\x67" \ "\x01\x24\x00\x48" \ "\x01\x25\x00\x68" \ "\x01\x26\x00\x48" \ "\x01\x27\x00\x68" \ "\x00\xcc\x00\x49" \ "\x00\xcd\x00\x49" \ "\x00\xed\x00\x69" \ "\x00\xce\x00\x49" \ "\x00\xee\x00\x69" \ "\x00\xcf\x00\x49" \ "\x00\xef\x00\x69" \ "\x01\x28\x00\x49" \ "\x01\x29\x00\x69" \ "\x01\x2a\x00\x49" \ "\x01\x2b\x00\x69" \ "\x01\x2c\x00\x49" \ "\x01\x2d\x00\x69" \ "\x01\x2e\x00\x49" \ "\x01\x2f\x00\x69" \ "\x01\x30\x00\x49" \ "\x01\x31\x00\x69" \ "\x01\xcf\x00\x49" \ "\x01\xd0\x00\x69" \ "\x1e\xc8\x00\x49" \ "\x1e\xc9\x00\x69" \ "\x1e\xca\x00\x49" \ "\x1e\xcb\x00\x69" \ "\x01\x34\x00\x4a" \ "\x01\x35\x00\x6a" \ "\x01\x36\x00\x4b" \ "\x01\x37\x00\x6b" \ "\x01\x39\x00\x4c" \ "\x01\x3a\x00\x6c" \ "\x01\x3b\x00\x4c" \ "\x01\x3c\x00\x6c" \ "\x01\x3d\x00\x4c" \ "\x01\x3e\x00\x6c" \ "\x01\x3f\x00\x4c" \ "\x01\x40\x00\x6c" \ "\x01\x41\x00\x4c" \ "\x01\x42\x00\x6c" \ "\x01\x43\x00\x4e" \ "\x01\x44\x00\x6e" \ "\x01\x45\x00\x4e" \ "\x01\x46\x00\x6e" \ "\x01\x47\x00\x4e" \ "\x01\x48\x00\x6e" \ "\x01\x49\x00\x6e" \ "\x00\xd2\x00\x4f" \ "\x00\xd3\x00\x4f" \ "\x00\xf3\x00\x6f" \ "\x00\xd4\x00\x4f" \ "\x00\xf4\x00\x6f" \ "\x00\xd5\x00\x4f" \ "\x00\xf5\x00\x6f" \ "\x01\x4c\x00\x4f" \ "\x01\x4d\x00\x6f" \ "\x01\x4e\x00\x4f" \ "\x01\x4f\x00\x6f" \ "\x01\x50\x00\x4f" \ "\x01\x51\x00\x6f" \ "\x01\xa0\x00\x4f" \ "\x01\xa1\x00\x6f" \ "\x01\xd1\x00\x4f" \ "\x01\xd2\x00\x6f" \ "\x1e\xcc\x00\x4f" \ "\x1e\xcd\x00\x6f" \ "\x1e\xce\x00\x4f" \ "\x1e\xcf\x00\x6f" \ "\x1e\xd0\x00\x4f" \ "\x1e\xd1\x00\x6f" \ "\x1e\xd2\x00\x4f" \ "\x1e\xd3\x00\x6f" \ "\x1e\xd4\x00\x4f" \ "\x1e\xd5\x00\x6f" \ "\x1e\xd6\x00\x4f" \ "\x1e\xd7\x00\x6f" \ "\x1e\xd8\x00\x4f" \ "\x1e\xd9\x00\x6f" \ "\x1e\xda\x00\x4f" \ "\x1e\xdb\x00\x6f" \ "\x1e\xdc\x00\x4f" \ "\x1e\xdd\x00\x6f" \ "\x1e\xde\x00\x4f" \ "\x1e\xdf\x00\x6f" \ "\x1e\xe0\x00\x4f" \ "\x1e\xe1\x00\x6f" \ "\x1e\xe2\x00\x4f" \ "\x1e\xe3\x00\x6f" \ "\x01\x54\x00\x52" \ "\x01\x55\x00\x72" \ "\x01\x56\x00\x52" \ "\x01\x57\x00\x72" \ "\x01\x58\x00\x52" \ "\x01\x59\x00\x72" \ "\x01\x5a\x00\x53" \ "\x01\x5b\x00\x73" \ "\x01\x5c\x00\x53" \ "\x01\x5d\x00\x73" \ "\x01\x5e\x00\x53" \ "\x01\x5f\x00\x73" \ "\x01\x60\x00\x53" \ "\x01\x61\x00\x73" \ "\x01\x62\x00\x54" \ "\x01\x63\x00\x74" \ "\x01\x64\x00\x54" \ "\x01\x65\x00\x74" \ "\x01\x66\x00\x54" \ "\x01\x67\x00\x74" \ "\x00\xd9\x00\x55" \ "\x00\xda\x00\x55" \ "\x00\xfa\x00\x75" \ "\x00\xdb\x00\x55" \ "\x00\xfb\x00\x75" \ "\x01\x68\x00\x55" \ "\x01\x69\x00\x75" \ "\x01\x6a\x00\x55" \ "\x01\x6b\x00\x75" \ "\x01\x6c\x00\x55" \ "\x01\x6d\x00\x75" \ "\x01\x6e\x00\x55" \ "\x01\x6f\x00\x75" \ "\x01\x70\x00\x55" \ "\x01\x71\x00\x75" \ "\x01\x72\x00\x55" \ "\x01\x73\x00\x75" \ "\x01\xaf\x00\x55" \ "\x01\xb0\x00\x75" \ "\x01\xd3\x00\x55" \ "\x01\xd4\x00\x75" \ "\x01\xd5\x00\x55" \ "\x01\xd6\x00\x75" \ "\x01\xd7\x00\x55" \ "\x01\xd8\x00\x75" \ "\x01\xd9\x00\x55" \ "\x01\xda\x00\x75" \ "\x01\xdb\x00\x55" \ "\x01\xdc\x00\x75" \ "\x1e\xe4\x00\x55" \ "\x1e\xe5\x00\x75" \ "\x1e\xe6\x00\x55" \ "\x1e\xe7\x00\x75" \ "\x1e\xe8\x00\x55" \ "\x1e\xe9\x00\x75" \ "\x1e\xea\x00\x55" \ "\x1e\xeb\x00\x75" \ "\x1e\xec\x00\x55" \ "\x1e\xed\x00\x75" \ "\x1e\xee\x00\x55" \ "\x1e\xef\x00\x75" \ "\x1e\xf0\x00\x55" \ "\x1e\xf1\x00\x75" \ "\x01\x74\x00\x57" \ "\x01\x75\x00\x77" \ "\x1e\x80\x00\x57" \ "\x1e\x81\x00\x77" \ "\x1e\x82\x00\x57" \ "\x1e\x83\x00\x77" \ "\x1e\x84\x00\x57" \ "\x1e\x85\x00\x77" \ "\x00\xdd\x00\x59" \ "\x00\xfd\x00\x79" \ "\x00\xff\x00\x79" \ "\x01\x76\x00\x59" \ "\x01\x77\x00\x79" \ "\x01\x78\x00\x59" \ "\x1e\xf2\x00\x59" \ "\x1e\xf3\x00\x75" \ "\x1e\xf4\x00\x59" \ "\x1e\xf5\x00\x79" \ "\x1e\xf6\x00\x59" \ "\x1e\xf7\x00\x79" \ "\x1e\xf8\x00\x59" \ "\x1e\xf9\x00\x79" \ "\x01\x79\x00\x5a" \ "\x01\x7a\x00\x7a" \ "\x01\x7b\x00\x5a" \ "\x01\x7c\x00\x7a" \ "\x01\x7d\x00\x5a" \ "\x01\x7e\x00\x7a" \ "\x01\xfc\x00\xc6" \ "\x01\xfd\x00\xe6" \ "\x01\xfe\x00\xd8" \ "\x01\xff\x00\xf8" \ "\x00\x00"; void EncodeDefault(unsigned char *dest, const unsigned char *src, size_t *len, gboolean UseExtensions, unsigned char *ExtraAlphabet) { size_t i,current=0; int j,z; char ret; gboolean FoundSpecial,FoundNormal; #ifdef DEBUG DumpMessageText(&GSM_global_debug, src, (*len)*2); #endif for (i = 0; i < *len; i++) { FoundSpecial = FALSE; j = 0; while (GSM_DefaultAlphabetCharsExtension[j][0]!=0x00 && UseExtensions) { if (src[i*2] == GSM_DefaultAlphabetCharsExtension[j][1] && src[i*2+1] == GSM_DefaultAlphabetCharsExtension[j][2]) { dest[current++] = 0x1b; dest[current++] = GSM_DefaultAlphabetCharsExtension[j][0]; FoundSpecial = TRUE; break; } j++; } if (!FoundSpecial) { ret = '?'; FoundNormal = FALSE; j = 0; while (GSM_DefaultAlphabetUnicode[j][1]!=0x00) { if (src[i*2] == GSM_DefaultAlphabetUnicode[j][0] && src[i*2+1] == GSM_DefaultAlphabetUnicode[j][1]) { ret = j; FoundNormal = TRUE; break; } j++; } if (ExtraAlphabet!=NULL && !FoundNormal) { j = 0; while (ExtraAlphabet[j] != 0x00 || ExtraAlphabet[j+1] != 0x00 || ExtraAlphabet[j+2] != 0x00) { if (ExtraAlphabet[j+1] == src[i*2] && ExtraAlphabet[j+2] == src[i*2 + 1]) { ret = ExtraAlphabet[j]; FoundSpecial = TRUE; break; } j=j+3; } } if (!FoundNormal && !FoundSpecial) { j = 0; FoundNormal = FALSE; while (ConvertTable[j*4] != 0x00 || ConvertTable[j*4+1] != 0x00) { if (src[i*2] == ConvertTable[j*4] && src[i*2+1] == ConvertTable[j*4+1]) { z = 0; while (GSM_DefaultAlphabetUnicode[z][1]!=0x00) { if (ConvertTable[j*4+2] == GSM_DefaultAlphabetUnicode[z][0] && ConvertTable[j*4+3] == GSM_DefaultAlphabetUnicode[z][1]) { ret = z; FoundNormal = TRUE; break; } z++; } if (FoundNormal) break; } j++; } } dest[current++]=ret; } } dest[current]=0; #ifdef DEBUG DumpMessageText(&GSM_global_debug, dest, current); #endif *len = current; } /* You don't have to use ConvertTable here - 1 char is replaced there by 1 char */ void FindDefaultAlphabetLen(const unsigned char *src, size_t *srclen, size_t *smslen, size_t maxlen) { size_t current=0,j,i; gboolean FoundSpecial; i = 0; while (src[i*2] != 0x00 || src[i*2+1] != 0x00) { FoundSpecial = FALSE; j = 0; while (GSM_DefaultAlphabetCharsExtension[j][0]!=0x00) { if (src[i*2] == GSM_DefaultAlphabetCharsExtension[j][1] && src[i*2+1] == GSM_DefaultAlphabetCharsExtension[j][2]) { FoundSpecial = TRUE; if (current+2 > maxlen) { *srclen = i; *smslen = current; return; } current+=2; break; } j++; } if (!FoundSpecial) { if (current+1 > maxlen) { *srclen = i; *smslen = current; return; } current++; } i++; } *srclen = i; *smslen = current; } #define ByteMask ((1 << Bits) - 1) int GSM_UnpackEightBitsToSeven(size_t offset, size_t in_length, size_t out_length, const unsigned char *input, unsigned char *output) { /* (c) by Pavel Janik and Pawel Kot */ unsigned char *output_pos = output; /* Current pointer to the output buffer */ const unsigned char *input_pos = input; /* Current pointer to the input buffer */ unsigned char Rest = 0x00; size_t Bits; Bits = offset ? offset : 7; while ((size_t)(input_pos - input) < in_length) { *output_pos = ((*input_pos & ByteMask) << (7 - Bits)) | Rest; Rest = *input_pos >> Bits; /* If we don't start from 0th bit, we shouldn't go to the next char. Under *output_pos we have now 0 and under Rest - _first_ part of the char. */ if ((input_pos != input) || (Bits == 7)) output_pos++; input_pos++; if ((size_t)(output_pos - 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) { *output_pos = Rest; output_pos++; Bits = 7; Rest = 0x00; } else { Bits--; } } return output_pos - output; } int GSM_PackSevenBitsToEight(size_t offset, const unsigned char *input, unsigned char *output, size_t length) { /* (c) by Pavel Janik and Pawel Kot */ unsigned char *output_pos = output; /* Current pointer to the output buffer */ const unsigned char *input_pos = input; /* Current pointer to the input buffer */ int Bits; /* Number of bits directly copied to * the output buffer */ Bits = (7 + offset) % 8; /* If we don't begin with 0th bit, we will write only a part of the first octet */ if (offset) { *output_pos = 0x00; output_pos++; } while ((size_t)(input_pos - input) < length) { unsigned char Byte = *input_pos; *output_pos = Byte >> (7 - Bits); /* If we don't write at 0th bit of the octet, we should write a second part of the previous octet */ if (Bits != 7) *(output_pos-1) |= (Byte & ((1 << (7-Bits)) - 1)) << (Bits+1); Bits--; if (Bits == -1) Bits = 7; else output_pos++; input_pos++; } return (output_pos - output); } GSM_Error GSM_UnpackSemiOctetNumber(GSM_Debug_Info *di, unsigned char *retval, const unsigned char *Number, size_t *pos, size_t bufferlength, gboolean semioctet) { unsigned char Buffer[GSM_MAX_NUMBER_LENGTH + 1]; size_t length = Number[*pos]; GSM_Error ret = ERR_NONE; smfprintf(di, "Number Length=%ld\n", (long)length); if (length == 0) { strcpy(Buffer, ""); goto out; } /* Default ouput on error */ strcpy(Buffer, ""); if (length > bufferlength) { smfprintf(di, "Number too long!\n"); return ERR_UNKNOWN; } if (semioctet) { /* Convert number of semioctets to number of chars */ if (length % 2) length++; length=length / 2 + 1; } /* Check length */ if (length > GSM_MAX_NUMBER_LENGTH) { smfprintf(di, "Number too big, not decoding! (Length=%ld, MAX=%d)\n", (long)length, GSM_MAX_NUMBER_LENGTH); ret = ERR_UNKNOWN; goto out; } /*without leading byte with format of number*/ length--; smfprintf(di, "Number type %02x (%d %d %d %d|%d %d %d %d)\n", Number[*pos + 1], Number[*pos + 1] & 0x80 ? 1 : 0, Number[*pos + 1] & 0x40 ? 1 : 0, Number[*pos + 1] & 0x20 ? 1 : 0, Number[*pos + 1] & 0x10 ? 1 : 0, Number[*pos + 1] & 0x08 ? 1 : 0, Number[*pos + 1] & 0x04 ? 1 : 0, Number[*pos + 1] & 0x02 ? 1 : 0, Number[*pos + 1] & 0x01 ? 1 : 0 ); if ((Number[*pos + 1] & 0x80) == 0) { smfprintf(di, "Numbering plan not supported!\n"); ret = ERR_UNKNOWN; goto out; } switch ((Number[*pos + 1] & 0x70)) { case (NUMBER_ALPHANUMERIC_NUMBERING_PLAN_UNKNOWN & 0x70): if (length > 6) length++; smfprintf(di, "Alphanumeric number, length %ld\n", (long)length); GSM_UnpackEightBitsToSeven(0, length, length, Number+*pos+2, Buffer); Buffer[length]=0; break; case (NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN & 0x70): smfprintf(di, "International number\n"); Buffer[0]='+'; DecodeBCD(Buffer+1,Number+*pos+2, length); break; default: DecodeBCD (Buffer, Number+*pos+2, length); break; } smfprintf(di, "Len %ld\n", (long)length); out: EncodeUnicode(retval,Buffer,strlen(Buffer)); if (semioctet) { *pos += 2 + ((Number[*pos] + 1) / 2); } else { *pos += 1 + Number[*pos]; } return ret; } /** * Packing some phone numbers (SMSC, SMS destination and others) * * See GSM 03.40 9.1.1: * 1 byte - length of number given in semioctets or bytes (when given in * bytes, includes one byte for byte with number format). * Returned by function (set semioctet to TRUE, if want result * in semioctets). * 1 byte - format of number (see GSM_NumberType in coding.h). Returned * in unsigned char *Output. * n bytes - 2n or 2n-1 semioctets with number. Returned in unsigned char * *Output. * * 1 semioctet = 4 bits = half of byte */ int GSM_PackSemiOctetNumber(const unsigned char *Number, unsigned char *Output, gboolean semioctet) { unsigned char format; size_t length, i, skip = 0; unsigned char *buffer; length = UnicodeLength(Number); buffer = (unsigned char*)malloc(length + 2); if (buffer == NULL) { return 0; } DecodeUnicode(Number, buffer); /* Checking for format number */ if (buffer[0] == '+') { format = NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN; skip = 1; } else if (buffer[0] == '0' && buffer[1] == '0' && buffer[2] == '0') { /* Most likely local provider number */ format = NUMBER_UNKNOWN_NUMBERING_PLAN_ISDN; } else if (buffer[0] == '0' && buffer[1] == '0') { format = NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN; skip = 2; } else if (buffer[0] == '+' && buffer[1] == '0' && buffer[2] == '0') { /* This is obviously wrong, but try to cope with that */ format = NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN; skip = 3; } else { format = NUMBER_UNKNOWN_NUMBERING_PLAN_ISDN; } for (i = 0; i < length; i++) { /* If there is something which can not be in normal * number, mark it as alphanumberic */ if (strchr("+0123456789*#pP", buffer[i]) == NULL) { format = NUMBER_ALPHANUMERIC_NUMBERING_PLAN_UNKNOWN; } } /** * First byte is used for saving type of number. See GSM 03.40 * section 9.1.2.5 */ Output[0]=format; /* After number type we will have number. GSM 03.40 section 9.1.2 */ switch (format) { case NUMBER_ALPHANUMERIC_NUMBERING_PLAN_UNKNOWN: length=GSM_PackSevenBitsToEight(0, buffer, Output+1, strlen(buffer))*2; if (strlen(buffer)==7) length--; break; case NUMBER_INTERNATIONAL_NUMBERING_PLAN_ISDN: length -= skip; EncodeBCD (Output+1, buffer + skip, length, TRUE); break; default: EncodeBCD (Output+1, buffer, length, TRUE); break; } free(buffer); if (semioctet) return length; /* Convert number of semioctets to number of chars */ if (length % 2) length++; return length / 2 + 1; } void CopyUnicodeString(unsigned char *Dest, const unsigned char *Source) { int j = 0; /* No need to copy if both are on same address */ if (Dest == Source) return; while (Source[j]!=0x00 || Source[j+1]!=0x00) { Dest[j] = Source[j]; Dest[j+1] = Source[j+1]; j=j+2; } Dest[j] = 0; Dest[j+1] = 0; } /* Changes minor/major order in Unicode string */ void ReverseUnicodeString(unsigned char *String) { int j = 0; unsigned char byte1, byte2; while (String[j]!=0x00 || String[j+1]!=0x00) { byte1 = String[j]; byte2 = String[j+1]; String[j+1] = byte1; String[j] = byte2; j=j+2; } String[j] = 0; String[j+1] = 0; } /* All input is in Unicode. First char can show Unicode minor/major order. Output is Unicode string in Gammu minor/major order */ void ReadUnicodeFile(unsigned char *Dest, const unsigned char *Source) { int j = 0, current = 0; if (Source[0] == 0xFF && Source[1] == 0xFE) j = 2; if (Source[0] == 0xFE && Source[1] == 0xFF) j = 2; while (Source[j]!=0x00 || Source[j+1]!=0x00) { if (Source[0] == 0xFF) { Dest[current++] = Source[j+1]; Dest[current++] = Source[j]; } else { Dest[current++] = Source[j]; Dest[current++] = Source[j+1]; } j=j+2; } Dest[current++] = 0; Dest[current] = 0; } int GetBit(unsigned char *Buffer, size_t BitNum) { return Buffer[BitNum / 8] & (1 << (7 - (BitNum % 8))); } int SetBit(unsigned char *Buffer, size_t BitNum) { return Buffer[BitNum / 8] |= 1 << (7 - (BitNum % 8)); } int ClearBit(unsigned char *Buffer, size_t BitNum) { return Buffer[BitNum / 8] &= 255 - (1 << (7 - (BitNum % 8))); } void BufferAlign(unsigned char *Destination, size_t *CurrentBit) { int i=0; while(((*CurrentBit) + i) % 8 != 0) { ClearBit(Destination, (*CurrentBit)+i); i++; } (*CurrentBit) = (*CurrentBit) + i; } void BufferAlignNumber(size_t *CurrentBit) { int i=0; while(((*CurrentBit) + i) % 8 != 0) { i++; } (*CurrentBit) = (*CurrentBit) + i; } void AddBuffer(unsigned char *Destination, size_t *CurrentBit, unsigned char *Source, size_t BitsToProcess) { size_t i; for (i = 0; i < BitsToProcess; i++) { if (GetBit(Source, i)) { SetBit(Destination, (*CurrentBit)+i); } else { ClearBit(Destination, (*CurrentBit)+i); } } (*CurrentBit) = (*CurrentBit) + BitsToProcess; } void AddBufferByte(unsigned char *Destination, size_t *CurrentBit, unsigned char Source, size_t BitsToProcess) { AddBuffer(Destination, CurrentBit, &Source, BitsToProcess); } void GetBuffer(unsigned char *Source, size_t *CurrentBit, unsigned char *Destination, size_t BitsToProcess) { size_t i=0; while (i!=BitsToProcess) { if (GetBit(Source, (*CurrentBit)+i)) { SetBit(Destination, i); } else { ClearBit(Destination, i); } i++; } (*CurrentBit) = (*CurrentBit) + BitsToProcess; } void GetBufferInt(unsigned char *Source, size_t *CurrentBit, int *integer, size_t BitsToProcess) { size_t l=0,z=128,i=0; while (i!=BitsToProcess) { if (GetBit(Source, (*CurrentBit)+i)) l=l+z; z=z/2; i++; } *integer=l; (*CurrentBit) = (*CurrentBit) + i; } void GetBufferI(unsigned char *Source, size_t *CurrentBit, int *result, size_t BitsToProcess) { size_t l=0,z,i=0; z = 1 << (BitsToProcess - 1); while (i!=BitsToProcess) { if (GetBit(Source, (*CurrentBit)+i)) l=l+z; z=z>>1; i++; } *result=l; (*CurrentBit) = (*CurrentBit) + i; } /* Unicode char 0x00 0x01 makes blinking in some Nokia phones. * We replace single ~ chars into it. When user give double ~, it's replaced * to single ~ */ void EncodeUnicodeSpecialNOKIAChars(unsigned char *dest, const unsigned char *src, size_t len) { size_t i,current = 0; gboolean special=FALSE; for (i = 0; i < len; i++) { if (special) { if (src[i*2] == 0x00 && src[i*2+1] == '~') { dest[current++] = 0x00; dest[current++] = '~'; } else { dest[current++] = 0x00; dest[current++] = 0x01; dest[current++] = src[i*2]; dest[current++] = src[i*2+1]; } special = FALSE; } else { if (src[i*2] == 0x00 && src[i*2+1] == '~') { special = TRUE; } else { dest[current++] = src[i*2]; dest[current++] = src[i*2+1]; } } } if (special) { dest[current++] = 0x00; dest[current++] = 0x01; } dest[current++] = 0x00; dest[current] = 0x00; } void DecodeUnicodeSpecialNOKIAChars(unsigned char *dest, const unsigned char *src, size_t len) { size_t i=0,current=0; for (i=0;i 0 && OutBuffer[pos - 1] == '=') { pos--; OutBuffer[pos] = 0; skip = TRUE; was_cr = (Buffer[*Pos] == 0x0d); was_lf = (Buffer[*Pos] == 0x0a); break; } /* (vCard continuation) Next line start with space? */ tmp = *Pos + 1; if (Buffer[*Pos + 1] == 0x0a || Buffer[*Pos + 1] == 0x0d) { tmp += 1; } if (Buffer[tmp] == ' ') { *Pos = tmp; break; } /* We ignore empty lines in this mode */ if (pos == 0) { continue; } } if (Buffer[*Pos] == 0x0d && (*Pos)+1 < MaxLen && Buffer[*Pos + 1] == 0x0a) { /* Skip \r\n */ (*Pos) += 2; } else { /* Skip single \r or \n */ (*Pos)++; } return ERR_NONE; } break; default: /* Detect quoted printable for possible escaping */ if (Buffer[*Pos] == ':' && strstr(OutBuffer, ";ENCODING=QUOTED-PRINTABLE") != NULL) { quoted_printable = TRUE; } skip = FALSE; OutBuffer[pos] = Buffer[*Pos]; pos++; OutBuffer[pos] = 0; if (pos + 1 >= MaxOutLen) return ERR_MOREMEMORY; } } return ERR_NONE; } GSM_Error GSM_GetVCSLine(char **OutBuffer, char *Buffer, size_t *Pos, size_t MaxLen, gboolean MergeLines) { gboolean skip = FALSE; gboolean quoted_printable = FALSE; gboolean was_cr = FALSE, was_lf = FALSE; size_t pos=0; int tmp=0; size_t OutLen = 200; *OutBuffer = (char *)malloc(OutLen); if (*OutBuffer == NULL) return ERR_MOREMEMORY; (*OutBuffer)[0] = 0; pos = 0; if (Buffer == NULL) return ERR_NONE; while ((*Pos) < MaxLen) { switch (Buffer[*Pos]) { case 0x00: return ERR_NONE; case 0x0A: case 0x0D: if (skip) { if (Buffer[*Pos] == 0x0d) { if (was_cr && skip) return ERR_NONE; was_cr = TRUE; } else { if (was_lf && skip) return ERR_NONE; was_lf = TRUE; } } if (pos != 0 && !skip) { if (MergeLines) { /* (Quote printable new line) Does string end with = ? */ if ((*OutBuffer)[pos - 1] == '=' && quoted_printable) { pos--; (*OutBuffer)[pos] = 0; skip = TRUE; was_cr = (Buffer[*Pos] == 0x0d); was_lf = (Buffer[*Pos] == 0x0a); break; } /* (vCard continuation) Next line start with space? */ tmp = *Pos + 1; if (Buffer[*Pos + 1] == 0x0a || Buffer[*Pos + 1] == 0x0d) { tmp += 1; } if (Buffer[tmp] == ' ') { *Pos = tmp; break; } } return ERR_NONE; } break; default: /* Detect quoted printable for possible escaping */ if (Buffer[*Pos] == ':' && strstr(*OutBuffer, ";ENCODING=QUOTED-PRINTABLE") != NULL) { quoted_printable = TRUE; } skip = FALSE; (*OutBuffer)[pos] = Buffer[*Pos]; pos++; (*OutBuffer)[pos] = 0; if (pos + 2 >= OutLen) { OutLen += 100; *OutBuffer = (char *)realloc(*OutBuffer, OutLen); if (*OutBuffer == NULL) return ERR_MOREMEMORY; } } (*Pos)++; } return ERR_NONE; } void StringToDouble(char *text, double *d) { gboolean before=TRUE; double multiply = 1; unsigned int i; *d = 0; for (i=0;i= 0xD800 && value <= 0xDBFF) { if ((i + 1) < len) { second = src[(i + 1) * 2] * 256 + src[(i + 1) * 2 + 1]; if (second >= 0xDC00 && second <= 0xDFFF) { value = ((value - 0xD800) << 10) + (second - 0xDC00) + 0x010000; } } else { /* Surrogate at the end of string */ value = 0xFFFD; /* REPLACEMENT CHARACTER */ } } z = EncodeWithUTF8Alphabet(value, mychar); if (z == 1 && mychar[0] < 32) { /* Need quoted printable for chars < 32 */ sprintf(dest + j, "=%02X", mychar[0]); j = j + 3; } else if (z == 1) { memcpy(dest + j, mychar, z); j += z; } else { /* Quoted printable unicode */ for (w = 0; w < z; w++) { sprintf(dest + j, "=%02X", mychar[w]); j = j + 3; } if (z > 1) { retval = TRUE; } } } dest[j] = 0; return retval; } gboolean EncodeUTF8(char *dest, const unsigned char *src) { size_t i, j=0, z, len; unsigned char mychar[8]; gboolean retval = FALSE; unsigned long value, second; len = UnicodeLength(src); for (i = 0; i < len; i++) { value = src[i * 2] * 256 + src[i * 2 + 1]; /* Decode UTF-16 */ if (value >= 0xD800 && value <= 0xDBFF ) { if ((i + 1) < len) { second = src[(i + 1) * 2] * 256 + src[(i + 1) * 2 + 1]; if (second >= 0xDC00 && second <= 0xDFFF) { i++; value = ((value - 0xD800) << 10) + (second - 0xDC00) + 0x010000; } } else { /* Surrogate at the end of string */ value = 0xFFFD; /* REPLACEMENT CHARACTER */ } } z = EncodeWithUTF8Alphabet(value, mychar); memcpy(dest + j, mychar, z); j += z; if (z > 1) { retval = TRUE; } } dest[j] = 0; return retval; } /* Decode UTF8 char to Unicode char */ int DecodeWithUTF8Alphabet(const unsigned char *src, gammu_char_t *dest, size_t len) { gammu_char_t src0, src1, src2, src3; if (len < 1) { return 0; } src0 = src[0]; // 1-byte sequence (no continuation bytes) if ((src0 & 0x80) == 0) { (*dest) = src0; return 1; } if (len < 2) { return 0; } src1 = src[1]; // 2-byte sequence if ((src0 & 0xE0) == 0xC0) { (*dest) = ((src0 & 0x1F) << 6) | (src1 & 0x3f); if (*dest >= 0x80) { return 2; } else { return 0; } } if (len < 3) { return 0; } src2 = src[2]; // 3-byte sequence (may include unpaired surrogates) if ((src0 & 0xF0) == 0xE0) { (*dest) = ((src0 & 0x0F) << 12) | ((src1 & 0x3f) << 6) | (src2 & 0x3f); if ((*dest) >= 0x0800) { if ((*dest) >= 0xD800 && (*dest) <= 0xDFFF) { return 0; } return 3; } } if (len < 4) { return 0; } src3 = src[3]; // 4-byte sequence if ((src0 & 0xF8) == 0xF0) { (*dest) = ((src0 & 0x07) << 0x12) | ((src1 & 0x3f) << 0x0C) | ((src2 & 0x3f) << 0x06) | (src3 & 0x3f); if ((*dest) >= 0x010000 && (*dest) <= 0x10FFFF) { return 4; } } return 0; } /* Make Unicode string from ISO-8859-1 string */ void DecodeISO88591QuotedPrintable(unsigned char *dest, const unsigned char *src, size_t len) { size_t i = 0, j = 0; while (i < len) { if (src[i] == '=' && i + 2 < len && DecodeWithHexBinAlphabet(src[i + 1]) != -1 && DecodeWithHexBinAlphabet(src[i + 2]) != -1) { dest[j++] = 0; dest[j++] = 16 * DecodeWithHexBinAlphabet(src[i + 1]) + DecodeWithHexBinAlphabet(src[i + 2]); i += 2; } else { dest[j++] = 0; dest[j++] = src[i]; } i++; } dest[j++] = 0; dest[j] = 0; } /* Make Unicode string from UTF8 string */ void DecodeUTF8QuotedPrintable(unsigned char *dest, const char *src, size_t len) { size_t i,j=0; int z; unsigned char mychar[10]; gammu_char_t ret; for (i = 0; i<=len; ) { z=0; while (TRUE) { if (src[z*3+i] != '=' || z*3+i+3>len || DecodeWithHexBinAlphabet(src[z*3+i+1])==-1 || DecodeWithHexBinAlphabet(src[z*3+i+2])==-1) { break; } mychar[z] = 16*DecodeWithHexBinAlphabet(src[z*3+i+1])+DecodeWithHexBinAlphabet(src[z*3+i+2]); z++; /* Is it plain ASCII? */ if (z == 1 && mychar[0] < 194) break; /* Do we already have valid UTF-8 char? */ if (DecodeWithUTF8Alphabet(mychar, &ret, z) == z) break; } if (z>0) { i += z * 3; /* we ignore wrong sequence */ if (DecodeWithUTF8Alphabet(mychar, &ret, z)==0) continue; } else { i += EncodeWithUnicodeAlphabet(&src[i], &ret); } if (StoreUTF16(dest + j, ret)) { j += 4; } else { j += 2; } } dest[j++] = 0; dest[j] = 0; } void DecodeUTF8(unsigned char *dest, const char *src, size_t len) { size_t i=0,j=0,z; gammu_char_t ret; while (i < len) { z = DecodeWithUTF8Alphabet(src+i, &ret, len - i); if (z < 1) { break; } i += z; if (StoreUTF16(dest + j, ret)) { j += 4; } else { j += 2; } } dest[j++] = 0; dest[j] = 0; } void DecodeXMLUTF8(unsigned char *dest, const char *src, size_t len) { char *tmp; char *pos, *pos_end; const char *lastpos; char *entity; unsigned long long int c; int tmplen; /* Allocate buffer */ tmp = (char *)calloc(2 * len, sizeof(char)); if (tmp == NULL) { /* We have no memory for XML decoding */ DecodeUTF8(dest, src, len); return; } if (src == NULL) { *dest = 0; free(tmp); return; } /* Find ampersand and decode the */ lastpos = src; while ((*lastpos != 0) && ((pos = strchr(lastpos, '&')) != NULL)) { /* Store current string */ strncat(tmp, lastpos, pos - lastpos); lastpos = pos; /* Skip ampersand */ pos++; /* Detect end of string */ if (*pos == 0) break; /* Find entity length */ pos_end = strchr(pos, ';'); if (pos_end - pos > 6 || pos_end == NULL) { if (pos_end == NULL) { dbgprintf(NULL, "No entity end found, ignoring!\n"); } else { dbgprintf(NULL, "Too long html entity, ignoring!\n"); } strncat(tmp, lastpos, 1); lastpos++; continue; } /* Create entity */ /* strndup would be better, but not portable */ entity = strdup(pos); if (entity == NULL) break; entity[pos_end - pos] = 0; dbgprintf(NULL, "Found XML entity: %s\n", entity); if (entity[0] == '#') { if (entity[1] == 'x' || entity[1] == 'X') { c = strtoull(entity + 2, NULL, 16); } else { c = strtoull(entity + 1, NULL, 10); } dbgprintf(NULL, "Unicode char 0x%04llx\n", c); tmplen = strlen(tmp); tmplen += EncodeWithUTF8Alphabet(c, tmp + tmplen); tmp[tmplen] = 0; } else if (strcmp(entity, "amp") == 0) { strcat(tmp, "&"); } else if (strcmp(entity, "apos") == 0) { strcat(tmp, "'"); } else if (strcmp(entity, "gt") == 0) { strcat(tmp, ">"); } else if (strcmp(entity, "lt") == 0) { strcat(tmp, "<"); } else if (strcmp(entity, "quot") == 0) { strcat(tmp, "\""); } else { dbgprintf(NULL, "Could not decode XML entity!\n"); strncat(tmp, lastpos, pos_end - pos + 1); } free(entity); entity=NULL; lastpos = pos_end + 1; } /* Copy rest of string */ strcat(tmp, lastpos); DecodeUTF8(dest, tmp, strlen(tmp)); free(tmp); tmp=NULL; } void DecodeUTF7(unsigned char *dest, const unsigned char *src, size_t len) { size_t i=0,j=0,z,p; gammu_char_t ret; while (i<=len) { if (len-5>=i) { if (src[i] == '+') { z=0; while (src[z+i+1] != '-' && z+i+1> 2 ]; out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ]; out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '='); out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '='); } void EncodeBASE64(const unsigned char *Input, char *Output, const size_t Length) { unsigned char in[3], out[4]; size_t i, pos = 0, len, outpos = 0; while (pos < Length) { len = 0; for (i = 0; i < 3; i++) { in[i] = 0; if (pos < Length) { in[i] = Input[pos]; len++; pos++; } } if(len) { EncodeBASE64Block(in, out, len); for (i = 0; i < 4; i++) Output[outpos++] = out[i]; } } Output[outpos] = 0; } static void DecodeBASE64Block(const char in[4], unsigned char out[3]) { out[0] = (unsigned char) ((in[0] << 2) | (in[1] >> 4)); out[1] = (unsigned char) ((in[1] << 4) | (in[2] >> 2)); out[2] = (unsigned char) (((in[2] << 6) & 0xc0) | in[3]); } int DecodeBASE64(const char *Input, unsigned char *Output, const size_t Length) { unsigned char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; unsigned char in[4], out[3], v; size_t i, len, pos = 0, outpos = 0; while (pos < Length) { len = 0; for(i = 0; i < 4; i++) { v = 0; while(v == 0) { if (pos >= Length) break; v = (unsigned char) Input[pos++]; v = (unsigned char) ((v < 43 || v > 122) ? 0 : cd64[ v - 43 ]); if (v) v = (unsigned char) ((v == '$') ? 0 : v - 61); } if(pos<=Length) { if (v) { len++; in[i] = (unsigned char) (v - 1); } } } if (len) { DecodeBASE64Block(in, out); for(i = 0; i < len - 1; i++) Output[outpos++] = out[i]; } } Output[outpos] = 0; return outpos; } #ifdef ICONV_FOUND #include #ifdef ICONV_SECOND_ARGUMENT_IS_CONST # define SECOND_ICONV_ARG const char * #else # define SECOND_ICONV_ARG char * #endif gboolean IconvDecode(const char *charset, const char *input, const size_t inlen, unsigned char *output, size_t outlen) { iconv_t ic; /* Add one to convert also trailing zero, this is broken for * multibyte input, but we don't use iconv for this so far */ size_t rest = inlen + 1; SECOND_ICONV_ARG in; char *out; ic = iconv_open("UCS-2BE", charset); if (ic == (iconv_t)(-1)) return FALSE; /* I know I loose const here, but it's iconv choice... */ in = (SECOND_ICONV_ARG)input; out = output; iconv(ic, &in, &rest, &out, &outlen); iconv_close(ic); return (rest == 0); } gboolean IconvEncode(const char *charset, const unsigned char *input, const size_t inlen, char *output, size_t outlen) { iconv_t ic; size_t rest = inlen; SECOND_ICONV_ARG in; char *out; ic = iconv_open(charset, "UCS-2BE"); if (ic == (iconv_t)(-1)) return FALSE; /* I know I loose const here, but it's iconv choice... */ in = (SECOND_ICONV_ARG)input; out = output; iconv(ic, &in, &rest, &out, &outlen); iconv_close(ic); return (rest == 0); } #endif /* How should editor hadle tabs in this file? Add editor commands here. * vim: noexpandtab sw=8 ts=8 sts=8: */