Blame obexd/plugins/vcard.c

Packit 34410b
/*
Packit 34410b
 * OBEX Server
Packit 34410b
 *
Packit 34410b
 * Copyright (C) 2008-2010 Intel Corporation.  All rights reserved.
Packit 34410b
 *
Packit 34410b
 * This program is free software; you can redistribute it and/or modify
Packit 34410b
 * it under the terms of the GNU General Public License as published by
Packit 34410b
 * the Free Software Foundation; either version 2 of the License, or
Packit 34410b
 * (at your option) any later version.
Packit 34410b
 *
Packit 34410b
 * This program is distributed in the hope that it will be useful,
Packit 34410b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 34410b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 34410b
 * GNU General Public License for more details.
Packit 34410b
 *
Packit 34410b
 * You should have received a copy of the GNU General Public License
Packit 34410b
 * along with this program; if not, write to the Free Software
Packit 34410b
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Packit 34410b
 *
Packit 34410b
 */
Packit 34410b
Packit 34410b
#ifdef HAVE_CONFIG_H
Packit 34410b
#include <config.h>
Packit 34410b
#endif
Packit 34410b
Packit 34410b
#include <string.h>
Packit 34410b
#include <stdio.h>
Packit 34410b
#include <stdlib.h>
Packit 34410b
#include <stdint.h>
Packit 34410b
#include <ctype.h>
Packit 34410b
#include <errno.h>
Packit 34410b
Packit 34410b
#include <glib.h>
Packit 34410b
Packit 34410b
#include "gdbus/gdbus.h"
Packit 34410b
Packit 34410b
#include "vcard.h"
Packit 34410b
Packit 34410b
#define ADDR_FIELD_AMOUNT 7
Packit 34410b
#define LEN_MAX 128
Packit 34410b
#define TYPE_INTERNATIONAL 145
Packit 34410b
Packit 34410b
#define PHONEBOOK_FLAG_CACHED 0x1
Packit 34410b
Packit 34410b
#define FILTER_VERSION (1 << 0)
Packit 34410b
#define FILTER_FN (1 << 1)
Packit 34410b
#define FILTER_N (1 << 2)
Packit 34410b
#define FILTER_PHOTO (1 << 3)
Packit 34410b
#define FILTER_BDAY (1 << 4)
Packit 34410b
#define FILTER_ADR (1 << 5)
Packit 34410b
#define FILTER_LABEL (1 << 6)
Packit 34410b
#define FILTER_TEL (1 << 7)
Packit 34410b
#define FILTER_EMAIL (1 << 8)
Packit 34410b
#define FILTER_MAILER (1 << 9)
Packit 34410b
#define FILTER_TZ (1 << 10)
Packit 34410b
#define FILTER_GEO (1 << 11)
Packit 34410b
#define FILTER_TITLE (1 << 12)
Packit 34410b
#define FILTER_ROLE (1 << 13)
Packit 34410b
#define FILTER_LOGO (1 << 14)
Packit 34410b
#define FILTER_AGENT (1 << 15)
Packit 34410b
#define FILTER_ORG (1 << 16)
Packit 34410b
#define FILTER_NOTE (1 << 17)
Packit 34410b
#define FILTER_REV (1 << 18)
Packit 34410b
#define FILTER_SOUND (1 << 19)
Packit 34410b
#define FILTER_URL (1 << 20)
Packit 34410b
#define FILTER_UID (1 << 21)
Packit 34410b
#define FILTER_KEY (1 << 22)
Packit 34410b
#define FILTER_NICKNAME (1 << 23)
Packit 34410b
#define FILTER_CATEGORIES (1 << 24)
Packit 34410b
#define FILTER_PROID (1 << 25)
Packit 34410b
#define FILTER_CLASS (1 << 26)
Packit 34410b
#define FILTER_SORT_STRING (1 << 27)
Packit 34410b
#define FILTER_X_IRMC_CALL_DATETIME (1 << 28)
Packit 34410b
Packit 34410b
#define FORMAT_VCARD21 0x00
Packit 34410b
#define FORMAT_VCARD30 0x01
Packit 34410b
Packit 34410b
#define QP_LINE_LEN 75
Packit 34410b
#define QP_CHAR_LEN 3
Packit 34410b
#define QP_CR 0x0D
Packit 34410b
#define QP_LF 0x0A
Packit 34410b
#define QP_ESC 0x5C
Packit 34410b
#define QP_SOFT_LINE_BREAK "="
Packit 34410b
#define QP_SELECT "\n!\"#$=@[\\]^`{|}~"
Packit 34410b
#define ASCII_LIMIT 0x7F
Packit 34410b
Packit 34410b
/* according to RFC 2425, the output string may need folding */
Packit 34410b
static void vcard_printf(GString *str, const char *fmt, ...)
Packit 34410b
{
Packit 34410b
	char buf[1024];
Packit 34410b
	va_list ap;
Packit 34410b
	int len_temp, line_number, i;
Packit 34410b
	unsigned int line_delimit = 75;
Packit 34410b
Packit 34410b
	va_start(ap, fmt);
Packit 34410b
	vsnprintf(buf, sizeof(buf), fmt, ap);
Packit 34410b
	va_end(ap);
Packit 34410b
Packit 34410b
	line_number = strlen(buf) / line_delimit + 1;
Packit 34410b
Packit 34410b
	for (i = 0; i < line_number; i++) {
Packit 34410b
		len_temp = MIN(line_delimit, strlen(buf) - line_delimit * i);
Packit 34410b
		g_string_append_len(str,  buf + line_delimit * i, len_temp);
Packit 34410b
		if (i != line_number - 1)
Packit 34410b
			g_string_append(str, "\r\n ");
Packit 34410b
	}
Packit 34410b
Packit 34410b
	g_string_append(str, "\r\n");
Packit 34410b
}
Packit 34410b
Packit 34410b
/* According to RFC 2426, we need escape following characters:
Packit 34410b
 *  '\n', '\r', ';', ',', '\'.
Packit 34410b
 */
