File indexing completed on 2024-05-12 05:17:16

0001 /**********************************************************************
0002  *
0003  *   rfccodecs.cpp - handler for various rfc/mime encodings
0004  *   SPDX-FileCopyrightText: 2000 s .carstens@gmx.de
0005  *
0006  *   SPDX-License-Identifier: LGPL-2.0-or-later
0007  *
0008  *********************************************************************/
0009 /**
0010  * @file
0011  * This file is part of the IMAP support library and defines the
0012  * RfcCodecs class.
0013  *
0014  * @brief
0015  * Defines the RfcCodecs class.
0016  *
0017  * @author Sven Carstens
0018  */
0019 
0020 #include "rfccodecs.h"
0021 
0022 #include <ctype.h>
0023 #include <sys/types.h>
0024 
0025 #include <stdio.h>
0026 #include <stdlib.h>
0027 
0028 #include <QByteArray>
0029 #include <QLatin1Char>
0030 
0031 using namespace KIMAP;
0032 
0033 // This part taken from rfc 2192 IMAP URL Scheme. C. Newman. September 1997.
0034 // adapted to QT-Toolkit by Sven Carstens <s.carstens@gmx.de> 2000
0035 
0036 //@cond PRIVATE
0037 static const unsigned char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
0038 #define UNDEFINED 64
0039 #define MAXLINE 76
0040 static const char especials[17] = "()<>@,;:\"/[]?.= ";
0041 
0042 /* UTF16 definitions */
0043 #define UTF16MASK 0x03FFUL
0044 #define UTF16SHIFT 10
0045 #define UTF16BASE 0x10000UL
0046 #define UTF16HIGHSTART 0xD800UL
0047 #define UTF16HIGHEND 0xDBFFUL
0048 #define UTF16LOSTART 0xDC00UL
0049 #define UTF16LOEND 0xDFFFUL
0050 //@endcond
0051 
0052 //-----------------------------------------------------------------------------
0053 QByteArray KIMAP::decodeImapFolderName(const QByteArray &inSrc)
0054 {
0055     unsigned char c;
0056     unsigned char i;
0057     unsigned char bitcount;
0058     unsigned long ucs4;
0059     unsigned long utf16;
0060     unsigned long bitbuf;
0061     unsigned char base64[256];
0062     unsigned char utf8[6];
0063     unsigned int srcPtr = 0;
0064     QByteArray dst;
0065     QByteArray src = inSrc;
0066     uint srcLen = inSrc.length();
0067 
0068     /* initialize modified base64 decoding table */
0069     memset(base64, UNDEFINED, sizeof(base64));
0070     for (i = 0; i < sizeof(base64chars); ++i) {
0071         base64[(int)base64chars[i]] = i;
0072     }
0073 
0074     /* loop until end of string */
0075     while (srcPtr < srcLen) {
0076         c = src[srcPtr++];
0077         /* deal with literal characters and &- */
0078         if (c != '&' || src[srcPtr] == '-') {
0079             /* encode literally */
0080             dst += c;
0081             /* skip over the '-' if this is an &- sequence */
0082             if (c == '&') {
0083                 srcPtr++;
0084             }
0085         } else {
0086             /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
0087             bitbuf = 0;
0088             bitcount = 0;
0089             ucs4 = 0;
0090             while ((c = base64[(unsigned char)src[srcPtr]]) != UNDEFINED) {
0091                 ++srcPtr;
0092                 bitbuf = (bitbuf << 6) | c;
0093                 bitcount += 6;
0094                 /* enough bits for a UTF-16 character? */
0095                 if (bitcount >= 16) {
0096                     bitcount -= 16;
0097                     utf16 = (bitcount ? bitbuf >> bitcount : bitbuf) & 0xffff;
0098                     /* convert UTF16 to UCS4 */
0099                     if (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND) {
0100                         ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT;
0101                         continue;
0102                     } else if (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND) {
0103                         ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
0104                     } else {
0105                         ucs4 = utf16;
0106                     }
0107                     /* convert UTF-16 range of UCS4 to UTF-8 */
0108                     if (ucs4 <= 0x7fUL) {
0109                         utf8[0] = ucs4;
0110                         i = 1;
0111                     } else if (ucs4 <= 0x7ffUL) {
0112                         utf8[0] = 0xc0 | (ucs4 >> 6);
0113                         utf8[1] = 0x80 | (ucs4 & 0x3f);
0114                         i = 2;
0115                     } else if (ucs4 <= 0xffffUL) {
0116                         utf8[0] = 0xe0 | (ucs4 >> 12);
0117                         utf8[1] = 0x80 | ((ucs4 >> 6) & 0x3f);
0118                         utf8[2] = 0x80 | (ucs4 & 0x3f);
0119                         i = 3;
0120                     } else {
0121                         utf8[0] = 0xf0 | (ucs4 >> 18);
0122                         utf8[1] = 0x80 | ((ucs4 >> 12) & 0x3f);
0123                         utf8[2] = 0x80 | ((ucs4 >> 6) & 0x3f);
0124                         utf8[3] = 0x80 | (ucs4 & 0x3f);
0125                         i = 4;
0126                     }
0127                     /* copy it */
0128                     for (c = 0; c < i; ++c) {
0129                         dst += utf8[c];
0130                     }
0131                 }
0132             }
0133             /* skip over trailing '-' in modified UTF-7 encoding */
0134             if (src[srcPtr] == '-') {
0135                 ++srcPtr;
0136             }
0137         }
0138     }
0139     return dst;
0140 }
0141 
0142 QString KIMAP::decodeImapFolderName(const QString &inSrc)
0143 {
0144     return QString::fromUtf8(decodeImapFolderName(inSrc.toUtf8()).constData());
0145 }
0146 
0147 //-----------------------------------------------------------------------------
0148 
0149 QByteArray KIMAP::quoteIMAP(const QByteArray &src)
0150 {
0151     int len = src.length();
0152     QByteArray result;
0153     result.reserve(2 * len);
0154     for (int i = 0; i < len; i++) {
0155         if (src[i] == '"' || src[i] == '\\') {
0156             result += '\\';
0157         }
0158         result += src[i];
0159     }
0160     result.squeeze();
0161     return result;
0162 }
0163 
0164 QString KIMAP::quoteIMAP(const QString &src)
0165 {
0166     uint len = src.length();
0167     QString result;
0168     result.reserve(2 * len);
0169     for (unsigned int i = 0; i < len; i++) {
0170         if (src[i] == QLatin1Char('"') || src[i] == QLatin1Char('\\')) {
0171             result += QLatin1Char('\\');
0172         }
0173         result += src[i];
0174     }
0175     // result.squeeze(); - unnecessary and slow
0176     return result;
0177 }
0178 
0179 //-----------------------------------------------------------------------------
0180 QString KIMAP::encodeImapFolderName(const QString &inSrc)
0181 {
0182     return QString::fromUtf8(encodeImapFolderName(inSrc.toUtf8()).constData());
0183 }
0184 
0185 QByteArray KIMAP::encodeImapFolderName(const QByteArray &inSrc)
0186 {
0187     unsigned int utf8pos;
0188     unsigned int utf8total;
0189     unsigned int c;
0190     unsigned int utf7mode;
0191     unsigned int bitstogo;
0192     unsigned int utf16flag;
0193     unsigned int ucs4;
0194     unsigned int bitbuf;
0195     QByteArray src = inSrc;
0196     QByteArray dst;
0197 
0198     int srcPtr = 0;
0199     utf7mode = 0;
0200     utf8total = 0;
0201     bitstogo = 0;
0202     utf8pos = 0;
0203     bitbuf = 0;
0204     ucs4 = 0;
0205     while (srcPtr < src.length()) {
0206         c = (unsigned char)src[srcPtr++];
0207         /* normal character? */
0208         if (c >= ' ' && c <= '~') {
0209             /* switch out of UTF-7 mode */
0210             if (utf7mode) {
0211                 if (bitstogo) {
0212                     dst += base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
0213                     bitstogo = 0;
0214                 }
0215                 dst += '-';
0216                 utf7mode = 0;
0217             }
0218             dst += c;
0219             /* encode '&' as '&-' */
0220             if (c == '&') {
0221                 dst += '-';
0222             }
0223             continue;
0224         }
0225         /* switch to UTF-7 mode */
0226         if (!utf7mode) {
0227             dst += '&';
0228             utf7mode = 1;
0229         }
0230         /* Encode US-ASCII characters as themselves */
0231         if (c < 0x80) {
0232             ucs4 = c;
0233             utf8total = 1;
0234         } else if (utf8total) {
0235             /* save UTF8 bits into UCS4 */
0236             ucs4 = (ucs4 << 6) | (c & 0x3FUL);
0237             if (++utf8pos < utf8total) {
0238                 continue;
0239             }
0240         } else {
0241             utf8pos = 1;
0242             if (c < 0xE0) {
0243                 utf8total = 2;
0244                 ucs4 = c & 0x1F;
0245             } else if (c < 0xF0) {
0246                 utf8total = 3;
0247                 ucs4 = c & 0x0F;
0248             } else {
0249                 /* NOTE: can't convert UTF8 sequences longer than 4 */
0250                 utf8total = 4;
0251                 ucs4 = c & 0x03;
0252             }
0253             continue;
0254         }
0255         /* loop to split ucs4 into two utf16 chars if necessary */
0256         utf8total = 0;
0257         do {
0258             if (ucs4 >= UTF16BASE) {
0259                 ucs4 -= UTF16BASE;
0260                 bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT) + UTF16HIGHSTART);
0261                 ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART;
0262                 utf16flag = 1;
0263             } else {
0264                 bitbuf = (bitbuf << 16) | ucs4;
0265                 utf16flag = 0;
0266             }
0267             bitstogo += 16;
0268             /* spew out base64 */
0269             while (bitstogo >= 6) {
0270                 bitstogo -= 6;
0271                 dst += base64chars[(bitstogo ? (bitbuf >> bitstogo) : bitbuf) & 0x3F];
0272             }
0273         } while (utf16flag);
0274     }
0275     /* if in UTF-7 mode, finish in ASCII */
0276     if (utf7mode) {
0277         if (bitstogo) {
0278             dst += base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
0279         }
0280         dst += '-';
0281     }
0282     return quoteIMAP(dst);
0283 }