8#include "kcontacts_debug.h"
9#include "vcardparser_p.h"
11#include <QStringDecoder>
12#include <QStringEncoder>
19 QString fromLatin1(
const QByteArray &value)
25 auto it = m_values.constFind(value);
26 if (it != m_values.constEnd()) {
31 m_values.insert(value,
string);
36 QHash<QByteArray, QString> m_values;
39using namespace KContacts;
41static void addEscapes(
QByteArray &str,
bool excludeEscapedComma)
44 if (!excludeEscapedComma) {
67 VCardLineParser(StringCache &cache, std::function<QByteArray()> fetchAnotherLine)
69 , m_fetchAnotherLine(fetchAnotherLine)
73 void parseLine(
const QByteArray ¤tLine, VCardLine *vCardLine);
76 void addParameter(
const QByteArray ¶mKey,
const QByteArray ¶mValue);
80 std::function<QByteArray()> m_fetchAnotherLine;
82 VCardLine *m_vCardLine =
nullptr;
83 QByteArray m_encoding;
89 if (paramKey ==
"encoding") {
90 m_encoding = paramValue.
toLower();
91 }
else if (paramKey ==
"charset") {
92 m_charset = paramValue.
toLower();
95 m_vCardLine->addParameter(m_cache.fromLatin1(paramKey), m_cache.fromLatin1(paramValue));
98void VCardLineParser::parseLine(
const QByteArray ¤tLine, KContacts::VCardLine *vCardLine)
101 m_vCardLine = vCardLine;
110 StateAfterParamValue,
113 State state = StateInitial;
114 const int lineLength = currentLine.
length();
115 const char *lineData = currentLine.
constData();
117 QByteArray paramValue;
120 for (; pos < lineLength; ++pos) {
121 const char ch = lineData[pos];
122 const bool colonOrSemicolon = (ch ==
';' || ch ==
':');
125 if (colonOrSemicolon) {
126 const QByteArray identifier = currentLine.
mid(
start, pos -
start);
128 vCardLine->setIdentifier(m_cache.fromLatin1(identifier));
132 state = StateParamKey;
133 }
else if (ch ==
':') {
135 }
else if (ch ==
'.') {
136 vCardLine->setGroup(m_cache.fromLatin1(currentLine.
mid(
start, pos -
start)));
141 if (colonOrSemicolon || ch ==
'=') {
145 if (colonOrSemicolon) {
147 paramValue = paramKey;
148 const QByteArray lowerKey = paramKey.
toLower();
149 if (lowerKey ==
"quoted-printable" || lowerKey ==
"base64") {
150 paramKey =
"encoding";
154 addParameter(paramKey, paramValue);
157 state = StateParamKey;
158 }
else if (ch ==
':') {
160 }
else if (ch ==
'=') {
161 state = StateParamValue;
164 case StateQuotedValue:
165 if (ch ==
'"' || (ch ==
',' && paramKey.
toLower() ==
"type")) {
168 addParameter(paramKey.
toLower(), paramValue);
171 state = StateAfterParamValue;
175 case StateParamValue:
176 if (colonOrSemicolon || ch ==
',') {
178 addParameter(paramKey.
toLower(), paramValue);
183 case StateAfterParamValue:
185 state = StateParamKey;
187 }
else if (ch ==
':') {
189 }
else if (pos ==
start && ch ==
'"') {
190 state = StateQuotedValue;
199 if (state == StateValue) {
204 if (state != StateValue) {
208 QByteArray value = currentLine.
mid(pos + 1);
209 removeEscapes(value);
212 bool wasBase64Encoded =
false;
214 if (!m_encoding.isEmpty()) {
216 if (m_encoding ==
"b" || m_encoding ==
"base64") {
218 wasBase64Encoded =
true;
219 }
else if (m_encoding ==
"quoted-printable") {
223 value.
append(m_fetchAnotherLine());
226 }
else if (m_encoding ==
"8bit") {
229 qDebug(
"Unknown vcard encoding type!");
235 if (!m_charset.isEmpty()) {
237 auto codec = QStringDecoder(m_charset.constData());
238 if (codec.isValid()) {
243 }
else if (wasBase64Encoded) {
244 vCardLine->setValue(output);
252VCardParser::VCardParser()
256VCardParser::~VCardParser()
260VCard::List VCardParser::parseVCards(
const QByteArray &text)
263 VCard::List vCardList;
267 int lineEnd = text.
indexOf(
'\n');
269 bool inVCard =
false;
272 for (; lineStart != text.
size() + 1;
273 lineStart = lineEnd + 1, lineEnd = (text.
indexOf(
'\n', lineStart) == -1) ? text.
size() : text.
indexOf(
'\n', lineStart)) {
288 if (inVCard && !currentLine.
isEmpty()) {
292 auto fetchAnotherLine = [&text, &lineStart, &lineEnd, &cur]() ->
QByteArray {
294 lineStart = lineEnd + 1;
295 lineEnd = text.
indexOf(
'\n', lineStart);
297 cur = text.
mid(lineStart, lineEnd - lineStart);
306 VCardLineParser lineParser(cache, fetchAnotherLine);
308 lineParser.parseLine(currentLine, &vCardLine);
310 currentVCard.addLine(vCardLine);
314 if (qstrnicmp(cur.
constData(),
"begin:vcard", 11) == 0) {
317 currentVCard.clear();
321 if (qstrnicmp(cur.
constData(),
"end:vcard", 9) == 0) {
323 vCardList.append(currentVCard);
325 currentVCard.clear();
336static const int FOLD_WIDTH = 75;
338QByteArray VCardParser::createVCards(
const VCard::List &list)
346 VCardLine::List lines;
353 for (
const VCard &card : list) {
354 text.
append(
"BEGIN:VCARD\r\n");
363 for (
const auto &
id : std::as_const(idents)) {
364 lines = card.lines(
id);
367 for (
const VCardLine &vline : std::as_const(lines)) {
370 if (vline.hasGroup()) {
371 textLine = vline.group().toLatin1() +
'.' + vline.identifier().toLatin1();
373 textLine = vline.identifier().toLatin1();
376 params = vline.parameterList();
379 for (
const QString ¶m : std::as_const(params)) {
382 encodingType = vline.parameter(QStringLiteral(
"encoding")).
toLower();
385 values = vline.parameters(param);
386 for (
const QString &str : std::as_const(values)) {
387 textLine.
append(
';' + param.toLatin1().toUpper());
397 bool checkMultibyte =
false;
400 const QString charset = vline.parameter(QStringLiteral(
"charset"));
403 const QString value = vline.value().toString();
405 if (codec.isValid()) {
406 input = codec.encode(value);
408 checkMultibyte =
true;
412 input = vline.value().toByteArray();
414 checkMultibyte =
true;
415 input = vline.value().toString().toUtf8();
421 checkMultibyte =
false;
423 }
else if (encodingType ==
QLatin1String(
"quoted-printable")) {
424 checkMultibyte =
false;
433 textLine.
append(
':' + output);
435 if (textLine.
length() > FOLD_WIDTH) {
436 if (checkMultibyte) {
440 for (
int i = 0; i < textLine.
length(); ++i) {
441 if ((textLine[i] & 0xC0) == 0xC0) {
442 int sequenceLength = 2;
443 if ((textLine[i] & 0xE0) == 0xE0) {
445 }
else if ((textLine[i] & 0xF0) == 0xF0) {
448 if ((lineLength + sequenceLength) > FOLD_WIDTH) {
450 text +=
"\r\n " + textLine.
mid(i, sequenceLength);
451 lineLength = 1 + sequenceLength;
453 text += textLine.
mid(i, sequenceLength);
454 lineLength += sequenceLength;
456 i += sequenceLength - 1;
461 if ((lineLength == FOLD_WIDTH) && (i < (textLine.
length() - 1))) {
468 for (
int i = 0; i <= (textLine.
length() / FOLD_WIDTH); ++i) {
469 text.
append((i == 0 ?
"" :
" ") + textLine.
mid(i * FOLD_WIDTH, FOLD_WIDTH) +
"\r\n");
481 text.
append(
"END:VCARD\r\n");
Q_SCRIPTABLE Q_NOREPLY void start()
KCODECS_EXPORT QByteArray quotedPrintableDecode(QByteArrayView in)
KCODECS_EXPORT QByteArray quotedPrintableEncode(QByteArrayView in, bool useCRLF=true)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QByteArray & append(QByteArrayView data)
const char * constData() const const
bool contains(QByteArrayView bv) const const
bool endsWith(QByteArrayView bv) const const
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
bool isEmpty() const const
qsizetype length() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QByteArray & replace(QByteArrayView before, QByteArrayView after)
void reserve(qsizetype size)
qsizetype size() const const
bool startsWith(QByteArrayView bv) const const
QByteArray toBase64(Base64Options options) const const
QByteArray toLower() const const
QByteArray trimmed() const const
bool isEmpty() const const
void prepend(parameter_type value)
qsizetype size() const const
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QByteArray toLatin1() const const
QString toLower() const const
QByteArray toUtf8() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
qsizetype indexOf(const QRegularExpression &re, qsizetype from) const const
QVariant fromValue(T &&value)
bool isValid() const const