//-----------------------------------------------------------------------------
/*
 * Description: This file contains the class AxMailTools
 *
 * See the file README in the main directory for a description of
 * this software, copyright information, and how to reach the author.
 *
 * Author: alex
 * Date:   12.12.2003
 *
 * Last modfied:
 *   $Author: alex $
 *   $Date: 2004/05/31 11:33:51 $
 *   $Revision: 1.4 $
 */
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
//     includes
//-----------------------------------------------------------------------------

//----- multiple defs of swap() workaround ------------------------------------
#include "AxPluginGlobals.h"

//----- qt --------------------------------------------------------------------
//----- CORBA -----------------------------------------------------------------
//----- ICE -------------------------------------------------------------------
//----- C++ -------------------------------------------------------------------
#include <string>
#include <algorithm>
#include <cctype>

//----- C ---------------------------------------------------------------------
//----- AxLib -----------------------------------------------------------------
#include <Ax/Tools/Trace.h>
// #include <Ax/Tools/TraceOn.h>

//----- local -----------------------------------------------------------------
#include "AxMailTools.h"

//=============================================================================
//     constants
//=============================================================================

 ///< non-printable in VDRs fontOsd (attn: NOT space)
#define NPRT char(0x80)

 ///< question mark in VDRs fontOsd
#define REPL char(0x3F)

//-----------------------------------------------------------------------
//     Map_Base64
//-----------------------------------------------------------------------
/** Mapping for Base64 decoding
 */
const short AxMailTools::Map_Base64[] =
{
//            0   1   2   3   4   5   6   7   8   9,
/*  00 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  10 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  20 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  30 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  40 */    -1, -1, -1, 62, -1, -1, -1, 63, 52, 53,
/*  50 */    54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
/*  60 */    -1, -2, -1, -1, -1,  0,  1,  2,  3,  4,
/*  70 */     5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
/*  80 */    15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
/*  90 */    25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
/* 100 */    29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
/* 110 */    39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
/* 120 */    49, 50, 51, -1, -1, -1, -1, -1, -1, -1,
/* 130 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 140 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 150 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 160 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 170 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 180 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 190 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 200 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 210 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 220 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 230 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 240 */    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* 250 */    -1, -1, -1, -1, -1, -1
};

//---------------------------------------------------------------------------------------------------------
//     Map_ISO_8859_1_to_VDR
//---------------------------------------------------------------------------------------------------------
/** Maps ISO-8859-1 to VDRs characters
 *
 *  After having compared VDR's fontosd.c and ISO-8559-1 I assume,
 *  that VDR uses a ISO-8559-1 font for it's osd.
 *
 *  Therefore this mapping is essantially a one-to-one mapping and
 *  only there for easier use.
 *
 *  0x00 - 0x7E : identical to VDRs fontOsd
 *  0x7F - 0x9F : control-codes replaced by vdr-nonprintable
 *  0xA0 - 0xFF : identical to VDRs fontOsd
 *
 *  (see <http://en.wikipedia.org/wiki/ISO_8859-1>)
 *
 */
const unsigned char AxMailTools::Map_ISO_8859_1_to_VDR[] =
{
//            x0    x1    x2    x3    x4    x5    x6    x7    x8    x9,   xA,   xB,   xC,   xD,   xE,   xF
/* 0x00 */  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 */  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
/* 0x20 */  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
/* 0x30 */  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
/* 0x40 */  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
/* 0x50 */  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
/* 0x60 */  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
/* 0x70 */  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, NPRT,
/* 0x80 */  NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT,
/* 0x90 */  NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT,
/* 0xA0 */  0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
/* 0xB0 */  0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
/* 0xC0 */  0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
/* 0xD0 */  0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
/* 0xE0 */  0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
/* 0xF0 */  0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};

