1 | /* |
2 | This file is part of the KContacts framework. |
3 | SPDX-FileCopyrightText: 2003 Tobias Koenig <tokoe@kde.org> |
4 | SPDX-FileCopyrightText: 2015-2019 Laurent Montel <montel@kde.org> |
5 | |
6 | SPDX-License-Identifier: LGPL-2.0-or-later |
7 | */ |
8 | |
9 | #include "fieldgroup.h" |
10 | #include "gender.h" |
11 | #include "kcontacts_debug.h" |
12 | #include "key.h" |
13 | #include "lang.h" |
14 | #include "picture.h" |
15 | #include "related.h" |
16 | #include "secrecy.h" |
17 | #include "sound.h" |
18 | #include "vcardtool_p.h" |
19 | |
20 | #include <QString> |
21 | #include <QTimeZone> |
22 | |
23 | using namespace KContacts; |
24 | |
25 | static bool needsEncoding(const QString &value) |
26 | { |
27 | int length = value.length(); |
28 | for (int i = 0; i < length; ++i) { |
29 | char c = value.at(i).toLatin1(); |
30 | if ((c < 33 || c > 126) && c != ' ' && c != '=') { |
31 | return true; |
32 | } |
33 | } |
34 | |
35 | return false; |
36 | } |
37 | |
38 | struct AddressTypeInfo { |
39 | const char *addressType; |
40 | Address::TypeFlag flag; |
41 | }; |
42 | |
43 | static const AddressTypeInfo s_addressTypes[] = { |
44 | {.addressType: "dom" , .flag: Address::Dom}, |
45 | {.addressType: "home" , .flag: Address::Home}, |
46 | {.addressType: "intl" , .flag: Address::Intl}, |
47 | {.addressType: "parcel" , .flag: Address::Parcel}, |
48 | {.addressType: "postal" , .flag: Address::Postal}, |
49 | {.addressType: "pref" , .flag: Address::Pref}, |
50 | {.addressType: "work" , .flag: Address::Work}, |
51 | }; |
52 | |
53 | static Address::TypeFlag stringToAddressType(const QString &str) |
54 | { |
55 | auto it = std::find_if(first: std::begin(arr: s_addressTypes), last: std::end(arr: s_addressTypes), pred: [&str](const AddressTypeInfo &info) { |
56 | return str == QLatin1String(info.addressType); |
57 | }); |
58 | return it != std::end(arr: s_addressTypes) ? it->flag : Address::TypeFlag{}; |
59 | } |
60 | |
61 | struct PhoneTypeInfo { |
62 | const char *phoneType; |
63 | PhoneNumber::TypeFlag flag; |
64 | }; |
65 | |
66 | static const PhoneTypeInfo s_phoneTypes[] = { |
67 | {.phoneType: "BBS" , .flag: PhoneNumber::Bbs}, |
68 | {.phoneType: "CAR" , .flag: PhoneNumber::Car}, |
69 | {.phoneType: "CELL" , .flag: PhoneNumber::Cell}, |
70 | {.phoneType: "FAX" , .flag: PhoneNumber::Fax}, |
71 | {.phoneType: "HOME" , .flag: PhoneNumber::Home}, |
72 | {.phoneType: "ISDN" , .flag: PhoneNumber::Isdn}, |
73 | {.phoneType: "MODEM" , .flag: PhoneNumber::Modem}, |
74 | {.phoneType: "MSG" , .flag: PhoneNumber::Msg}, |
75 | {.phoneType: "PAGER" , .flag: PhoneNumber::Pager}, |
76 | {.phoneType: "PCS" , .flag: PhoneNumber::Pcs}, |
77 | {.phoneType: "PREF" , .flag: PhoneNumber::Pref}, |
78 | {.phoneType: "VIDEO" , .flag: PhoneNumber::Video}, |
79 | {.phoneType: "VOICE" , .flag: PhoneNumber::Voice}, |
80 | {.phoneType: "WORK" , .flag: PhoneNumber::Work}, |
81 | }; |
82 | |
83 | static PhoneNumber::TypeFlag stringToPhoneType(const QString &str) |
84 | { |
85 | auto it = std::find_if(first: std::begin(arr: s_phoneTypes), last: std::end(arr: s_phoneTypes), pred: [&str](const PhoneTypeInfo &info) { |
86 | return str == QLatin1String(info.phoneType); |
87 | }); |
88 | return it != std::end(arr: s_phoneTypes) ? it->flag : PhoneNumber::TypeFlag{}; |
89 | } |
90 | |
91 | VCardTool::VCardTool() |
92 | { |
93 | } |
94 | |
95 | VCardTool::~VCardTool() |
96 | { |
97 | } |
98 | |
99 | QByteArray VCardTool::exportVCards(const Addressee::List &list, VCard::Version version) const |
100 | { |
101 | return createVCards(list, version, exportVcard: true /*export vcard*/); |
102 | } |
103 | |
104 | QByteArray VCardTool::createVCards(const Addressee::List &list, VCard::Version version) const |
105 | { |
106 | return createVCards(list, version, exportVcard: false /*don't export*/); |
107 | } |
108 | |
109 | void VCardTool::addParameter(VCardLine *line, VCard::Version version, const QString &key, const QStringList &valueStringList) const |
110 | { |
111 | if (version == VCard::v2_1) { |
112 | for (const QString &valueStr : valueStringList) { |
113 | line->addParameter(param: valueStr, value: QString()); |
114 | } |
115 | } else if (version == VCard::v3_0) { |
116 | line->addParameter(param: key, value: valueStringList.join(sep: QLatin1Char(','))); |
117 | } else { |
118 | if (valueStringList.count() < 2) { |
119 | line->addParameter(param: key, value: valueStringList.join(sep: QLatin1Char(','))); |
120 | } else { |
121 | line->addParameter(param: key, value: QLatin1Char('"') + valueStringList.join(sep: QLatin1Char(',')) + QLatin1Char('"')); |
122 | } |
123 | } |
124 | } |
125 | |
126 | void VCardTool::processAddresses(const Address::List &addresses, VCard::Version version, VCard *card) const |
127 | { |
128 | for (const auto &addr : addresses) { |
129 | QStringList address; |
130 | |
131 | // clang-format off |
132 | const bool isEmpty = addr.postOfficeBox().isEmpty() |
133 | && addr.extended().isEmpty() |
134 | && addr.street().isEmpty() |
135 | && addr.locality().isEmpty() |
136 | && addr.region().isEmpty() |
137 | && addr.postalCode().isEmpty() |
138 | && addr.country().isEmpty(); |
139 | // clang-format on |
140 | |
141 | address.append(t: addr.postOfficeBox().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
142 | address.append(t: addr.extended().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
143 | address.append(t: addr.street().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
144 | address.append(t: addr.locality().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
145 | address.append(t: addr.region().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
146 | address.append(t: addr.postalCode().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
147 | address.append(t: addr.country().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
148 | |
149 | const QString addressJoined(address.join(sep: QLatin1Char(';'))); |
150 | VCardLine adrLine(QStringLiteral("ADR" ), addressJoined); |
151 | if (version == VCard::v2_1 && needsEncoding(value: addressJoined)) { |
152 | adrLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
153 | adrLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
154 | } |
155 | |
156 | const bool hasLabel = !addr.label().isEmpty(); |
157 | QStringList addreLineType; |
158 | QStringList labelLineType; |
159 | |
160 | for (const auto &info : s_addressTypes) { |
161 | if (info.flag & addr.type()) { |
162 | const QString str = QString::fromLatin1(ba: info.addressType); |
163 | addreLineType << str; |
164 | if (hasLabel) { |
165 | labelLineType << str; |
166 | } |
167 | } |
168 | } |
169 | |
170 | if (hasLabel) { |
171 | if (version == VCard::v4_0) { |
172 | if (!addr.label().isEmpty()) { |
173 | adrLine.addParameter(QStringLiteral("LABEL" ), QStringLiteral("\"%1\"" ).arg(a: addr.label())); |
174 | } |
175 | } else { |
176 | VCardLine labelLine(QStringLiteral("LABEL" ), addr.label()); |
177 | if (version == VCard::v2_1 && needsEncoding(value: addr.label())) { |
178 | labelLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
179 | labelLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
180 | } |
181 | addParameter(line: &labelLine, version, QStringLiteral("TYPE" ), valueStringList: labelLineType); |
182 | card->addLine(line: labelLine); |
183 | } |
184 | } |
185 | if (version == VCard::v4_0) { |
186 | Geo geo = addr.geo(); |
187 | if (geo.isValid()) { |
188 | QString str = QString::asprintf(format: "\"geo:%.6f,%.6f\"" , geo.latitude(), geo.longitude()); |
189 | adrLine.addParameter(QStringLiteral("GEO" ), value: str); |
190 | } |
191 | } |
192 | if (!isEmpty) { |
193 | addParameter(line: &adrLine, version, QStringLiteral("TYPE" ), valueStringList: addreLineType); |
194 | card->addLine(line: adrLine); |
195 | } |
196 | } |
197 | } |
198 | |
199 | void VCardTool::processEmailList(const Email::List &emailList, VCard::Version version, VCard *card) const |
200 | { |
201 | for (const auto &email : emailList) { |
202 | VCardLine line(QStringLiteral("EMAIL" ), email.mail()); |
203 | const ParameterMap pMap = email.params(); |
204 | for (const auto &[param, l] : pMap) { |
205 | QStringList list = l; |
206 | if (version == VCard::v2_1) { |
207 | if (param.toLower() == QLatin1String("type" )) { |
208 | bool hasPreferred = false; |
209 | const int removeItems = list.removeAll(QStringLiteral("PREF" )); |
210 | if (removeItems > 0) { |
211 | hasPreferred = true; |
212 | } |
213 | if (!list.isEmpty()) { |
214 | addParameter(line: &line, version, key: param, valueStringList: list); |
215 | } |
216 | if (hasPreferred) { |
217 | line.addParameter(QStringLiteral("PREF" ), value: QString()); |
218 | } |
219 | } else { |
220 | line.addParameter(param, value: list.join(sep: QLatin1Char(','))); |
221 | } |
222 | } else { |
223 | line.addParameter(param, value: list.join(sep: QLatin1Char(','))); |
224 | } |
225 | } |
226 | card->addLine(line); |
227 | } |
228 | } |
229 | |
230 | void VCardTool::processOrganizations(const Addressee &addressee, VCard::Version version, VCard *card) const |
231 | { |
232 | const QList<Org> lstOrg = addressee.extraOrganizationList(); |
233 | for (const Org &org : lstOrg) { |
234 | QStringList organization{org.organization().replace(c: QLatin1Char(';'), after: QLatin1String("\\;" ))}; |
235 | if (!addressee.department().isEmpty()) { |
236 | organization.append(t: addressee.department().replace(c: QLatin1Char(';'), after: QLatin1String("\\;" ))); |
237 | } |
238 | const QString orgStr = organization.join(sep: QLatin1Char(';')); |
239 | VCardLine orgLine(QStringLiteral("ORG" ), orgStr); |
240 | if (version == VCard::v2_1 && needsEncoding(value: orgStr)) { |
241 | orgLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
242 | orgLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
243 | } |
244 | orgLine.addParameters(params: org.params()); |
245 | card->addLine(line: orgLine); |
246 | } |
247 | } |
248 | |
249 | void VCardTool::processPhoneNumbers(const PhoneNumber::List &phoneNumbers, VCard::Version version, VCard *card) const |
250 | { |
251 | for (const auto &phone : phoneNumbers) { |
252 | VCardLine line(QStringLiteral("TEL" ), phone.number()); |
253 | const ParameterMap paramsMap = phone.params(); |
254 | for (const auto &[param, list] : paramsMap) { |
255 | if (param.toUpper() != QLatin1String("TYPE" )) { |
256 | line.addParameter(param, value: list.join(sep: QLatin1Char(','))); |
257 | } |
258 | } |
259 | |
260 | const PhoneNumber::Type type = phone.type(); |
261 | QStringList lst; |
262 | for (const auto &pType : s_phoneTypes) { |
263 | if (pType.flag & type) { |
264 | const QString str = QString::fromLatin1(ba: pType.phoneType); |
265 | if (version == VCard::v4_0) { |
266 | lst << str.toLower(); |
267 | } else { |
268 | lst << str; |
269 | } |
270 | } |
271 | } |
272 | if (!lst.isEmpty()) { |
273 | addParameter(line: &line, version, QStringLiteral("TYPE" ), valueStringList: lst); |
274 | } |
275 | card->addLine(line); |
276 | } |
277 | } |
278 | |
279 | void VCardTool::processCustoms(const QStringList &customs, VCard::Version version, VCard *card, bool exportVcard) const |
280 | { |
281 | for (const auto &str : customs) { |
282 | QString identifier = QLatin1String("X-" ) + QStringView(str).left(n: str.indexOf(c: QLatin1Char(':'))); |
283 | const QString value = str.mid(position: str.indexOf(c: QLatin1Char(':')) + 1); |
284 | if (value.isEmpty()) { |
285 | continue; |
286 | } |
287 | // Convert to standard identifier |
288 | if (exportVcard) { |
289 | if (identifier == QLatin1String("X-messaging/aim-All" )) { |
290 | identifier = QStringLiteral("X-AIM" ); |
291 | } else if (identifier == QLatin1String("X-messaging/icq-All" )) { |
292 | identifier = QStringLiteral("X-ICQ" ); |
293 | } else if (identifier == QLatin1String("X-messaging/xmpp-All" )) { |
294 | identifier = QStringLiteral("X-JABBER" ); |
295 | } else if (identifier == QLatin1String("X-messaging/msn-All" )) { |
296 | identifier = QStringLiteral("X-MSN" ); |
297 | } else if (identifier == QLatin1String("X-messaging/yahoo-All" )) { |
298 | identifier = QStringLiteral("X-YAHOO" ); |
299 | } else if (identifier == QLatin1String("X-messaging/gadu-All" )) { |
300 | identifier = QStringLiteral("X-GADUGADU" ); |
301 | } else if (identifier == QLatin1String("X-messaging/skype-All" )) { |
302 | identifier = QStringLiteral("X-SKYPE" ); |
303 | } else if (identifier == QLatin1String("X-messaging/groupwise-All" )) { |
304 | identifier = QStringLiteral("X-GROUPWISE" ); |
305 | } else if (identifier == QLatin1String("X-messaging/sms-All" )) { |
306 | identifier = QStringLiteral("X-SMS" ); |
307 | } else if (identifier == QLatin1String("X-messaging/meanwhile-All" )) { |
308 | identifier = QStringLiteral("X-MEANWHILE" ); |
309 | } else if (identifier == QLatin1String("X-messaging/irc-All" )) { |
310 | identifier = QStringLiteral("X-IRC" ); // Not defined by rfc but need for fixing #300869 |
311 | } else if (identifier == QLatin1String("X-messaging/googletalk-All" )) { |
312 | // Not defined by rfc but need for fixing #300869 |
313 | identifier = QStringLiteral("X-GTALK" ); |
314 | } else if (identifier == QLatin1String("X-messaging/twitter-All" )) { |
315 | identifier = QStringLiteral("X-TWITTER" ); |
316 | } |
317 | } |
318 | |
319 | if (identifier.toLower() == QLatin1String("x-kaddressbook-x-anniversary" ) && version == VCard::v4_0) { |
320 | // ANNIVERSARY |
321 | if (!value.isEmpty()) { |
322 | const QDate date = QDate::fromString(string: value, format: Qt::ISODate); |
323 | QDateTime dt = QDateTime(date.startOfDay()); |
324 | dt.setTime(QTime()); |
325 | VCardLine line(QStringLiteral("ANNIVERSARY" ), createDateTime(dateTime: dt, version, withTime: false)); |
326 | card->addLine(line); |
327 | } |
328 | } else if (identifier.toLower() == QLatin1String("x-kaddressbook-x-spousesname" ) && version == VCard::v4_0) { |
329 | if (!value.isEmpty()) { |
330 | VCardLine line(QStringLiteral("RELATED" ), QStringLiteral(";" )); |
331 | line.addParameter(QStringLiteral("TYPE" ), QStringLiteral("spouse" )); |
332 | line.addParameter(QStringLiteral("VALUE" ), value); |
333 | card->addLine(line); |
334 | } |
335 | } else { |
336 | VCardLine line(identifier, value); |
337 | if (version == VCard::v2_1 && needsEncoding(value)) { |
338 | line.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
339 | line.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
340 | } |
341 | card->addLine(line); |
342 | } |
343 | } |
344 | } |
345 | |
346 | QByteArray VCardTool::createVCards(const Addressee::List &list, VCard::Version version, bool exportVcard) const |
347 | { |
348 | VCard::List vCardList; |
349 | |
350 | for (const auto &addressee : list) { |
351 | VCard card; |
352 | // VERSION |
353 | if (version == VCard::v2_1) { |
354 | card.addLine(line: VCardLine(QStringLiteral("VERSION" ), QStringLiteral("2.1" ))); |
355 | } else if (version == VCard::v3_0) { |
356 | card.addLine(line: VCardLine(QStringLiteral("VERSION" ), QStringLiteral("3.0" ))); |
357 | } else if (version == VCard::v4_0) { |
358 | card.addLine(line: VCardLine(QStringLiteral("VERSION" ), QStringLiteral("4.0" ))); |
359 | } |
360 | |
361 | // ADR + LABEL |
362 | const Address::List addresses = addressee.addresses(); |
363 | processAddresses(addresses, version, card: &card); |
364 | |
365 | // BDAY |
366 | const bool withTime = addressee.birthdayHasTime(); |
367 | const QString birthdayString = createDateTime(dateTime: addressee.birthday(), version, withTime); |
368 | card.addLine(line: VCardLine(QStringLiteral("BDAY" ), birthdayString)); |
369 | |
370 | // CATEGORIES only > 2.1 |
371 | if (version != VCard::v2_1) { |
372 | QStringList categories = addressee.categories(); |
373 | for (auto &cat : categories) { |
374 | cat.replace(c: QLatin1Char(','), after: QLatin1String("\\," )); |
375 | } |
376 | |
377 | VCardLine catLine(QStringLiteral("CATEGORIES" ), categories.join(sep: QLatin1Char(','))); |
378 | card.addLine(line: catLine); |
379 | } |
380 | // MEMBER (only in 4.0) |
381 | if (version == VCard::v4_0) { |
382 | // The KIND property must be set to "group" in order to use this property. |
383 | if (addressee.kind().toLower() == QLatin1String("group" )) { |
384 | const QStringList lst = addressee.members(); |
385 | for (const QString &member : lst) { |
386 | card.addLine(line: VCardLine(QStringLiteral("MEMBER" ), member)); |
387 | } |
388 | } |
389 | } |
390 | // SOURCE |
391 | const QList<QUrl> lstUrl = addressee.sourcesUrlList(); |
392 | for (const QUrl &url : lstUrl) { |
393 | VCardLine line = VCardLine(QStringLiteral("SOURCE" ), url.url()); |
394 | card.addLine(line); |
395 | } |
396 | |
397 | const Related::List relatedList = addressee.relationships(); |
398 | for (const auto &rel : relatedList) { |
399 | VCardLine line(QStringLiteral("RELATED" ), rel.related()); |
400 | line.addParameters(params: rel.params()); |
401 | card.addLine(line); |
402 | } |
403 | // CLASS only for version == 3.0 |
404 | if (version == VCard::v3_0) { |
405 | card.addLine(line: createSecrecy(secrecy: addressee.secrecy())); |
406 | } |
407 | // LANG only for version == 4.0 |
408 | if (version == VCard::v4_0) { |
409 | const Lang::List langList = addressee.langs(); |
410 | for (const auto &lang : langList) { |
411 | VCardLine line(QStringLiteral("LANG" ), lang.language()); |
412 | line.addParameters(params: lang.params()); |
413 | card.addLine(line); |
414 | } |
415 | } |
416 | // CLIENTPIDMAP |
417 | if (version == VCard::v4_0) { |
418 | const ClientPidMap::List clientpidmapList = addressee.clientPidMapList(); |
419 | for (const auto &pMap : clientpidmapList) { |
420 | VCardLine line(QStringLiteral("CLIENTPIDMAP" ), pMap.clientPidMap()); |
421 | line.addParameters(params: pMap.params()); |
422 | card.addLine(line); |
423 | } |
424 | } |
425 | // EMAIL |
426 | const Email::List emailList = addressee.emailList(); |
427 | processEmailList(emailList, version, card: &card); |
428 | |
429 | // FN required for only version > 2.1 |
430 | VCardLine fnLine(QStringLiteral("FN" ), addressee.formattedName()); |
431 | if (version == VCard::v2_1 && needsEncoding(value: addressee.formattedName())) { |
432 | fnLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
433 | fnLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
434 | } |
435 | card.addLine(line: fnLine); |
436 | |
437 | // GEO |
438 | const Geo geo = addressee.geo(); |
439 | if (geo.isValid()) { |
440 | QString str; |
441 | if (version == VCard::v4_0) { |
442 | str = QString::asprintf(format: "geo:%.6f,%.6f" , geo.latitude(), geo.longitude()); |
443 | } else { |
444 | str = QString::asprintf(format: "%.6f;%.6f" , geo.latitude(), geo.longitude()); |
445 | } |
446 | card.addLine(line: VCardLine(QStringLiteral("GEO" ), str)); |
447 | } |
448 | |
449 | // KEY |
450 | const Key::List keys = addressee.keys(); |
451 | for (const auto &k : keys) { |
452 | card.addLine(line: createKey(key: k, version)); |
453 | } |
454 | |
455 | // LOGO |
456 | card.addLine(line: createPicture(QStringLiteral("LOGO" ), pic: addressee.logo(), version)); |
457 | const QList<Picture> lstLogo = addressee.extraLogoList(); |
458 | for (const Picture &logo : lstLogo) { |
459 | card.addLine(line: createPicture(QStringLiteral("LOGO" ), pic: logo, version)); |
460 | } |
461 | |
462 | // MAILER only for version < 4.0 |
463 | if (version != VCard::v4_0) { |
464 | VCardLine mailerLine(QStringLiteral("MAILER" ), addressee.mailer()); |
465 | if (version == VCard::v2_1 && needsEncoding(value: addressee.mailer())) { |
466 | mailerLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
467 | mailerLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
468 | } |
469 | card.addLine(line: mailerLine); |
470 | } |
471 | |
472 | // N required for only version < 4.0 |
473 | QStringList name; |
474 | name.append(t: addressee.familyName().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
475 | name.append(t: addressee.givenName().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
476 | name.append(t: addressee.additionalName().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
477 | name.append(t: addressee.prefix().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
478 | name.append(t: addressee.suffix().replace(c: QLatin1Char(';'), QStringLiteral("\\;" ))); |
479 | |
480 | VCardLine nLine(QStringLiteral("N" ), name.join(sep: QLatin1Char(';'))); |
481 | if (version == VCard::v2_1 && needsEncoding(value: name.join(sep: QLatin1Char(';')))) { |
482 | nLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
483 | nLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
484 | } |
485 | if (version == VCard::v4_0 && !addressee.sortString().isEmpty()) { |
486 | nLine.addParameter(QStringLiteral("SORT-AS" ), value: addressee.sortString()); |
487 | } |
488 | |
489 | card.addLine(line: nLine); |
490 | |
491 | // NAME only for version < 4.0 |
492 | if (version != VCard::v4_0) { |
493 | VCardLine nameLine(QStringLiteral("NAME" ), addressee.name()); |
494 | if (version == VCard::v2_1 && needsEncoding(value: addressee.name())) { |
495 | nameLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
496 | nameLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
497 | } |
498 | card.addLine(line: nameLine); |
499 | } |
500 | |
501 | // NICKNAME only for version > 2.1 |
502 | if (version != VCard::v2_1) { |
503 | const QList<NickName> lstNickName = addressee.extraNickNameList(); |
504 | for (const NickName &nickName : lstNickName) { |
505 | VCardLine nickNameLine(QStringLiteral("NICKNAME" ), nickName.nickname()); |
506 | nickNameLine.addParameters(params: nickName.params()); |
507 | |
508 | card.addLine(line: nickNameLine); |
509 | } |
510 | } |
511 | |
512 | // NOTE |
513 | VCardLine noteLine(QStringLiteral("NOTE" ), addressee.note()); |
514 | if (version == VCard::v2_1 && needsEncoding(value: addressee.note())) { |
515 | noteLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
516 | noteLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
517 | } |
518 | card.addLine(line: noteLine); |
519 | |
520 | // ORG |
521 | processOrganizations(addressee, version, card: &card); |
522 | |
523 | // PHOTO |
524 | card.addLine(line: createPicture(QStringLiteral("PHOTO" ), pic: addressee.photo(), version)); |
525 | const QList<Picture> = addressee.extraPhotoList(); |
526 | for (const Picture &photo : lstExtraPhoto) { |
527 | card.addLine(line: createPicture(QStringLiteral("PHOTO" ), pic: photo, version)); |
528 | } |
529 | |
530 | // PROID only for version > 2.1 |
531 | if (version != VCard::v2_1) { |
532 | card.addLine(line: VCardLine(QStringLiteral("PRODID" ), addressee.productId())); |
533 | } |
534 | |
535 | // REV |
536 | card.addLine(line: VCardLine(QStringLiteral("REV" ), createDateTime(dateTime: addressee.revision(), version))); |
537 | |
538 | // ROLE |
539 | const QList<Role> = addressee.extraRoleList(); |
540 | for (const Role &role : lstExtraRole) { |
541 | VCardLine roleLine(QStringLiteral("ROLE" ), role.role()); |
542 | if (version == VCard::v2_1 && needsEncoding(value: role.role())) { |
543 | roleLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
544 | roleLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
545 | } |
546 | roleLine.addParameters(params: role.params()); |
547 | card.addLine(line: roleLine); |
548 | } |
549 | |
550 | // SORT-STRING |
551 | if (version == VCard::v3_0) { |
552 | card.addLine(line: VCardLine(QStringLiteral("SORT-STRING" ), addressee.sortString())); |
553 | } |
554 | |
555 | // SOUND |
556 | card.addLine(line: createSound(snd: addressee.sound(), version)); |
557 | const QList<Sound> lstSound = addressee.extraSoundList(); |
558 | for (const Sound &sound : lstSound) { |
559 | card.addLine(line: createSound(snd: sound, version)); |
560 | } |
561 | |
562 | // TEL |
563 | const PhoneNumber::List phoneNumbers = addressee.phoneNumbers(); |
564 | processPhoneNumbers(phoneNumbers, version, card: &card); |
565 | |
566 | // TITLE |
567 | const QList<Title> lstTitle = addressee.extraTitleList(); |
568 | for (const Title &title : lstTitle) { |
569 | VCardLine titleLine(QStringLiteral("TITLE" ), title.title()); |
570 | if (version == VCard::v2_1 && needsEncoding(value: title.title())) { |
571 | titleLine.addParameter(QStringLiteral("charset" ), QStringLiteral("UTF-8" )); |
572 | titleLine.addParameter(QStringLiteral("encoding" ), QStringLiteral("QUOTED-PRINTABLE" )); |
573 | } |
574 | titleLine.addParameters(params: title.params()); |
575 | |
576 | card.addLine(line: titleLine); |
577 | } |
578 | |
579 | // TZ |
580 | // TODO Add vcard4.0 support |
581 | const TimeZone timeZone = addressee.timeZone(); |
582 | if (timeZone.isValid()) { |
583 | int neg = 1; |
584 | if (timeZone.offset() < 0) { |
585 | neg = -1; |
586 | } |
587 | |
588 | QString str = |
589 | QString::asprintf(format: "%c%02d:%02d" , (timeZone.offset() >= 0 ? '+' : '-'), (timeZone.offset() / 60) * neg, (timeZone.offset() % 60) * neg); |
590 | |
591 | card.addLine(line: VCardLine(QStringLiteral("TZ" ), str)); |
592 | } |
593 | |
594 | // UID |
595 | card.addLine(line: VCardLine(QStringLiteral("UID" ), addressee.uid())); |
596 | |
597 | // URL |
598 | const QList<ResourceLocatorUrl> = addressee.extraUrlList(); |
599 | for (const ResourceLocatorUrl &url : lstExtraUrl) { |
600 | VCardLine line(QStringLiteral("URL" ), url.url()); |
601 | line.addParameters(params: url.params()); |
602 | card.addLine(line); |
603 | } |
604 | if (version == VCard::v4_0) { |
605 | // GENDER |
606 | const Gender gender = addressee.gender(); |
607 | if (gender.isValid()) { |
608 | QString genderStr; |
609 | if (!gender.gender().isEmpty()) { |
610 | genderStr = gender.gender(); |
611 | } |
612 | if (!gender.comment().isEmpty()) { |
613 | genderStr += QLatin1Char(';') + gender.comment(); |
614 | } |
615 | VCardLine line(QStringLiteral("GENDER" ), genderStr); |
616 | card.addLine(line); |
617 | } |
618 | // KIND |
619 | if (!addressee.kind().isEmpty()) { |
620 | VCardLine line(QStringLiteral("KIND" ), addressee.kind()); |
621 | card.addLine(line); |
622 | } |
623 | } |
624 | // From vcard4. |
625 | if (version == VCard::v4_0) { |
626 | const QList<CalendarUrl> lstCalendarUrl = addressee.calendarUrlList(); |
627 | for (const CalendarUrl &url : lstCalendarUrl) { |
628 | if (url.isValid()) { |
629 | QString type; |
630 | switch (url.type()) { |
631 | case CalendarUrl::Unknown: |
632 | case CalendarUrl::EndCalendarType: |
633 | break; |
634 | case CalendarUrl::FBUrl: |
635 | type = QStringLiteral("FBURL" ); |
636 | break; |
637 | case CalendarUrl::CALUri: |
638 | type = QStringLiteral("CALURI" ); |
639 | break; |
640 | case CalendarUrl::CALADRUri: |
641 | type = QStringLiteral("CALADRURI" ); |
642 | break; |
643 | } |
644 | if (!type.isEmpty()) { |
645 | VCardLine line(type, url.url().toDisplayString()); |
646 | line.addParameters(params: url.params()); |
647 | card.addLine(line); |
648 | } |
649 | } |
650 | } |
651 | } |
652 | |
653 | // FieldGroup |
654 | const QList<FieldGroup> lstGroup = addressee.fieldGroupList(); |
655 | for (const FieldGroup &group : lstGroup) { |
656 | VCardLine line(group.fieldGroupName(), group.value()); |
657 | line.addParameters(params: group.params()); |
658 | card.addLine(line); |
659 | } |
660 | |
661 | // IMPP (supported in vcard 3 too) |
662 | const QList<Impp> lstImpp = addressee.imppList(); |
663 | for (const Impp &impp : lstImpp) { |
664 | VCardLine line(QStringLiteral("IMPP" ), impp.address().url()); |
665 | const ParameterMap pMap = impp.params(); |
666 | for (const auto &[param, list] : pMap) { |
667 | if (param.toLower() != QLatin1String("x-service-type" )) { |
668 | line.addParameter(param, value: list.join(sep: QLatin1Char(','))); |
669 | } |
670 | } |
671 | card.addLine(line); |
672 | } |
673 | |
674 | // X- |
675 | const QStringList customs = addressee.customs(); |
676 | processCustoms(customs, version, card: &card, exportVcard); |
677 | |
678 | vCardList.append(t: card); |
679 | } |
680 | |
681 | return VCardParser::createVCards(list: vCardList); |
682 | } |
683 | |
684 | Addressee::List VCardTool::parseVCards(const QByteArray &vcard) const |
685 | { |
686 | static const QLatin1Char semicolonSep(';'); |
687 | static const QLatin1Char commaSep(','); |
688 | QString identifier; |
689 | QString group; |
690 | Addressee::List addrList; |
691 | const VCard::List vCardList = VCardParser::parseVCards(text: vcard); |
692 | |
693 | VCard::List::ConstIterator cardIt; |
694 | VCard::List::ConstIterator listEnd(vCardList.end()); |
695 | for (cardIt = vCardList.begin(); cardIt != listEnd; ++cardIt) { |
696 | Addressee addr; |
697 | |
698 | const QStringList idents = (*cardIt).identifiers(); |
699 | QStringList::ConstIterator identIt; |
700 | QStringList::ConstIterator identEnd(idents.end()); |
701 | for (identIt = idents.begin(); identIt != identEnd; ++identIt) { |
702 | const VCardLine::List lines = (*cardIt).lines(identifier: (*identIt)); |
703 | VCardLine::List::ConstIterator lineIt; |
704 | |
705 | // iterate over the lines |
706 | for (lineIt = lines.begin(); lineIt != lines.end(); ++lineIt) { |
707 | identifier = (*lineIt).identifier().toLower(); |
708 | group = (*lineIt).group(); |
709 | if (!group.isEmpty() && identifier != QLatin1String("adr" )) { |
710 | KContacts::FieldGroup groupField(group + QLatin1Char('.') + (*lineIt).identifier()); |
711 | groupField.setParams((*lineIt).parameterMap()); |
712 | groupField.setValue((*lineIt).value().toString()); |
713 | addr.insertFieldGroup(fieldGroup: groupField); |
714 | } |
715 | // ADR |
716 | else if (identifier == QLatin1String("adr" )) { |
717 | Address address; |
718 | const QStringList addrParts = splitString(sep: semicolonSep, value: (*lineIt).value().toString()); |
719 | const int addrPartsCount(addrParts.count()); |
720 | if (addrPartsCount > 0) { |
721 | address.setPostOfficeBox(addrParts.at(i: 0)); |
722 | } |
723 | if (addrPartsCount > 1) { |
724 | address.setExtended(addrParts.at(i: 1)); |
725 | } |
726 | if (addrPartsCount > 2) { |
727 | address.setStreet(addrParts.at(i: 2)); |
728 | } |
729 | if (addrPartsCount > 3) { |
730 | address.setLocality(addrParts.at(i: 3)); |
731 | } |
732 | if (addrPartsCount > 4) { |
733 | address.setRegion(addrParts.at(i: 4)); |
734 | } |
735 | if (addrPartsCount > 5) { |
736 | address.setPostalCode(addrParts.at(i: 5)); |
737 | } |
738 | if (addrPartsCount > 6) { |
739 | address.setCountry(addrParts.at(i: 6)); |
740 | } |
741 | |
742 | Address::Type type; |
743 | |
744 | const QStringList types = (*lineIt).parameters(QStringLiteral("type" )); |
745 | QStringList::ConstIterator end(types.end()); |
746 | for (QStringList::ConstIterator it = types.begin(); it != end; ++it) { |
747 | type |= stringToAddressType(str: (*it).toLower()); |
748 | } |
749 | |
750 | address.setType(type); |
751 | QString label = (*lineIt).parameter(QStringLiteral("label" )); |
752 | if (!label.isEmpty()) { |
753 | if (label.length() > 1) { |
754 | if (label.at(i: 0) == QLatin1Char('"') && label.at(i: label.length() - 1) == QLatin1Char('"')) { |
755 | label = label.mid(position: 1, n: label.length() - 2); |
756 | } |
757 | } |
758 | address.setLabel(label); |
759 | } |
760 | QString geoStr = (*lineIt).parameter(QStringLiteral("geo" )); |
761 | if (!geoStr.isEmpty()) { |
762 | geoStr.remove(c: QLatin1Char('\"')); |
763 | geoStr.remove(QStringLiteral("geo:" )); |
764 | if (geoStr.contains(c: QLatin1Char(','))) { |
765 | QStringList arguments = geoStr.split(sep: QLatin1Char(',')); |
766 | KContacts::Geo geo; |
767 | geo.setLatitude(arguments.at(i: 0).toDouble()); |
768 | geo.setLongitude(arguments.at(i: 1).toDouble()); |
769 | address.setGeo(geo); |
770 | } |
771 | } |
772 | addr.insertAddress(address); |
773 | } |
774 | // ANNIVERSARY |
775 | else if (identifier == QLatin1String("anniversary" )) { |
776 | const QString t = (*lineIt).value().toString(); |
777 | const QDateTime dt(parseDateTime(str: t)); |
778 | addr.insertCustom(QStringLiteral("KADDRESSBOOK" ), QStringLiteral("X-Anniversary" ), value: dt.date().toString(format: Qt::ISODate)); |
779 | } |
780 | // BDAY |
781 | else if (identifier == QLatin1String("bday" )) { |
782 | bool withTime; |
783 | const QDateTime bday = parseDateTime(str: (*lineIt).value().toString(), timeValid: &withTime); |
784 | addr.setBirthday(birthday: bday, withTime); |
785 | } |
786 | // CATEGORIES |
787 | else if (identifier == QLatin1String("categories" )) { |
788 | const QStringList categories = splitString(sep: commaSep, value: (*lineIt).value().toString()); |
789 | addr.setCategories(categories); |
790 | } |
791 | // FBURL |
792 | else if (identifier == QLatin1String("fburl" )) { |
793 | CalendarUrl calurl; |
794 | calurl.setType(CalendarUrl::FBUrl); |
795 | const QUrl url = QUrl((*lineIt).value().toString()); |
796 | calurl.setUrl(url); |
797 | calurl.setParams((*lineIt).parameterMap()); |
798 | addr.insertCalendarUrl(calendarUrl: calurl); |
799 | } |
800 | // CALADRURI |
801 | else if (identifier == QLatin1String("caladruri" )) { |
802 | CalendarUrl calurl; |
803 | calurl.setType(CalendarUrl::CALADRUri); |
804 | const QUrl url = QUrl((*lineIt).value().toString()); |
805 | calurl.setUrl(url); |
806 | calurl.setParams((*lineIt).parameterMap()); |
807 | addr.insertCalendarUrl(calendarUrl: calurl); |
808 | } |
809 | // CALURI |
810 | else if (identifier == QLatin1String("caluri" )) { |
811 | CalendarUrl calurl; |
812 | calurl.setType(CalendarUrl::CALUri); |
813 | const QUrl url = QUrl((*lineIt).value().toString()); |
814 | calurl.setUrl(url); |
815 | calurl.setParams((*lineIt).parameterMap()); |
816 | addr.insertCalendarUrl(calendarUrl: calurl); |
817 | } |
818 | // IMPP |
819 | else if (identifier == QLatin1String("impp" )) { |
820 | QUrl imppUrl((*lineIt).value().toString()); |
821 | Impp impp; |
822 | impp.setParams((*lineIt).parameterMap()); |
823 | if (!(*lineIt).parameter(QStringLiteral("x-service-type" )).isEmpty() && imppUrl.scheme().isEmpty()) { |
824 | imppUrl.setScheme(normalizeImppServiceType(serviceType: (*lineIt).parameter(QStringLiteral("x-service-type" )).toLower())); |
825 | } |
826 | impp.setAddress(imppUrl); |
827 | addr.insertImpp(impp); |
828 | } |
829 | // CLASS |
830 | else if (identifier == QLatin1String("class" )) { |
831 | addr.setSecrecy(parseSecrecy(line: *lineIt)); |
832 | } |
833 | // GENDER |
834 | else if (identifier == QLatin1String("gender" )) { |
835 | QString genderStr = (*lineIt).value().toString(); |
836 | if (!genderStr.isEmpty()) { |
837 | Gender gender; |
838 | if (genderStr.at(i: 0) != QLatin1Char(';')) { |
839 | gender.setGender(genderStr.at(i: 0)); |
840 | if (genderStr.length() > 2 && (genderStr.at(i: 1) == QLatin1Char(';'))) { |
841 | gender.setComment(genderStr.right(n: genderStr.length() - 2)); |
842 | } |
843 | } else { |
844 | gender.setComment(genderStr.right(n: genderStr.length() - 1)); |
845 | } |
846 | addr.setGender(gender); |
847 | } |
848 | } |
849 | // LANG |
850 | else if (identifier == QLatin1String("lang" )) { |
851 | Lang lang; |
852 | lang.setLanguage((*lineIt).value().toString()); |
853 | lang.setParams((*lineIt).parameterMap()); |
854 | addr.insertLang(language: lang); |
855 | } |
856 | // EMAIL |
857 | else if (identifier == QLatin1String("email" )) { |
858 | const QStringList types = (*lineIt).parameters(QStringLiteral("type" )); |
859 | Email mail((*lineIt).value().toString()); |
860 | mail.setParams((*lineIt).parameterMap()); |
861 | addr.addEmail(email: mail); |
862 | } |
863 | // KIND |
864 | else if (identifier == QLatin1String("kind" )) { |
865 | addr.setKind((*lineIt).value().toString()); |
866 | } |
867 | // FN |
868 | else if (identifier == QLatin1String("fn" )) { |
869 | addr.setFormattedName((*lineIt).value().toString()); |
870 | } |
871 | // GEO |
872 | else if (identifier == QLatin1String("geo" )) { |
873 | Geo geo; |
874 | QString lineStr = (*lineIt).value().toString(); |
875 | if (lineStr.startsWith(s: QLatin1String("geo:" ))) { // VCard 4.0 |
876 | lineStr.remove(QStringLiteral("geo:" )); |
877 | const QStringList geoParts = lineStr.split(sep: QLatin1Char(','), behavior: Qt::KeepEmptyParts); |
878 | if (geoParts.size() >= 2) { |
879 | geo.setLatitude(geoParts.at(i: 0).toFloat()); |
880 | geo.setLongitude(geoParts.at(i: 1).toFloat()); |
881 | addr.setGeo(geo); |
882 | } |
883 | } else { |
884 | const QStringList geoParts = lineStr.split(sep: QLatin1Char(';'), behavior: Qt::KeepEmptyParts); |
885 | if (geoParts.size() >= 2) { |
886 | geo.setLatitude(geoParts.at(i: 0).toFloat()); |
887 | geo.setLongitude(geoParts.at(i: 1).toFloat()); |
888 | addr.setGeo(geo); |
889 | } |
890 | } |
891 | } |
892 | // KEY |
893 | else if (identifier == QLatin1String("key" )) { |
894 | addr.insertKey(key: parseKey(line: *lineIt)); |
895 | } |
896 | // LABEL |
897 | else if (identifier == QLatin1String("label" )) { |
898 | Address::Type type; |
899 | |
900 | const QStringList types = (*lineIt).parameters(QStringLiteral("type" )); |
901 | QStringList::ConstIterator end(types.end()); |
902 | for (QStringList::ConstIterator it = types.begin(); it != end; ++it) { |
903 | type |= stringToAddressType(str: (*it).toLower()); |
904 | } |
905 | |
906 | bool available = false; |
907 | KContacts::Address::List addressList = addr.addresses(); |
908 | for (KContacts::Address::List::Iterator it = addressList.begin(); it != addressList.end(); ++it) { |
909 | if ((*it).type() == type) { |
910 | (*it).setLabel((*lineIt).value().toString()); |
911 | addr.insertAddress(address: *it); |
912 | available = true; |
913 | break; |
914 | } |
915 | } |
916 | |
917 | if (!available) { // a standalone LABEL tag |
918 | KContacts::Address address(type); |
919 | address.setLabel((*lineIt).value().toString()); |
920 | addr.insertAddress(address); |
921 | } |
922 | } |
923 | // LOGO |
924 | else if (identifier == QLatin1String("logo" )) { |
925 | Picture picture = parsePicture(line: *lineIt); |
926 | if (addr.logo().isEmpty()) { |
927 | addr.setLogo(picture); |
928 | } else { |
929 | addr.insertExtraLogo(logo: picture); |
930 | } |
931 | } |
932 | // MAILER |
933 | else if (identifier == QLatin1String("mailer" )) { |
934 | addr.setMailer((*lineIt).value().toString()); |
935 | } |
936 | // N |
937 | else if (identifier == QLatin1Char('n')) { |
938 | const QStringList nameParts = splitString(sep: semicolonSep, value: (*lineIt).value().toString()); |
939 | const int numberOfParts(nameParts.count()); |
940 | if (numberOfParts > 0) { |
941 | addr.setFamilyName(nameParts.at(i: 0)); |
942 | } |
943 | if (numberOfParts > 1) { |
944 | addr.setGivenName(nameParts.at(i: 1)); |
945 | } |
946 | if (numberOfParts > 2) { |
947 | addr.setAdditionalName(nameParts.at(i: 2)); |
948 | } |
949 | if (numberOfParts > 3) { |
950 | addr.setPrefix(nameParts.at(i: 3)); |
951 | } |
952 | if (numberOfParts > 4) { |
953 | addr.setSuffix(nameParts.at(i: 4)); |
954 | } |
955 | if (!(*lineIt).parameter(QStringLiteral("sort-as" )).isEmpty()) { |
956 | addr.setSortString((*lineIt).parameter(QStringLiteral("sort-as" ))); |
957 | } |
958 | } |
959 | // NAME |
960 | else if (identifier == QLatin1String("name" )) { |
961 | addr.setName((*lineIt).value().toString()); |
962 | } |
963 | // NICKNAME |
964 | else if (identifier == QLatin1String("nickname" )) { |
965 | NickName nickName((*lineIt).value().toString()); |
966 | nickName.setParams((*lineIt).parameterMap()); |
967 | addr.insertExtraNickName(nickName); |
968 | } |
969 | // NOTE |
970 | else if (identifier == QLatin1String("note" )) { |
971 | addr.setNote((*lineIt).value().toString()); |
972 | } |
973 | // ORGANIZATION |
974 | else if (identifier == QLatin1String("org" )) { |
975 | const QStringList orgParts = splitString(sep: semicolonSep, value: (*lineIt).value().toString()); |
976 | const int orgPartsCount(orgParts.count()); |
977 | if (orgPartsCount > 0) { |
978 | Org organization(orgParts.at(i: 0)); |
979 | organization.setParams((*lineIt).parameterMap()); |
980 | addr.insertExtraOrganization(organization); |
981 | } |
982 | if (orgPartsCount > 1) { |
983 | addr.setDepartment(orgParts.at(i: 1)); |
984 | } |
985 | if (!(*lineIt).parameter(QStringLiteral("sort-as" )).isEmpty()) { |
986 | addr.setSortString((*lineIt).parameter(QStringLiteral("sort-as" ))); |
987 | } |
988 | } |
989 | // PHOTO |
990 | else if (identifier == QLatin1String("photo" )) { |
991 | Picture picture = parsePicture(line: *lineIt); |
992 | if (addr.photo().isEmpty()) { |
993 | addr.setPhoto(picture); |
994 | } else { |
995 | addr.insertExtraPhoto(picture); |
996 | } |
997 | } |
998 | // PROID |
999 | else if (identifier == QLatin1String("prodid" )) { |
1000 | addr.setProductId((*lineIt).value().toString()); |
1001 | } |
1002 | // REV |
1003 | else if (identifier == QLatin1String("rev" )) { |
1004 | addr.setRevision(parseDateTime(str: (*lineIt).value().toString())); |
1005 | } |
1006 | // ROLE |
1007 | else if (identifier == QLatin1String("role" )) { |
1008 | Role role((*lineIt).value().toString()); |
1009 | role.setParams((*lineIt).parameterMap()); |
1010 | addr.insertExtraRole(role); |
1011 | } |
1012 | // SORT-STRING |
1013 | else if (identifier == QLatin1String("sort-string" )) { |
1014 | addr.setSortString((*lineIt).value().toString()); |
1015 | } |
1016 | // SOUND |
1017 | else if (identifier == QLatin1String("sound" )) { |
1018 | Sound sound = parseSound(line: *lineIt); |
1019 | if (addr.sound().isEmpty()) { |
1020 | addr.setSound(sound); |
1021 | } else { |
1022 | addr.insertExtraSound(sound); |
1023 | } |
1024 | } |
1025 | // TEL |
1026 | else if (identifier == QLatin1String("tel" )) { |
1027 | PhoneNumber phone; |
1028 | phone.setNumber((*lineIt).value().toString()); |
1029 | |
1030 | PhoneNumber::Type type; |
1031 | bool foundType = false; |
1032 | const QStringList types = (*lineIt).parameters(QStringLiteral("type" )); |
1033 | QStringList::ConstIterator typeEnd(types.constEnd()); |
1034 | for (QStringList::ConstIterator it = types.constBegin(); it != typeEnd; ++it) { |
1035 | type |= stringToPhoneType(str: (*it).toUpper()); |
1036 | foundType = true; |
1037 | } |
1038 | phone.setType(foundType ? type : PhoneNumber::Undefined); |
1039 | phone.setParams((*lineIt).parameterMap()); |
1040 | |
1041 | addr.insertPhoneNumber(phoneNumber: phone); |
1042 | } |
1043 | // TITLE |
1044 | else if (identifier == QLatin1String("title" )) { |
1045 | Title title((*lineIt).value().toString()); |
1046 | title.setParams((*lineIt).parameterMap()); |
1047 | addr.insertExtraTitle(title); |
1048 | } |
1049 | // TZ |
1050 | else if (identifier == QLatin1String("tz" )) { |
1051 | // TODO add vcard4 support |
1052 | TimeZone tz; |
1053 | const QString date = (*lineIt).value().toString(); |
1054 | |
1055 | if (!date.isEmpty()) { |
1056 | const QStringView dateView(date); |
1057 | int hours = dateView.mid(pos: 1, n: 2).toInt(); |
1058 | int minutes = dateView.mid(pos: 4, n: 2).toInt(); |
1059 | int offset = (hours * 60) + minutes; |
1060 | offset = offset * (date[0] == QLatin1Char('+') ? 1 : -1); |
1061 | |
1062 | tz.setOffset(offset); |
1063 | addr.setTimeZone(tz); |
1064 | } |
1065 | } |
1066 | // UID |
1067 | else if (identifier == QLatin1String("uid" )) { |
1068 | addr.setUid((*lineIt).value().toString()); |
1069 | } |
1070 | // URL |
1071 | else if (identifier == QLatin1String("url" )) { |
1072 | const QUrl url = QUrl((*lineIt).value().toString()); |
1073 | ResourceLocatorUrl resourceLocatorUrl; |
1074 | resourceLocatorUrl.setUrl(url); |
1075 | resourceLocatorUrl.setParams((*lineIt).parameterMap()); |
1076 | addr.insertExtraUrl(url: resourceLocatorUrl); |
1077 | } |
1078 | // SOURCE |
1079 | else if (identifier == QLatin1String("source" )) { |
1080 | const QUrl url = QUrl((*lineIt).value().toString()); |
1081 | addr.insertSourceUrl(url); |
1082 | } |
1083 | // MEMBER (vcard 4.0) |
1084 | else if (identifier == QLatin1String("member" )) { |
1085 | addr.insertMember(member: (*lineIt).value().toString()); |
1086 | } |
1087 | // RELATED (vcard 4.0) |
1088 | else if (identifier == QLatin1String("related" )) { |
1089 | Related related; |
1090 | related.setRelated((*lineIt).value().toString()); |
1091 | related.setParams((*lineIt).parameterMap()); |
1092 | addr.insertRelationship(related); |
1093 | } |
1094 | // CLIENTPIDMAP (vcard 4.0) |
1095 | else if (identifier == QLatin1String("clientpidmap" )) { |
1096 | ClientPidMap clientpidmap; |
1097 | clientpidmap.setClientPidMap((*lineIt).value().toString()); |
1098 | clientpidmap.setParams((*lineIt).parameterMap()); |
1099 | addr.insertClientPidMap(clientpidmap); |
1100 | } |
1101 | // X- |
1102 | // TODO import X-GENDER |
1103 | else if (identifier.startsWith(s: QLatin1String("x-" ))) { |
1104 | QString ident = (*lineIt).identifier(); |
1105 | // clang-format off |
1106 | //X-Evolution |
1107 | // also normalize case of our own extensions, some backends "adjust" that |
1108 | if (identifier == QLatin1String("x-evolution-spouse" ) |
1109 | || identifier == QLatin1String("x-spouse" )) { |
1110 | ident = QStringLiteral("X-KADDRESSBOOK-X-SpousesName" ); |
1111 | } else if (identifier == QLatin1String("x-evolution-blog-url" ) || identifier.compare(other: QLatin1String("X-KADDRESSBOOK-BLOGFEED" ), cs: Qt::CaseInsensitive) == 0) { |
1112 | ident = QStringLiteral("X-KADDRESSBOOK-BlogFeed" ); |
1113 | } else if (identifier == QLatin1String("x-evolution-assistant" ) |
1114 | || identifier == QLatin1String("x-assistant" ) |
1115 | || identifier.compare(other: QLatin1String("X-KADDRESSBOOK-X-ASSISTANTSNAME" ), cs: Qt::CaseInsensitive) == 0) { |
1116 | ident = QStringLiteral("X-KADDRESSBOOK-X-AssistantsName" ); |
1117 | } else if (identifier == QLatin1String("x-evolution-anniversary" ) |
1118 | || identifier == QLatin1String("x-anniversary" ) |
1119 | || identifier.compare(other: QLatin1String("X-KADDRESSBOOK-X-ANNIVERSARY" ), cs: Qt::CaseInsensitive) == 0) { |
1120 | ident = QStringLiteral("X-KADDRESSBOOK-X-Anniversary" ); |
1121 | } else if (identifier == QLatin1String("x-evolution-manager" ) |
1122 | || identifier == QLatin1String("x-manager" ) |
1123 | || identifier.compare(other: QLatin1String("X-KADDRESSBOOK-X-MANAGERSNAME" ), cs: Qt::CaseInsensitive) == 0) { |
1124 | // clang-format on |
1125 | ident = QStringLiteral("X-KADDRESSBOOK-X-ManagersName" ); |
1126 | } else if (identifier.compare(other: QLatin1String("X-KADDRESSBOOK-X-PROFESSION" ), cs: Qt::CaseInsensitive) == 0) { |
1127 | ident = QStringLiteral("X-KADDRESSBOOK-X-Profession" ); |
1128 | } else if (identifier.compare(other: QLatin1String("X-KADDRESSBOOK-X-OFFICE" ), cs: Qt::CaseInsensitive) == 0) { |
1129 | ident = QStringLiteral("X-KADDRESSBOOK-X-Office" ); |
1130 | } else if (identifier.compare(other: QLatin1String("X-KADDRESSBOOK-X-SPOUSESNAME" ), cs: Qt::CaseInsensitive) == 0) { |
1131 | ident = QStringLiteral("X-KADDRESSBOOK-X-SpousesName" ); |
1132 | } else if (identifier == QLatin1String("x-aim" )) { |
1133 | ident = QStringLiteral("X-messaging/aim-All" ); |
1134 | } else if (identifier == QLatin1String("x-icq" )) { |
1135 | ident = QStringLiteral("X-messaging/icq-All" ); |
1136 | } else if (identifier == QLatin1String("x-jabber" )) { |
1137 | ident = QStringLiteral("X-messaging/xmpp-All" ); |
1138 | } else if (identifier == QLatin1String("x-jabber" )) { |
1139 | ident = QStringLiteral("X-messaging/xmpp-All" ); |
1140 | } else if (identifier == QLatin1String("x-msn" )) { |
1141 | ident = QStringLiteral("X-messaging/msn-All" ); |
1142 | } else if (identifier == QLatin1String("x-yahoo" )) { |
1143 | ident = QStringLiteral("X-messaging/yahoo-All" ); |
1144 | } else if (identifier == QLatin1String("x-gadugadu" )) { |
1145 | ident = QStringLiteral("X-messaging/gadu-All" ); |
1146 | } else if (identifier == QLatin1String("x-skype" )) { |
1147 | ident = QStringLiteral("X-messaging/skype-All" ); |
1148 | } else if (identifier == QLatin1String("x-groupwise" )) { |
1149 | ident = QStringLiteral("X-messaging/groupwise-All" ); |
1150 | } else if (identifier == QLatin1String("x-sms" )) { |
1151 | ident = QStringLiteral("X-messaging/sms-All" ); |
1152 | } else if (identifier == QLatin1String("x-meanwhile" )) { |
1153 | ident = QStringLiteral("X-messaging/meanwhile-All" ); |
1154 | } else if (identifier == QLatin1String("x-irc" )) { |
1155 | ident = QStringLiteral("X-messaging/irc-All" ); |
1156 | } else if (identifier == QLatin1String("x-gtalk" )) { |
1157 | ident = QStringLiteral("X-messaging/googletalk-All" ); |
1158 | } else if (identifier == QLatin1String("x-twitter" )) { |
1159 | ident = QStringLiteral("X-messaging/twitter-All" ); |
1160 | } |
1161 | |
1162 | const QString key = ident.mid(position: 2); |
1163 | const int dash = key.indexOf(c: QLatin1Char('-')); |
1164 | |
1165 | // convert legacy messaging fields into IMPP ones |
1166 | if (key.startsWith(s: QLatin1String("messaging/" ))) { |
1167 | QUrl url; |
1168 | url.setScheme(normalizeImppServiceType(serviceType: key.mid(position: 10, n: dash - 10))); |
1169 | const auto values = (*lineIt).value().toString().split(sep: QChar(0xE000), behavior: Qt::SkipEmptyParts); |
1170 | for (const auto &value : values) { |
1171 | url.setPath(path: value); |
1172 | Impp impp; |
1173 | impp.setParams((*lineIt).parameterMap()); |
1174 | impp.setAddress(url); |
1175 | addr.insertImpp(impp); |
1176 | } |
1177 | } else { |
1178 | addr.insertCustom(app: key.left(n: dash), name: key.mid(position: dash + 1), value: (*lineIt).value().toString()); |
1179 | } |
1180 | } |
1181 | } |
1182 | } |
1183 | |
1184 | addrList.append(t: addr); |
1185 | } |
1186 | |
1187 | return addrList; |
1188 | } |
1189 | |
1190 | QDateTime VCardTool::parseDateTime(const QString &str, bool *timeValid) |
1191 | { |
1192 | static const QLatin1Char sep('-'); |
1193 | |
1194 | const int posT = str.indexOf(c: QLatin1Char('T')); |
1195 | QString dateString = posT >= 0 ? str.left(n: posT) : str; |
1196 | const bool noYear = dateString.startsWith(s: QLatin1String("--" )); |
1197 | dateString.remove(c: QLatin1Char('-')); |
1198 | QDate date; |
1199 | |
1200 | const QStringView dstr{dateString}; |
1201 | if (noYear) { |
1202 | date.setDate(year: -1, month: dstr.mid(pos: 0, n: 2).toInt(), day: dstr.mid(pos: 2, n: 2).toInt()); |
1203 | } else { |
1204 | // E.g. 20160120 |
1205 | date.setDate(year: dstr.mid(pos: 0, n: 4).toInt(), month: dstr.mid(pos: 4, n: 2).toInt(), day: dstr.mid(pos: 6, n: 2).toInt()); |
1206 | } |
1207 | |
1208 | QTime time; |
1209 | QTimeZone tz = QTimeZone::LocalTime; |
1210 | if (posT >= 0) { |
1211 | QString timeString = str.mid(position: posT + 1); |
1212 | timeString.remove(c: QLatin1Char(':')); |
1213 | const int zPos = timeString.indexOf(c: QLatin1Char('Z')); |
1214 | const int plusPos = timeString.indexOf(c: QLatin1Char('+')); |
1215 | const int minusPos = timeString.indexOf(c: sep); |
1216 | const int tzPos = qMax(a: qMax(a: zPos, b: plusPos), b: minusPos); |
1217 | const QStringView hhmmssString = tzPos >= 0 ? QStringView(timeString).left(n: tzPos) : QStringView(timeString); |
1218 | int hour = 0; |
1219 | int minutes = 0; |
1220 | int seconds = 0; |
1221 | switch (hhmmssString.size()) { |
1222 | case 2: |
1223 | hour = hhmmssString.toInt(); |
1224 | break; |
1225 | case 4: |
1226 | hour = hhmmssString.mid(pos: 0, n: 2).toInt(); |
1227 | minutes = hhmmssString.mid(pos: 2, n: 2).toInt(); |
1228 | break; |
1229 | case 6: |
1230 | hour = hhmmssString.mid(pos: 0, n: 2).toInt(); |
1231 | minutes = hhmmssString.mid(pos: 2, n: 2).toInt(); |
1232 | seconds = hhmmssString.mid(pos: 4, n: 2).toInt(); |
1233 | break; |
1234 | } |
1235 | time.setHMS(h: hour, m: minutes, s: seconds); |
1236 | |
1237 | if (tzPos >= 0) { |
1238 | if (zPos >= 0) { |
1239 | tz = QTimeZone::UTC; |
1240 | } else { |
1241 | int offsetSecs = 0; |
1242 | const auto offsetString = QStringView(timeString).mid(pos: tzPos + 1); |
1243 | switch (offsetString.size()) { |
1244 | case 2: // format: "hh" |
1245 | offsetSecs = offsetString.left(n: 2).toInt() * 3600; |
1246 | break; |
1247 | case 4: // format: "hhmm" |
1248 | offsetSecs = offsetString.left(n: 2).toInt() * 3600 + offsetString.mid(pos: 2, n: 2).toInt() * 60; |
1249 | break; |
1250 | } |
1251 | if (minusPos >= 0) { |
1252 | offsetSecs *= -1; |
1253 | } |
1254 | tz = QTimeZone::fromSecondsAheadOfUtc(offset: offsetSecs); |
1255 | } |
1256 | } |
1257 | } |
1258 | if (timeValid) { |
1259 | *timeValid = time.isValid(); |
1260 | } |
1261 | |
1262 | return QDateTime(date, time, tz); |
1263 | } |
1264 | |
1265 | QString VCardTool::createDateTime(const QDateTime &dateTime, VCard::Version version, bool withTime) |
1266 | { |
1267 | if (!dateTime.date().isValid()) { |
1268 | return QString(); |
1269 | } |
1270 | QString str = createDate(date: dateTime.date(), version); |
1271 | if (!withTime) { |
1272 | return str; |
1273 | } |
1274 | str += createTime(time: dateTime.time(), version); |
1275 | if (dateTime.timeSpec() == Qt::UTC) { |
1276 | str += QLatin1Char('Z'); |
1277 | } else if (dateTime.timeSpec() == Qt::OffsetFromUTC) { |
1278 | const int offsetSecs = dateTime.offsetFromUtc(); |
1279 | if (offsetSecs >= 0) { |
1280 | str += QLatin1Char('+'); |
1281 | } else { |
1282 | str += QLatin1Char('-'); |
1283 | } |
1284 | QTime offsetTime = QTime(0, 0).addSecs(secs: abs(x: offsetSecs)); |
1285 | if (version == VCard::v4_0) { |
1286 | str += offsetTime.toString(QStringLiteral("HHmm" )); |
1287 | } else { |
1288 | str += offsetTime.toString(QStringLiteral("HH:mm" )); |
1289 | } |
1290 | } |
1291 | return str; |
1292 | } |
1293 | |
1294 | QString VCardTool::createDate(const QDate &date, VCard::Version version) |
1295 | { |
1296 | QString format; |
1297 | if (date.year() > 0) { |
1298 | format = QStringLiteral("yyyyMMdd" ); |
1299 | } else { |
1300 | format = QStringLiteral("--MMdd" ); |
1301 | } |
1302 | if (version != VCard::v4_0) { |
1303 | format.replace(QStringLiteral("yyyy" ), QStringLiteral("yyyy-" )); |
1304 | format.replace(QStringLiteral("MM" ), QStringLiteral("MM-" )); |
1305 | } |
1306 | return date.toString(format); |
1307 | } |
1308 | |
1309 | QString VCardTool::createTime(const QTime &time, VCard::Version version) |
1310 | { |
1311 | QString format; |
1312 | if (version == VCard::v4_0) { |
1313 | format = QStringLiteral("HHmmss" ); |
1314 | } else { |
1315 | format = QStringLiteral("HH:mm:ss" ); |
1316 | } |
1317 | return QLatin1Char('T') + time.toString(format); |
1318 | } |
1319 | |
1320 | Picture VCardTool::parsePicture(const VCardLine &line) const |
1321 | { |
1322 | Picture pic; |
1323 | |
1324 | const QStringList params = line.parameterList(); |
1325 | QString type; |
1326 | if (params.contains(str: QLatin1String("type" ))) { |
1327 | type = line.parameter(QStringLiteral("type" )); |
1328 | } |
1329 | if (params.contains(str: QLatin1String("encoding" ))) { |
1330 | pic.setRawData(rawData: line.value().toByteArray(), type); |
1331 | } else if (params.contains(str: QLatin1String("value" ))) { |
1332 | if (line.parameter(QStringLiteral("value" )).toLower() == QLatin1String("uri" )) { |
1333 | pic.setUrl(line.value().toString()); |
1334 | } |
1335 | } |
1336 | |
1337 | return pic; |
1338 | } |
1339 | |
1340 | VCardLine VCardTool::createPicture(const QString &identifier, const Picture &pic, VCard::Version version) const |
1341 | { |
1342 | VCardLine line(identifier); |
1343 | |
1344 | if (pic.isEmpty()) { |
1345 | return line; |
1346 | } |
1347 | |
1348 | if (pic.isIntern()) { |
1349 | line.setValue(pic.rawData()); |
1350 | if (version == VCard::v2_1) { |
1351 | line.addParameter(QStringLiteral("ENCODING" ), QStringLiteral("BASE64" )); |
1352 | line.addParameter(param: pic.type(), value: QString()); |
1353 | } else { /*if (version == VCard::v3_0) */ |
1354 | line.addParameter(QStringLiteral("encoding" ), QStringLiteral("b" )); |
1355 | line.addParameter(QStringLiteral("type" ), value: pic.type()); |
1356 | #if 0 |
1357 | } else { //version 4.0 |
1358 | line.addParameter(QStringLiteral("data" ) + QStringLiteral(":image/" ) + pic.type(), QStringLiteral("base64" )); |
1359 | #endif |
1360 | } |
1361 | } else { |
1362 | line.setValue(pic.url()); |
1363 | line.addParameter(QStringLiteral("value" ), QStringLiteral("URI" )); |
1364 | } |
1365 | |
1366 | return line; |
1367 | } |
1368 | |
1369 | Sound VCardTool::parseSound(const VCardLine &line) const |
1370 | { |
1371 | Sound snd; |
1372 | |
1373 | const QStringList params = line.parameterList(); |
1374 | if (params.contains(str: QLatin1String("encoding" ))) { |
1375 | snd.setData(line.value().toByteArray()); |
1376 | } else if (params.contains(str: QLatin1String("value" ))) { |
1377 | if (line.parameter(QStringLiteral("value" )).toLower() == QLatin1String("uri" )) { |
1378 | snd.setUrl(line.value().toString()); |
1379 | } |
1380 | } |
1381 | |
1382 | /* TODO: support sound types |
1383 | if ( params.contains( "type" ) ) |
1384 | snd.setType( line.parameter( "type" ) ); |
1385 | */ |
1386 | |
1387 | return snd; |
1388 | } |
1389 | |
1390 | VCardLine VCardTool::createSound(const Sound &snd, VCard::Version version) const |
1391 | { |
1392 | Q_UNUSED(version); |
1393 | VCardLine line(QStringLiteral("SOUND" )); |
1394 | |
1395 | if (snd.isIntern()) { |
1396 | if (!snd.data().isEmpty()) { |
1397 | line.setValue(snd.data()); |
1398 | if (version == VCard::v2_1) { |
1399 | line.addParameter(QStringLiteral("ENCODING" ), QStringLiteral("BASE64" )); |
1400 | } else { |
1401 | line.addParameter(QStringLiteral("encoding" ), QStringLiteral("b" )); |
1402 | } |
1403 | // TODO: need to store sound type!!! |
1404 | } |
1405 | } else if (!snd.url().isEmpty()) { |
1406 | line.setValue(snd.url()); |
1407 | line.addParameter(QStringLiteral("value" ), QStringLiteral("URI" )); |
1408 | } |
1409 | |
1410 | return line; |
1411 | } |
1412 | |
1413 | Key VCardTool::parseKey(const VCardLine &line) const |
1414 | { |
1415 | Key key; |
1416 | |
1417 | const QStringList params = line.parameterList(); |
1418 | if (params.contains(str: QLatin1String("encoding" ))) { |
1419 | key.setBinaryData(line.value().toByteArray()); |
1420 | } else { |
1421 | key.setTextData(line.value().toString()); |
1422 | } |
1423 | |
1424 | if (params.contains(str: QLatin1String("type" ))) { |
1425 | if (line.parameter(QStringLiteral("type" )).toLower() == QLatin1String("x509" )) { |
1426 | key.setType(Key::X509); |
1427 | } else if (line.parameter(QStringLiteral("type" )).toLower() == QLatin1String("pgp" )) { |
1428 | key.setType(Key::PGP); |
1429 | } else { |
1430 | key.setType(Key::Custom); |
1431 | key.setCustomTypeString(line.parameter(QStringLiteral("type" ))); |
1432 | } |
1433 | } else if (params.contains(str: QLatin1String("mediatype" ))) { |
1434 | const QString param = line.parameter(QStringLiteral("mediatype" )).toLower(); |
1435 | if (param == QLatin1String("application/x-x509-ca-cert" )) { |
1436 | key.setType(Key::X509); |
1437 | } else if (param == QLatin1String("application/pgp-keys" )) { |
1438 | key.setType(Key::PGP); |
1439 | } else { |
1440 | key.setType(Key::Custom); |
1441 | key.setCustomTypeString(line.parameter(QStringLiteral("type" ))); |
1442 | } |
1443 | } |
1444 | |
1445 | return key; |
1446 | } |
1447 | |
1448 | VCardLine VCardTool::createKey(const Key &key, VCard::Version version) const |
1449 | { |
1450 | VCardLine line(QStringLiteral("KEY" )); |
1451 | |
1452 | if (key.isBinary()) { |
1453 | if (!key.binaryData().isEmpty()) { |
1454 | line.setValue(key.binaryData()); |
1455 | if (version == VCard::v2_1) { |
1456 | line.addParameter(QStringLiteral("ENCODING" ), QStringLiteral("BASE64" )); |
1457 | } else { |
1458 | line.addParameter(QStringLiteral("encoding" ), QStringLiteral("b" )); |
1459 | } |
1460 | } |
1461 | } else if (!key.textData().isEmpty()) { |
1462 | line.setValue(key.textData()); |
1463 | } |
1464 | |
1465 | if (version == VCard::v4_0) { |
1466 | if (key.type() == Key::X509) { |
1467 | line.addParameter(QStringLiteral("MEDIATYPE" ), QStringLiteral("application/x-x509-ca-cert" )); |
1468 | } else if (key.type() == Key::PGP) { |
1469 | line.addParameter(QStringLiteral("MEDIATYPE" ), QStringLiteral("application/pgp-keys" )); |
1470 | } else if (key.type() == Key::Custom) { |
1471 | line.addParameter(QStringLiteral("MEDIATYPE" ), value: key.customTypeString()); |
1472 | } |
1473 | } else { |
1474 | if (key.type() == Key::X509) { |
1475 | line.addParameter(QStringLiteral("type" ), QStringLiteral("X509" )); |
1476 | } else if (key.type() == Key::PGP) { |
1477 | line.addParameter(QStringLiteral("type" ), QStringLiteral("PGP" )); |
1478 | } else if (key.type() == Key::Custom) { |
1479 | line.addParameter(QStringLiteral("type" ), value: key.customTypeString()); |
1480 | } |
1481 | } |
1482 | |
1483 | return line; |
1484 | } |
1485 | |
1486 | Secrecy VCardTool::parseSecrecy(const VCardLine &line) const |
1487 | { |
1488 | Secrecy secrecy; |
1489 | |
1490 | const QString value = line.value().toString().toLower(); |
1491 | if (value == QLatin1String("public" )) { |
1492 | secrecy.setType(Secrecy::Public); |
1493 | } else if (value == QLatin1String("private" )) { |
1494 | secrecy.setType(Secrecy::Private); |
1495 | } else if (value == QLatin1String("confidential" )) { |
1496 | secrecy.setType(Secrecy::Confidential); |
1497 | } |
1498 | |
1499 | return secrecy; |
1500 | } |
1501 | |
1502 | VCardLine VCardTool::createSecrecy(const Secrecy &secrecy) const |
1503 | { |
1504 | VCardLine line(QStringLiteral("CLASS" )); |
1505 | |
1506 | int type = secrecy.type(); |
1507 | |
1508 | if (type == Secrecy::Public) { |
1509 | line.setValue(QStringLiteral("PUBLIC" )); |
1510 | } else if (type == Secrecy::Private) { |
1511 | line.setValue(QStringLiteral("PRIVATE" )); |
1512 | } else if (type == Secrecy::Confidential) { |
1513 | line.setValue(QStringLiteral("CONFIDENTIAL" )); |
1514 | } |
1515 | |
1516 | return line; |
1517 | } |
1518 | |
1519 | QStringList VCardTool::splitString(QChar sep, const QString &str) const |
1520 | { |
1521 | QStringList list; |
1522 | QString value(str); |
1523 | |
1524 | int start = 0; |
1525 | int pos = value.indexOf(c: sep, from: start); |
1526 | |
1527 | while (pos != -1) { |
1528 | if (pos == 0 || value[pos - 1] != QLatin1Char('\\')) { |
1529 | if (pos > start && pos <= value.length()) { |
1530 | list << value.mid(position: start, n: pos - start); |
1531 | } else { |
1532 | list << QString(); |
1533 | } |
1534 | |
1535 | start = pos + 1; |
1536 | pos = value.indexOf(c: sep, from: start); |
1537 | } else { |
1538 | value.replace(i: pos - 1, len: 2, after: sep); |
1539 | pos = value.indexOf(c: sep, from: pos); |
1540 | } |
1541 | } |
1542 | |
1543 | int l = value.length() - 1; |
1544 | const QString mid = value.mid(position: start, n: l - start + 1); |
1545 | if (!mid.isEmpty()) { |
1546 | list << mid; |
1547 | } else { |
1548 | list << QString(); |
1549 | } |
1550 | |
1551 | return list; |
1552 | } |
1553 | |
1554 | QString VCardTool::normalizeImppServiceType(const QString &serviceType) const |
1555 | { |
1556 | if (serviceType == QLatin1String("jabber" )) { |
1557 | return QStringLiteral("xmpp" ); |
1558 | } |
1559 | if (serviceType == QLatin1String("yahoo" )) { |
1560 | return QStringLiteral("ymsgr" ); |
1561 | } |
1562 | if (serviceType == QLatin1String("gadugadu" )) { |
1563 | return QStringLiteral("gg" ); |
1564 | } |
1565 | return serviceType; |
1566 | } |
1567 | |