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 }