//---------------------------------------------------------------------------------------------------------
//     Map_ISO_8859_X_to_VDR
//---------------------------------------------------------------------------------------------------------
/** Maps ISO-8859-* to VDRs characters
 *
 *  0x00 - 0x7E : identical to VDRs fontOsd
 *  0x7F - 0x9F : control-codes replaced by vdr-nonprintable
 *  0xA0 - 0xFF : use the 'replacement' REPL '?', as these can't be displayed.
 *
 *  From <http://en.wikipedia.org/wiki/ISO_8859>:
 *
 *  ISO 8859 sought to remedy this problem by extending 7-bit ASCII
 *  to eight bits, allowing positions for another 128 characters. However,
 *  more characters were needed to achieve this than could fit in a single
 *  8-bit character encoding, so several were developed. All the encodings,
 *  however, encode the first 128 positions (from 0 to 127) in the same way
 *  as each other and the same way as ASCII. Positions 128 to 159 contain
 *  control characters. The upper 96 code points of each ISO 8859 encoding
 *  differ.
 *
 */
const unsigned char AxMailTools::Map_ISO_8859_X_to_VDR[] =
{
//            x0    x1    x2    x3    x4    x5    x6    x7    x8    x9,   xA,   xB,   xC,   xD,   xE,   xF
/* 0x00 */  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 */  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
/* 0x20 */  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
/* 0x30 */  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
/* 0x40 */  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
/* 0x50 */  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
/* 0x60 */  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
/* 0x70 */  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, NPRT,
/* 0x80 */  NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT,
/* 0x90 */  NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT,
/* 0xA0 */  REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL,
/* 0xB0 */  REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL,
/* 0xC0 */  REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL,
/* 0xD0 */  REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL,
/* 0xE0 */  REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL,
/* 0xF0 */  REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL
};

//---------------------------------------------------------------------------------------------------------
//     Map_ISO_8859_15_to_VDR
//---------------------------------------------------------------------------------------------------------
/** Maps ISO-8859-15 to VDRs characters
 *
 *  The difference of ISO-8859-15 to ISO-8859-1 are eight characters.
 *  As these eight characters aren't present in VDRs fontOsd,
 *  I simply replaced the not-printable characters with question
 *  marks (0x3F in ISO-8859, const REPL).
 *
 *  Therefore this table is essantially the same as the ISO_8895_1 with
 *  the exception of eight posistions which use REPL ('?') as these
 *  characters can't be displayed.
 *
 *  0x00 - 0x7E : identical to VDRs fontOsd
 *  0x7F - 0x9F : control-codes replaced by vdr-nonprintable
 *  0xA0 - 0xFF : identical to VDRs fontOsd,
 *                replaced eight characters with REPL ('?')
 *
 *  From <http://en.wikipedia.org/wiki/ISO_8859-15> :
 *
 *  ISO 8859-15 (also known as "Latin 9") is part 15 of ISO 8859, a
 *  standard character encoding defined by ISO. It encodes characters
 *  as 8 bits and can be used to represent the alphabet and other important
 *  characters for storing English, French, German, Spanish and Portuguese
 *  (among other western European languages) texts on computers. It is
 *  identical to ISO-8859-1 except for these eight replacements:
 *
 */
const unsigned char AxMailTools::Map_ISO_8859_15_to_VDR[] =
{
//            x0    x1    x2    x3    x4    x5    x6    x7    x8    x9,   xA,   xB,   xC,   xD,   xE,   xF
/* 0x00 */  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 */  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
/* 0x20 */  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
/* 0x30 */  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
/* 0x40 */  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
/* 0x50 */  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
/* 0x60 */  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
/* 0x70 */  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, NPRT,
/* 0x80 */  NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT,
/* 0x90 */  NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT, NPRT,
/* 0xA0 */  0xA0, 0xA1, 0xA2, 0xA3, REPL, 0xA5, REPL, 0xA7, REPL, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
/* 0xB0 */  0xB0, 0xB1, 0xB2, 0xB3, REPL, 0xB5, 0xB6, 0xB7, REPL, 0xB9, 0xBA, 0xBB, REPL, REPL, REPL, 0xBF,
/* 0xC0 */  0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
/* 0xD0 */  0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
/* 0xE0 */  0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
/* 0xF0 */  0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};

