csync-git
[mirrors/ArchLinux-Packages.git] / pidgin-qip-decoder / qip-decoder.c
1 /*
2 Copyright (C) 2008 Viktor Michna
3
4 Version 1.3
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21
22 #define PURPLE_PLUGINS
23
24 #include <glib.h>
25
26 /* This will prevent compiler errors in some instances and is better explained in the
27 * how-to documents on the wiki */
28 #ifndef G_GNUC_NULL_TERMINATED
29 # if __GNUC__ >= 4
30 # define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
31 # else
32 # define G_GNUC_NULL_TERMINATED
33 # endif
34 #endif
35
36 #include "notify.h"
37 #include "plugin.h"
38 #include "version.h"
39
40 #include <unistd.h>
41 #include <string.h>
42
43 #include <stdio.h>
44
45 #include <sys/types.h>
46
47 #ifdef _WIN32
48 #include <windows.h>
49 #endif
50
51 #ifndef _WIN32
52 // Central European languages that use Latin script
53 static char* win1250_locales = {
54 "ro,sl,hu,sk,pl,sq,sr,hr,cs"
55 };
56 // Cyrillic alphabets
57 static char* win1251_locales = {
58 //"mn,mk,uz,uk,az,tt,kk,be,ky,bg,sr,ru"
59 "mn,mk,uk,tt,kk,be,ky,bg,ru" // uz,az,sr - cyrillic disabled
60 };
61 // Western languages
62 static char* win1252_locales = {
63 "en,fr,nl,gl,de,uq,fi,fo,ca,da,es,af,is,id,it,nn,pt,nb,ms"
64 };
65 // Greek
66 static char* win1253_locales = {
67 "el"
68 };
69 // Turkish
70 static char* win1254_locales = {
71 "uz,az,tr"
72 };
73 // Hebrew
74 static char* win1255_locales = {
75 "he"
76 };
77 // Arabic
78 static char* win1256_locales = {
79 "ar,fa,ur"
80 };
81 // Baltic languages
82 static char* win1257_locales = {
83 "et,lv,lt"
84 };
85 // Vietnamese
86 static char* win1258_locales = {
87 "vi"
88 };
89 #endif
90
91 static unsigned short win1250_table[] = {
92 0x20AC, 0xFFFD, 0x201A, 0xFFFD, 0x201E, 0x2026, 0x2020, 0x2021, 0xFFFD, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
93 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
94 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
95 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
96 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
97 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
98 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
99 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
100 };
101
102 static unsigned short win1251_table[] = {
103 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
104 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
105 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
106 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
107 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
108 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
109 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
110 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
111 };
112
113 static unsigned short win1252_table[] = {
114 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFD, 0x017D, 0xFFFD,
115 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0xFFFD, 0x017E, 0x0178,
116 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
117 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
118 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
119 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
120 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
121 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
122 };
123
124 static unsigned short win1253_table[] = {
125 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0xFFFD, 0x2030, 0xFFFD, 0x2039, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,
126 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0x2122, 0xFFFD, 0x203A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,
127 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0xFFFD, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,
128 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
129 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
130 0x03A0, 0x03A1, 0xFFFD, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
131 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
132 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0xFFFD
133 };
134
135 static unsigned short win1254_table[] = {
136 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0xFFFD, 0xFFFD, 0xFFFD,
137 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0xFFFD, 0xFFFD, 0x0178,
138 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
139 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
140 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
141 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF,
142 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
143 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
144 };
145
146 static unsigned short win1255_table[] = {
147 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0xFFFD, 0x2039, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,
148 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0xFFFD, 0x203A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,
149 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AA, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
150 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
151 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, 0x05B8, 0x05B9, 0xFFFD, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
152 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,
153 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
154 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0xFFFD, 0xFFFD, 0x200E, 0x200F, 0xFFFD
155 };
156
157 static unsigned short win1256_table[] = {
158 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,
159 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,
160 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
161 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,
162 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
163 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0641, 0x0642, 0x0643,
164 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,
165 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2
166 };
167
168 static unsigned short win1257_table[] = {
169 0x20AC, 0xFFFD, 0x201A, 0xFFFD, 0x201E, 0x2026, 0x2020, 0x2021, 0xFFFD, 0x2030, 0xFFFD, 0x2039, 0xFFFD, 0x00A8, 0x02C7, 0x00B8,
170 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0xFFFD, 0x2122, 0xFFFD, 0x203A, 0xFFFD, 0x00AF, 0x02DB, 0xFFFD,
171 0x00A0, 0xFFFD, 0x00A2, 0x00A3, 0x00A4, 0xFFFD, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,
172 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
173 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
174 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
175 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
176 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9
177 };
178
179 static unsigned short win1258_table[] = {
180 0x20AC, 0xFFFD, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0xFFFD, 0x2039, 0x0152, 0xFFFD, 0xFFFD, 0xFFFD,
181 0xFFFD, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0xFFFD, 0x203A, 0x0153, 0xFFFD, 0xFFFD, 0x0178,
182 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
183 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
184 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,
185 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,
186 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,
187 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF
188 };
189
190 static unsigned short* win125x_tables[] = {
191 win1250_table, win1251_table, win1252_table, win1253_table,
192 win1254_table, win1255_table, win1256_table, win1257_table,
193 win1258_table
194 };
195
196 #define CODEPAGE_MIN 1250
197 #define CODEPAGE_MAX 1258
198 #define CODEPAGE_TABLE(cp) (((CODEPAGE_MIN<=(cp)) && ((cp)<=CODEPAGE_MAX)) ? win125x_tables[(cp)-CODEPAGE_MIN] : NULL)
199
200 // default encoding table for nonspecified UINs or NULL to disable
201 static unsigned short* encoding_table;
202 // mapping UIN => encoding table
203 static GHashTable* qip_codepages;
204
205
206 #ifndef _WIN32
207 static unsigned int GetACP() {
208 char* locale;
209 char lang[3];
210 //
211 locale = getenv("LANG");
212 if (!locale) return 0;
213 if (strlen(locale)<2) return 0;
214 //
215 lang[0] = locale[0];
216 lang[1] = locale[1];
217 lang[2] = 0;
218 //
219 if (strstr(win1250_locales,lang)) {
220 return 1250;
221 } else if (strstr(win1251_locales,lang)) {
222 return 1251;
223 } else if (strstr(win1252_locales,lang)) {
224 return 1252;
225 } else if (strstr(win1253_locales,lang)) {
226 return 1253;
227 } else if (strstr(win1254_locales,lang)) {
228 return 1254;
229 } else if (strstr(win1255_locales,lang)) {
230 return 1255;
231 } else if (strstr(win1256_locales,lang)) {
232 return 1256;
233 } else if (strstr(win1257_locales,lang)) {
234 return 1257;
235 } else if (strstr(win1258_locales,lang)) {
236 return 1258;
237 } else {
238 return 0;
239 }
240 }
241 #endif
242
243 static int HexValue(char c) {
244 if (c>='0' && c<='9') {
245 return c-'0';
246 } else if (c>='A' && c<='F') {
247 return c-('A'-0x0A);
248 } else if (c>='a' && c<='f') {
249 return c-('a'-0x0A);
250 } else {
251 return -1;
252 }
253 }
254
255 static int UTF8Char(char* str, unsigned short code) {
256 if (code<=0x7F) {
257 if (str) {
258 str[0] = code;
259 }
260 return 1;
261 } else if (code<=0x7FF) {
262 if (str) {
263 str[0] = 0xC0 | (code >> 6);
264 str[1] = 0x80 | (code & 0x3F);
265 }
266 return 2;
267 } else {
268 if (str) {
269 str[0] = 0xE0 | (code >> 12);
270 str[1] = 0x80 | ((code >> 6) & 0x3F);
271 str[2] = 0x80 | (code & 0x3F);
272 }
273 return 3;
274 }
275 }
276
277 static unsigned short* get_table_by_uin(char* uin) {
278 char* key;
279 unsigned short* table = encoding_table;
280 if (uin && qip_codepages) {
281 if (!g_hash_table_lookup_extended(qip_codepages, uin, (gpointer *)&key, (gpointer *)&table)) {
282 table = encoding_table;
283 }
284 }
285 return table;
286 }
287
288 static gboolean receiving_im_msg_cb(PurpleAccount *account, char **sender, char **buffer, PurpleConversation *conv, PurpleMessageFlags *flags, void *data) {
289
290 char c;
291 int hex_digit;
292 char* str;
293 int remaining;
294 int length;
295 int new_length;
296 int position;
297 int encoded_size;
298 int decoded_size;
299 unsigned short char_code; // ansi/unicode character
300 unsigned short* table;
301
302 // return immediately, if not the ICQ protocol
303 if (strcmp("prpl-icq",purple_account_get_protocol_id(account))!=0) return FALSE;
304
305 // return if fix is disabled for the UIN
306 table = get_table_by_uin(*sender);
307 if (!table) return FALSE;
308
309
310 // The following conversion is made so we almost never need message buffer
311 // memory reallocation.
312
313 str = *buffer; // position in the message
314 length = strlen(str);
315 remaining = length; // remaining chars to iterate
316 position = 0;
317
318 // pro each character in the message
319 for (; remaining>0; str++, position++, remaining--) {
320 c=*str;
321 if (c==0) break;
322
323 // Check, if there's a broken character at the current position.
324 // If so, we'll compute the ansi code (0x80-0xFF) in char_code.
325 //
326 // The QIP uses WINDOWS-1251 character encoding, but not complete.
327 // It seems, russian authors of QIP implemented only support for
328 // russian characters, but not for other languages that are using
329 // WINDOWS-1251 encoding. Maybe, they wanted to avoid the usage of
330 // a complete convert table or system unicode functions, so they
331 // only used the approximate relationship between unicode cyrillic
332 // codes and windows codes (linear function) and corrected only
333 // a few of characters. I managed to determine the technique, so now
334 // we can perform the reverse sequence.
335 //
336 if ((c=='&') && (remaining>=6) && (str[1]=='#') && (str[2]=='x') && (str[5]==';')) {
337 // hexa code &#xXX;
338 hex_digit = HexValue(str[3]);
339 if (hex_digit<0) continue;
340 char_code = hex_digit << 4;
341 hex_digit = HexValue(str[4]);
342 if (hex_digit<0) continue;
343 char_code += hex_digit;
344 encoded_size = 6;
345 } else if ((c==(char)0xC2) && (remaining>=2)) {
346 // code 0xC2,0xXX
347 char_code = str[1] & 0xFF;
348 if (char_code<0x80 || char_code>0xBF) continue;
349 encoded_size = 2;
350 } else if ((c==(char)0xD0) && (remaining>=2)) {
351 // code 0xD0,0xXX
352 char_code = str[1] & 0xFF;
353 if (char_code == 0x81) {
354 char_code = 0xA8;
355 } else {
356 if (char_code<0x90 || char_code>0xBF) continue;
357 char_code += 0x30;
358 }
359 encoded_size = 2;
360 } else if ((c==(char)0xD1) && (remaining>=2)) {
361 // code 0xD1,0xXX
362 char_code = str[1] & 0xFF;
363 if (char_code == 0x91) {
364 char_code = 0xB8;
365 } else {
366 if (char_code<0x80 || char_code>0x8F) continue;
367 char_code += 0x70;
368 }
369 encoded_size = 2;
370 } else {
371 continue;
372 }
373
374 // convert native character code to unicode character
375 if (char_code>=0x80) {
376 char_code = table[char_code-0x80];
377 }
378
379 // now we must replace the bad character code with a right one
380 decoded_size = UTF8Char(NULL, char_code);
381
382 // check if there's enough space to replace characters
383 if ((decoded_size - encoded_size) > (length - (position + remaining))) {
384 // we must realloc the message buffer;
385 // compute minimal new buffer size (plus zero term. char)
386 new_length = position + remaining + decoded_size - encoded_size + 1;
387 // ceil round to multiplier of 256
388 new_length = ((new_length + 255) / 256) * 256;
389 // realloc
390 str = malloc(new_length);
391 if (!str) return TRUE;
392 // correct size to that without zero term. char
393 new_length--;
394 // copy message begin
395 memcpy(
396 str,
397 *buffer,
398 position
399 );
400 // copy message end
401 memcpy(
402 &(str[position+decoded_size]),
403 &((*buffer)[position+encoded_size]),
404 remaining-encoded_size
405 );
406 // free old buffer
407 free(*buffer);
408 *buffer = str;
409 str += position;
410 //
411 length = new_length;
412 remaining += (decoded_size-encoded_size);
413 encoded_size = decoded_size;
414 // zero the added chars
415 memset(
416 &((*buffer)[position+remaining]),
417 0,
418 length-(position+remaining)+1
419 );
420 } else if (decoded_size!=encoded_size) {
421 // shift message
422 memmove(
423 &(str[decoded_size]),
424 &(str[encoded_size]),
425 remaining-encoded_size
426 );
427 }
428
429 // correct the character
430 UTF8Char(str, char_code);
431
432 // zero the remaining characters
433 if (encoded_size>decoded_size) {
434 memset(
435 &(str[remaining-(encoded_size-decoded_size)]),
436 0,
437 encoded_size-decoded_size
438 );
439 }
440
441 // shift
442 position += (decoded_size-1);
443 str += (decoded_size-1);
444 remaining -= (encoded_size-1);
445
446 }
447 return FALSE;
448
449 }
450
451 unsigned int str2intdef(const char *str, unsigned int def) {
452 unsigned int num;
453 if (!str || sscanf(str,"%d",&num)!=1) {
454 return def;
455 }
456 return num;
457 }
458
459 static gboolean plugin_load(PurplePlugin *plugin) {
460
461 const char *uin;
462 xmlnode *xml,*node;
463 unsigned int codepage;
464 gboolean enabled;
465 gboolean default_enabled;
466 unsigned int default_codepage;
467
468 qip_codepages = NULL;
469 default_enabled = TRUE;
470 default_codepage = 0;
471
472 // load codepage configuration
473 xml = purple_util_read_xml_from_file("qips.xml","qips.xml");
474 if (xml) {
475 node = xmlnode_get_child(xml,"default");
476 if (node) {
477 // default setting for all UINs
478 default_enabled = str2intdef(xmlnode_get_attrib(node,"enabled"),default_enabled);
479 default_codepage = str2intdef(xmlnode_get_attrib(node,"codepage"),default_codepage);
480 }
481 }
482
483 // get default codepage from system
484 if (default_codepage<CODEPAGE_MIN || default_codepage>CODEPAGE_MAX) {
485 // try to use environment variable 'QIPACP' containing CP number
486 default_codepage = str2intdef(getenv("QIPACP"),0);
487 if (default_codepage<CODEPAGE_MIN || default_codepage>CODEPAGE_MAX) {
488 // now, try to use system ansi code page
489 default_codepage = GetACP();
490 if (default_codepage<CODEPAGE_MIN || default_codepage>CODEPAGE_MAX) {
491 default_codepage = 0;
492 default_enabled = FALSE;
493 }
494 }
495 }
496
497 if (xml) {
498 // UIN based codepage setting
499 for (node = xmlnode_get_child(xml,"qip"); node; node = xmlnode_get_next_twin(node)) {
500 uin = xmlnode_get_attrib(node,"uin");
501 if (!uin) continue;
502 if (!qip_codepages) qip_codepages = g_hash_table_new(g_str_hash,g_str_equal);
503 enabled = str2intdef(xmlnode_get_attrib(node,"enabled"),default_enabled);
504 codepage = str2intdef(xmlnode_get_attrib(node,"codepage"),default_codepage);
505 //
506 if (!enabled || codepage<CODEPAGE_MIN || codepage>CODEPAGE_MAX) codepage = 0;
507 //
508 g_hash_table_insert(qip_codepages,g_strdup(uin),CODEPAGE_TABLE(codepage));
509 }
510 xmlnode_free(xml);
511 }
512
513 // finally, assign the unicode table according to the code page
514 if (!default_enabled) default_codepage = 0;
515 encoding_table = CODEPAGE_TABLE(default_codepage);
516
517 //
518 purple_signal_connect(purple_conversations_get_handle(), "receiving-im-msg", plugin, PURPLE_CALLBACK(receiving_im_msg_cb), NULL);
519 return TRUE;
520 }
521
522 static gboolean dealloc_all(gpointer key, gpointer val, gpointer user_data) {
523 g_free(key);
524 return (TRUE);
525 }
526
527 static gboolean plugin_unload(PurplePlugin *plugin) {
528 purple_signal_disconnect(purple_conversations_get_handle(), "receiving-im-msg", plugin, PURPLE_CALLBACK(receiving_im_msg_cb));
529 //
530 if (qip_codepages) {
531 g_hash_table_foreach_remove(qip_codepages, dealloc_all, NULL);
532 g_hash_table_destroy(qip_codepages);
533 qip_codepages = NULL;
534 }
535 return TRUE;
536 }
537
538 static PurplePluginInfo info = {
539 PURPLE_PLUGIN_MAGIC,
540 PURPLE_MAJOR_VERSION,
541 PURPLE_MINOR_VERSION,
542 PURPLE_PLUGIN_STANDARD,
543 NULL,
544 0,
545 NULL,
546 PURPLE_PRIORITY_DEFAULT,
547
548 "qip-decoder",
549 "QIP Decoder",
550 "1.3",
551
552 "QIP Decoder Plugin",
553 "Displays diacritic letters from QIP IM correctly.",
554 "Viktor Michna (viktor.michna@sedaha.cz)",
555 "http://www.sedaha.cz/qip-decoder/",
556
557 plugin_load,
558 plugin_unload,
559 NULL,
560
561 NULL,
562 NULL,
563 NULL,
564 NULL,
565 NULL,
566 NULL,
567 NULL,
568 NULL
569 };
570
571 static void init_plugin(PurplePlugin *plugin) {
572
573 }
574
575 PURPLE_INIT_PLUGIN(qip, init_plugin, info);
This page took 0.840496 seconds and 4 git commands to generate.