Hello community, here is the log from the commit of package kcontacts for openSUSE:Factory checked in at 2017-05-22 10:39:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kcontacts (Old) and /work/SRC/openSUSE:Factory/.kcontacts.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kcontacts" Mon May 22 10:39:39 2017 rev:20 rq:495919 version:17.04.1 Changes: -------- --- /work/SRC/openSUSE:Factory/kcontacts/kcontacts.changes 2017-05-08 18:49:15.552726797 +0200 +++ /work/SRC/openSUSE:Factory/.kcontacts.new/kcontacts.changes 2017-05-22 10:39:41.810780238 +0200 @@ -1,0 +2,14 @@ +Wed May 17 20:36:00 CEST 2017 - lbeltr...@kde.org + +- Update to 17.04.1 + * New bugfix release + * For more details please see: + * https://www.kde.org/announcements/announce-applications-17.04.1.php +- Changes since 17.04.0: + * VCardParser: gain 1% of performance by using indexOf+mid instead of split('\n') + * Add missing include (make it happy jenkins) + * VCardParser: rewrite parser to use a state machine instead of split() + * VCardParser: extract class for parsing the current line + * Revert 1760aca (which commented out some unittests...). + +------------------------------------------------------------------- Old: ---- kcontacts-17.04.0.tar.xz New: ---- kcontacts-17.04.1.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kcontacts.spec ++++++ --- /var/tmp/diff_new_pack.IyEDR7/_old 2017-05-22 10:39:43.458547905 +0200 +++ /var/tmp/diff_new_pack.IyEDR7/_new 2017-05-22 10:39:43.458547905 +0200 @@ -20,7 +20,7 @@ %define kf5_version 5.5.0 Name: kcontacts -Version: 17.04.0 +Version: 17.04.1 Release: 0 %define kf5_version 5.26.0 # Latest stable Applications (e.g. 16.08 in KA, but 16.11.80 in KUA) ++++++ kcontacts-17.04.0.tar.xz -> kcontacts-17.04.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcontacts-17.04.0/CMakeLists.txt new/kcontacts-17.04.1/CMakeLists.txt --- old/kcontacts-17.04.0/CMakeLists.txt 2017-04-14 02:21:53.000000000 +0200 +++ new/kcontacts-17.04.1/CMakeLists.txt 2017-05-09 02:26:31.000000000 +0200 @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.0) -set(PIM_VERSION "5.5.0") +set(PIM_VERSION "5.5.1") project(KContacts VERSION ${PIM_VERSION}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcontacts-17.04.0/autotests/data/vcard10.vcf.ref new/kcontacts-17.04.1/autotests/data/vcard10.vcf.ref --- old/kcontacts-17.04.0/autotests/data/vcard10.vcf.ref 2017-04-08 06:47:01.000000000 +0200 +++ new/kcontacts-17.04.1/autotests/data/vcard10.vcf.ref 2017-04-26 06:55:00.000000000 +0200 @@ -35,7 +35,8 @@ N:Name;Surname;;Dr. med.; NOTE:Sprechzeiten:\nMo 8–12\, 15–18\nDi 8–12\nMi 8–12\, 14 –15\nDo 14–19\nFr 8–12 -TITLE:Facharzt für Allgemeinmedizin +TITLE;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=46acharzt f=C3=BCr Allgemein + medizin UID:b4fc4930-043f-446a-9a76-cf0b548368d8 END:VCARD diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcontacts-17.04.0/autotests/importexportvcardtest.cpp new/kcontacts-17.04.1/autotests/importexportvcardtest.cpp --- old/kcontacts-17.04.0/autotests/importexportvcardtest.cpp 2017-04-08 06:47:01.000000000 +0200 +++ new/kcontacts-17.04.1/autotests/importexportvcardtest.cpp 2017-04-26 06:55:00.000000000 +0200 @@ -21,6 +21,7 @@ #include "importexportvcardtest.h" #include <QTest> #include "vcardtool.h" +#include <QDebug> ImportExportVCardTest::ImportExportVCardTest(QObject *parent) : QObject(parent) @@ -33,6 +34,27 @@ } +static void compareBuffers(const QByteArray &outputData, const QByteArray &expected) +{ + if (outputData != expected) { + qDebug() << " outputData " << outputData; + qDebug() << " expected " << expected; + } + const QList<QByteArray> outputLines = outputData.split('\n'); + const QList<QByteArray> outputRefLines = expected.split('\n'); + for (int i = 0; i < qMin(outputLines.count(), outputRefLines.count()); ++i) { + const QByteArray actual = outputLines.at(i); + const QByteArray expect = outputRefLines.at(i); + if (actual != expect) { + qCritical() << "Mismatch at output line" << (i + 1); + QCOMPARE(actual, expect); + QCOMPARE(actual.count(), expect.count()); + } + } + QCOMPARE(outputLines.count(), outputRefLines.count()); + QCOMPARE(outputData.size(), expected.size()); +} + void ImportExportVCardTest::shouldExportFullTestVcard4() { QByteArray vcarddata("BEGIN:VCARD\r\n" @@ -71,8 +93,9 @@ "END:VCARD\r\n\r\n"); QByteArray vcardexpected("BEGIN:VCARD\r\n" "VERSION:4.0\r\n" - "ADR;GEO=\"geo:51.523701,0.158500\";LABEL=\"Mr Sherlock Holmes\";TYPE:;;221B Bak\r\n" - " er Street;London;;NW1;United Kingdom\r\n" + "ADR;GEO=\"geo:51.523701,0.158500\";LABEL=\"Mr Sherlock Holmes, 221B Baker Stre\r\n" + " et, London NW1, England, United Kingdom\";TYPE:;;221B Baker Street;London;;\r\n" + " NW1;United Kingdom\r\n" "ANNIVERSARY:19960415\r\n" "BDAY:19531015T231000Z\r\n" "CALADRURI;PREF=1:mailto:detect...@sherlockholmes.com\r\n" @@ -93,7 +116,7 @@ "PRODID:-//KADDRESSBOOK//NONSGML Version 1//EN\r\n" "REV:20140722T222710Z\r\n" "ROLE:Detective\r\n" - "TEL;PREF=1;VALUE=uri:ext=5555\r\n" + "TEL;TYPE=\"home,voice\";PREF=1;VALUE=uri:tel:+44-555-555-5555;ext=5555\r\n" "TEL;TYPE=\"cell,voice\";VALUE=uri:tel:+44-555-555-6666\r\n" "TEL;TYPE=\"voice,work\";VALUE=uri:tel:+44-555-555-7777\r\n" "TITLE;ALTID=1;LANGUAGE=fr:Patron\r\n" @@ -107,8 +130,7 @@ const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata); const QByteArray result = vcard.exportVCards(lst, KContacts::VCard::v4_0); - //qDebug() << " result " << result; - QCOMPARE(result, vcardexpected); + compareBuffers(result, vcardexpected); } void ImportExportVCardTest::shouldExportMiscElementVcard4() @@ -140,9 +162,7 @@ const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata); const QByteArray result = vcard.exportVCards(lst, KContacts::VCard::v4_0); - //qDebug() << " result " << result; - QCOMPARE(result, vcardexpected); - + compareBuffers(result, vcardexpected); } void ImportExportVCardTest::shouldExportMemberElementVcard4() @@ -174,8 +194,9 @@ const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata); const QByteArray result = vcard.exportVCards(lst, KContacts::VCard::v4_0); - //qDebug() << " result " << result; - QCOMPARE(result, vcardexpected); + compareBuffers(result, vcardexpected); } +// TODO please make this data driven before copy/pasting more methods here... + QTEST_MAIN(ImportExportVCardTest) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcontacts-17.04.0/autotests/testroundtrip.cpp new/kcontacts-17.04.1/autotests/testroundtrip.cpp --- old/kcontacts-17.04.0/autotests/testroundtrip.cpp 2017-04-08 06:47:01.000000000 +0200 +++ new/kcontacts-17.04.1/autotests/testroundtrip.cpp 2017-04-26 06:55:00.000000000 +0200 @@ -99,6 +99,27 @@ } } +static void compareBuffers(const char *version, const QByteArray &outputData, const QByteArray &outputRefData) +{ + if (outputData != outputRefData) { + qDebug() << " outputData " << outputData; + qDebug() << " outputRefData " << outputRefData; + } + const QList<QByteArray> outputLines = outputData.split('\n'); + const QList<QByteArray> outputRefLines = outputRefData.split('\n'); + for (int i = 0; i < qMin(outputLines.count(), outputRefLines.count()); ++i) { + const QByteArray actual = outputLines.at(i); + const QByteArray expect = outputRefLines.at(i); + if (actual != expect) { + qCritical() << "Mismatch in" << version << "output line" << (i + 1); + QCOMPARE(actual, expect); + QCOMPARE(actual.count(), expect.count()); + } + } + QCOMPARE(outputLines.count(), outputRefLines.count()); + QCOMPARE(outputData.size(), outputRefData.size()); +} + void RoundtripTest::testVCardRoundtrip() { QFETCH(QString, inputFile); @@ -128,23 +149,7 @@ QVERIFY(outputFile.open(QIODevice::ReadOnly)); const QByteArray outputRefData = outputFile.readAll(); - QCOMPARE(outputData.size(), outputRefData.size()); - - const QList<QByteArray> outputLines = outputData.split('\n'); - const QList<QByteArray> outputRefLines = outputRefData.split('\n'); - QCOMPARE(outputLines.count(), outputRefLines.count()); - for (int i = 0; i < outputLines.count(); ++i) { - const QByteArray actual = outputLines[i]; - const QByteArray expect = outputRefLines[i]; - - if (actual != expect) { - qCritical() << "Mismatch in v2.1 output line" << (i + 1); - QCOMPARE(actual.count(), expect.count()); - - qCritical() << "\nActual:" << actual << "\nExpect:" << expect; - QCOMPARE(actual, expect); - } - } + compareBuffers("v2.1", outputData, outputRefData); } if (!output3_0File.isEmpty()) { @@ -154,26 +159,7 @@ QVERIFY(outputFile.open(QIODevice::ReadOnly)); const QByteArray outputRefData = outputFile.readAll(); - if (outputData.size() != outputRefData.size()) { - qDebug() << " outputRefData " << outputRefData << endl; - qDebug() << " outputData " << outputData; - } - QCOMPARE(outputData.size(), outputRefData.size()); - const QList<QByteArray> outputLines = outputData.split('\n'); - const QList<QByteArray> outputRefLines = outputRefData.split('\n'); - QCOMPARE(outputLines.count(), outputRefLines.count()); - for (int i = 0; i < outputLines.count(); ++i) { - const QByteArray actual = outputLines[i]; - const QByteArray expect = outputRefLines[i]; - - if (actual != expect) { - qCritical() << "Mismatch in v3.0 output line" << (i + 1); - - qCritical() << "\nActual:" << actual << "\nExpect:" << expect; - QCOMPARE(actual.count(), expect.count()); - QCOMPARE(actual, expect); - } - } + compareBuffers("v3.0", outputData, outputRefData); } #if 0 if (!output4_0File.isEmpty()) { @@ -183,28 +169,7 @@ QVERIFY(outputFile.open(QIODevice::ReadOnly)); const QByteArray outputRefData = outputFile.readAll(); - if (outputData.size() != outputRefData.size()) { - qDebug() << " outputRefData " << outputRefData << endl; - qDebug() << " outputData " << outputData; - } - //QCOMPARE( outputData.size(), outputRefData.size() ); - - const QList<QByteArray> outputLines = outputData.split('\n'); - const QList<QByteArray> outputRefLines = outputRefData.split('\n'); - //QCOMPARE(outputLines.count(), outputRefLines.count()); - - for (int i = 0; i < outputLines.count(); ++i) { - const QByteArray actual = outputLines[i]; - const QByteArray expect = outputRefLines[i]; - - if (actual != expect) { - qCritical() << "Mismatch in v4.0 output line" << (i + 1); - - qCritical() << "\nActual:" << actual << "\nExpect:" << expect; - QCOMPARE(actual.count(), expect.count()); - QCOMPARE(actual, expect); - } - } + compareBuffers("v4.0", outputData, outputRefData); } #endif } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcontacts-17.04.0/autotests/testroundtrip.qrc new/kcontacts-17.04.1/autotests/testroundtrip.qrc --- old/kcontacts-17.04.0/autotests/testroundtrip.qrc 2017-04-08 06:47:01.000000000 +0200 +++ new/kcontacts-17.04.1/autotests/testroundtrip.qrc 2017-04-26 06:55:00.000000000 +0200 @@ -3,14 +3,14 @@ <qresource prefix="/input"> <file>data/vcard1.vcf</file> <file>data/vcard2.vcf</file> - <!-- <file>data/vcard3.vcf</file> - <file>data/vcard4.vcf</file> --> + <file>data/vcard3.vcf</file> + <file>data/vcard4.vcf</file> <file>data/vcard5.vcf</file> <file>data/vcard6.vcf</file> <file>data/vcard7.vcf</file> <file>data/vcard8.vcf</file> <file>data/vcard9.vcf</file> - <!-- <file>data/vcard10.vcf</file> --> + <file>data/vcard10.vcf</file> <file>data/vcard11.vcf</file> <file>data/vcard12.vcf</file> <file>data/vcard13.vcf</file> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcontacts-17.04.0/src/vcardparser/vcardparser.cpp new/kcontacts-17.04.1/src/vcardparser/vcardparser.cpp --- old/kcontacts-17.04.0/src/vcardparser/vcardparser.cpp 2017-04-08 06:47:01.000000000 +0200 +++ new/kcontacts-17.04.1/src/vcardparser/vcardparser.cpp 2017-04-26 06:55:00.000000000 +0200 @@ -22,6 +22,7 @@ #include <kcodecs.h> #include "kcontacts_debug.h" #include <QtCore/QTextCodec> +#include <functional> // This cache for QString::fromLatin1() isn't about improving speed, but about reducing memory usage by sharing common strings class StringCache @@ -72,6 +73,185 @@ str.replace("\\\\", "\\"); } +class VCardLineParser +{ +public: + VCardLineParser(StringCache &cache, std::function<QByteArray()> fetchAnotherLine) + : m_cache(cache), m_fetchAnotherLine(fetchAnotherLine) + { + } + + void parseLine(const QByteArray ¤tLine, VCardLine *vCardLine); + +private: + void addParameter(const QByteArray ¶mKey, const QByteArray ¶mValue); + +private: + StringCache &m_cache; + std::function<QByteArray()> m_fetchAnotherLine; + + VCardLine *m_vCardLine; + QByteArray m_encoding; + QByteArray m_charset; +}; + +void VCardLineParser::addParameter(const QByteArray& paramKey, const QByteArray& paramValue) +{ + if (paramKey == "encoding") { + m_encoding = paramValue.toLower(); + } else if (paramKey == "charset") { + m_charset = paramValue.toLower(); + } + //qDebug() << " add parameter" << paramKey << " = " << paramValue; + m_vCardLine->addParameter(m_cache.fromLatin1(paramKey), m_cache.fromLatin1(paramValue)); +} + +void VCardLineParser::parseLine(const QByteArray& currentLine, KContacts::VCardLine* vCardLine) +{ + //qDebug() << currentLine; + m_vCardLine = vCardLine; + // The syntax is key:value, but the key can contain semicolon-separated parameters, which can contain a ':', so indexOf(':') is wrong. + // EXAMPLE: ADR;GEO="geo:22.500000,45.099998";LABEL="My Label";TYPE=home:P.O. Box 101;;;Any Town;CA;91921-1234; + // Therefore we need a small state machine, just the way I like it. + enum State { StateInitial, StateParamKey, StateParamValue, StateQuotedValue, StateAfterParamValue, StateValue }; + State state = StateInitial; + const int lineLength = currentLine.length(); + const char *lineData = currentLine.constData(); // to skip length checks from at() in debug mode + QByteArray paramKey; + QByteArray paramValue; + int start = 0; + int pos = 0; + for (; pos < lineLength; ++pos) { + const char ch = lineData[pos]; + const bool colonOrSemicolon = (ch == ';' || ch == ':'); + switch (state) { + case StateInitial: + if (colonOrSemicolon) { + const QByteArray identifier = currentLine.mid(start, pos - start); + //qDebug() << " identifier" << identifier; + vCardLine->setIdentifier(m_cache.fromLatin1(identifier)); + start = pos + 1; + } + if (ch == ';') { + state = StateParamKey; + } else if (ch == ':') { + state = StateValue; + } else if (ch == '.') { + vCardLine->setGroup(m_cache.fromLatin1(currentLine.mid(start, pos - start))); + start = pos + 1; + } + break; + case StateParamKey: + if (colonOrSemicolon || ch == '=') { + paramKey = currentLine.mid(start, pos - start); + start = pos + 1; + } + if (colonOrSemicolon) { + // correct the so-called 2.1 'standard' + paramValue = paramKey; + const QByteArray lowerKey = paramKey.toLower(); + if (lowerKey == "quoted-printable" || lowerKey == "base64") { + paramKey = "encoding"; + } else { + paramKey = "type"; + } + addParameter(paramKey, paramValue); + } + if (ch == ';') { + state = StateParamKey; + } else if (ch == ':') { + state = StateValue; + } else if (ch == '=') { + state = StateParamValue; + } + break; + case StateQuotedValue: + if (ch == '"' || (ch == ',' && paramKey.toLower() == "type")) { + // TODO the hack above is for TEL;TYPE=\"voice,home\":... without breaking GEO.... TODO: check spec + paramValue = currentLine.mid(start, pos - start); + addParameter(paramKey.toLower(), paramValue); + start = pos + 1; + if (ch == '"') { + state = StateAfterParamValue; // to avoid duplicating code with StateParamValue, we use this intermediate state for one char + } + } + break; + case StateParamValue: + if (colonOrSemicolon || ch == ',') { + paramValue = currentLine.mid(start, pos - start); + addParameter(paramKey.toLower(), paramValue); + start = pos + 1; + } + // fall-through intended + case StateAfterParamValue: + if (ch == ';') { + state = StateParamKey; + start = pos + 1; + } else if (ch == ':') { + state = StateValue; + } else if (pos == start && ch == '"') { // don't treat foo"bar" as quoted - TODO check the vCard 3.0 spec. + state = StateQuotedValue; + start = pos + 1; + } + break; + case StateValue: + Q_UNREACHABLE(); + break; + } + + if (state == StateValue) { + break; + } + } + + if (state != StateValue) { // invalid line, no ':' + return; + } + + QByteArray value = currentLine.mid(pos + 1); + removeEscapes(value); + + QByteArray output; + bool wasBase64Encoded = false; + + if (!m_encoding.isEmpty()) { + // have to decode the data + if (m_encoding == "b" || m_encoding == "base64") { + output = QByteArray::fromBase64(value); + wasBase64Encoded = true; + } else if (m_encoding == "quoted-printable") { + // join any qp-folded lines + while (value.endsWith('=')) { + value.chop(1); // remove the '=' + value.append(m_fetchAnotherLine()); + } + KCodecs::quotedPrintableDecode(value, output); + } else if (m_encoding == "8bit") { + output = value; + } else { + qDebug("Unknown vcard encoding type!"); + } + } else { + output = value; + } + + if (!m_charset.isEmpty()) { + // have to convert the data + QTextCodec *codec = QTextCodec::codecForName(m_charset); + if (codec) { + vCardLine->setValue(codec->toUnicode(output)); + } else { + vCardLine->setValue(QString::fromUtf8(output)); + } + } else if (wasBase64Encoded) { + vCardLine->setValue(output); + } else { + vCardLine->setValue(QString::fromUtf8(output)); + } +} + +//// + VCardParser::VCardParser() : d(nullptr) { @@ -87,15 +267,14 @@ VCard::List vCardList; QByteArray currentLine; - const QList<QByteArray> lines = text.split('\n'); + int lineStart = 0; + int lineEnd = text.indexOf('\n'); bool inVCard = false; - const QList<QByteArray>::const_iterator linesEnd(lines.end()); StringCache cache; - - for (auto it = lines.begin(); it != linesEnd; ++it) { - QByteArray cur = *it; + for (; lineEnd != -1; lineStart = lineEnd + 1, lineEnd = text.indexOf('\n', lineStart)) { + QByteArray cur = text.mid(lineStart, lineEnd - lineStart); // remove the trailing \r, left from \r\n if (cur.endsWith('\r')) { cur.chop(1); @@ -110,164 +289,26 @@ continue; } if (inVCard && !currentLine.isEmpty()) { // now parse the line - int colon = currentLine.indexOf(':'); - if (colon == -1) { // invalid line - currentLine = cur; - continue; - } - bool keyFound = false; - QByteArray key; - QList<QByteArray> params; - const int currentLineLength(currentLine.length()); - for (int i = 0; i < currentLineLength; ++i) { - char character = currentLine.at(i); - if (keyFound) { - const QList<QByteArray> tmpParams = currentLine.mid(i).split(';'); - QByteArray tmpParameter; - bool valueAdded = false; - for (const QByteArray ¶meter : tmpParams) { - if (parameter.contains('=')) { - if (tmpParameter.isEmpty()) { - tmpParameter = parameter; - } else { - params << tmpParameter; - tmpParameter = parameter; - } - } else { - if (tmpParameter.isEmpty() && !valueAdded) { - tmpParameter = parameter; - valueAdded = true; - } else { - tmpParameter += ';' + parameter; - } - } - } - if (!tmpParameter.isEmpty()) { - params << tmpParameter; - } - break; - } else { - if ((character == ';' || character == ':') && !keyFound) { - keyFound = true; - } else { - key += character; - } - } - } VCardLine vCardLine; - QByteArray value; - if (!params.isEmpty()) { - value = params.takeLast(); - if (value.contains('=')) { - colon = value.indexOf(':'); - const QByteArray lastParam = value.left(colon).trimmed(); - if ((lastParam != "geo")) { - params.append(lastParam); - value = value.mid(colon + 1); - } else { - params.append(value); - } - } - } - params.prepend(key); - // check for group - const QByteArray firstParam = params.at(0); - const int groupPos = firstParam.indexOf('.'); - if (groupPos != -1) { - vCardLine.setGroup(cache.fromLatin1(firstParam.left(groupPos))); - vCardLine.setIdentifier(cache.fromLatin1(firstParam.mid(groupPos + 1))); - } else { - vCardLine.setIdentifier(cache.fromLatin1(firstParam)); - } - if (params.count() > 1) { // find all parameters - QList<QByteArray>::ConstIterator paramIt(params.constBegin()); - for (++paramIt; paramIt != params.constEnd(); ++paramIt) { - QList<QByteArray> pair = (*paramIt).split('='); - QByteArray first = pair.at(0).toLower(); - if (pair.count() == 1) { - // correct the fucking 2.1 'standard' - if (first == "quoted-printable") { - pair[ 0 ] = "encoding"; - pair.append("quoted-printable"); - } else if (first == "base64") { - pair[ 0 ] = "encoding"; - pair.append("base64"); - } else { - pair.prepend("type"); - } - first = pair.at(0); - } - const QByteArray second = pair.at(1); - if (second.contains(':')) { - vCardLine.addParameter(cache.fromLatin1(first), - cache.fromLatin1(second)); - } else if (second.contains(',')) { // parameter in type=x,y,z format - const QList<QByteArray> args = second.split(','); - for (QByteArray tmpArg : args) { - if (tmpArg.startsWith('"')) { - tmpArg = tmpArg.mid(1); - } - if (tmpArg.endsWith('"')) { - tmpArg.chop(1); - } - vCardLine.addParameter(cache.fromLatin1(first), - cache.fromLatin1(tmpArg)); - } - } else { - vCardLine.addParameter(cache.fromLatin1(first), - cache.fromLatin1(second)); + // Provide a way for the parseVCardLine function to read more lines (for quoted-printable support) + auto fetchAnotherLine = [&text, &lineStart, &lineEnd, &cur]() -> QByteArray { + const QByteArray ret = cur; + lineStart = lineEnd + 1; + lineEnd = text.indexOf('\n', lineStart); + if (lineEnd != -1) { + cur = text.mid(lineStart, lineEnd - lineStart); + // remove the trailing \r, left from \r\n + if (cur.endsWith('\r')) { + cur.chop(1); } } - } - - removeEscapes(value); + return ret; + }; - QByteArray output; - bool wasBase64Encoded = false; + VCardLineParser lineParser(cache, fetchAnotherLine); - const QString encoding = vCardLine.parameter(QStringLiteral("encoding")).toLower(); - if (!encoding.isEmpty()) { - - // have to decode the data - if (encoding == QLatin1String("b") || encoding == QLatin1String("base64")) { - output = QByteArray::fromBase64(value); - wasBase64Encoded = true; - } else if (encoding == QLatin1String("quoted-printable")) { - // join any qp-folded lines - while (value.endsWith('=') && it != linesEnd) { - value.chop(1); // remove the '=' - value.append(cur); - cur = *(++it); - // remove the trailing \r, left from \r\n - if (cur.endsWith('\r')) { - cur.chop(1); - } - } - KCodecs::quotedPrintableDecode(value, output); - } else if (encoding == QLatin1String("8bit")) { - output = value; - } else { - qDebug("Unknown vcard encoding type!"); - } - } else { - output = value; - } - - const QString charset = vCardLine.parameter(QStringLiteral("charset")); - if (!charset.isEmpty()) { - // have to convert the data - QTextCodec *codec = QTextCodec::codecForName(charset.toLatin1()); - if (codec) { - vCardLine.setValue(codec->toUnicode(output)); - } else { - vCardLine.setValue(QString::fromUtf8(output)); - } - } else if (wasBase64Encoded) { - vCardLine.setValue(output); - } else { - vCardLine.setValue(QString::fromUtf8(output)); - } + lineParser.parseLine(currentLine, &vCardLine); currentVCard.addLine(vCardLine); }