//---------------------------------------------------------------------------------------------------------
//     Map_Windows_1252_to_VDR
//---------------------------------------------------------------------------------------------------------
/** Maps Windows_1252 to VDRs characters
 *
 *  The difference of Windows-1252 to ISO-8859-1 are in the range of
 *  0x80 (128) to 0x9f (159). As VDR's fontOsd (ISO-8859-1) has no
 *  characters in this range, I simply replaced the not-printable
 *  codes (0x80-0x9f) with question marks (0x3F in ISO-8859, const REPL).
 *
 *  Therefore this table is essantially the same as the ISO_8895_1 with
 *  the exception of eight posistions which use REPL ('?') as these
 *  characters can't be displayed.
 *
 *  0x00 - 0x7E : identical to VDRs fontOsd
 *  0x7F - 0x9F : windows has printable here,
 *                replaced eight characters with REPL ('?')
 *  0xA0 - 0xFF : identical to VDRs fontOsd,
 *
 *  From <http://en.wikipedia.org/wiki/ISO_8859-1> :
 *
 * ISO-8859-1 vs Windows ANSI
 *
 * The legacy components of Microsoft Windows use, by default, an encoding
 * that differs from ISO-8859-1, using graphic characters rather than control
 * characters in the 80-9F range. Windows calls it ANSI generically, but
 * depending on where the operating system was sold, the character set will
 * have another name, e.g. CP1252 in the US and Western European markets,
 * with the IANA-approved name Windows-1252.
 *
 */
const unsigned char AxMailTools::Map_Windows_1252_to_VDR[] =
{
//            x0    x1    x2    x3    x4    x5    x6    x7    x8    x9,   xA,   xB,   xC,   xD,   xE,   xF
/* 0x00 */  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
/* 0x10 */  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
/* 0x20 */  0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
/* 0x30 */  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
/* 0x40 */  0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
/* 0x50 */  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
/* 0x60 */  0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
/* 0x70 */  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, REPL,
/* 0x80 */  REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL,
/* 0x90 */  REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL, REPL,
/* 0xA0 */  0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
/* 0xB0 */  0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
/* 0xC0 */  0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
/* 0xD0 */  0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
/* 0xE0 */  0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
/* 0xF0 */  0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};

//=============================================================================
//     class AxMailTools
//=============================================================================

