void VObject::fromNativeEncoding() { bool is_30 = !wcscmp(getVersion(), TEXT("3.0")); for (int index = propertiesCount() - 1; index >= 0; index--) { VProperty *vprop = getProperty(index); if (vprop->equalsEncoding(TEXT("QUOTED-PRINTABLE"))) { // remove this, we cannot recreate it vprop->removeParameter(TEXT("ENCODING")); } WCHAR *native = vprop->getValue(); // in the worst case every comma/linebreak is replaced with // two characters and each \n with =0D=0A WCHAR *foreign = new WCHAR[6 * wcslen(native) + 1]; WCHAR curr; int in = 0, out = 0; // line break is encoded with either one or two // characters on different platforms const int linebreaklen = wcslen(SYNC4J_LINEBREAK); // use backslash for special characters, // if necessary do quoted-printable encoding bool doquoted = !is_30 && wcsstr(native, SYNC4J_LINEBREAK) != NULL; while ((curr = native[in]) != 0) { in++; switch (curr) { case ',': if (!is_30) { // normal character foreign[out] = curr; out++; break; } // no break! case ';': case '\\': foreign[out] = '\\'; out++; foreign[out] = curr; out++; break; case SEMICOLON_REPLACEMENT: foreign[out] = ';'; out++; break; default: if (doquoted && (curr == '=' || (unsigned char)curr >= 128)) { // escape = and non-ASCII characters swprintf(foreign + out, 4, TEXT("=%02X"), (unsigned int)(unsigned char)curr); out += 3; } else if (!wcsncmp(native + in - 1, SYNC4J_LINEBREAK, linebreaklen)) { // line break if (is_30) { foreign[out] = '\\'; out++; foreign[out] = 'n'; out++; } else { wcscpy(foreign + out, TEXT("=0D=0A")); out += 6; } in += linebreaklen - 1; } else { foreign[out] = curr; out++; } break; } } foreign[out] = 0; vprop->setValue(foreign); delete [] foreign; if (doquoted) { // we have used quoted-printable encoding vprop->addParameter(TEXT("ENCODING"), TEXT("QUOTED-PRINTABLE")); } } }
void VObject::toNativeEncoding() { bool is_30 = !wcscmp(getVersion(), TEXT("3.0")); // line break is encoded with either one or two // characters on different platforms const int linebreaklen = wcslen(SYNC4J_LINEBREAK); for (int index = propertiesCount() - 1; index >= 0; index--) { VProperty *vprop = getProperty(index); WCHAR *foreign = vprop->getValue(); // the native encoding is always shorter than the foreign one WCHAR *native = new WCHAR[wcslen(foreign) + 1]; if (vprop->equalsEncoding(TEXT("QUOTED-PRINTABLE"))) { int in = 0, out = 0; WCHAR curr; // this is a very crude quoted-printable decoder, // based on Wikipedia's explanation of quoted-printable while ((curr = foreign[in]) != 0) { in++; if (curr == '=') { WCHAR values[2]; values[0] = foreign[in]; in++; if (!values[0]) { // incomplete?! break; } values[1] = foreign[in]; in++; if (values[0] == '\r' && values[1] == '\n') { // soft line break, ignore it } else { native[out] = (hex2int(values[0]) << 4) | hex2int(values[1]); out++; // replace \r\n with \n? if ( linebreaklen == 1 && out >= 2 && native[out - 2] == '\r' && native[out - 1] == '\n' ) { native[out - 2] = SYNC4J_LINEBREAK[0]; out--; } // the conversion to wchar on Windows is // probably missing here } } else { native[out] = curr; out++; } } native[out] = 0; out++; } else { wcscpy(native, foreign); } // decode escaped characters after backslash: // \n is line break only in 3.0 WCHAR curr; int in = 0, out = 0; while ((curr = native[in]) != 0) { in++; switch (curr) { case '\\': curr = native[in]; in++; switch (curr) { case 'n': if (is_30) { // replace with line break wcsncpy(native + out, SYNC4J_LINEBREAK, linebreaklen); out += linebreaklen; } else { // normal escaped character native[out] = curr; out++; } break; case 0: // unexpected end of string break; default: // just copy next character native[out] = curr; out++; break; } break; case ';': // field separator - must replace with something special // so that we can encode it again in fromNativeEncoding() native[out] = SEMICOLON_REPLACEMENT; out++; break; default: native[out] = curr; out++; } } native[out] = 0; out++; // charset handling: // - doesn't exist at the moment, vCards have to be in ASCII or UTF-8 // - an explicit CHARSET parameter is removed because its parameter // value might differ between 2.1 and 3.0 (quotation marks allowed in // 3.0 but not 2.1) and thus would require extra code to convert it; // when charsets really get supported this needs to be addressed WCHAR *charset = vprop->getParameterValue(TEXT("CHARSET")); if (charset) { // proper decoding of the value and the property value text // would go here, for the time being we just remove the // value if (_wcsicmp(charset, TEXT("UTF-8")) && _wcsicmp(charset, TEXT("\"UTF-8\""))) { LOG.error("ignoring unsupported charset"); } vprop->removeParameter(TEXT("CHARSET")); } vprop->setValue(native); delete [] native; } }