File indexing completed on 2024-12-08 04:20:08
0001 /* 0002 * Copyright (C) 2003-2005 Justin Karneges <justin@affinix.com> 0003 * Copyright (C) 2004,2005 Brad Hards <bradh@frogmouth.net> 0004 * 0005 * This library is free software; you can redistribute it and/or 0006 * modify it under the terms of the GNU Lesser General Public 0007 * License as published by the Free Software Foundation; either 0008 * version 2.1 of the License, or (at your option) any later version. 0009 * 0010 * This library is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 * Lesser General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU Lesser General Public 0016 * License along with this library; if not, write to the Free Software 0017 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0018 * 02110-1301 USA 0019 * 0020 */ 0021 0022 #include "qca_textfilter.h" 0023 0024 namespace QCA { 0025 0026 //---------------------------------------------------------------------------- 0027 // TextFilter 0028 //---------------------------------------------------------------------------- 0029 TextFilter::TextFilter(Direction dir) 0030 { 0031 setup(dir); 0032 } 0033 0034 void TextFilter::setup(Direction dir) 0035 { 0036 _dir = dir; 0037 } 0038 0039 Direction TextFilter::direction() const 0040 { 0041 return _dir; 0042 } 0043 0044 MemoryRegion TextFilter::encode(const MemoryRegion &a) 0045 { 0046 setup(Encode); 0047 return process(a); 0048 } 0049 0050 MemoryRegion TextFilter::decode(const MemoryRegion &a) 0051 { 0052 setup(Decode); 0053 return process(a); 0054 } 0055 0056 QString TextFilter::arrayToString(const MemoryRegion &a) 0057 { 0058 return QString::fromLatin1(encode(a).toByteArray()); 0059 } 0060 0061 MemoryRegion TextFilter::stringToArray(const QString &s) 0062 { 0063 if (s.isEmpty()) 0064 return MemoryRegion(); 0065 return decode(s.toLatin1()); 0066 } 0067 0068 QString TextFilter::encodeString(const QString &s) 0069 { 0070 return arrayToString(s.toUtf8()); 0071 } 0072 0073 QString TextFilter::decodeString(const QString &s) 0074 { 0075 return QString::fromUtf8(stringToArray(s).toByteArray()); 0076 } 0077 0078 //---------------------------------------------------------------------------- 0079 // Hex 0080 //---------------------------------------------------------------------------- 0081 static int enhex(uchar c) 0082 { 0083 if (c < 10) 0084 return c + '0'; 0085 else if (c < 16) 0086 return c - 10 + 'a'; 0087 else 0088 return -1; 0089 } 0090 0091 static int dehex(char c) 0092 { 0093 if (c >= 'a' && c <= 'f') 0094 return c - 'a' + 10; 0095 else if (c >= 'A' && c <= 'F') 0096 return c - 'A' + 10; 0097 else if (c >= '0' && c <= '9') 0098 return c - '0'; 0099 else 0100 return -1; 0101 } 0102 0103 Hex::Hex(Direction dir) 0104 : TextFilter(dir) 0105 { 0106 clear(); 0107 } 0108 0109 void Hex::clear() 0110 { 0111 partial = false; 0112 _ok = true; 0113 } 0114 0115 MemoryRegion Hex::update(const MemoryRegion &m) 0116 { 0117 const QByteArray a = m.toByteArray(); 0118 if (_dir == Encode) { 0119 QByteArray out(a.size() * 2, 0); 0120 int at = 0; 0121 int c; 0122 for (const char ac : a) { 0123 uchar lo = (uchar)ac & 0x0f; 0124 uchar hi = (uchar)ac >> 4; 0125 c = enhex(hi); 0126 if (c == -1) { 0127 _ok = false; 0128 break; 0129 } 0130 out[at++] = (char)c; 0131 c = enhex(lo); 0132 if (c == -1) { 0133 _ok = false; 0134 break; 0135 } 0136 out[at++] = (char)c; 0137 } 0138 if (!_ok) 0139 return MemoryRegion(); 0140 0141 return out; 0142 } else { 0143 uchar lo = 0; 0144 uchar hi = 0; 0145 bool flag = false; 0146 if (partial) { 0147 hi = val; 0148 flag = true; 0149 } 0150 0151 QByteArray out(a.size() / 2, 0); 0152 int at = 0; 0153 int c; 0154 for (const char ac : a) { 0155 c = dehex(ac); 0156 if (c == -1) { 0157 _ok = false; 0158 break; 0159 } 0160 if (flag) { 0161 lo = (uchar)c; 0162 uchar full = ((hi & 0x0f) << 4) + (lo & 0x0f); 0163 out[at++] = full; 0164 flag = false; 0165 } else { 0166 hi = (uchar)c; 0167 flag = true; 0168 } 0169 } 0170 if (!_ok) 0171 return MemoryRegion(); 0172 0173 if (flag) { 0174 val = hi; 0175 partial = true; 0176 } 0177 return out; 0178 } 0179 } 0180 0181 MemoryRegion Hex::final() 0182 { 0183 if (partial) 0184 _ok = false; 0185 return MemoryRegion(); 0186 } 0187 0188 bool Hex::ok() const 0189 { 0190 return _ok; 0191 } 0192 0193 //---------------------------------------------------------------------------- 0194 // Base64 0195 //---------------------------------------------------------------------------- 0196 Base64::Base64(Direction dir) 0197 : TextFilter(dir) 0198 { 0199 _lb_enabled = false; 0200 _lb_column = 76; 0201 } 0202 0203 void Base64::clear() 0204 { 0205 partial.resize(0); 0206 _ok = true; 0207 col = 0; 0208 } 0209 0210 bool Base64::lineBreaksEnabled() const 0211 { 0212 return _lb_enabled; 0213 } 0214 0215 int Base64::lineBreaksColumn() const 0216 { 0217 return _lb_column; 0218 } 0219 0220 void Base64::setLineBreaksEnabled(bool b) 0221 { 0222 _lb_enabled = b; 0223 } 0224 0225 void Base64::setLineBreaksColumn(int column) 0226 { 0227 if (column > 0) 0228 _lb_column = column; 0229 else 0230 _lb_column = 76; 0231 } 0232 0233 static QByteArray b64encode(const QByteArray &s) 0234 { 0235 int i; 0236 const int len = s.size(); 0237 static const char tbl[] = 0238 "ABCDEFGH" 0239 "IJKLMNOP" 0240 "QRSTUVWX" 0241 "YZabcdef" 0242 "ghijklmn" 0243 "opqrstuv" 0244 "wxyz0123" 0245 "456789+/" 0246 "="; 0247 int a, b, c; 0248 0249 QByteArray p((len + 2) / 3 * 4, 0); 0250 int at = 0; 0251 for (i = 0; i < len; i += 3) { 0252 a = ((unsigned char)s[i] & 3) << 4; 0253 if (i + 1 < len) { 0254 a += (unsigned char)s[i + 1] >> 4; 0255 b = ((unsigned char)s[i + 1] & 0xf) << 2; 0256 if (i + 2 < len) { 0257 b += (unsigned char)s[i + 2] >> 6; 0258 c = (unsigned char)s[i + 2] & 0x3f; 0259 } else 0260 c = 64; 0261 } else 0262 b = c = 64; 0263 0264 p[at++] = tbl[(unsigned char)s[i] >> 2]; 0265 p[at++] = tbl[a]; 0266 p[at++] = tbl[b]; 0267 p[at++] = tbl[c]; 0268 } 0269 return p; 0270 } 0271 0272 static QByteArray b64decode(const QByteArray &s, bool *ok) 0273 { 0274 // -1 specifies invalid 0275 // 64 specifies eof 0276 // everything else specifies data 0277 0278 static const signed char tbl[] = { 0279 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0280 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 0281 56, 57, 58, 59, 60, 61, -1, -1, -1, 64, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0282 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 0283 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, 0284 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0285 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0286 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0287 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0288 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0289 }; 0290 0291 // return value 0292 QByteArray p; 0293 *ok = true; 0294 0295 // this should be a multiple of 4 0296 const int len = s.size(); 0297 if (len % 4) { 0298 *ok = false; 0299 return p; 0300 } 0301 0302 p.resize(len / 4 * 3); 0303 0304 int i; 0305 int at = 0; 0306 0307 int a, b, c, d; 0308 c = d = 0; 0309 0310 for (i = 0; i < len; i += 4) { 0311 a = tbl[(int)s[i]]; 0312 b = tbl[(int)s[i + 1]]; 0313 c = tbl[(int)s[i + 2]]; 0314 d = tbl[(int)s[i + 3]]; 0315 if ((a == 64 || b == 64) || (a < 0 || b < 0 || c < 0 || d < 0)) { 0316 p.resize(0); 0317 *ok = false; 0318 return p; 0319 } 0320 p[at++] = ((a & 0x3F) << 2) | ((b >> 4) & 0x03); 0321 p[at++] = ((b & 0x0F) << 4) | ((c >> 2) & 0x0F); 0322 p[at++] = ((c & 0x03) << 6) | ((d >> 0) & 0x3F); 0323 } 0324 0325 if (c & 64) 0326 p.resize(at - 2); 0327 else if (d & 64) 0328 p.resize(at - 1); 0329 0330 return p; 0331 } 0332 0333 static int findLF(const QByteArray &in, int offset) 0334 { 0335 for (int n = offset; n < in.size(); ++n) { 0336 if (in[n] == '\n') 0337 return n; 0338 } 0339 return -1; 0340 } 0341 0342 static QByteArray insert_linebreaks(const QByteArray &s, int *col, int lfAt) 0343 { 0344 QByteArray out = s; 0345 0346 const int needed = (out.size() + *col) / lfAt; // how many newlines needed? 0347 if (needed > 0) { 0348 const int firstlen = lfAt - *col; // length of first chunk 0349 int at = firstlen + (lfAt * (needed - 1)); // position of last newline 0350 const int lastlen = out.size() - at; // length of last chunk 0351 0352 // printf("size=%d,needed=%d,firstlen=%d,at=%d,lastlen=%d\n", out.size(), needed, firstlen, at, lastlen); 0353 0354 // make room 0355 out.resize(out.size() + needed); 0356 0357 // move backwards 0358 for (int n = 0; n < needed; ++n) { 0359 char *p = out.data() + at; 0360 int len; 0361 if (n == 0) 0362 len = lastlen; 0363 else 0364 len = lfAt; 0365 memmove(p + needed - n, p, len); 0366 p[needed - n - 1] = '\n'; 0367 at -= lfAt; 0368 } 0369 0370 *col = lastlen; 0371 } else 0372 *col += out.size(); 0373 0374 return out; 0375 } 0376 0377 static QByteArray remove_linebreaks(const QByteArray &s) 0378 { 0379 QByteArray out = s; 0380 0381 int removed = 0; 0382 int at = findLF(out, 0); 0383 while (at != -1) { 0384 int next = findLF(out, at + 1); 0385 int len; 0386 if (next != -1) 0387 len = next - at; 0388 else 0389 len = out.size() - at; 0390 0391 if (len > 1) { 0392 char *p = out.data() + at; 0393 memmove(p - removed, p + 1, len - 1); 0394 } 0395 ++removed; 0396 at = next; 0397 } 0398 out.resize(out.size() - removed); 0399 0400 return out; 0401 } 0402 0403 static void appendArray(QByteArray *a, const QByteArray &b) 0404 { 0405 a->append(b); 0406 } 0407 0408 MemoryRegion Base64::update(const MemoryRegion &m) 0409 { 0410 QByteArray in; 0411 if (_dir == Decode && _lb_enabled) 0412 in = remove_linebreaks(m.toByteArray()); 0413 else 0414 in = m.toByteArray(); 0415 0416 if (in.isEmpty()) 0417 return MemoryRegion(); 0418 0419 int chunk; 0420 if (_dir == Encode) 0421 chunk = 3; 0422 else 0423 chunk = 4; 0424 0425 const int size = partial.size() + in.size(); 0426 if (size < chunk) { 0427 appendArray(&partial, in); 0428 return MemoryRegion(); 0429 } 0430 0431 int eat = size % chunk; 0432 0433 // s = partial + a - eat 0434 QByteArray s(partial.size() + in.size() - eat, 0); 0435 memcpy(s.data(), partial.data(), partial.size()); 0436 memcpy(s.data() + partial.size(), in.data(), in.size() - eat); 0437 0438 partial.resize(eat); 0439 memcpy(partial.data(), in.data() + in.size() - eat, eat); 0440 0441 if (_dir == Encode) { 0442 if (_lb_enabled) 0443 return insert_linebreaks(b64encode(s), &col, _lb_column); 0444 else 0445 return b64encode(s); 0446 } else { 0447 bool ok; 0448 const QByteArray out = b64decode(s, &ok); 0449 if (!ok) 0450 _ok = false; 0451 return out; 0452 } 0453 } 0454 0455 MemoryRegion Base64::final() 0456 { 0457 if (_dir == Encode) { 0458 if (_lb_enabled) 0459 return insert_linebreaks(b64encode(partial), &col, _lb_column); 0460 else 0461 return b64encode(partial); 0462 } else { 0463 bool ok; 0464 const QByteArray out = b64decode(partial, &ok); 0465 if (!ok) 0466 _ok = false; 0467 return out; 0468 } 0469 } 0470 0471 bool Base64::ok() const 0472 { 0473 return _ok; 0474 } 0475 0476 }