//-----------------------------------------------------------------------
//     AxMailTools::decodeEncodedWords()
//-----------------------------------------------------------------------
bool AxMailTools::decodeEncodedWords( const std::string  &theSrc
                                    ,       std::string  &theDst
                                    ,       AxEncStat    *theStat
                                    )
{
  wsdebug(("> AxMailTools::decodeEncodedWords() Src: '%s'", theSrc.c_str()));
  bool fOK      = true;
  bool fHasCode = false;
  std::string::size_type aPosStart = 0;

  theDst = theSrc;

  while ((aPosStart = theDst.find("=?", aPosStart)) != std::string::npos)
  {
    std::string::size_type aStrLen = aStrLen   = theDst.length();
    std::string::size_type aPosQ1  = aPosStart == std::string::npos || aPosStart + 2 > aStrLen ? std::string::npos : theDst.find("?" , aPosStart + 2);
    std::string::size_type aPosQ2  = aPosQ1    == std::string::npos || aPosQ1    + 1 > aStrLen ? std::string::npos : theDst.find("?" , aPosQ1    + 1);
    std::string::size_type aPosEnd = aPosQ2    == std::string::npos || aPosQ2    + 1 > aStrLen ? std::string::npos : theDst.find("?=", aPosQ2    + 1);

    wsdebug(("  AxMailTools::decodeEncodedWords() Start: %3d  Q1: %3d  Q2: %3d  End: %3d  Len: %d"
          , aPosStart, aPosQ1, aPosQ2, aPosEnd, aStrLen
          ));
    wsdebug(("  AxMailTools::decodeEncodedWords() Remaining: '%s'", &(theDst.c_str()[aPosStart])));

    if ( aPosStart != std::string::npos
      && aPosQ1    != std::string::npos
      && aPosQ2    != std::string::npos
      && aPosEnd   != std::string::npos
       )
    {
      wsdebug(("  AxMailTools::decodeEncodedWords() Start: %3d  Q1: %3d  Q2: %3d  End: %3d  Len: %d"
             , aPosStart, aPosQ1, aPosQ2, aPosEnd, aStrLen
             ));

      std::string aCharSet  = theDst.substr(aPosStart + 2, aPosQ1  - aPosStart - 2);
      std::string aEncoding = theDst.substr(aPosQ1    + 1, aPosQ2  - aPosQ1    - 1);
      std::string aText     = theDst.substr(aPosQ2    + 1, aPosEnd - aPosQ2    - 1);

      // convert charset and encoding to upper case
      std::transform(aCharSet .begin(), aCharSet .end(), aCharSet .begin(), toupper);
      std::transform(aEncoding.begin(), aEncoding.end(), aEncoding.begin(), toupper);

      wsdebug(("  AxMailTools::decodeEncodedWords() charset: '%s'  encoding: '%s'  Text: '%s'"
             , aCharSet.c_str(), aEncoding.c_str(), aText.c_str()
             ));

      fHasCode = true;

      std::string aNewText;

      if (!decodeEncodedWord(aCharSet, aEncoding, aText, aNewText, theStat))
      {
        wsinfo(("AxMailTools::decodeEncodedWords() unable to decode: charset: %-14s  encoding: '%s'  Text: '%s'"
              , aCharSet.c_str(), aEncoding.c_str(), aText.c_str()
              ));
        // continue after the current encoded word
        aPosStart = aPosEnd;
        fOK = false;
      }
      else
      {
        wsdebug(("  AxMailTools::decodeEncodedWords() replacing with: '%s'"
               , aNewText.c_str()
               ));

        // replace the current encoded word
        theDst.replace(aPosStart, aPosEnd - aPosStart + 2, aNewText);

        aPosStart += aNewText.length();
      } // if
    }
    else if (aPosEnd == std::string::npos)
    {
      // no furhter tries needed
      break;
    } // if
  } // while

  wsdebug(("  AxMailTools::decodeEncodedWords() fOK: %d", fOK));
  wsdebug(("< AxMailTools::decodeEncodedWords() Dst: '%s'", theDst.c_str()));
  return fOK;
} // AxMailTools::decodeEncodedWords()

