vcard.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * OBEX Server
  4. *
  5. * Copyright (C) 2008-2010 Intel Corporation. All rights reserved.
  6. *
  7. */
  8. #ifdef HAVE_CONFIG_H
  9. #include <config.h>
  10. #endif
  11. #include <string.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <stdint.h>
  15. #include <ctype.h>
  16. #include <errno.h>
  17. #include <glib.h>
  18. #include "gdbus/gdbus.h"
  19. #include "vcard.h"
  20. #define ADDR_FIELD_AMOUNT 7
  21. #define LEN_MAX 128
  22. #define TYPE_INTERNATIONAL 145
  23. #define PHONEBOOK_FLAG_CACHED 0x1
  24. #define FILTER_VERSION (1 << 0)
  25. #define FILTER_FN (1 << 1)
  26. #define FILTER_N (1 << 2)
  27. #define FILTER_PHOTO (1 << 3)
  28. #define FILTER_BDAY (1 << 4)
  29. #define FILTER_ADR (1 << 5)
  30. #define FILTER_LABEL (1 << 6)
  31. #define FILTER_TEL (1 << 7)
  32. #define FILTER_EMAIL (1 << 8)
  33. #define FILTER_MAILER (1 << 9)
  34. #define FILTER_TZ (1 << 10)
  35. #define FILTER_GEO (1 << 11)
  36. #define FILTER_TITLE (1 << 12)
  37. #define FILTER_ROLE (1 << 13)
  38. #define FILTER_LOGO (1 << 14)
  39. #define FILTER_AGENT (1 << 15)
  40. #define FILTER_ORG (1 << 16)
  41. #define FILTER_NOTE (1 << 17)
  42. #define FILTER_REV (1 << 18)
  43. #define FILTER_SOUND (1 << 19)
  44. #define FILTER_URL (1 << 20)
  45. #define FILTER_UID (1 << 21)
  46. #define FILTER_KEY (1 << 22)
  47. #define FILTER_NICKNAME (1 << 23)
  48. #define FILTER_CATEGORIES (1 << 24)
  49. #define FILTER_PROID (1 << 25)
  50. #define FILTER_CLASS (1 << 26)
  51. #define FILTER_SORT_STRING (1 << 27)
  52. #define FILTER_X_IRMC_CALL_DATETIME (1 << 28)
  53. #define FORMAT_VCARD21 0x00
  54. #define FORMAT_VCARD30 0x01
  55. #define QP_LINE_LEN 75
  56. #define QP_CHAR_LEN 3
  57. #define QP_CR 0x0D
  58. #define QP_LF 0x0A
  59. #define QP_ESC 0x5C
  60. #define QP_SOFT_LINE_BREAK "="
  61. #define QP_SELECT "\n!\"#$=@[\\]^`{|}~"
  62. #define ASCII_LIMIT 0x7F
  63. /* according to RFC 2425, the output string may need folding */
  64. static void vcard_printf(GString *str, const char *fmt, ...)
  65. {
  66. char buf[1024];
  67. va_list ap;
  68. int len_temp, line_number, i;
  69. unsigned int line_delimit = 75;
  70. va_start(ap, fmt);
  71. vsnprintf(buf, sizeof(buf), fmt, ap);
  72. va_end(ap);
  73. line_number = strlen(buf) / line_delimit + 1;
  74. for (i = 0; i < line_number; i++) {
  75. len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i);
  76. g_string_append_len(str, buf + line_delimit * i, len_temp);
  77. if (i != line_number - 1)
  78. g_string_append(str, "\r\n ");
  79. }
  80. g_string_append(str, "\r\n");
  81. }
  82. /* According to RFC 2426, we need escape following characters:
  83. * '\n', '\r', ';', ',', '\'.
  84. */
  85. static void add_slash(char *dest, const char *src, int len_max, int len)
  86. {
  87. int i, j;
  88. for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) {
  89. /* filling dest buffer - last field need to be reserved
  90. * for '\0'*/
  91. switch (src[i]) {
  92. case '\n':
  93. if (j + 2 >= len_max)
  94. /* not enough space in the buffer to put char
  95. * preceded with escaping sequence (and '\0' in
  96. * the end) */
  97. goto done;
  98. dest[j++] = '\\';
  99. dest[j] = 'n';
  100. break;
  101. case '\r':
  102. if (j + 2 >= len_max)
  103. goto done;
  104. dest[j++] = '\\';
  105. dest[j] = 'r';
  106. break;
  107. case '\\':
  108. case ';':
  109. case ',':
  110. if (j + 2 >= len_max)
  111. goto done;
  112. dest[j++] = '\\';
  113. /* fall through */
  114. default:
  115. dest[j] = src[i];
  116. break;
  117. }
  118. }
  119. done:
  120. dest[j] = 0;
  121. }
  122. static void escape_semicolon(char *dest, const char *src, int len_max, int len)
  123. {
  124. int i, j;
  125. for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) {
  126. if (src[i] == ';') {
  127. if (j + 2 >= len_max)
  128. break;
  129. dest[j++] = '\\';
  130. }
  131. dest[j] = src[i];
  132. }
  133. dest[j] = 0;
  134. }
  135. static void set_escape(uint8_t format, char *dest, const char *src,
  136. int len_max, int len)
  137. {
  138. if (format == FORMAT_VCARD30)
  139. add_slash(dest, src, len_max, len);
  140. else if (format == FORMAT_VCARD21)
  141. escape_semicolon(dest, src, len_max, len);
  142. }
  143. static void get_escaped_fields(uint8_t format, char **fields, ...)
  144. {
  145. va_list ap;
  146. GString *line;
  147. char *field;
  148. char escaped[LEN_MAX];
  149. va_start(ap, fields);
  150. line = g_string_new("");
  151. for (field = va_arg(ap, char *); field; ) {
  152. set_escape(format, escaped, field, LEN_MAX, strlen(field));
  153. g_string_append(line, escaped);
  154. field = va_arg(ap, char *);
  155. if (field)
  156. g_string_append(line, ";");
  157. }
  158. va_end(ap);
  159. *fields = g_string_free(line, FALSE);
  160. }
  161. static gboolean set_qp_encoding(char c)
  162. {
  163. unsigned char q = c;
  164. if (strchr(QP_SELECT, q) != NULL)
  165. return TRUE;
  166. if (q < '!' || q > '~')
  167. return TRUE;
  168. return FALSE;
  169. }
  170. static void append_qp_break_line(GString *vcards, size_t *limit)
  171. {
  172. /* Quoted Printable lines of text must be limited to less than 76
  173. * characters and terminated by Quoted Printable softline break
  174. * sequence of "=" (if some more characters left) */
  175. g_string_append(vcards, QP_SOFT_LINE_BREAK);
  176. g_string_append(vcards, "\r\n ");
  177. *limit = QP_LINE_LEN - 1;
  178. }
  179. static void append_qp_ascii(GString *vcards, size_t *limit, char c)
  180. {
  181. if (*limit == 0)
  182. append_qp_break_line(vcards, limit);
  183. g_string_append_c(vcards, c);
  184. --*limit;
  185. }
  186. static void append_qp_hex(GString *vcards, size_t *limit, char c)
  187. {
  188. if (*limit < QP_CHAR_LEN)
  189. append_qp_break_line(vcards, limit);
  190. g_string_append_printf(vcards, "=%2.2X", (unsigned char) c);
  191. *limit -= QP_CHAR_LEN;
  192. }
  193. static void append_qp_new_line(GString *vcards, size_t *limit)
  194. {
  195. /* Multiple lines of text are separated with a Quoted Printable CRLF
  196. * sequence of "=0D" followed by "=0A" followed by a Quoted Printable
  197. * softline break sequence of "=" */
  198. append_qp_hex(vcards, limit, QP_CR);
  199. append_qp_hex(vcards, limit, QP_LF);
  200. append_qp_break_line(vcards, limit);
  201. }
  202. static gboolean utf8_select(const char *field)
  203. {
  204. const char *pos;
  205. if (g_utf8_validate(field, -1, NULL) == FALSE)
  206. return FALSE;
  207. for (pos = field; *pos != '\0'; pos = g_utf8_next_char(pos)) {
  208. /* Test for non-standard UTF-8 character (out of range
  209. * standard ASCII set), composed of more than single byte
  210. * and represented by 32-bit value greater than 0x7F */
  211. if (g_utf8_get_char(pos) > ASCII_LIMIT)
  212. return TRUE;
  213. }
  214. return FALSE;
  215. }
  216. static void vcard_qp_print_encoded(GString *vcards, const char *desc, ...)
  217. {
  218. const char *field, *charset = "";
  219. const char *encoding = ";ENCODING=QUOTED-PRINTABLE";
  220. size_t limit, param_len;
  221. va_list ap;
  222. va_start(ap, desc);
  223. for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) {
  224. if (utf8_select(field) == TRUE) {
  225. charset = ";CHARSET=UTF-8";
  226. break;
  227. }
  228. }
  229. va_end(ap);
  230. vcard_printf(vcards, "%s%s%s:", desc, encoding, charset);
  231. g_string_truncate(vcards, vcards->len - 2);
  232. param_len = strlen(desc) + strlen(encoding) + strlen(charset) + 1;
  233. limit = QP_LINE_LEN - param_len;
  234. va_start(ap, desc);
  235. for (field = va_arg(ap, char *); field != NULL; ) {
  236. size_t i, size = strlen(field);
  237. for (i = 0; i < size; ++i) {
  238. if (set_qp_encoding(field[i])) {
  239. if (field[i] == '\n') {
  240. append_qp_new_line(vcards, &limit);
  241. continue;
  242. }
  243. append_qp_hex(vcards, &limit, field[i]);
  244. } else {
  245. /* According to vCard 2.1 spec. semicolons in
  246. * property parameter value must be escaped */
  247. if (field[i] == ';')
  248. append_qp_hex(vcards, &limit, QP_ESC);
  249. append_qp_ascii(vcards, &limit, field[i]);
  250. }
  251. }
  252. field = va_arg(ap, char *);
  253. if (field)
  254. append_qp_ascii(vcards, &limit, ';');
  255. }
  256. va_end(ap);
  257. g_string_append(vcards, "\r\n");
  258. }
  259. static gboolean select_qp_encoding(uint8_t format, ...)
  260. {
  261. char *field;
  262. va_list ap;
  263. if (format != FORMAT_VCARD21)
  264. return FALSE;
  265. va_start(ap, format);
  266. for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) {
  267. int i;
  268. unsigned char c;
  269. if (strpbrk(field, QP_SELECT)) {
  270. va_end(ap);
  271. return TRUE;
  272. }
  273. /* Quoted Printable encoding is selected if there is
  274. * a character, which value is out of range standard
  275. * ASCII set, since it may be a part of some
  276. * non-standard character such as specified by UTF-8 */
  277. for (i = 0; (c = field[i]) != '\0'; ++i) {
  278. if (c > ASCII_LIMIT) {
  279. va_end(ap);
  280. return TRUE;
  281. }
  282. }
  283. }
  284. va_end(ap);
  285. return FALSE;
  286. }
  287. static void vcard_printf_begin(GString *vcards, uint8_t format)
  288. {
  289. vcard_printf(vcards, "BEGIN:VCARD");
  290. if (format == FORMAT_VCARD30)
  291. vcard_printf(vcards, "VERSION:3.0");
  292. else if (format == FORMAT_VCARD21)
  293. vcard_printf(vcards, "VERSION:2.1");
  294. }
  295. /* check if there is at least one contact field with personal data present */
  296. static gboolean contact_fields_present(struct phonebook_contact * contact)
  297. {
  298. if (contact->family && strlen(contact->family) > 0)
  299. return TRUE;
  300. if (contact->given && strlen(contact->given) > 0)
  301. return TRUE;
  302. if (contact->additional && strlen(contact->additional) > 0)
  303. return TRUE;
  304. if (contact->prefix && strlen(contact->prefix) > 0)
  305. return TRUE;
  306. if (contact->suffix && strlen(contact->suffix) > 0)
  307. return TRUE;
  308. /* none of the personal data fields are present*/
  309. return FALSE;
  310. }
  311. static void vcard_printf_name(GString *vcards, uint8_t format,
  312. struct phonebook_contact *contact)
  313. {
  314. char *fields;
  315. if (contact_fields_present(contact) == FALSE) {
  316. /* If fields are empty, add only 'N:' as parameter.
  317. * This is crucial for some devices (Nokia BH-903) which
  318. * have problems with history listings and can't determine
  319. * that a parameter is really empty if there are unnecessary
  320. * characters after 'N:' (e.g. 'N:;;;;').
  321. * We need to add only'N:' param - without semicolons.
  322. */
  323. vcard_printf(vcards, "N:");
  324. return;
  325. }
  326. if (select_qp_encoding(format, contact->family, contact->given,
  327. contact->additional, contact->prefix,
  328. contact->suffix, NULL)) {
  329. vcard_qp_print_encoded(vcards, "N", contact->family,
  330. contact->given, contact->additional,
  331. contact->prefix, contact->suffix,
  332. NULL);
  333. return;
  334. }
  335. get_escaped_fields(format, &fields, contact->family,
  336. contact->given, contact->additional,
  337. contact->prefix, contact->suffix,
  338. NULL);
  339. vcard_printf(vcards, "N:%s", fields);
  340. g_free(fields);
  341. }
  342. static void vcard_printf_fullname(GString *vcards, uint8_t format,
  343. const char *text)
  344. {
  345. char field[LEN_MAX];
  346. if (!text || strlen(text) == 0) {
  347. vcard_printf(vcards, "FN:");
  348. return;
  349. }
  350. if (select_qp_encoding(format, text, NULL)) {
  351. vcard_qp_print_encoded(vcards, "FN", text, NULL);
  352. return;
  353. }
  354. set_escape(format, field, text, LEN_MAX, strlen(text));
  355. vcard_printf(vcards, "FN:%s", field);
  356. }
  357. static void vcard_printf_number(GString *vcards, uint8_t format,
  358. const char *number, int type,
  359. enum phonebook_number_type category)
  360. {
  361. const char *intl = "", *category_string = "";
  362. char buf[LEN_MAX], field[LEN_MAX];
  363. /* TEL is a mandatory field, include even if empty */
  364. if (!number || !strlen(number) || !type) {
  365. vcard_printf(vcards, "TEL:");
  366. return;
  367. }
  368. switch (category) {
  369. case TEL_TYPE_HOME:
  370. if (format == FORMAT_VCARD21)
  371. category_string = "HOME;VOICE";
  372. else if (format == FORMAT_VCARD30)
  373. category_string = "TYPE=HOME;TYPE=VOICE";
  374. break;
  375. case TEL_TYPE_MOBILE:
  376. if (format == FORMAT_VCARD21)
  377. category_string = "CELL;VOICE";
  378. else if (format == FORMAT_VCARD30)
  379. category_string = "TYPE=CELL;TYPE=VOICE";
  380. break;
  381. case TEL_TYPE_FAX:
  382. if (format == FORMAT_VCARD21)
  383. category_string = "FAX";
  384. else if (format == FORMAT_VCARD30)
  385. category_string = "TYPE=FAX";
  386. break;
  387. case TEL_TYPE_WORK:
  388. if (format == FORMAT_VCARD21)
  389. category_string = "WORK;VOICE";
  390. else if (format == FORMAT_VCARD30)
  391. category_string = "TYPE=WORK;TYPE=VOICE";
  392. break;
  393. case TEL_TYPE_OTHER:
  394. if (format == FORMAT_VCARD21)
  395. category_string = "OTHER;VOICE";
  396. else if (format == FORMAT_VCARD30)
  397. category_string = "TYPE=OTHER;TYPE=VOICE";
  398. break;
  399. }
  400. if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
  401. intl = "+";
  402. snprintf(field, sizeof(field), "%s%s", intl, number);
  403. if (select_qp_encoding(format, number, NULL)) {
  404. snprintf(buf, sizeof(buf), "TEL;%s", category_string);
  405. vcard_qp_print_encoded(vcards, buf, field, NULL);
  406. return;
  407. }
  408. vcard_printf(vcards, "TEL;%s:%s", category_string, field);
  409. }
  410. static void vcard_printf_tag(GString *vcards, uint8_t format,
  411. const char *tag, const char *category,
  412. const char *fld)
  413. {
  414. int len;
  415. char *separator = "", *type = "";
  416. char buf[LEN_MAX], field[LEN_MAX];
  417. if (tag == NULL || strlen(tag) == 0)
  418. return;
  419. if (fld == NULL || (len = strlen(fld)) == 0) {
  420. vcard_printf(vcards, "%s:", tag);
  421. return;
  422. }
  423. if (category && strlen(category)) {
  424. separator = ";";
  425. if (format == FORMAT_VCARD30)
  426. type = "TYPE=";
  427. } else {
  428. category = "";
  429. }
  430. snprintf(buf, LEN_MAX, "%s%s%s%s", tag, separator, type, category);
  431. if (select_qp_encoding(format, fld, NULL)) {
  432. vcard_qp_print_encoded(vcards, buf, fld, NULL);
  433. return;
  434. }
  435. set_escape(format, field, fld, LEN_MAX, len);
  436. vcard_printf(vcards, "%s:%s", buf, field);
  437. }
  438. static void vcard_printf_email(GString *vcards, uint8_t format,
  439. const char *address,
  440. enum phonebook_field_type category)
  441. {
  442. const char *category_string = "";
  443. char buf[LEN_MAX], field[LEN_MAX];
  444. int len = 0;
  445. if (!address || !(len = strlen(address))) {
  446. vcard_printf(vcards, "EMAIL:");
  447. return;
  448. }
  449. switch (category) {
  450. case FIELD_TYPE_HOME:
  451. if (format == FORMAT_VCARD21)
  452. category_string = "INTERNET;HOME";
  453. else if (format == FORMAT_VCARD30)
  454. category_string = "TYPE=INTERNET;TYPE=HOME";
  455. break;
  456. case FIELD_TYPE_WORK:
  457. if (format == FORMAT_VCARD21)
  458. category_string = "INTERNET;WORK";
  459. else if (format == FORMAT_VCARD30)
  460. category_string = "TYPE=INTERNET;TYPE=WORK";
  461. break;
  462. case FIELD_TYPE_OTHER:
  463. default:
  464. if (format == FORMAT_VCARD21)
  465. category_string = "INTERNET";
  466. else if (format == FORMAT_VCARD30)
  467. category_string = "TYPE=INTERNET;TYPE=OTHER";
  468. }
  469. if (select_qp_encoding(format, address, NULL)) {
  470. snprintf(buf, sizeof(buf), "EMAIL;%s", category_string);
  471. vcard_qp_print_encoded(vcards, buf, address, NULL);
  472. return;
  473. }
  474. set_escape(format, field, address, LEN_MAX, len);
  475. vcard_printf(vcards, "EMAIL;%s:%s", category_string, field);
  476. }
  477. static void vcard_printf_url(GString *vcards, uint8_t format,
  478. const char *url,
  479. enum phonebook_field_type category)
  480. {
  481. const char *category_string = "";
  482. char buf[LEN_MAX], field[LEN_MAX];
  483. if (!url || strlen(url) == 0) {
  484. vcard_printf(vcards, "URL:");
  485. return;
  486. }
  487. switch (category) {
  488. case FIELD_TYPE_HOME:
  489. if (format == FORMAT_VCARD21)
  490. category_string = "INTERNET;HOME";
  491. else if (format == FORMAT_VCARD30)
  492. category_string = "TYPE=INTERNET;TYPE=HOME";
  493. break;
  494. case FIELD_TYPE_WORK:
  495. if (format == FORMAT_VCARD21)
  496. category_string = "INTERNET;WORK";
  497. else if (format == FORMAT_VCARD30)
  498. category_string = "TYPE=INTERNET;TYPE=WORK";
  499. break;
  500. case FIELD_TYPE_OTHER:
  501. default:
  502. if (format == FORMAT_VCARD21)
  503. category_string = "INTERNET";
  504. else if (format == FORMAT_VCARD30)
  505. category_string = "TYPE=INTERNET";
  506. break;
  507. }
  508. if (select_qp_encoding(format, url, NULL)) {
  509. snprintf(buf, sizeof(buf), "URL;%s", category_string);
  510. vcard_qp_print_encoded(vcards, buf, url, NULL);
  511. return;
  512. }
  513. set_escape(format, field, url, LEN_MAX, strlen(url));
  514. vcard_printf(vcards, "URL;%s:%s", category_string, field);
  515. }
  516. static gboolean org_fields_present(struct phonebook_contact *contact)
  517. {
  518. if (contact->company && strlen(contact->company))
  519. return TRUE;
  520. if (contact->department && strlen(contact->department))
  521. return TRUE;
  522. return FALSE;
  523. }
  524. static void vcard_printf_org(GString *vcards, uint8_t format,
  525. struct phonebook_contact *contact)
  526. {
  527. char *fields;
  528. if (org_fields_present(contact) == FALSE)
  529. return;
  530. if (select_qp_encoding(format, contact->company,
  531. contact->department, NULL)) {
  532. vcard_qp_print_encoded(vcards, "ORG", contact->company,
  533. contact->department, NULL);
  534. return;
  535. }
  536. get_escaped_fields(format, &fields, contact->company,
  537. contact->department, NULL);
  538. vcard_printf(vcards, "ORG:%s", fields);
  539. g_free(fields);
  540. }
  541. static void vcard_printf_address(GString *vcards, uint8_t format,
  542. struct phonebook_addr *address)
  543. {
  544. char *fields, field_esc[LEN_MAX];
  545. const char *category_string = "";
  546. char buf[LEN_MAX], *address_fields[ADDR_FIELD_AMOUNT];
  547. int i;
  548. size_t len;
  549. GSList *l;
  550. if (!address) {
  551. vcard_printf(vcards, "ADR:");
  552. return;
  553. }
  554. switch (address->type) {
  555. case FIELD_TYPE_HOME:
  556. if (format == FORMAT_VCARD21)
  557. category_string = "HOME";
  558. else if (format == FORMAT_VCARD30)
  559. category_string = "TYPE=HOME";
  560. break;
  561. case FIELD_TYPE_WORK:
  562. if (format == FORMAT_VCARD21)
  563. category_string = "WORK";
  564. else if (format == FORMAT_VCARD30)
  565. category_string = "TYPE=WORK";
  566. break;
  567. default:
  568. if (format == FORMAT_VCARD21)
  569. category_string = "OTHER";
  570. else if (format == FORMAT_VCARD30)
  571. category_string = "TYPE=OTHER";
  572. break;
  573. }
  574. for (i = 0, l = address->fields; l; l = l->next)
  575. address_fields[i++] = l->data;
  576. if (select_qp_encoding(format, address_fields[0], address_fields[1],
  577. address_fields[2], address_fields[3],
  578. address_fields[4], address_fields[5],
  579. address_fields[6], NULL)) {
  580. snprintf(buf, sizeof(buf), "ADR;%s", category_string);
  581. vcard_qp_print_encoded(vcards, buf,
  582. address_fields[0], address_fields[1],
  583. address_fields[2], address_fields[3],
  584. address_fields[4], address_fields[5],
  585. address_fields[6], NULL);
  586. return;
  587. }
  588. /* allocate enough memory to insert address fields separated by ';'
  589. * and terminated by '\0' */
  590. len = ADDR_FIELD_AMOUNT * LEN_MAX;
  591. fields = g_malloc0(len);
  592. for (l = address->fields; l; l = l->next) {
  593. char *field = l->data;
  594. if (field) {
  595. set_escape(format, field_esc, field, LEN_MAX,
  596. strlen(field));
  597. g_strlcat(fields, field_esc, len);
  598. }
  599. if (l->next)
  600. /* not adding ';' after last addr field */
  601. g_strlcat(fields, ";", len);
  602. }
  603. vcard_printf(vcards,"ADR;%s:%s", category_string, fields);
  604. g_free(fields);
  605. }
  606. static void vcard_printf_datetime(GString *vcards, uint8_t format,
  607. struct phonebook_contact *contact)
  608. {
  609. const char *type;
  610. char buf[LEN_MAX];
  611. switch (contact->calltype) {
  612. case CALL_TYPE_MISSED:
  613. type = "MISSED";
  614. break;
  615. case CALL_TYPE_INCOMING:
  616. type = "RECEIVED";
  617. break;
  618. case CALL_TYPE_OUTGOING:
  619. type = "DIALED";
  620. break;
  621. case CALL_TYPE_NOT_A_CALL:
  622. default:
  623. return;
  624. }
  625. if (select_qp_encoding(format, contact->datetime, NULL)) {
  626. snprintf(buf, sizeof(buf), "X-IRMC-CALL-DATETIME;%s", type);
  627. vcard_qp_print_encoded(vcards, buf, contact->datetime, NULL);
  628. return;
  629. }
  630. vcard_printf(vcards, "X-IRMC-CALL-DATETIME;%s:%s", type,
  631. contact->datetime);
  632. }
  633. static void vcard_printf_end(GString *vcards)
  634. {
  635. vcard_printf(vcards, "END:VCARD");
  636. }
  637. void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact,
  638. uint64_t filter, uint8_t format)
  639. {
  640. if (format == FORMAT_VCARD30 && filter)
  641. filter |= (FILTER_VERSION | FILTER_FN | FILTER_N | FILTER_TEL);
  642. else if (format == FORMAT_VCARD21 && filter)
  643. filter |= (FILTER_VERSION | FILTER_N | FILTER_TEL);
  644. else
  645. filter = (FILTER_VERSION | FILTER_UID | FILTER_N | FILTER_FN |
  646. FILTER_TEL | FILTER_EMAIL | FILTER_ADR |
  647. FILTER_BDAY | FILTER_NICKNAME | FILTER_URL |
  648. FILTER_PHOTO | FILTER_ORG | FILTER_ROLE |
  649. FILTER_TITLE | FILTER_X_IRMC_CALL_DATETIME);
  650. vcard_printf_begin(vcards, format);
  651. if (filter & FILTER_UID && *contact->uid)
  652. vcard_printf_tag(vcards, format, "UID", NULL, contact->uid);
  653. if (filter & FILTER_N)
  654. vcard_printf_name(vcards, format, contact);
  655. if (filter & FILTER_FN && (*contact->fullname ||
  656. format == FORMAT_VCARD30))
  657. vcard_printf_fullname(vcards, format, contact->fullname);
  658. if (filter & FILTER_TEL) {
  659. GSList *l = contact->numbers;
  660. if (g_slist_length(l) == 0)
  661. vcard_printf_number(vcards, format, NULL, 1,
  662. TEL_TYPE_OTHER);
  663. for (; l; l = l->next) {
  664. struct phonebook_field *number = l->data;
  665. vcard_printf_number(vcards, format, number->text, 1,
  666. number->type);
  667. }
  668. }
  669. if (filter & FILTER_EMAIL) {
  670. GSList *l = contact->emails;
  671. for (; l; l = l->next) {
  672. struct phonebook_field *email = l->data;
  673. vcard_printf_email(vcards, format, email->text,
  674. email->type);
  675. }
  676. }
  677. if (filter & FILTER_ADR) {
  678. GSList *l = contact->addresses;
  679. for (; l; l = l->next) {
  680. struct phonebook_addr *addr = l->data;
  681. vcard_printf_address(vcards, format, addr);
  682. }
  683. }
  684. if (filter & FILTER_BDAY && *contact->birthday)
  685. vcard_printf_tag(vcards, format, "BDAY", NULL,
  686. contact->birthday);
  687. if (filter & FILTER_NICKNAME && *contact->nickname)
  688. vcard_printf_tag(vcards, format, "NICKNAME", NULL,
  689. contact->nickname);
  690. if (filter & FILTER_URL) {
  691. GSList *l = contact->urls;
  692. for (; l; l = l->next) {
  693. struct phonebook_field *url = l->data;
  694. vcard_printf_url(vcards, format, url->text, url->type);
  695. }
  696. }
  697. if (filter & FILTER_PHOTO && *contact->photo)
  698. vcard_printf_tag(vcards, format, "PHOTO", NULL,
  699. contact->photo);
  700. if (filter & FILTER_ORG)
  701. vcard_printf_org(vcards, format, contact);
  702. if (filter & FILTER_ROLE && *contact->role)
  703. vcard_printf_tag(vcards, format, "ROLE", NULL, contact->role);
  704. if (filter & FILTER_TITLE && *contact->title)
  705. vcard_printf_tag(vcards, format, "TITLE", NULL, contact->title);
  706. if (filter & FILTER_X_IRMC_CALL_DATETIME)
  707. vcard_printf_datetime(vcards, format, contact);
  708. vcard_printf_end(vcards);
  709. }
  710. static void field_free(gpointer data)
  711. {
  712. struct phonebook_field *field = data;
  713. g_free(field->text);
  714. g_free(field);
  715. }
  716. void phonebook_addr_free(gpointer addr)
  717. {
  718. struct phonebook_addr *address = addr;
  719. g_slist_free_full(address->fields, g_free);
  720. g_free(address);
  721. }
  722. void phonebook_contact_free(struct phonebook_contact *contact)
  723. {
  724. if (contact == NULL)
  725. return;
  726. g_slist_free_full(contact->numbers, field_free);
  727. g_slist_free_full(contact->emails, field_free);
  728. g_slist_free_full(contact->addresses, phonebook_addr_free);
  729. g_slist_free_full(contact->urls, field_free);
  730. g_free(contact->uid);
  731. g_free(contact->fullname);
  732. g_free(contact->given);
  733. g_free(contact->family);
  734. g_free(contact->additional);
  735. g_free(contact->prefix);
  736. g_free(contact->suffix);
  737. g_free(contact->birthday);
  738. g_free(contact->nickname);
  739. g_free(contact->photo);
  740. g_free(contact->company);
  741. g_free(contact->department);
  742. g_free(contact->role);
  743. g_free(contact->title);
  744. g_free(contact->datetime);
  745. g_free(contact);
  746. }