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