//-----------------------------------------------------------------------
//     AxMailTools::decodeEncodedWord()
//-----------------------------------------------------------------------
bool AxMailTools::decodeEncodedWord( const std::string  &theCharSet
                                   , const std::string  &theEncoding
                                   , const std::string  &theSrc
                                   ,       std::string  &theDst
                                   ,       AxEncStat    *theStat
                                   )
{
  bool fOK = false;
  wsdebug(("> AxMailTools::decodeEncodedWord() charset: '%s'  encoding: '%s'  Text: '%s'"
        , theCharSet.c_str(), theEncoding.c_str(), theSrc.c_str()
        ));

  if (theEncoding == "Q")
  {
    // Q = quoted encoding
    if      (theCharSet == "ISO-8859-1"          ) { fOK = decodeQuoted(Map_ISO_8859_1_to_VDR  , theSrc, theDst); }
    else if (theCharSet == "ISO-8859-15"         ) { fOK = decodeQuoted(Map_ISO_8859_15_to_VDR , theSrc, theDst); }
    else if (theCharSet == "WINDOWS-1252"        ) { fOK = decodeQuoted(Map_Windows_1252_to_VDR, theSrc, theDst); }
    else if (theCharSet.substr(0,8) == "ISO-8859") { fOK = decodeQuoted(Map_ISO_8859_X_to_VDR  , theSrc, theDst); }
    else
    {
      theDst = theSrc;
    } // if
  }
  else if (theEncoding == "B")
  {
    // B = Base64 encoding
    if      (theCharSet == "ISO-8859-1"          ) { fOK = decodeBase64(Map_ISO_8859_1_to_VDR  , theSrc, theDst); }
    else if (theCharSet == "ISO-8859-15"         ) { fOK = decodeBase64(Map_ISO_8859_15_to_VDR , theSrc, theDst); }
    else if (theCharSet == "Windows-1252"        ) { fOK = decodeBase64(Map_Windows_1252_to_VDR, theSrc, theDst); }
    else if (theCharSet.substr(0,8) == "ISO-8859") { fOK = decodeBase64(Map_ISO_8859_X_to_VDR  , theSrc, theDst); }
    else
    {
      theDst = theSrc;
    } // if
  }
  else
  {
    wsdebug(("  AxMailTools::decodeEncodedWord() invalid encoding!"));
    // invalid encoding
    theDst = theSrc;
  } // if

  //----- add to statistics map ----
  if (theStat)
  {
    if (fOK) theStat->CountCodesOK ++;
    else     theStat->CountCodesNOK++;

    std::string aKey = theCharSet
                     + std::string(theCharSet.length() <= 14 ? 14 - theCharSet.length() : 0, ' ')
                     + std::string(":")
                     + theEncoding
                     + std::string(":")
                     + (fOK ? std::string("OK") : std::string("NOK"))
                     ;

    AxEncStatMap::iterator aPos = theStat->MapEnc.find(aKey);
    if (aPos == theStat->MapEnc.end())  { theStat->MapEnc[aKey] = 1; }
    else                                { aPos->second++;            }
  } // if

  wsdebug(("< AxMailTools::decodeEncodedWord() charset: '%s'  encoding: '%s'  Text: '%s'  fOK: %d"
        , theCharSet.c_str(), theEncoding.c_str(), theDst.c_str(), fOK
        ));
  return fOK;
} // AxMailTools::decodeEncodedWord()

