| 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 |