Packit 34410b
static void add_slash(char *dest, const char *src, int len_max, int len)
Packit 34410b
{
Packit 34410b
	int i, j;
Packit 34410b
Packit 34410b
	for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) {
Packit 34410b
		/* filling dest buffer - last field need to be reserved
Packit 34410b
		 * for '\0'*/
Packit 34410b
		switch (src[i]) {
Packit 34410b
		case '\n':
Packit 34410b
			if (j + 2 >= len_max)
Packit 34410b
				/* not enough space in the buffer to put char
Packit 34410b
				 * preceded with escaping sequence (and '\0' in
Packit 34410b
				 * the end) */
Packit 34410b
				goto done;
Packit 34410b
Packit 34410b
			dest[j++] = '\\';
Packit 34410b
			dest[j] = 'n';
Packit 34410b
			break;
Packit 34410b
		case '\r':
Packit 34410b
			if (j + 2 >= len_max)
Packit 34410b
				goto done;
Packit 34410b
Packit 34410b
			dest[j++] = '\\';
Packit 34410b
			dest[j] = 'r';
Packit 34410b
			break;
Packit 34410b
		case '\\':
Packit 34410b
		case ';':
Packit 34410b
		case ',':
Packit 34410b
			if (j + 2 >= len_max)
Packit 34410b
				goto done;
Packit 34410b
Packit 34410b
			dest[j++] = '\\';
Packit 34410b
			/* fall through */
Packit 34410b
		default:
Packit 34410b
			dest[j] = src[i];
Packit 34410b
			break;
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
done:
Packit 34410b
	dest[j] = 0;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void escape_semicolon(char *dest, const char *src, int len_max, int len)
Packit 34410b
{
Packit 34410b
	int i, j;
Packit 34410b
Packit 34410b
	for (i = 0, j = 0; i < len && j + 1 < len_max; i++, j++) {
Packit 34410b
		if (src[i] == ';') {
Packit 34410b
			if (j + 2 >= len_max)
Packit 34410b
				break;
Packit 34410b
Packit 34410b
			dest[j++] = '\\';
Packit 34410b
		}
Packit 34410b
Packit 34410b
		dest[j] = src[i];
Packit 34410b
	}
Packit 34410b
Packit 34410b
	dest[j] = 0;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void set_escape(uint8_t format, char *dest, const char *src,
Packit 34410b
							int len_max, int len)
Packit 34410b
{
Packit 34410b
	if (format == FORMAT_VCARD30)
Packit 34410b
		add_slash(dest, src, len_max, len);
Packit 34410b
	else if (format == FORMAT_VCARD21)
Packit 34410b
		escape_semicolon(dest, src, len_max, len);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void get_escaped_fields(uint8_t format, char **fields, ...)
Packit 34410b
{
Packit 34410b
	va_list ap;
Packit 34410b
	GString *line;
Packit 34410b
	char *field;
Packit 34410b
	char escaped[LEN_MAX];
Packit 34410b
Packit 34410b
	va_start(ap, fields);
Packit 34410b
	line = g_string_new("");
Packit 34410b
Packit 34410b
	for (field = va_arg(ap, char *); field; ) {
Packit 34410b
		set_escape(format, escaped, field, LEN_MAX, strlen(field));
Packit 34410b
		g_string_append(line, escaped);
Packit 34410b
Packit 34410b
		field = va_arg(ap, char *);
Packit 34410b
Packit 34410b
		if (field)
Packit 34410b
			g_string_append(line, ";");
Packit 34410b
	}
Packit 34410b
Packit 34410b
	va_end(ap);
Packit 34410b
Packit 34410b
	*fields = g_string_free(line, FALSE);
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean set_qp_encoding(char c)
Packit 34410b
{
Packit 34410b
	unsigned char q = c;
Packit 34410b
Packit 34410b
	if (strchr(QP_SELECT, q) != NULL)
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	if (q < '!' || q > '~')
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void append_qp_break_line(GString *vcards, size_t *limit)
Packit 34410b
{
Packit 34410b
	/* Quoted Printable lines of text must be limited to less than 76
Packit 34410b
	 * characters and terminated by Quoted Printable softline break
Packit 34410b
	 * sequence of "=" (if some more characters left) */
Packit 34410b
	g_string_append(vcards, QP_SOFT_LINE_BREAK);
Packit 34410b
	g_string_append(vcards, "\r\n ");
Packit 34410b
	*limit = QP_LINE_LEN - 1;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void append_qp_ascii(GString *vcards, size_t *limit, char c)
Packit 34410b
{
Packit 34410b
	if (*limit == 0)
Packit 34410b
		append_qp_break_line(vcards, limit);
Packit 34410b
Packit 34410b
	g_string_append_c(vcards, c);
Packit 34410b
	--*limit;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void append_qp_hex(GString *vcards, size_t *limit, char c)
Packit 34410b
{
Packit 34410b
	if (*limit < QP_CHAR_LEN)
Packit 34410b
		append_qp_break_line(vcards, limit);
Packit 34410b
Packit 34410b
	g_string_append_printf(vcards, "=%2.2X", (unsigned char) c);
Packit 34410b
	*limit -= QP_CHAR_LEN;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void append_qp_new_line(GString *vcards, size_t *limit)
Packit 34410b
{
Packit 34410b
	/* Multiple lines of text are separated with a Quoted Printable CRLF
Packit 34410b
	 * sequence of "=0D" followed by "=0A" followed by a Quoted Printable
Packit 34410b
	 * softline break sequence of "=" */
Packit 34410b
	append_qp_hex(vcards, limit, QP_CR);
Packit 34410b
	append_qp_hex(vcards, limit, QP_LF);
Packit 34410b
	append_qp_break_line(vcards, limit);
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean utf8_select(const char *field)
Packit 34410b
{
Packit 34410b
	const char *pos;
Packit 34410b
Packit 34410b
	if (g_utf8_validate(field, -1, NULL) == FALSE)
Packit 34410b
		return FALSE;
Packit 34410b
Packit 34410b
	for (pos = field; *pos != '\0'; pos = g_utf8_next_char(pos)) {
Packit 34410b
		/* Test for non-standard UTF-8 character (out of range
Packit 34410b
		 * standard ASCII set), composed of more than single byte
Packit 34410b
		 * and represented by 32-bit value greater than 0x7F */
Packit 34410b
		if (g_utf8_get_char(pos) > ASCII_LIMIT)
Packit 34410b
			return TRUE;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_qp_print_encoded(GString *vcards, const char *desc, ...)
Packit 34410b
{
Packit 34410b
	const char *field, *charset = "";
Packit 34410b
	const char *encoding = ";ENCODING=QUOTED-PRINTABLE";
Packit 34410b
	size_t limit, param_len;
Packit 34410b
	va_list ap;
Packit 34410b
Packit 34410b
	va_start(ap, desc);
Packit 34410b
Packit 34410b
	for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) {
Packit 34410b
		if (utf8_select(field) == TRUE) {
Packit 34410b
			charset = ";CHARSET=UTF-8";
Packit 34410b
			break;
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	va_end(ap);
Packit 34410b
Packit 34410b
	vcard_printf(vcards, "%s%s%s:", desc, encoding, charset);
Packit 34410b
	g_string_truncate(vcards, vcards->len - 2);
Packit 34410b
Packit 34410b
	param_len = strlen(desc) + strlen(encoding) + strlen(charset) + 1;
Packit 34410b
	limit = QP_LINE_LEN - param_len;
Packit 34410b
Packit 34410b
	va_start(ap, desc);
Packit 34410b
Packit 34410b
	for (field = va_arg(ap, char *); field != NULL; ) {
Packit 34410b
		size_t i, size = strlen(field);
Packit 34410b
Packit 34410b
		for (i = 0; i < size; ++i) {
Packit 34410b
			if (set_qp_encoding(field[i])) {
Packit 34410b
				if (field[i] == '\n') {
Packit 34410b
					append_qp_new_line(vcards, &limit);
Packit 34410b
					continue;
Packit 34410b
				}
Packit 34410b
Packit 34410b
				append_qp_hex(vcards, &limit, field[i]);
Packit 34410b
			} else {
Packit 34410b
				/* According to vCard 2.1 spec. semicolons in
Packit 34410b
				 * property parameter value must be escaped */
Packit 34410b
				if (field[i] == ';')
Packit 34410b
					append_qp_hex(vcards, &limit, QP_ESC);
Packit 34410b
Packit 34410b
				append_qp_ascii(vcards, &limit, field[i]);
Packit 34410b
			}
Packit 34410b
		}
Packit 34410b
Packit 34410b
		field = va_arg(ap, char *);
Packit 34410b
		if (field)
Packit 34410b
			append_qp_ascii(vcards, &limit, ';');
Packit 34410b
	}
Packit 34410b
Packit 34410b
	va_end(ap);
Packit 34410b
Packit 34410b
	g_string_append(vcards, "\r\n");
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean select_qp_encoding(uint8_t format, ...)
Packit 34410b
{
Packit 34410b
	char *field;
Packit 34410b
	va_list ap;
Packit 34410b
Packit 34410b
	if (format != FORMAT_VCARD21)
Packit 34410b
		return FALSE;
Packit 34410b
Packit 34410b
	va_start(ap, format);
Packit 34410b
Packit 34410b
	for (field = va_arg(ap, char *); field; field = va_arg(ap, char *)) {
Packit 34410b
		int i;
Packit 34410b
		unsigned char c;
Packit 34410b
Packit 34410b
		if (strpbrk(field, QP_SELECT)) {
Packit 34410b
			va_end(ap);
Packit 34410b
			return TRUE;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		/* Quoted Printable encoding is selected if there is
Packit 34410b
		 * a character, which value is out of range standard
Packit 34410b
		 * ASCII set, since it may be a part of some
Packit 34410b
		 * non-standard character such as specified by UTF-8 */
Packit 34410b
		for (i = 0; (c = field[i]) != '\0'; ++i) {
Packit 34410b
			if (c > ASCII_LIMIT) {
Packit 34410b
				va_end(ap);
Packit 34410b
				return TRUE;
Packit 34410b
			}
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	va_end(ap);
Packit 34410b
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_begin(GString *vcards, uint8_t format)
Packit 34410b
{
Packit 34410b
	vcard_printf(vcards, "BEGIN:VCARD");
Packit 34410b
Packit 34410b
	if (format == FORMAT_VCARD30)
Packit 34410b
		vcard_printf(vcards, "VERSION:3.0");
Packit 34410b
	else if (format == FORMAT_VCARD21)
Packit 34410b
		vcard_printf(vcards, "VERSION:2.1");
Packit 34410b
}
Packit 34410b
Packit 34410b
/* check if there is at least one contact field with personal data present */
Packit 34410b
static gboolean contact_fields_present(struct phonebook_contact * contact)
Packit 34410b
{
Packit 34410b
	if (contact->family && strlen(contact->family) > 0)
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	if (contact->given && strlen(contact->given) > 0)
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	if (contact->additional && strlen(contact->additional) > 0)
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	if (contact->prefix && strlen(contact->prefix) > 0)
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	if (contact->suffix && strlen(contact->suffix) > 0)
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	/* none of the personal data fields are present*/
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_name(GString *vcards, uint8_t format,
Packit 34410b
					struct phonebook_contact *contact)
Packit 34410b
{
Packit 34410b
	char *fields;
Packit 34410b
Packit 34410b
	if (contact_fields_present(contact) == FALSE) {
Packit 34410b
		/* If fields are empty, add only 'N:' as parameter.
Packit 34410b
		 * This is crucial for some devices (Nokia BH-903) which
Packit 34410b
		 * have problems with history listings and can't determine
Packit 34410b
		 * that a parameter is really empty if there are unnecessary
Packit 34410b
		 * characters after 'N:' (e.g. 'N:;;;;').
Packit 34410b
		 * We need to add only'N:' param - without semicolons.
Packit 34410b
		 */
Packit 34410b
		vcard_printf(vcards, "N:");
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, contact->family, contact->given,
Packit 34410b
					contact->additional, contact->prefix,
Packit 34410b
					contact->suffix, NULL)) {
Packit 34410b
		vcard_qp_print_encoded(vcards, "N", contact->family,
Packit 34410b
					contact->given, contact->additional,
Packit 34410b
					contact->prefix, contact->suffix,
Packit 34410b
					NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	get_escaped_fields(format, &fields, contact->family,
Packit 34410b
				contact->given, contact->additional,
Packit 34410b
				contact->prefix, contact->suffix,
Packit 34410b
				NULL);
Packit 34410b
Packit 34410b
	vcard_printf(vcards, "N:%s", fields);
Packit 34410b
Packit 34410b
	g_free(fields);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_fullname(GString *vcards, uint8_t format,
Packit 34410b
							const char *text)
Packit 34410b
{
Packit 34410b
	char field[LEN_MAX];
Packit 34410b
Packit 34410b
	if (!text || strlen(text) == 0) {
Packit 34410b
		vcard_printf(vcards, "FN:");
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, text, NULL)) {
Packit 34410b
		vcard_qp_print_encoded(vcards, "FN", text, NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	set_escape(format, field, text, LEN_MAX, strlen(text));
Packit 34410b
	vcard_printf(vcards, "FN:%s", field);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_number(GString *vcards, uint8_t format,
Packit 34410b
					const char *number, int type,
Packit 34410b
					enum phonebook_number_type category)
Packit 34410b
{
Packit 34410b
	const char *intl = "", *category_string = "";
Packit 34410b
	char buf[LEN_MAX], field[LEN_MAX];
Packit 34410b
Packit 34410b
	/* TEL is a mandatory field, include even if empty */
Packit 34410b
	if (!number || !strlen(number) || !type) {
Packit 34410b
		vcard_printf(vcards, "TEL:");
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	switch (category) {
Packit 34410b
	case TEL_TYPE_HOME:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "HOME;VOICE";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=HOME;TYPE=VOICE";
Packit 34410b
		break;
Packit 34410b
	case TEL_TYPE_MOBILE:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "CELL;VOICE";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=CELL;TYPE=VOICE";
Packit 34410b
		break;
Packit 34410b
	case TEL_TYPE_FAX:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "FAX";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=FAX";
Packit 34410b
		break;
Packit 34410b
	case TEL_TYPE_WORK:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "WORK;VOICE";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=WORK;TYPE=VOICE";
Packit 34410b
		break;
Packit 34410b
	case TEL_TYPE_OTHER:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "OTHER;VOICE";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=OTHER;TYPE=VOICE";
Packit 34410b
		break;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if ((type == TYPE_INTERNATIONAL) && (number[0] != '+'))
Packit 34410b
		intl = "+";
Packit 34410b
Packit 34410b
	snprintf(field, sizeof(field), "%s%s", intl, number);
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, number, NULL)) {
Packit 34410b
		snprintf(buf, sizeof(buf), "TEL;%s", category_string);
Packit 34410b
		vcard_qp_print_encoded(vcards, buf, field, NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	vcard_printf(vcards, "TEL;%s:%s", category_string, field);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_tag(GString *vcards, uint8_t format,
Packit 34410b
					const char *tag, const char *category,
Packit 34410b
					const char *fld)
Packit 34410b
{
Packit 34410b
	int len;
Packit 34410b
	char *separator = "", *type = "";
Packit 34410b
	char buf[LEN_MAX], field[LEN_MAX];
Packit 34410b
Packit 34410b
	if (tag == NULL || strlen(tag) == 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (fld == NULL || (len = strlen(fld)) == 0) {
Packit 34410b
		vcard_printf(vcards, "%s:", tag);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (category && strlen(category)) {
Packit 34410b
		separator = ";";
Packit 34410b
		if (format == FORMAT_VCARD30)
Packit 34410b
			type = "TYPE=";
Packit 34410b
	} else {
Packit 34410b
		category = "";
Packit 34410b
	}
Packit 34410b
Packit 34410b
	snprintf(buf, LEN_MAX, "%s%s%s%s", tag, separator, type, category);
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, fld, NULL)) {
Packit 34410b
		vcard_qp_print_encoded(vcards, buf, fld, NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	set_escape(format, field, fld, LEN_MAX, len);
Packit 34410b
	vcard_printf(vcards, "%s:%s", buf, field);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_email(GString *vcards, uint8_t format,
Packit 34410b
					const char *address,
Packit 34410b
					enum phonebook_field_type category)
Packit 34410b
{
Packit 34410b
	const char *category_string = "";
Packit 34410b
	char buf[LEN_MAX], field[LEN_MAX];
Packit 34410b
	int len = 0;
Packit 34410b
Packit 34410b
	if (!address || !(len = strlen(address))) {
Packit 34410b
		vcard_printf(vcards, "EMAIL:");
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
	switch (category) {
Packit 34410b
	case FIELD_TYPE_HOME:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "INTERNET;HOME";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=INTERNET;TYPE=HOME";
Packit 34410b
		break;
Packit 34410b
	case FIELD_TYPE_WORK:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "INTERNET;WORK";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=INTERNET;TYPE=WORK";
Packit 34410b
		break;
Packit 34410b
	case FIELD_TYPE_OTHER:
Packit 34410b
	default:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "INTERNET";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=INTERNET;TYPE=OTHER";
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, address, NULL)) {
Packit 34410b
		snprintf(buf, sizeof(buf), "EMAIL;%s", category_string);
Packit 34410b
		vcard_qp_print_encoded(vcards, buf, address, NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	set_escape(format, field, address, LEN_MAX, len);
Packit 34410b
	vcard_printf(vcards, "EMAIL;%s:%s", category_string, field);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_url(GString *vcards, uint8_t format,
Packit 34410b
					const char *url,
Packit 34410b
					enum phonebook_field_type category)
Packit 34410b
{
Packit 34410b
	const char *category_string = "";
Packit 34410b
	char buf[LEN_MAX], field[LEN_MAX];
Packit 34410b
Packit 34410b
	if (!url || strlen(url) == 0) {
Packit 34410b
		vcard_printf(vcards, "URL:");
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	switch (category) {
Packit 34410b
	case FIELD_TYPE_HOME:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "INTERNET;HOME";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=INTERNET;TYPE=HOME";
Packit 34410b
		break;
Packit 34410b
	case FIELD_TYPE_WORK:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "INTERNET;WORK";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=INTERNET;TYPE=WORK";
Packit 34410b
		break;
Packit 34410b
	case FIELD_TYPE_OTHER:
Packit 34410b
	default:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "INTERNET";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=INTERNET";
Packit 34410b
		break;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, url, NULL)) {
Packit 34410b
		snprintf(buf, sizeof(buf), "URL;%s", category_string);
Packit 34410b
		vcard_qp_print_encoded(vcards, buf, url, NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	set_escape(format, field, url, LEN_MAX, strlen(url));
Packit 34410b
	vcard_printf(vcards, "URL;%s:%s", category_string, field);
Packit 34410b
}
Packit 34410b
Packit 34410b
static gboolean org_fields_present(struct phonebook_contact *contact)
Packit 34410b
{
Packit 34410b
	if (contact->company && strlen(contact->company))
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	if (contact->department && strlen(contact->department))
Packit 34410b
		return TRUE;
Packit 34410b
Packit 34410b
	return FALSE;
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_org(GString *vcards, uint8_t format,
Packit 34410b
					struct phonebook_contact *contact)
Packit 34410b
{
Packit 34410b
	char *fields;
Packit 34410b
Packit 34410b
	if (org_fields_present(contact) == FALSE)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, contact->company,
Packit 34410b
						contact->department, NULL)) {
Packit 34410b
		vcard_qp_print_encoded(vcards, "ORG", contact->company,
Packit 34410b
						contact->department, NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	get_escaped_fields(format, &fields, contact->company,
Packit 34410b
					contact->department, NULL);
Packit 34410b
Packit 34410b
	vcard_printf(vcards, "ORG:%s", fields);
Packit 34410b
Packit 34410b
	g_free(fields);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_address(GString *vcards, uint8_t format,
Packit 34410b
					struct phonebook_addr *address)
Packit 34410b
{
Packit 34410b
	char *fields, field_esc[LEN_MAX];
Packit 34410b
	const char *category_string = "";
Packit 34410b
	char buf[LEN_MAX], *address_fields[ADDR_FIELD_AMOUNT];
Packit 34410b
	int i;
Packit 34410b
	size_t len;
Packit 34410b
	GSList *l;
Packit 34410b
Packit 34410b
	if (!address) {
Packit 34410b
		vcard_printf(vcards, "ADR:");
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	switch (address->type) {
Packit 34410b
	case FIELD_TYPE_HOME:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "HOME";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=HOME";
Packit 34410b
		break;
Packit 34410b
	case FIELD_TYPE_WORK:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "WORK";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=WORK";
Packit 34410b
		break;
Packit 34410b
	default:
Packit 34410b
		if (format == FORMAT_VCARD21)
Packit 34410b
			category_string = "OTHER";
Packit 34410b
		else if (format == FORMAT_VCARD30)
Packit 34410b
			category_string = "TYPE=OTHER";
Packit 34410b
		break;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	for (i = 0, l = address->fields; l; l = l->next)
Packit 34410b
		address_fields[i++] = l->data;
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, address_fields[0], address_fields[1],
Packit 34410b
					address_fields[2], address_fields[3],
Packit 34410b
					address_fields[4], address_fields[5],
Packit 34410b
					address_fields[6], NULL)) {
Packit 34410b
		snprintf(buf, sizeof(buf), "ADR;%s", category_string);
Packit 34410b
		vcard_qp_print_encoded(vcards, buf,
Packit 34410b
					address_fields[0], address_fields[1],
Packit 34410b
					address_fields[2], address_fields[3],
Packit 34410b
					address_fields[4], address_fields[5],
Packit 34410b
					address_fields[6], NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* allocate enough memory to insert address fields separated by ';'
Packit 34410b
	 * and terminated by '\0' */
Packit 34410b
	len = ADDR_FIELD_AMOUNT * LEN_MAX;
Packit 34410b
	fields = g_malloc0(len);
Packit 34410b
Packit 34410b
	for (l = address->fields; l; l = l->next) {
Packit 34410b
		char *field = l->data;
Packit 34410b
Packit 34410b
		if (field) {
Packit 34410b
			set_escape(format, field_esc, field, LEN_MAX,
Packit 34410b
								strlen(field));
Packit 34410b
			g_strlcat(fields, field_esc, len);
Packit 34410b
		}
Packit 34410b
Packit 34410b
		if (l->next)
Packit 34410b
			/* not adding ';' after last addr field */
Packit 34410b
			g_strlcat(fields, ";", len);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	vcard_printf(vcards,"ADR;%s:%s", category_string, fields);
Packit 34410b
Packit 34410b
	g_free(fields);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_datetime(GString *vcards, uint8_t format,
Packit 34410b
					struct phonebook_contact *contact)
Packit 34410b
{
Packit 34410b
	const char *type;
Packit 34410b
	char buf[LEN_MAX];
Packit 34410b
Packit 34410b
	switch (contact->calltype) {
Packit 34410b
	case CALL_TYPE_MISSED:
Packit 34410b
		type = "MISSED";
Packit 34410b
		break;
Packit 34410b
Packit 34410b
	case CALL_TYPE_INCOMING:
Packit 34410b
		type = "RECEIVED";
Packit 34410b
		break;
Packit 34410b
Packit 34410b
	case CALL_TYPE_OUTGOING:
Packit 34410b
		type = "DIALED";
Packit 34410b
		break;
Packit 34410b
Packit 34410b
	case CALL_TYPE_NOT_A_CALL:
Packit 34410b
	default:
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (select_qp_encoding(format, contact->datetime, NULL)) {
Packit 34410b
		snprintf(buf, sizeof(buf), "X-IRMC-CALL-DATETIME;%s", type);
Packit 34410b
		vcard_qp_print_encoded(vcards, buf, contact->datetime, NULL);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	vcard_printf(vcards, "X-IRMC-CALL-DATETIME;%s:%s", type,
Packit 34410b
							contact->datetime);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void vcard_printf_end(GString *vcards)
Packit 34410b
{
Packit 34410b
	vcard_printf(vcards, "END:VCARD");
Packit 34410b
}
Packit 34410b
Packit 34410b
void phonebook_add_contact(GString *vcards, struct phonebook_contact *contact,
Packit 34410b
					uint64_t filter, uint8_t format)
Packit 34410b
{
Packit 34410b
	if (format == FORMAT_VCARD30 && filter)
Packit 34410b
		filter |= (FILTER_VERSION | FILTER_FN | FILTER_N | FILTER_TEL);
Packit 34410b
	else if (format == FORMAT_VCARD21 && filter)
Packit 34410b
		filter |= (FILTER_VERSION | FILTER_N | FILTER_TEL);
Packit 34410b
	else
Packit 34410b
		filter = (FILTER_VERSION | FILTER_UID | FILTER_N | FILTER_FN |
Packit 34410b
				FILTER_TEL | FILTER_EMAIL | FILTER_ADR |
Packit 34410b
				FILTER_BDAY | FILTER_NICKNAME | FILTER_URL |
Packit 34410b
				FILTER_PHOTO | FILTER_ORG | FILTER_ROLE |
Packit 34410b
				FILTER_TITLE | FILTER_X_IRMC_CALL_DATETIME);
Packit 34410b
Packit 34410b
	vcard_printf_begin(vcards, format);
Packit 34410b
Packit 34410b
	if (filter & FILTER_UID && *contact->uid)
Packit 34410b
		vcard_printf_tag(vcards, format, "UID", NULL, contact->uid);
Packit 34410b
Packit 34410b
	if (filter & FILTER_N)
Packit 34410b
		vcard_printf_name(vcards, format, contact);
Packit 34410b
Packit 34410b
	if (filter & FILTER_FN && (*contact->fullname ||
Packit 34410b
					format == FORMAT_VCARD30))
Packit 34410b
		vcard_printf_fullname(vcards, format, contact->fullname);
Packit 34410b
Packit 34410b
	if (filter & FILTER_TEL) {
Packit 34410b
		GSList *l = contact->numbers;
Packit 34410b
Packit 34410b
		if (g_slist_length(l) == 0)
Packit 34410b
			vcard_printf_number(vcards, format, NULL, 1,
Packit 34410b
							TEL_TYPE_OTHER);
Packit 34410b
Packit 34410b
		for (; l; l = l->next) {
Packit 34410b
			struct phonebook_field *number = l->data;
Packit 34410b
Packit 34410b
			vcard_printf_number(vcards, format, number->text, 1,
Packit 34410b
								number->type);
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (filter & FILTER_EMAIL) {
Packit 34410b
		GSList *l = contact->emails;
Packit 34410b
Packit 34410b
		for (; l; l = l->next) {
Packit 34410b
			struct phonebook_field *email = l->data;
Packit 34410b
			vcard_printf_email(vcards, format, email->text,
Packit 34410b
								email->type);
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (filter & FILTER_ADR) {
Packit 34410b
		GSList *l = contact->addresses;
Packit 34410b
Packit 34410b
		for (; l; l = l->next) {
Packit 34410b
			struct phonebook_addr *addr = l->data;
Packit 34410b
			vcard_printf_address(vcards, format, addr);
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (filter & FILTER_BDAY && *contact->birthday)
Packit 34410b
		vcard_printf_tag(vcards, format, "BDAY", NULL,
Packit 34410b
						contact->birthday);
Packit 34410b
Packit 34410b
	if (filter & FILTER_NICKNAME && *contact->nickname)
Packit 34410b
		vcard_printf_tag(vcards, format, "NICKNAME", NULL,
Packit 34410b
							contact->nickname);
Packit 34410b
Packit 34410b
	if (filter & FILTER_URL) {
Packit 34410b
		GSList *l = contact->urls;
Packit 34410b
Packit 34410b
		for (; l; l = l->next) {
Packit 34410b
			struct phonebook_field *url = l->data;
Packit 34410b
			vcard_printf_url(vcards, format, url->text, url->type);
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (filter & FILTER_PHOTO && *contact->photo)
Packit 34410b
		vcard_printf_tag(vcards, format, "PHOTO", NULL,
Packit 34410b
							contact->photo);
Packit 34410b
Packit 34410b
	if (filter & FILTER_ORG)
Packit 34410b
		vcard_printf_org(vcards, format, contact);
Packit 34410b
Packit 34410b
	if (filter & FILTER_ROLE && *contact->role)
Packit 34410b
		vcard_printf_tag(vcards, format, "ROLE", NULL, contact->role);
Packit 34410b
Packit 34410b
	if (filter & FILTER_TITLE && *contact->title)
Packit 34410b
		vcard_printf_tag(vcards, format, "TITLE", NULL, contact->title);
Packit 34410b
Packit 34410b
	if (filter & FILTER_X_IRMC_CALL_DATETIME)
Packit 34410b
		vcard_printf_datetime(vcards, format, contact);
Packit 34410b
Packit 34410b
	vcard_printf_end(vcards);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void field_free(gpointer data)
Packit 34410b
{
Packit 34410b
	struct phonebook_field *field = data;
Packit 34410b
Packit 34410b
	g_free(field->text);
Packit 34410b
	g_free(field);
Packit 34410b
}
Packit 34410b
Packit 34410b
void phonebook_addr_free(gpointer addr)
Packit 34410b
{
Packit 34410b
	struct phonebook_addr *address = addr;
Packit 34410b
Packit 34410b
	g_slist_free_full(address->fields, g_free);
Packit 34410b
	g_free(address);
Packit 34410b
}
Packit 34410b
Packit 34410b
void phonebook_contact_free(struct phonebook_contact *contact)
Packit 34410b
{
Packit 34410b
	if (contact == NULL)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	g_slist_free_full(contact->numbers, field_free);
Packit 34410b
	g_slist_free_full(contact->emails, field_free);
Packit 34410b
	g_slist_free_full(contact->addresses, phonebook_addr_free);
Packit 34410b
	g_slist_free_full(contact->urls, field_free);
Packit 34410b
Packit 34410b
	g_free(contact->uid);
Packit 34410b
	g_free(contact->fullname);
Packit 34410b
	g_free(contact->given);
Packit 34410b
	g_free(contact->family);
Packit 34410b
	g_free(contact->additional);
Packit 34410b
	g_free(contact->prefix);
Packit 34410b
	g_free(contact->suffix);
Packit 34410b
	g_free(contact->birthday);
Packit 34410b
	g_free(contact->nickname);
Packit 34410b
	g_free(contact->photo);
Packit 34410b
	g_free(contact->company);
Packit 34410b
	g_free(contact->department);
Packit 34410b
	g_free(contact->role);
Packit 34410b
	g_free(contact->title);
Packit 34410b
	g_free(contact->datetime);
Packit 34410b
	g_free(contact);
Packit 34410b
}