Added support for quoted-printable encoding to contact media.

This commit is contained in:
23rd 2024-06-13 17:19:24 +03:00 committed by John Preston
parent 10f7b985c7
commit 82428aef28
3 changed files with 89 additions and 47 deletions

View file

@ -1219,6 +1219,92 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
_document);
}
SharedContact::VcardItems SharedContact::ParseVcard(const QString &data) {
const auto decode = [&](const QByteArray &input) -> QString {
auto output = QByteArray();
for (auto i = 0; i < input.size(); ++i) {
if ((input.at(i) == '=') && ((i + 2) < input.size())) {
const auto value = input.mid((++i)++, 2);
auto converted = false;
const auto character = char(value.toUInt(&converted, 16));
if (converted) {
output.append(character);
} else {
output.append('=');
output.append(value);
}
} else {
output.append(input.at(i));
}
}
return QString::fromUtf8(output);
};
using Type = SharedContact::VcardItemType;
auto items = SharedContact::VcardItems();
for (const auto &item : data.split('\n')) {
const auto parts = item.split(':');
if (parts.size() == 2) {
const auto &type = parts.front();
const auto attributes = type.split(';', Qt::SkipEmptyParts);
const auto c = Qt::CaseInsensitive;
auto isQuotedPrintable = false;
for (const auto &attribute : attributes) {
const auto parts = attribute.split('=', Qt::SkipEmptyParts);
if (parts.size() == 2) {
if (parts.front().startsWith("ENCODING", c)) {
isQuotedPrintable = parts[1].startsWith(
"QUOTED-PRINTABLE",
c);
break;
}
}
}
const auto &value = isQuotedPrintable
? decode(parts[1].toUtf8())
: parts[1];
if (type.startsWith("TEL")) {
const auto telType = type.contains("PREF")
? Type::PhoneMain
: type.contains("HOME")
? Type::PhoneHome
: type.contains("WORK")
? Type::PhoneWork
: (type.contains("CELL")
|| type.contains("MOBILE"))
? Type::PhoneMobile
: type.contains("OTHER")
? Type::PhoneOther
: Type::Phone;
items[telType] = value;
} else if (type.startsWith("EMAIL")) {
items[Type::Email] = value;
} else if (type.startsWith("URL")) {
items[Type::Url] = value;
} else if (type.startsWith("NOTE")) {
items[Type::Note] = value;
} else if (type.startsWith("ORG")) {
items[Type::Organization] = base::duplicate(value)
.replace(';', ' ')
.trimmed();
} else if (type.startsWith("ADR")) {
items[Type::Address] = value;
} else if (type.startsWith("BDAY")) {
items[Type::Birthday] = value;
} else if (type.startsWith("N")) {
items[Type::Name] = base::duplicate(value)
.replace(';', ' ')
.trimmed();
}
}
}
return items;
}
MediaContact::MediaContact(
not_null<HistoryItem*> parent,
UserId userId,

View file

@ -70,6 +70,8 @@ struct SharedContact final {
};
using VcardItems = base::flat_map<VcardItemType, QString>;
static VcardItems ParseVcard(const QString &);
VcardItems vcardItems;
};

View file

@ -211,59 +211,13 @@ std::unique_ptr<Data::Media> HistoryItem::CreateMedia(
const MTPMessageMedia &media) {
using Result = std::unique_ptr<Data::Media>;
return media.match([&](const MTPDmessageMediaContact &media) -> Result {
const auto vcardItems = [&] {
using Type = Data::SharedContact::VcardItemType;
auto items = Data::SharedContact::VcardItems();
for (const auto &item : qs(media.vvcard()).split('\n')) {
const auto parts = item.split(':');
if (parts.size() == 2) {
const auto &type = parts.front();
const auto &value = parts[1];
if (type.startsWith("TEL")) {
const auto telType = type.contains("PREF")
? Type::PhoneMain
: type.contains("HOME")
? Type::PhoneHome
: type.contains("WORK")
? Type::PhoneWork
: (type.contains("CELL")
|| type.contains("MOBILE"))
? Type::PhoneMobile
: type.contains("OTHER")
? Type::PhoneOther
: Type::Phone;
items[telType] = value;
} else if (type.startsWith("EMAIL")) {
items[Type::Email] = value;
} else if (type.startsWith("URL")) {
items[Type::Url] = value;
} else if (type.startsWith("NOTE")) {
items[Type::Note] = value;
} else if (type.startsWith("ORG")) {
items[Type::Organization] = value;
items[Type::Organization].replace(';', ' ');
} else if (type.startsWith("ADR")) {
items[Type::Address] = value;
} else if (type.startsWith("BDAY")) {
items[Type::Birthday] = value;
} else if (type.startsWith("N")) {
items[Type::Name] = value;
items[Type::Name].replace(';', ' ');
}
}
}
return items;
}();
return std::make_unique<Data::MediaContact>(
item,
media.vuser_id().v,
qs(media.vfirst_name()),
qs(media.vlast_name()),
qs(media.vphone_number()),
vcardItems);
Data::SharedContact::ParseVcard(qs(media.vvcard())));
}, [&](const MTPDmessageMediaGeo &media) -> Result {
return media.vgeo().match([&](const MTPDgeoPoint &point) -> Result {
return std::make_unique<Data::MediaLocation>(