summaryrefslogtreecommitdiffstats
path: root/tdeioslaves/imap4/rfcdecoder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tdeioslaves/imap4/rfcdecoder.cc')
-rw-r--r--tdeioslaves/imap4/rfcdecoder.cc668
1 files changed, 668 insertions, 0 deletions
diff --git a/tdeioslaves/imap4/rfcdecoder.cc b/tdeioslaves/imap4/rfcdecoder.cc
new file mode 100644
index 000000000..08b641b7d
--- /dev/null
+++ b/tdeioslaves/imap4/rfcdecoder.cc
@@ -0,0 +1,668 @@
+/**********************************************************************
+ *
+ * rfcdecoder.cc - handler for various rfc/mime encodings
+ * Copyright (C) 2000 s.carstens@gmx.de
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Send comments and bug fixes to s.carstens@gmx.de
+ *
+ *********************************************************************/
+#include "rfcdecoder.h"
+
+#include <ctype.h>
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <tqtextcodec.h>
+#include <tqbuffer.h>
+#include <tqregexp.h>
+#include <kmdcodec.h>
+
+// This part taken from rfc 2192 IMAP URL Scheme. C. Newman. September 1997.
+// adapted to QT-Toolkit by Sven Carstens <s.carstens@gmx.de> 2000
+
+static unsigned char base64chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+#define UNDEFINED 64
+#define MAXLINE 76
+
+/* UTF16 definitions */
+#define UTF16MASK 0x03FFUL
+#define UTF16SHIFT 10
+#define UTF16BASE 0x10000UL
+#define UTF16HIGHSTART 0xD800UL
+#define UTF16HIGHEND 0xDBFFUL
+#define UTF16LOSTART 0xDC00UL
+#define UTF16LOEND 0xDFFFUL
+
+/* Convert an IMAP mailbox to a Unicode path
+ */
+TQString rfcDecoder::fromIMAP (const TQString & inSrc)
+{
+ unsigned char c, i, bitcount;
+ unsigned long ucs4, utf16, bitbuf;
+ unsigned char base64[256], utf8[6];
+ unsigned long srcPtr = 0;
+ TQCString dst;
+ TQCString src = inSrc.ascii ();
+ uint srcLen = inSrc.length();
+
+ /* initialize modified base64 decoding table */
+ memset (base64, UNDEFINED, sizeof (base64));
+ for (i = 0; i < sizeof (base64chars); ++i)
+ {
+ base64[(int)base64chars[i]] = i;
+ }
+
+ /* loop until end of string */
+ while (srcPtr < srcLen)
+ {
+ c = src[srcPtr++];
+ /* deal with literal characters and &- */
+ if (c != '&' || src[srcPtr] == '-')
+ {
+ /* encode literally */
+ dst += c;
+ /* skip over the '-' if this is an &- sequence */
+ if (c == '&')
+ srcPtr++;
+ }
+ else
+ {
+ /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
+ bitbuf = 0;
+ bitcount = 0;
+ ucs4 = 0;
+ while ((c = base64[(unsigned char) src[srcPtr]]) != UNDEFINED)
+ {
+ ++srcPtr;
+ bitbuf = (bitbuf << 6) | c;
+ bitcount += 6;
+ /* enough bits for a UTF-16 character? */
+ if (bitcount >= 16)
+ {
+ bitcount -= 16;
+ utf16 = (bitcount ? bitbuf >> bitcount : bitbuf) & 0xffff;
+ /* convert UTF16 to UCS4 */
+ if (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND)
+ {
+ ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT;
+ continue;
+ }
+ else if (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND)
+ {
+ ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
+ }
+ else
+ {
+ ucs4 = utf16;
+ }
+ /* convert UTF-16 range of UCS4 to UTF-8 */
+ if (ucs4 <= 0x7fUL)
+ {
+ utf8[0] = ucs4;
+ i = 1;
+ }
+ else if (ucs4 <= 0x7ffUL)
+ {
+ utf8[0] = 0xc0 | (ucs4 >> 6);
+ utf8[1] = 0x80 | (ucs4 & 0x3f);
+ i = 2;
+ }
+ else if (ucs4 <= 0xffffUL)
+ {
+ utf8[0] = 0xe0 | (ucs4 >> 12);
+ utf8[1] = 0x80 | ((ucs4 >> 6) & 0x3f);
+ utf8[2] = 0x80 | (ucs4 & 0x3f);
+ i = 3;
+ }
+ else
+ {
+ utf8[0] = 0xf0 | (ucs4 >> 18);
+ utf8[1] = 0x80 | ((ucs4 >> 12) & 0x3f);
+ utf8[2] = 0x80 | ((ucs4 >> 6) & 0x3f);
+ utf8[3] = 0x80 | (ucs4 & 0x3f);
+ i = 4;
+ }
+ /* copy it */
+ for (c = 0; c < i; ++c)
+ {
+ dst += utf8[c];
+ }
+ }
+ }
+ /* skip over trailing '-' in modified UTF-7 encoding */
+ if (src[srcPtr] == '-')
+ ++srcPtr;
+ }
+ }
+ return TQString::fromUtf8 (dst.data ());
+}
+
+/* replace " with \" and \ with \\ " and \ characters */
+TQString rfcDecoder::quoteIMAP(const TQString &src)
+{
+ uint len = src.length();
+ TQString result;
+ result.reserve(2 * len);
+ for (unsigned int i = 0; i < len; i++)
+ {
+ if (src[i] == '"' || src[i] == '\\')
+ result += '\\';
+ result += src[i];
+ }
+ //result.squeeze(); - unnecessary and slow
+ return result;
+}
+
+/* Convert Unicode path to modified UTF-7 IMAP mailbox
+ */
+TQString rfcDecoder::toIMAP (const TQString & inSrc)
+{
+ unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag;
+ unsigned long ucs4, bitbuf;
+ TQCString src = inSrc.utf8 ();
+ TQString dst;
+
+ ulong srcPtr = 0;
+ utf7mode = 0;
+ utf8total = 0;
+ bitstogo = 0;
+ utf8pos = 0;
+ bitbuf = 0;
+ ucs4 = 0;
+ while (srcPtr < src.length ())
+ {
+ c = (unsigned char) src[srcPtr++];
+ /* normal character? */
+ if (c >= ' ' && c <= '~')
+ {
+ /* switch out of UTF-7 mode */
+ if (utf7mode)
+ {
+ if (bitstogo)
+ {
+ dst += base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
+ bitstogo = 0;
+ }
+ dst += '-';
+ utf7mode = 0;
+ }
+ dst += c;
+ /* encode '&' as '&-' */
+ if (c == '&')
+ {
+ dst += '-';
+ }
+ continue;
+ }
+ /* switch to UTF-7 mode */
+ if (!utf7mode)
+ {
+ dst += '&';
+ utf7mode = 1;
+ }
+ /* Encode US-ASCII characters as themselves */
+ if (c < 0x80)
+ {
+ ucs4 = c;
+ utf8total = 1;
+ }
+ else if (utf8total)
+ {
+ /* save UTF8 bits into UCS4 */
+ ucs4 = (ucs4 << 6) | (c & 0x3FUL);
+ if (++utf8pos < utf8total)
+ {
+ continue;
+ }
+ }
+ else
+ {
+ utf8pos = 1;
+ if (c < 0xE0)
+ {
+ utf8total = 2;
+ ucs4 = c & 0x1F;
+ }
+ else if (c < 0xF0)
+ {
+ utf8total = 3;
+ ucs4 = c & 0x0F;
+ }
+ else
+ {
+ /* NOTE: can't convert UTF8 sequences longer than 4 */
+ utf8total = 4;
+ ucs4 = c & 0x03;
+ }
+ continue;
+ }
+ /* loop to split ucs4 into two utf16 chars if necessary */
+ utf8total = 0;
+ do
+ {
+ if (ucs4 >= UTF16BASE)
+ {
+ ucs4 -= UTF16BASE;
+ bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT) + UTF16HIGHSTART);
+ ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART;
+ utf16flag = 1;
+ }
+ else
+ {
+ bitbuf = (bitbuf << 16) | ucs4;
+ utf16flag = 0;
+ }
+ bitstogo += 16;
+ /* spew out base64 */
+ while (bitstogo >= 6)
+ {
+ bitstogo -= 6;
+ dst += base64chars[(bitstogo ? (bitbuf >> bitstogo) : bitbuf) & 0x3F];
+ }
+ }
+ while (utf16flag);
+ }
+ /* if in UTF-7 mode, finish in ASCII */
+ if (utf7mode)
+ {
+ if (bitstogo)
+ {
+ dst += base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
+ }
+ dst += '-';
+ }
+ return quoteIMAP(dst);
+}
+
+//-----------------------------------------------------------------------------
+TQString rfcDecoder::decodeQuoting(const TQString &aStr)
+{
+ TQString result;
+ unsigned int strLength(aStr.length());
+ for (unsigned int i = 0; i < strLength ; i++)
+ {
+ if (aStr[i] == "\\") i++;
+ result += aStr[i];
+ }
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+TQTextCodec *
+rfcDecoder::codecForName (const TQString & _str)
+{
+ if (_str.isEmpty ())
+ return NULL;
+ return TQTextCodec::codecForName (_str.lower ().
+ replace ("windows", "cp").latin1 ());
+}
+
+//-----------------------------------------------------------------------------
+const TQString
+rfcDecoder::decodeRFC2047String (const TQString & _str)
+{
+ TQString throw_away;
+
+ return decodeRFC2047String (_str, throw_away);
+}
+
+//-----------------------------------------------------------------------------
+const TQString
+rfcDecoder::decodeRFC2047String (const TQString & _str, TQString & charset)
+{
+ TQString throw_away;
+
+ return decodeRFC2047String (_str, charset, throw_away);
+}
+
+//-----------------------------------------------------------------------------
+const TQString
+rfcDecoder::decodeRFC2047String (const TQString & _str, TQString & charset,
+ TQString & language)
+{
+ //do we have a rfc string
+ if (_str.find("=?") < 0)
+ return _str;
+
+ TQCString aStr = _str.ascii (); // TQString.length() means Unicode chars
+ TQCString result;
+ char *pos, *beg, *end, *mid = NULL;
+ TQCString str;
+ char encoding = 0, ch;
+ bool valid;
+ const int maxLen = 200;
+ int i;
+
+// result.truncate(aStr.length());
+ for (pos = aStr.data (); *pos; pos++)
+ {
+ if (pos[0] != '=' || pos[1] != '?')
+ {
+ result += *pos;
+ continue;
+ }
+ beg = pos + 2;
+ end = beg;
+ valid = TRUE;
+ // parse charset name
+ for (i = 2, pos += 2;
+ i < maxLen && (*pos != '?' && (ispunct (*pos) || isalnum (*pos)));
+ i++)
+ pos++;
+ if (*pos != '?' || i < 4 || i >= maxLen)
+ valid = FALSE;
+ else
+ {
+ charset = TQCString (beg, i - 1); // -2 + 1 for the zero
+ int pt = charset.findRev('*');
+ if (pt != -1)
+ {
+ // save language for later usage
+ language = charset.right (charset.length () - pt - 1);
+
+ // tie off language as defined in rfc2047
+ charset.truncate(pt);
+ }
+ // get encoding and check delimiting question marks
+ encoding = toupper (pos[1]);
+ if (pos[2] != '?'
+ || (encoding != 'Q' && encoding != 'B' && encoding != 'q'
+ && encoding != 'b'))
+ valid = FALSE;
+ pos += 3;
+ i += 3;
+// kdDebug(7116) << "rfcDecoder::decodeRFC2047String - charset " << charset << " - language " << language << " - '" << pos << "'" << endl;
+ }
+ if (valid)
+ {
+ mid = pos;
+ // search for end of encoded part
+ while (i < maxLen && *pos && !(*pos == '?' && *(pos + 1) == '='))
+ {
+ i++;
+ pos++;
+ }
+ end = pos + 2; //end now points to the first char after the encoded string
+ if (i >= maxLen || !*pos)
+ valid = FALSE;
+ }
+ if (valid)
+ {
+ ch = *pos;
+ *pos = '\0';
+ str = TQCString (mid).left ((int) (mid - pos - 1));
+ if (encoding == 'Q')
+ {
+ // decode quoted printable text
+ for (i = str.length () - 1; i >= 0; i--)
+ if (str[i] == '_')
+ str[i] = ' ';
+// kdDebug(7116) << "rfcDecoder::decodeRFC2047String - before QP '" << str << "'" << endl;
+
+ str = KCodecs::quotedPrintableDecode(str);
+// kdDebug(7116) << "rfcDecoder::decodeRFC2047String - after QP '" << str << "'" << endl;
+ }
+ else
+ {
+ // decode base64 text
+ str = KCodecs::base64Decode(str);
+ }
+ *pos = ch;
+ int len = str.length();
+ for (i = 0; i < len; i++)
+ result += (char) (TQChar) str[i];
+
+ pos = end - 1;
+ }
+ else
+ {
+// kdDebug(7116) << "rfcDecoder::decodeRFC2047String - invalid" << endl;
+ //result += "=?";
+ //pos = beg -1; // because pos gets increased shortly afterwards
+ pos = beg - 2;
+ result += *pos++;
+ result += *pos;
+ }
+ }
+ if (!charset.isEmpty ())
+ {
+ TQTextCodec *aCodec = codecForName (charset.ascii ());
+ if (aCodec)
+ {
+// kdDebug(7116) << "Codec is " << aCodec->name() << endl;
+ return aCodec->toUnicode (result);
+ }
+ }
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+const char especials[17] = "()<>@,;:\"/[]?.= ";
+
+const TQString
+rfcDecoder::encodeRFC2047String (const TQString & _str)
+{
+ if (_str.isEmpty ())
+ return _str;
+ const signed char *latin = reinterpret_cast<const signed char *>(_str.latin1()), *l, *start, *stop;
+ char hexcode;
+ int numQuotes, i;
+ int rptr = 0;
+ // My stats show this number results in 12 resize() out of 73,000
+ int resultLen = 3 * _str.length() / 2;
+ TQCString result(resultLen);
+
+ while (*latin)
+ {
+ l = latin;
+ start = latin;
+ while (*l)
+ {
+ if (*l == 32)
+ start = l + 1;
+ if (*l < 0)
+ break;
+ l++;
+ }
+ if (*l)
+ {
+ numQuotes = 1;
+ while (*l)
+ {
+ /* The encoded word must be limited to 75 character */
+ for (i = 0; i < 16; i++)
+ if (*l == especials[i])
+ numQuotes++;
+ if (*l < 0)
+ numQuotes++;
+ /* Stop after 58 = 75 - 17 characters or at "<user@host..." */
+ if (l - start + 2 * numQuotes >= 58 || *l == 60)
+ break;
+ l++;
+ }
+ if (*l)
+ {
+ stop = l - 1;
+ while (stop >= start && *stop != 32)
+ stop--;
+ if (stop <= start)
+ stop = l;
+ }
+ else
+ stop = l;
+ if (resultLen - rptr - 1 <= start - latin + 1 + 16 /* =?iso-88... */) {
+ resultLen += (start - latin + 1) * 2 + 20; // more space
+ result.resize(resultLen);
+ }
+ while (latin < start)
+ {
+ result[rptr++] = *latin;
+ latin++;
+ }
+ strcpy(&result[rptr], "=?iso-8859-1?q?"); rptr += 15;
+ if (resultLen - rptr - 1 <= 3*(stop - latin + 1)) {
+ resultLen += (stop - latin + 1) * 4 + 20; // more space
+ result.resize(resultLen);
+ }
+ while (latin < stop) // can add up to 3 chars/iteration
+ {
+ numQuotes = 0;
+ for (i = 0; i < 16; i++)
+ if (*latin == especials[i])
+ numQuotes = 1;
+ if (*latin < 0)
+ numQuotes = 1;
+ if (numQuotes)
+ {
+ result[rptr++] = '=';
+ hexcode = ((*latin & 0xF0) >> 4) + 48;
+ if (hexcode >= 58)
+ hexcode += 7;
+ result[rptr++] = hexcode;
+ hexcode = (*latin & 0x0F) + 48;
+ if (hexcode >= 58)
+ hexcode += 7;
+ result[rptr++] = hexcode;
+ }
+ else
+ {
+ result[rptr++] = *latin;
+ }
+ latin++;
+ }
+ result[rptr++] = '?';
+ result[rptr++] = '=';
+ }
+ else
+ {
+ while (*latin)
+ {
+ if (rptr == resultLen - 1) {
+ resultLen += 30;
+ result.resize(resultLen);
+ }
+ result[rptr++] = *latin;
+ latin++;
+ }
+ }
+ }
+ result[rptr] = 0;
+ //free (latinStart);
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+const TQString
+rfcDecoder::encodeRFC2231String (const TQString & _str)
+{
+ if (_str.isEmpty ())
+ return _str;
+ signed char *latin = (signed char *) calloc (1, _str.length () + 1);
+ char *latin_us = (char *) latin;
+ strcpy (latin_us, _str.latin1 ());
+ signed char *l = latin;
+ char hexcode;
+ int i;
+ bool quote;
+ while (*l)
+ {
+ if (*l < 0)
+ break;
+ l++;
+ }
+ if (!*l) {
+ free(latin);
+ return _str.ascii ();
+ }
+ TQCString result;
+ l = latin;
+ while (*l)
+ {
+ quote = *l < 0;
+ for (i = 0; i < 16; i++)
+ if (*l == especials[i])
+ quote = true;
+ if (quote)
+ {
+ result += "%";
+ hexcode = ((*l & 0xF0) >> 4) + 48;
+ if (hexcode >= 58)
+ hexcode += 7;
+ result += hexcode;
+ hexcode = (*l & 0x0F) + 48;
+ if (hexcode >= 58)
+ hexcode += 7;
+ result += hexcode;
+ }
+ else
+ {
+ result += *l;
+ }
+ l++;
+ }
+ free (latin);
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+const TQString
+rfcDecoder::decodeRFC2231String (const TQString & _str)
+{
+ int p = _str.find ('\'');
+
+ //see if it is an rfc string
+ if (p < 0)
+ return _str;
+
+ int l = _str.findRev ('\'');
+
+ //second is language
+ if (p >= l)
+ return _str;
+
+ //first is charset or empty
+ TQString charset = _str.left (p);
+ TQString st = _str.mid (l + 1);
+ TQString language = _str.mid (p + 1, l - p - 1);
+
+ //kdDebug(7116) << "Charset: " << charset << " Language: " << language << endl;
+
+ char ch, ch2;
+ p = 0;
+ while (p < (int) st.length ())
+ {
+ if (st.at (p) == 37)
+ {
+ ch = st.at (p + 1).latin1 () - 48;
+ if (ch > 16)
+ ch -= 7;
+ ch2 = st.at (p + 2).latin1 () - 48;
+ if (ch2 > 16)
+ ch2 -= 7;
+ st.at (p) = ch * 16 + ch2;
+ st.remove (p + 1, 2);
+ }
+ p++;
+ }
+ return st;
+}