//-----------------------------------------------------------------------
//     AxMailTools::decodeQuoted()
//-----------------------------------------------------------------------
bool AxMailTools::decodeQuoted(const unsigned char *theTable, const std::string &theSrc, std::string &theDst)
{
  wsdebug(("> AxMailTools::decodeQuoted() Src: '%s'", theSrc.c_str()));

  bool fGlobalOK = true;

  theDst = "";

  for (std::string::const_iterator aSrcIter  = theSrc.begin();
                                   aSrcIter != theSrc.end();
                                 ++aSrcIter)
  {
    if (*aSrcIter == '=')
    {
      if ( aSrcIter + 1 != theSrc.end()
        && aSrcIter + 2 != theSrc.end()
         )
      {
        char aC1 = toupper(*(aSrcIter + 1));
        char aC2 = toupper(*(aSrcIter + 2));
        int  aCC = 0;

        bool fOK = true;

        if      (aC1 >= '0' && aC1 <= '9') { aCC  = (aC1 - '0'     ) * 16; }
        else if (aC1 >= 'A' && aC1 <= 'F') { aCC  = (aC1 - 'A' + 10) * 16; }
        else                               { fOK  = false;                 }

        if      (aC2 >= '0' && aC2 <= '9') { aCC +=  aC2 - '0'      ; }
        else if (aC2 >= 'A' && aC2 <= 'F') { aCC += (aC2 - 'A' + 10); }
        else                               { fOK  = false;            }

        if (fOK)
        {
          // a character-table could be used
          theDst += char(theTable[aCC]);

          aSrcIter++;
          aSrcIter++;
          // src will be incremented once again in for
        }
        else
        {
          theDst += char(*aSrcIter); aSrcIter++;
          theDst += char(*aSrcIter); aSrcIter++;
          theDst += char(*aSrcIter); // aSrcIter will be incremented once again in for
          fGlobalOK = false;
        } // if
      }
      else
      {
        theDst += char(*aSrcIter); // aSrcIter will be incremented once again in for
      } // if
    }
    else if (*aSrcIter == '_')
    {
      // 20 is (mostly) space
//      theDst += char(20);  // aSrcIter will be incremented once again in for
      theDst += char(*aSrcIter); // aSrcIter will be incremented once again in for
    }
    else if (*aSrcIter != '_' && *aSrcIter != '?' && *aSrcIter != '=')
    {
      theDst += char(*aSrcIter); // aSrcIter will be incremented once again in for
    }
    else
    {
      wswarn(("AxMailTools::decodeQuoted() unexpected character '%c", *aSrcIter));
      theDst += char(*aSrcIter); // aSrcIter will be incremented once again in for
    } // if
  } // for#

  wsdebug(("< AxMailTools::decodeQuoted() Dst: '%s'  fOK: %d", theDst.c_str(), fGlobalOK));
  return fGlobalOK;
} // AxMailTools::decodeQuoted()

//-----------------------------------------------------------------------
//     AxMailTools::decodeBase64()
//-----------------------------------------------------------------------
bool AxMailTools::decodeBase64(const unsigned char *theTable, const std::string &theSrc, std::string &theDst)
{
  wsdebug(("> AxMailTools::decodeBase64() Src: '%s'", theSrc.c_str()));

  bool fGlobalOK = true;

  theDst = "";

  unsigned short anIndex = 0;
  unsigned long  aValue  = 0;

  for (std::string::const_iterator aSrcIter  = theSrc.begin();
                                   aSrcIter != theSrc.end();
                                 ++aSrcIter)
  {
    short aB64_1 = Map_Base64[int(*aSrcIter)];

    switch (aB64_1)
    {
      case -1: // invalid character
      {
        wswarn(("AxMailTools::decodeBase64() invalid character '%c' = 0x%02X in base64 encoding"
              ,     *aSrcIter
              , int(*aSrcIter)
              ));
        break;
      }
      case -2: // padding '=' received
      {
        break;
      }

      default:
      {
        anIndex++;
        aValue = (aValue << 6) + aB64_1;

        if (anIndex == 4)
        {
          theDst += char(theTable[((aValue >> 16) & 0xFF)]);
          theDst += char(theTable[((aValue >>  8) & 0xFF)]);
          theDst += char(theTable[((aValue >>  0) & 0xFF)]);

          aValue  = 0;
          anIndex = 0;
        } // if

        break;
      }
    } // switch
  } // for

  // process padding at end of code
  switch (anIndex)
  {
    case 2:
    {
      aValue = aValue >> 4;
      theDst += char(theTable[((aValue >> 0) & 0xFF)]);
      break;
    }
    case 3:
    {
      aValue = aValue >> 2;
      theDst += char(theTable[((aValue >>  8) & 0xFF)]);
      theDst += char(theTable[((aValue >>  0) & 0xFF)]);
      break;
    }
    case 0: // no final padding
    {
      break;
    }
    default:
    {
      wswarn(("AxMailTools::decodeBase64() invalid state (idx: %d) at end of decoding of '%s'"
            , anIndex, theSrc.c_str()
            ));
    }
  } // switch

  wsdebug(("< AxMailTools::decodeBase64() Dst: '%s'  fOK: %d", theDst.c_str(), fGlobalOK));
  return fGlobalOK;
} // AxMailTools::decodeBase64()
