/*
 *
 *  neard - Near Field Communication manager
 *
 *  Copyright (C) 2011-2016  Intel Corporation. All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>

#include <glib.h>

#include <gdbus.h>

#include "near.h"
#include "ndef-private.h"

enum record_tnf {
	RECORD_TNF_EMPTY     = 0x00,
	RECORD_TNF_WELLKNOWN = 0x01,
	RECORD_TNF_MIME      = 0x02,
	RECORD_TNF_URI       = 0x03,
	RECORD_TNF_EXTERNAL  = 0x04,
	RECORD_TNF_UNKNOWN   = 0x05,
	RECORD_TNF_UNCHANGED = 0x06,
};

#define RECORD_ACTION_DO   0x00
#define RECORD_ACTION_SAVE 0x01
#define RECORD_ACTION_EDIT 0x02

#define RECORD_MB_BIT(val)  ((val & 0x80) >> 7)
#define RECORD_ME_BIT(val)  ((val & 0x40) >> 6)
#define RECORD_CF_BIT(val)  ((val & 0x20) >> 5)
#define RECORD_SR_BIT(val)  ((val & 0x10) >> 4)
#define RECORD_IL_BIT(val)  ((val & 0x8)  >> 3)
#define RECORD_TNF_BIT(val) (val & 0x7)

#define NDEF_MSG_MIN_LENGTH 0x03
#define NDEF_PAYLOAD_LENGTH_OFFSET 0x02

#define RECORD_MB    0x80
#define RECORD_ME    0x40
#define RECORD_CF    0x20
#define RECORD_SR    0x10
#define RECORD_IL    0x08
#define RECORD_TNF_EMPTY_SET(val)     ((val & ~0x7) | RECORD_TNF_EMPTY)
#define RECORD_TNF_WKT_SET(val)       ((val & ~0x7) | RECORD_TNF_WELLKNOWN)
#define RECORD_TNF_MIME_SET(val)      ((val & ~0x7) | RECORD_TNF_MIME)
#define RECORD_TNF_URI_SET(val)       ((val & ~0x7) | RECORD_TNF_URI)
#define RECORD_TNF_EXTERNAL_SET(val)  ((val & ~0x7) | RECORD_TNF_EXTERNAL)
#define RECORD_TNF_UKNOWN_SET(val)    ((val & ~0x7) | RECORD_TNF_UNKNOWN)
#define RECORD_TNF_UNCHANGED_SET(val) ((val & ~0x7) | RECORD_TNF_UNCHANGED)

#define NDEF_MSG_SHORT_RECORD_MAX_LENGTH 0xFF
#define NDEF_TEXT_RECORD_TYPE_NAME_HEX_VALUE 0x54
#define NDEF_TEXT_RECORD_UTF16_STATUS 0x80

#define AC_CPS_MASK 0x03

static DBusConnection *connection = NULL;

static inline void fillb8(uint8_t *ptr, uint32_t len)
{
	(*(uint8_t *)(ptr)) = ((uint8_t)(len));
}

static inline void fillb16(uint8_t *ptr, uint32_t len)
{
	fillb8((ptr), (uint16_t)(len) >> 8);
	fillb8((uint8_t *)(ptr) + 1, len);
}

static inline void fillb32(uint8_t *ptr, uint32_t len)
{
	fillb16((ptr), (uint32_t)(len) >> 16);
	fillb16((uint8_t *)(ptr) + 2, (uint32_t)(len));
}

char *__near_ndef_record_get_path(struct near_ndef_record *record)
{
	return record->path;
}

char *__near_ndef_record_get_type(struct near_ndef_record *record)
{
	return record->type;
}

uint8_t *__near_ndef_record_get_data(struct near_ndef_record *record,
								size_t *len)
{
	*len = record->data_len;

	return record->data;
}

uint8_t *__near_ndef_record_get_payload(struct near_ndef_record *record,
								size_t *len)
{
	*len = record->header->payload_len;

	return record->data + record->header->header_len;
}

void __near_ndef_append_records(DBusMessageIter *iter, GList *records)
{
	GList *list;

	DBG("");

	for (list = records; list; list = list->next) {
		struct near_ndef_record *record = list->data;
		uint8_t *data;
		size_t data_len;

		data = __near_ndef_record_get_data(record, &data_len);
		if (!data)
			continue;

		dbus_message_iter_append_fixed_array(iter, DBUS_TYPE_BYTE,
							&data, data_len);
	}
}

static const char *uri_prefixes[NFC_MAX_URI_ID + 1] = {
	"",
	"http://www.",
	"https://www.",
	"http://",
	"https://",
	"tel:",
	"mailto:",
	"ftp://anonymous:anonymous@",
	"ftp://ftp.",
	"ftps://",
	"sftp://",
	"smb://",
	"nfs://",
	"ftp://",
	"dav://",
	"news:",
	"telnet://",
	"imap:",
	"rstp://",
	"urn:",
	"pop:",
	"sip:",
	"sips:",
	"tftp:",
	"btspp://",
	"btl2cap://",
	"btgoep://",
	"tcpobex://",
	"irdaobex://",
	"file://",
	"urn:epc:id:",
	"urn:epc:tag:",
	"urn:epc:pat:",
	"urn:epc:raw:",
	"urn:epc:",
	"urn:nfc:",
};

const char *__near_ndef_get_uri_prefix(uint8_t id)
{
	if (id > NFC_MAX_URI_ID)
		return NULL;

	return uri_prefixes[id];
}

static gboolean property_get_type(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;
	const char *type;

	DBG("");

	switch (record->header->rec_type) {
	case RECORD_TYPE_WKT_SIZE:
	case RECORD_TYPE_WKT_TYPE:
	case RECORD_TYPE_WKT_ACTION:
	case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
	case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
	case RECORD_TYPE_WKT_ERROR:
	case RECORD_TYPE_UNKNOWN:
	case RECORD_TYPE_ERROR:
	default:
		type = NULL;
		break;

	case RECORD_TYPE_WKT_TEXT:
		type = "Text";
		break;

	case RECORD_TYPE_WKT_URI:
		type = "URI";
		break;

	case RECORD_TYPE_WKT_SMART_POSTER:
		type = "SmartPoster";
		break;

	case RECORD_TYPE_WKT_HANDOVER_REQUEST:
		type = "HandoverRequest";
		break;

	case RECORD_TYPE_WKT_HANDOVER_SELECT:
		type = "HandoverSelect";
		break;

	case RECORD_TYPE_WKT_HANDOVER_CARRIER:
		type = "HandoverCarrier";
		break;

	case RECORD_TYPE_MIME_TYPE:
		type = "MIME";
		break;

	case RECORD_TYPE_EXT_AAR:
		type = "AAR";
		break;
	}

	if (!type)
		return FALSE;

	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &type);

	return TRUE;
}

static gboolean text_exists(const GDBusPropertyTable *property, void *data)
{
	struct near_ndef_record *record = data;

	DBG("");

	if (record->text)
		return TRUE;

	if (record->sp && record->sp->title_records)
		return TRUE;

	DBG("No text");

	return FALSE;
}

static const char *get_text_payload(const GDBusPropertyTable *property,
					struct near_ndef_text_payload *text)
{
	if (!strcmp(property->name, "Encoding"))
		return text->encoding;
	else if (!strcmp(property->name, "Language"))
		if (!text->language_code)
			return "en";
		else
			return text->language_code;
	else if (!strcmp(property->name, "Representation"))
		return text->data;
	else
		return NULL;
}

static gboolean property_get_text(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;
	const char *text;

	DBG("");

	if (record->text) {
		text = get_text_payload(property, record->text);
		if (!text)
			return FALSE;

		DBG("text %s", text);

		dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &text);

		return TRUE;
	}

	if (record->sp && record->sp->title_records) {
		int i;

		for (i = 0; i < record->sp->number_of_title_records; i++) {
			text = get_text_payload(property,
						record->sp->title_records[i]);
			if (!text)
				continue;

			dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
									&text);
		}

		return TRUE;
	}

	return FALSE;
}

static gboolean uri_exists(const GDBusPropertyTable *property, void *data)
{
	struct near_ndef_record *record = data;

	DBG("");

	if (record->uri)
		return TRUE;

	if (record->sp && record->sp->uri)
		return TRUE;

	DBG("No URI");

	return FALSE;
}

static gboolean property_get_uri(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;
	struct near_ndef_uri_payload *uri;
	char *value;
	const char *prefix = NULL;

	DBG("");

	if (record->uri)
		uri = record->uri;
	else if (record->sp && record->sp->uri)
		uri = record->sp->uri;
	else
		return FALSE;

	if (uri->identifier > NFC_MAX_URI_ID) {
		near_error("Invalid URI identifier 0x%x", uri->identifier);
		return FALSE;
	}

	prefix = uri_prefixes[uri->identifier];

	DBG("URI prefix %s", prefix);

	value = g_strdup_printf("%s%.*s", prefix, (int)uri->field_length,
				uri->field);

	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &value);

	g_free(value);

	return TRUE;
}

static gboolean sp_action_exists(const GDBusPropertyTable *property,
								void *data)
{
	struct near_ndef_record *record = data;

	DBG("");

	if (record->sp && record->sp->action)
		return TRUE;

	DBG("No SmartPoster action");

	return FALSE;
}

static gboolean sp_mime_exists(const GDBusPropertyTable *property, void *data)
{
	struct near_ndef_record *record = data;

	DBG("");

	if (record->sp && record->sp->type)
		return TRUE;

	DBG("No SmartPoster MIME type");

	return FALSE;
}

static gboolean sp_size_exists(const GDBusPropertyTable *property, void *data)
{
	struct near_ndef_record *record = data;

	DBG("");

	if (record->sp && record->sp->size)
		return TRUE;

	DBG("No SmartPoster size");

	return FALSE;
}

static gboolean property_get_action(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;

	DBG("%s", record->sp->action);

	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &record->sp->action);

	return TRUE;
}

static gboolean property_get_mime_type(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;

	DBG("%s", record->sp->type);

	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &record->sp->type);

	return TRUE;
}

static gboolean property_get_size(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;

	DBG("%u", record->sp->size);

	dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &record->sp->size);

	return TRUE;
}

static gboolean mime_exists(const GDBusPropertyTable *property, void *data)
{
	struct near_ndef_record *record = data;

	DBG("");

	if (record->mime)
		return TRUE;

	DBG("No MIME");

	return FALSE;
}

static gboolean property_get_mime(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;

	DBG("");

	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &record->mime->type);

	return TRUE;
}

static gboolean aar_exists(const GDBusPropertyTable *property, void *data)
{
	struct near_ndef_record *record = data;

	DBG("");

	if (record->aar)
		return TRUE;

	DBG("No AAR");

	return FALSE;
}

static gboolean property_get_aar(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;

	DBG("");

	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &record->aar->package);

	return TRUE;
}

static gboolean mime_payload_exists(const GDBusPropertyTable *property,
					void *data)
{
	struct near_ndef_record *record = data;
	gboolean exists = record->mime && record->mime->payload_len
						&& record->mime->payload;

	DBG("%s", exists ? "" : "No MIME payload");

	return exists;
}

static gboolean property_get_mime_payload(const GDBusPropertyTable *property,
					DBusMessageIter *iter, void *user_data)
{
	struct near_ndef_record *record = user_data;
	DBusMessageIter array;

	DBG("");

	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
			DBUS_TYPE_BYTE_AS_STRING, &array);
	dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
			&record->mime->payload, record->mime->payload_len);
	dbus_message_iter_close_container(iter, &array);

	return TRUE;
}

static const GDBusPropertyTable record_properties[] = {

	{ "Type", "s", property_get_type },
	{ "Encoding", "s", property_get_text, NULL, text_exists },
	{ "Language", "s", property_get_text, NULL, text_exists },
	{ "Representation", "s", property_get_text, NULL, text_exists},
	{ "URI", "s", property_get_uri, NULL, uri_exists },
	{ "Action", "s", property_get_action, NULL, sp_action_exists },
	{ "MIMEType", "s", property_get_mime_type, NULL, sp_mime_exists },
	{ "Size", "u", property_get_size, NULL, sp_size_exists },
	{ "MIME", "s", property_get_mime, NULL, mime_exists },
	{ "AAR", "s", property_get_aar, NULL, aar_exists },
	{ "MIMEPayload", "ay", property_get_mime_payload, NULL,
						mime_payload_exists },
	{ }
};

static void free_text_payload(struct near_ndef_text_payload *text)
{
	if (!text)
		return;

	g_free(text->encoding);
	g_free(text->language_code);
	g_free(text->data);
	g_free(text);
}

static void free_uri_payload(struct near_ndef_uri_payload *uri)
{
	if (!uri)
		return;

	g_free(uri->field);
	g_free(uri);
}

static void free_sp_payload(struct near_ndef_sp_payload *sp)
{
	uint8_t i;

	if (!sp)
		return;

	free_uri_payload(sp->uri);

	if (sp->title_records) {
		for (i = 0; i < sp->number_of_title_records; i++)
			free_text_payload(sp->title_records[i]);
	}

	g_free(sp->title_records);
	g_free(sp->type);
	g_free(sp->action);
	g_free(sp);
}

static void free_mime_payload(struct near_ndef_mime_payload *mime)
{
	if (!mime)
		return;

	g_free(mime->payload);
	g_free(mime->type);
	g_free(mime);
}

static void free_ac_payload(struct near_ndef_ac_payload *ac)
{
	if (!ac)
		return;

	g_free(ac->cdr);
	g_free(ac->adata);
	g_free(ac);
}

static void free_ho_payload(struct near_ndef_ho_payload *ho)
{
	int i;

	if (!ho)
		return;

	if (ho->ac_payloads) {
		for (i = 0; i < ho->number_of_ac_payloads; i++)
			free_ac_payload(ho->ac_payloads[i]);
	}

	g_free(ho->ac_payloads);
	g_free(ho);
}

static void free_aar_payload(struct near_ndef_aar_payload *aar)
{
	if (!aar)
		return;

	g_free(aar->package);
	g_free(aar);
}

static void free_ndef_record(struct near_ndef_record *record)
{
	if (!record)
		return;

	g_free(record->path);

	if (record->header) {

		switch (record->header->rec_type) {
		case RECORD_TYPE_WKT_SIZE:
		case RECORD_TYPE_WKT_TYPE:
		case RECORD_TYPE_WKT_ACTION:
		case RECORD_TYPE_WKT_HANDOVER_CARRIER:
		case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
		case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
		case RECORD_TYPE_WKT_ERROR:
		case RECORD_TYPE_UNKNOWN:
		case RECORD_TYPE_ERROR:
			break;

		case RECORD_TYPE_WKT_HANDOVER_REQUEST:
		case RECORD_TYPE_WKT_HANDOVER_SELECT:
			free_ho_payload(record->ho);
			break;

		case RECORD_TYPE_WKT_TEXT:
			free_text_payload(record->text);
			break;

		case RECORD_TYPE_WKT_URI:
			free_uri_payload(record->uri);
			break;

		case RECORD_TYPE_WKT_SMART_POSTER:
			free_sp_payload(record->sp);
			break;

		case RECORD_TYPE_MIME_TYPE:
			free_mime_payload(record->mime);
			break;

		case RECORD_TYPE_EXT_AAR:
			free_aar_payload(record->aar);
			break;
		}

		g_free(record->header->il_field);
		g_free(record->header->type_name);
	}

	g_free(record->header);
	g_free(record->type);
	g_free(record->data);
	g_free(record);
}

void __near_ndef_record_free(struct near_ndef_record *record)
{
	g_dbus_unregister_interface(connection, record->path,
						NFC_RECORD_INTERFACE);

	free_ndef_record(record);
}

static char *action_to_string(uint8_t action)
{
	switch (action) {
	case RECORD_ACTION_DO:
		return "Do";
	case RECORD_ACTION_SAVE:
		return "Save";
	case RECORD_ACTION_EDIT:
		return "Edit";
	default:
		near_error("Unknown action 0x%x", action);
		return NULL;
	}
}

static enum record_type get_external_record_type(uint8_t *type,
						size_t type_length)
{
	DBG("");

	if (strncmp((char *) type, BT_MIME_STRING_2_0,
					sizeof(BT_MIME_STRING_2_0) - 1) == 0)
		return RECORD_TYPE_MIME_TYPE;
	else if (strncmp((char *) type, AAR_STRING,
					sizeof(AAR_STRING) - 1) == 0)
		return RECORD_TYPE_EXT_AAR;
	else
		return RECORD_TYPE_UNKNOWN;
}

static enum record_type get_record_type(enum record_tnf tnf,
				uint8_t *type, size_t type_length)
{
	unsigned int i;

	DBG("");

	switch (tnf) {
	case RECORD_TNF_EMPTY:
	case RECORD_TNF_URI:
	case RECORD_TNF_UNKNOWN:
	case RECORD_TNF_UNCHANGED:
		break;

	case RECORD_TNF_WELLKNOWN:
		for (i = 0; i < type_length; i++)
			if (!isprint(type[i]))
				return RECORD_TYPE_ERROR;

		if (type_length == 1) {
			if (type[0] == 'T')
				return RECORD_TYPE_WKT_TEXT;
			else if (type[0] == 'U')
				return RECORD_TYPE_WKT_URI;
			else if (type[0] == 's')
				return RECORD_TYPE_WKT_SIZE;
			else if (type[0] == 't')
				return RECORD_TYPE_WKT_TYPE;
			else
				return RECORD_TYPE_UNKNOWN;

		} else if (type_length == 2) {
			if (strncmp((char *)type, "Sp", 2) == 0)
				return RECORD_TYPE_WKT_SMART_POSTER;
			else if (strncmp((char *) type, "Hr", 2) == 0)
				return RECORD_TYPE_WKT_HANDOVER_REQUEST;
			else if (strncmp((char *) type, "Hs", 2) == 0)
				return RECORD_TYPE_WKT_HANDOVER_SELECT;
			else if (strncmp((char *) type, "Hc", 2) == 0)
				return RECORD_TYPE_WKT_HANDOVER_CARRIER;
			else if (strncmp((char *) type, "ac", 2) == 0)
				return RECORD_TYPE_WKT_ALTERNATIVE_CARRIER;
			else if (strncmp((char *) type, "cr", 2) == 0)
				return RECORD_TYPE_WKT_COLLISION_RESOLUTION;
			else
				return RECORD_TYPE_UNKNOWN;

		} else if (type_length == 3) {
			if (strncmp((char *)type, "act", 3) == 0)
				return RECORD_TYPE_WKT_ACTION;
			else if (strncmp((char *)type, "err", 3) == 0)
				return RECORD_TYPE_WKT_ERROR;
			else
				return RECORD_TYPE_UNKNOWN;

		} else
			return RECORD_TYPE_UNKNOWN;

	case RECORD_TNF_MIME:
		return RECORD_TYPE_MIME_TYPE;

	case RECORD_TNF_EXTERNAL:
		return get_external_record_type(type, type_length);

	}

	return RECORD_TYPE_UNKNOWN;
}

static int build_record_type_string(struct near_ndef_record *rec)
{
	uint8_t tnf;

	DBG("");

	if (!rec || !rec->header)
		return -EINVAL;

	tnf = rec->header->tnf;

	if (rec->header->rec_type == RECORD_TYPE_WKT_SMART_POSTER) {
		rec->type = g_strdup_printf(RECORD_TYPE_WKT "U");
		return 0;
	}

	switch (tnf) {
	case RECORD_TNF_EMPTY:
	case RECORD_TNF_UNKNOWN:
	case RECORD_TNF_UNCHANGED:
		return -EINVAL;

	case RECORD_TNF_URI:
	case RECORD_TNF_MIME:
		rec->type = g_strndup(rec->header->type_name,
				      rec->header->type_len);
		break;

	case RECORD_TNF_WELLKNOWN:
		rec->type = g_strdup_printf(RECORD_TYPE_WKT "%s",
				      rec->header->type_name);
		break;

	case RECORD_TNF_EXTERNAL:
		rec->type = g_strdup_printf(RECORD_TYPE_EXTERNAL "%s",
				      rec->header->type_name);
		break;
	}

	return 0;
}

static uint8_t validate_record_begin_and_end_bits(uint8_t *msg_mb,
					uint8_t *msg_me, uint8_t rec_mb,
					uint8_t rec_me)
{
	DBG("");

	if (!msg_mb || !msg_me)
		return 0;

	/* Validating record header begin and end bits
	 * eg: Single record: [mb:1,me:1]
	 *     Two records:   [mb:1,me:0 - mb:0,me:1]
	 *     Three or more records [mb:1,me:0 - mb:0,me:0 .. mb:0,me:1]
	 **/

	if (rec_mb == 1) {
		if (*msg_mb != 1)
			*msg_mb = rec_mb;
		else
			return -EINVAL;

	}

	if (rec_me == 1) {
		if (*msg_me != 1) {
			if (*msg_mb == 1)
				*msg_me = rec_me;
			else
				return -EINVAL;

		} else
			return -EINVAL;

	}

	return 0;
}

/*
 * Parse the ndef record header and cache the begin, end, chunkflag,
 * short-record and type-name-format bits. ID length and field, record
 * type, payload length and offset (where payload byte starts in input
 * parameter). Validate offset for every step forward against total
 * available length.
 */
static struct near_ndef_record_header *parse_record_header(uint8_t *rec,
					uint32_t offset, uint32_t length)
{
	struct near_ndef_record_header *rec_header = NULL;
	uint8_t *type = NULL;
	uint32_t header_len = 0;

	DBG("length %u", length);

	if (!rec || offset >= length)
		return NULL;

	/* This check is for empty record. */
	if ((length - offset) < NDEF_MSG_MIN_LENGTH)
		return NULL;

	rec_header = g_try_malloc0(sizeof(struct near_ndef_record_header));
	if (!rec_header)
		return NULL;

	rec_header->mb = RECORD_MB_BIT(rec[offset]);
	rec_header->me = RECORD_ME_BIT(rec[offset]);
	rec_header->sr = RECORD_SR_BIT(rec[offset]);
	rec_header->il = RECORD_IL_BIT(rec[offset]);
	rec_header->tnf = RECORD_TNF_BIT(rec[offset]);

	DBG("mb %d me %d sr %d il %d tnf %d",
		rec_header->mb, rec_header->me, rec_header->sr,
		rec_header->il, rec_header->tnf);

	offset++;
	rec_header->type_len = rec[offset++];
	header_len = 2; /* type length + header bits */

	if (rec_header->sr == 1) {
		rec_header->payload_len = rec[offset++];
		header_len++;
	} else {
		rec_header->payload_len = near_get_be32(rec + offset);
		offset += 4;
		header_len += 4;

		if ((offset + rec_header->payload_len) > length)
			goto fail;
	}

	DBG("payload length %u", rec_header->payload_len);

	if (rec_header->il == 1) {
		rec_header->il_length = rec[offset++];
		header_len++;

		if ((offset + rec_header->payload_len) > length)
			goto fail;
	}

	if (rec_header->type_len > 0) {
		if ((offset + rec_header->type_len) > length)
			goto fail;

		type = g_try_malloc0(rec_header->type_len);
		if (!type)
			goto fail;

		memcpy(type, rec + offset, rec_header->type_len);
		offset += rec_header->type_len;
		header_len += rec_header->type_len;

		if ((offset + rec_header->payload_len) > length)
			goto fail;
	}

	if (rec_header->il_length > 0) {
		if ((offset + rec_header->il_length) > length)
			goto fail;

		rec_header->il_field = g_try_malloc0(rec_header->il_length);
		if (!rec_header->il_field)
			goto fail;

		memcpy(rec_header->il_field, rec + offset,
					rec_header->il_length);
		offset += rec_header->il_length;
		header_len += rec_header->il_length;

		if ((offset + rec_header->payload_len) > length)
			goto fail;
	}

	rec_header->rec_type = get_record_type(rec_header->tnf, type,
							rec_header->type_len);
	rec_header->offset = offset;
	rec_header->header_len = header_len;
	rec_header->type_name = g_strndup((char *) type, rec_header->type_len);

	g_free(type);

	return rec_header;

fail:
	near_error("parsing record header failed");

	g_free(type);
	g_free(rec_header->il_field);
	g_free(rec_header->type_name);
	g_free(rec_header);

	return NULL;
}

static struct near_ndef_text_payload *
parse_text_payload(uint8_t *payload, uint32_t length)
{
	struct near_ndef_text_payload *text_payload = NULL;
	uint8_t status, lang_length, len;
	char *g_str, *txt;
	uint32_t offset;
	gboolean valid;

	DBG("");

	if (!payload)
		return NULL;

	offset = 0;
	text_payload = g_try_malloc0(sizeof(struct near_ndef_text_payload));
	if (!text_payload)
		return NULL;

	/* 0th bit is LSB */
	status = ((payload[offset] & NDEF_TEXT_RECORD_UTF16_STATUS) >> 7);

	text_payload->encoding = (status == 0) ?
					g_strdup("UTF-8") : g_strdup("UTF-16");

	/* 0x3F is used to get 5th-0th bits value (0th bit is LSB) */
	lang_length = (payload[offset] & 0x3F);
	offset++;

	if (lang_length > 0) {
		if ((offset + lang_length) >= length)
			goto fail;

		text_payload->language_code = g_strndup(
						(char *)(payload + offset),
						lang_length);
	} else {
		text_payload->language_code = NULL;
	}

	offset += lang_length;

	len = length - lang_length - 1;

	if (status && (len % 2)) {
		DBG("Payload not valid UTF-16 (length %d does not match)", len);
		goto fail;
	}

	if (len > 0) {
		txt = (char *)(payload + offset);

		if (status)
			/* Cast to void to silence the 1-to-2 alignment warning */
			g_str = g_utf16_to_utf8((gunichar2 *)(void *)txt, len, NULL,
						NULL, NULL);
		else
			g_str = txt;

		valid = g_utf8_validate(g_str, len, NULL);

		if (status)
			g_free(g_str);

		if (!valid)
			goto fail;

		text_payload->data = g_strndup(txt, len);
	} else {
		text_payload->data = NULL;
	}

	if (offset >= length)
		goto fail;

	DBG("Encoding  '%s'", text_payload->encoding);
	DBG("Language Code  '%s'", text_payload->language_code);
	DBG("Data  '%s'", text_payload->data);

	return text_payload;

fail:
	near_error("text payload parsing failed");
	free_text_payload(text_payload);

	return NULL;
}

static struct near_ndef_uri_payload *
parse_uri_payload(uint8_t *payload, uint32_t length)
{
	struct near_ndef_uri_payload *uri_payload = NULL;
	uint32_t index, offset;

	DBG("");

	if (!payload)
		return NULL;

	offset = 0;
	uri_payload = g_try_malloc0(sizeof(struct near_ndef_uri_payload));
	if (!uri_payload)
		return NULL;

	uri_payload->identifier = payload[offset];
	offset++;

	uri_payload->field_length = length - 1;

	if (uri_payload->field_length > 0) {
		uri_payload->field = g_try_malloc0(uri_payload->field_length);
		if (!uri_payload->field)
			goto fail;

		memcpy(uri_payload->field, payload + offset,
				uri_payload->field_length);

		for (index = 0; index < uri_payload->field_length; index++) {
			/* URI Record Type Definition 1.0 [3.2.3]
			 * Any character value within the URI between
			 * (and including) 0 and 31 SHALL be recorded as
			 * an error, and the URI record to be discarded */
			if (uri_payload->field[index] <= 31)
				goto fail;
		}

	}

	DBG("Identifier  '0X%X'", uri_payload->identifier);
	DBG("Field  '%.*s'", (int)uri_payload->field_length, uri_payload->field);

	return uri_payload;

fail:
	near_error("uri payload parsing failed");
	free_uri_payload(uri_payload);

	return NULL;
}

/*
 * Validate titles records language code in Smartposter.
 * There must not be two or more records with the same language identifier.
 */
static int8_t validate_language_code_in_sp_record(GSList *titles)
{
	uint8_t i, j, length;
	struct near_ndef_text_payload *title1, *title2;

	DBG("");

	if (!titles)
		return -EINVAL;

	length = g_slist_length(titles);

	for (i = 0; i < length; i++) {
		title1 = g_slist_nth_data(titles, i);

		for (j = i + 1; j < length; j++) {
			title2 = g_slist_nth_data(titles, j);

			if ((!title1->language_code) &&
					(!title2->language_code))
				continue;

			if (g_strcmp0(title1->language_code,
					title2->language_code) == 0)
				return -EINVAL;
		}
	}

	return 0;
}

static struct near_ndef_sp_payload *
parse_sp_payload(uint8_t *payload, uint32_t length)
{
	struct near_ndef_sp_payload *sp_payload = NULL;
	struct near_ndef_record_header *rec_header = NULL;
	uint8_t mb = 0, me = 0, i;
	uint32_t offset;
	GSList *titles = NULL, *temp;

	DBG("");

	if (!payload)
		return NULL;

	offset = 0;
	sp_payload = g_try_malloc0(sizeof(struct near_ndef_sp_payload));
	if (!sp_payload)
		return NULL;

	while (offset < length) {

		DBG("Record header : 0x%x", payload[offset]);

		rec_header = parse_record_header(payload, offset, length);
		if (!rec_header)
			goto fail;

		if (validate_record_begin_and_end_bits(&mb, &me,
					rec_header->mb,	rec_header->me) != 0) {
			DBG("validate mb me failed");
			goto fail;
		}

		offset = rec_header->offset;

		switch (rec_header->rec_type) {
		case RECORD_TYPE_WKT_SMART_POSTER:
		case RECORD_TYPE_WKT_HANDOVER_REQUEST:
		case RECORD_TYPE_WKT_HANDOVER_SELECT:
		case RECORD_TYPE_WKT_HANDOVER_CARRIER:
		case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
		case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
		case RECORD_TYPE_MIME_TYPE:
		case RECORD_TYPE_WKT_ERROR:
		case RECORD_TYPE_EXT_AAR:
		case RECORD_TYPE_UNKNOWN:
		case RECORD_TYPE_ERROR:
			break;

		case RECORD_TYPE_WKT_URI:
			/* URI record should be only one. */
			if (sp_payload->uri)
				goto fail;

			sp_payload->uri = parse_uri_payload(payload + offset,
						rec_header->payload_len);
			if (!sp_payload->uri)
				goto fail;

			break;

		case RECORD_TYPE_WKT_TEXT:
			/*
			 * Title records can zero or more. First fill the
			 * records in list and validate language identifier
			 * and then cache them into sp record structure.
			 */
			{
			struct near_ndef_text_payload *title;
			title = parse_text_payload(payload + offset,
						rec_header->payload_len);
			if (!title)
				goto fail;

			titles = g_slist_append(titles, title);
			}
			break;

		case RECORD_TYPE_WKT_SIZE:
			/*
			 * If payload length is not exactly 4 bytes
			 * then record is wrong.
			 */
			if (rec_header->payload_len != 4)
				goto fail;

			sp_payload->size = near_get_be32(payload + offset);
			break;

		case RECORD_TYPE_WKT_TYPE:

			if (rec_header->payload_len > 0) {
				sp_payload->type = g_try_malloc0(
						rec_header->payload_len);
				if (!sp_payload->type)
					goto fail;

				sp_payload->type = g_strndup(
						(char *) payload + offset,
						rec_header->payload_len);
			}

			break;

		case RECORD_TYPE_WKT_ACTION:
			/*
			 * If the action record exists, payload should be
			 * single byte, otherwise consider it as error.
			 */
			if (rec_header->payload_len != 1)
				goto fail;

			sp_payload->action =
				g_strdup(action_to_string(payload[offset]));

			break;
		}

		offset += rec_header->payload_len;
		g_free(rec_header->il_field);
		g_free(rec_header->type_name);
		g_free(rec_header);
		rec_header = NULL;
	}

	/*
	 * Code to fill smart poster record structure from
	 * 'titles' list.
	 */
	if (!titles)
		return sp_payload;

	if (validate_language_code_in_sp_record(titles) != 0) {
		DBG("language code validation failed");
		goto fail;
	}

	temp = titles;
	sp_payload->number_of_title_records = g_slist_length(temp);
	sp_payload->title_records = g_try_malloc0(
				sp_payload->number_of_title_records *
				 sizeof(struct near_ndef_text_payload *));
	if (!sp_payload->title_records)
		goto fail;

	for (i = 0; i < sp_payload->number_of_title_records; i++) {
		sp_payload->title_records[i] = temp->data;
		temp = temp->next;
	}

	g_slist_free(titles);
	titles = NULL;

	return sp_payload;

fail:
	near_error("smart poster payload parsing failed");

	if (rec_header) {
		g_free(rec_header->type_name);
		g_free(rec_header->il_field);
		g_free(rec_header);
	}

	free_sp_payload(sp_payload);
	g_slist_free(titles);

	return NULL;
}

static void correct_eir_len(struct carrier_data *data)
{
	/*
	 * Android 4.1 BUG - OOB EIR length should be in LE, but is in BE.
	 * Fortunately payload length is 1 byte so this can be detected and
	 * corrected before sending it to handover agent.
	 */
	if (data->data[0] == 0) {
		DBG("EIR length in BE");
		data->data[0] = data->data[1];
		data->data[1] = 0;
	}

	/*
	 * Some Nokia BH-505 report total OOB block length without length field
	 * size.
	 */
	if (data->data[0] == data->size - 2) {
		DBG("EIR length without length field size");
		data->data[0] += 2;
	}
}

static int process_mime_type(struct near_ndef_mime_payload *mime,
					struct carrier_data *c_data)
{
	int err = -EINVAL;

	DBG("");

	if (!mime || !c_data)
		return -EINVAL;

	switch (mime->handover.carrier_type) {
	case NEAR_CARRIER_BLUETOOTH:
		err = __near_agent_handover_push_data(HO_AGENT_BT, c_data);
		if (err == -ESRCH)
			err = __near_bluetooth_parse_oob_record(c_data,
					&mime->handover.properties, true);
		break;

	case NEAR_CARRIER_WIFI:
		err = __near_agent_handover_push_data(HO_AGENT_WIFI, c_data);
		break;

	case NEAR_CARRIER_EMPTY:
	case NEAR_CARRIER_UNKNOWN:
		break;
	}

	return err;
}

static struct near_ndef_mime_payload *parse_mime_type(
			struct near_ndef_record *record, uint8_t *ndef_data,
			size_t ndef_length, size_t offset,
			uint32_t payload_length, struct carrier_data **c_data)
{
	struct near_ndef_mime_payload *mime;
	struct carrier_data *c_temp;

	DBG("");

	if (!c_data || !ndef_data ||
			((offset + payload_length) > ndef_length))
		return NULL;

	mime = g_try_malloc0(sizeof(struct near_ndef_mime_payload));
	if (!mime)
		return NULL;

	c_temp = g_try_malloc0(sizeof(struct carrier_data));
	if (!c_temp) {
		g_free(mime);
		return NULL;
	}

	mime->type = g_strdup(record->header->type_name);

	DBG("MIME Type '%s'", mime->type);
	if (strcmp(mime->type, BT_MIME_STRING_2_1) == 0) {
		mime->handover.carrier_type = NEAR_CARRIER_BLUETOOTH;
		c_temp->type = BT_MIME_V2_1;
		c_temp->size = record->header->payload_len;
		memcpy(c_temp->data, ndef_data + offset, c_temp->size);
		correct_eir_len(c_temp);
	} else if (strcmp(mime->type, BT_MIME_STRING_2_0) == 0) {
		mime->handover.carrier_type = NEAR_CARRIER_BLUETOOTH;
		c_temp->type = BT_MIME_V2_0;
		c_temp->size = record->header->payload_len;
		memcpy(c_temp->data, ndef_data + offset, c_temp->size);
	} else if (strcmp(mime->type, WIFI_WSC_MIME_STRING) == 0) {
		mime->handover.carrier_type = NEAR_CARRIER_WIFI;
		c_temp->type = WIFI_WSC_MIME;
		c_temp->size = record->header->payload_len;
		memcpy(c_temp->data, ndef_data + offset, c_temp->size);
	} else {
		g_free(c_temp);
		c_temp = NULL;

		if (!record->header->payload_len)
			goto fail;

		mime->payload = g_try_malloc0(record->header->payload_len);
		if (!mime->payload) {
			near_error("Memory allocation failed (MIME payload)");
			goto fail;
		}

		mime->payload_len = record->header->payload_len;
		memcpy(mime->payload, ndef_data + offset, mime->payload_len);
	}
fail:
	*c_data = c_temp;

	return mime;
}

/* Set the MB bit in message header */
static uint8_t near_ndef_set_mb(uint8_t *hdr, bool first_rec)
{
	/* Reset bits 0x40 */
	*hdr &= (0xFF & (~RECORD_MB));

	/* Set if needed */
	if (first_rec)
		*hdr |= RECORD_MB;

	return *hdr;
}

/* Set the MB/ME bit in message header */
static uint8_t near_ndef_set_me(uint8_t *hdr, bool last_rec)
{
	/* Reset bits 0x80 */
	*hdr &= (0xFF & (~RECORD_ME));

	/* Set if needed */
	if (last_rec)
		*hdr |= RECORD_ME;

	return *hdr;
}

/* Set the MB/ME bit in message header */
static uint8_t near_ndef_set_mb_me(uint8_t *hdr, bool first_rec,
						bool last_rec)
{
	near_ndef_set_mb(hdr, first_rec);
	return near_ndef_set_me(hdr, last_rec);
}

/* Caller should put own payload starting from offset value */
static struct near_ndef_message *ndef_message_alloc_complete(char *type_name,
		uint32_t payload_len,
		char *payload_id,
		uint8_t payload_id_len,
		enum record_tnf tnf,
		bool first_rec,
		bool last_rec)
{
	struct near_ndef_message *msg;
	uint8_t hdr = 0, type_len, sr_bit, il_bit, id_len;

	msg = g_try_malloc0(sizeof(struct near_ndef_message));
	if (!msg)
		return NULL;

	msg->length = 0;
	msg->offset = 0;
	msg->length++; /* record header*/
	msg->length++; /* type name length byte*/

	type_len = (type_name) ? strlen(type_name) : 0;
	id_len = (payload_id) ? payload_id_len : 0;
	sr_bit =  (payload_len <= NDEF_MSG_SHORT_RECORD_MAX_LENGTH)
					? TRUE : FALSE;

	il_bit = (payload_id) ? TRUE : FALSE;

	msg->length += (sr_bit) ? 1 : 4;
	msg->length += (il_bit) ? 1 : 0;
	msg->length += type_len;
	msg->length += payload_len;
	msg->length += id_len;

	msg->data = g_try_malloc0(msg->length);
	if (!msg->data)
		goto fail;

	/* Set MB ME bits */
	hdr = near_ndef_set_mb_me(&hdr, first_rec, last_rec);

	if (sr_bit)
		hdr |= RECORD_SR;

	hdr = RECORD_TNF_WKT_SET(hdr);
	if (il_bit)
		hdr |= RECORD_IL;

	switch (tnf) {
	case RECORD_TNF_EMPTY:
		hdr = RECORD_TNF_EMPTY_SET(hdr);
		break;

	case RECORD_TNF_URI:
		hdr = RECORD_TNF_URI_SET(hdr);
		break;

	case RECORD_TNF_EXTERNAL:
		hdr = RECORD_TNF_EXTERNAL_SET(hdr);
		break;
	case RECORD_TNF_UNKNOWN:
		hdr = RECORD_TNF_UKNOWN_SET(hdr);
		break;

	case RECORD_TNF_UNCHANGED:
		hdr = RECORD_TNF_UNCHANGED_SET(hdr);
		break;

	case RECORD_TNF_WELLKNOWN:
		hdr = RECORD_TNF_WKT_SET(hdr);
		break;

	case RECORD_TNF_MIME:
		hdr = RECORD_TNF_MIME_SET(hdr);
		break;
	}

	msg->data[msg->offset++] = hdr;
	msg->data[msg->offset++] = type_len;

	if (sr_bit) {
		msg->data[msg->offset++] = payload_len;
	} else {
		fillb32((msg->data + msg->offset), payload_len);
		msg->offset += 4;
	}

	if (il_bit)
		msg->data[msg->offset++] = payload_id_len;

	if (type_name) {
		memcpy(msg->data + msg->offset, type_name, type_len);
		msg->offset += type_len;
	}

	if (il_bit) {
		memcpy(msg->data + msg->offset, payload_id, payload_id_len);
		msg->offset += payload_id_len;
	}

	return msg;

fail:
	near_error("ndef message struct allocation failed");
	near_ndef_msg_free(msg);

	return NULL;
}

/*
 *  This is a wrapper to ndef_message_alloc, as, in most cases,
 *  there's no payload id, and MB=TRUE and ME=TRUE. Default type name format
 *  is also set to RECORD_TNF_WELLKNOWN
 *
 */
static struct near_ndef_message *ndef_message_alloc(char *type_name,
							uint32_t payload_len)
{
	return ndef_message_alloc_complete(type_name, payload_len,
			NULL, 0,
			RECORD_TNF_WELLKNOWN,
			true, true);
}

static enum carrier_power_state get_cps(uint8_t data)
{
	/* enum carrier_power_state values match binary format */
	return data & AC_CPS_MASK;
}

static struct near_ndef_ac_payload *parse_ac_payload(uint8_t *payload,
						uint32_t length)
{
	struct near_ndef_ac_payload *ac_payload = NULL;
	uint32_t offset;

	DBG("");

	if (!payload)
		return NULL;

	offset = 0;
	ac_payload = g_try_malloc0(sizeof(struct near_ndef_ac_payload));
	if (!ac_payload)
		goto fail;

	/* Carrier flag */
	ac_payload->cps = get_cps(payload[offset]);
	offset++;

	/* Carrier data reference length */
	ac_payload->cdr_len = payload[offset];
	offset++;

	if (ac_payload->cdr_len == 0)
		goto fail;

	/* Carrier data reference */
	ac_payload->cdr = g_try_malloc0(ac_payload->cdr_len + 1);
	if (!ac_payload->cdr)
		goto fail;

	memcpy(ac_payload->cdr, payload + offset, ac_payload->cdr_len);
	offset = offset + ac_payload->cdr_len;

	/* Auxiliary data reference count */
	ac_payload->adata_refcount = payload[offset];
	offset++;

	if (ac_payload->adata_refcount == 0)
		return ac_payload;

	/* save the auxiliary data reference */
	ac_payload->adata = g_try_malloc0(
			ac_payload->adata_refcount * sizeof(uint16_t));
	if (!ac_payload->adata)
		goto fail;

	memcpy(ac_payload->adata, payload + offset,
			ac_payload->adata_refcount * sizeof(uint16_t));

	/* and leave */
	return ac_payload;

fail:
	near_error("ac payload parsing failed");
	free_ac_payload(ac_payload);

	return NULL;
}

/* carrier power state & carrier reference */
static struct near_ndef_message *near_ndef_prepare_ac_message(uint8_t cps,
								char *cdr,
								uint8_t cdr_len)
{
	struct near_ndef_message *ac_msg;

	/* alloc "ac" message minus adata*/
	ac_msg = ndef_message_alloc_complete("ac",
						AC_RECORD_PAYLOAD_LEN(cdr_len),
						NULL, 0,
						RECORD_TNF_WELLKNOWN,
						true, true);
	if (!ac_msg)
		return NULL;

	/* Prepare ac message */
	ac_msg->data[ac_msg->offset++] = cps;

	ac_msg->data[ac_msg->offset++] = cdr_len;
	memcpy(ac_msg->data + ac_msg->offset, cdr, cdr_len); /* cdr */
	ac_msg->offset += cdr_len;

	ac_msg->data[ac_msg->offset] = 0; /* adata ref count */

	/* Check if we want an empty record */
	if (*cdr == 0x00)
		ac_msg->length = 0;

	return ac_msg;
}

/* Collision Record message */
static struct near_ndef_message *near_ndef_prepare_cr_message(uint16_t cr_id)
{
	struct near_ndef_message *cr_msg;

	cr_msg = ndef_message_alloc_complete("cr", sizeof(uint16_t),
						NULL, 0,
						RECORD_TNF_WELLKNOWN,
						true, true);
	if (!cr_msg)
		return NULL;

	/* Prepare ac message */
	near_put_be16(cr_id, cr_msg->data + cr_msg->offset);

	return cr_msg;
}

static struct near_ndef_message *near_ndef_prepare_cfg_message(char *mime_type,
					uint8_t *data, int data_len,
					char *cdr, uint8_t cdr_len)
{
	struct near_ndef_message *msg = NULL;

	DBG(" %s", mime_type);

	if (!mime_type)
		return NULL;

	msg = ndef_message_alloc_complete(mime_type, data_len, cdr, cdr_len,
						RECORD_TNF_MIME, true, true);
	if (!msg)
		return NULL;

	/* store data */
	if (data)
		memcpy(msg->data + msg->offset, data, data_len);

	return msg;
}

/*
 * Prepare alternative carrier and configuration records
 * (e.g. bluetooth or wifi or Hc)
 */
static int near_ndef_prepare_ac_and_cfg_records(enum record_type type,
					enum handover_carrier carrier,
					struct near_ndef_message **ac,
					struct near_ndef_message **cfg,
					struct near_ndef_mime_payload *mime,
					struct carrier_data *remote_carrier)
{
	struct carrier_data *local_carrier;
	char cdr;
	char *mime_type, *carrier_string;
	uint16_t prop;

	DBG("");

	if (!ac || !cfg)
		return -EINVAL;

	switch (carrier) {
	case NEAR_CARRIER_BLUETOOTH:
		cdr = '0';
		carrier_string = "Bluetooth";
		mime_type = BT_MIME_STRING_2_1;
		local_carrier = __near_agent_handover_request_data(
					HO_AGENT_BT, remote_carrier);
		if (local_carrier)
			break;

		prop = (mime) ? mime->handover.properties :
							OOB_PROPS_EMPTY;
		local_carrier = __near_bluetooth_local_get_properties(prop);

		break;

	case NEAR_CARRIER_WIFI:
		cdr = '1';
		carrier_string = "WiFi-WSC";
		mime_type = WIFI_WSC_MIME_STRING;
		local_carrier = __near_agent_handover_request_data(
						HO_AGENT_WIFI, remote_carrier);
		break;

	case NEAR_CARRIER_EMPTY:
	case NEAR_CARRIER_UNKNOWN:
	default:
		return -EINVAL;
	}

	/*
	 * WiFi has to be handled a bit differently when we're trying
	 * to send the first Hr or Hs (i.e. remote_carrier == NULL).
	 * There are 2 invalid combinations:
	 *	a) AP mode and sending an Hr, as we won't be able to
	 *	   initiate the association.
	 *	b) STA mode and sending an Hs, as the AP won't be able
	 *	   initiate the association.
	 *
	 * a) is detected when local_carrier != NULL and record_type is
	 * Handover Request.
	 * b) is detected when local_carrier == NULL and record_type is
	 * Handover Select.
	 * In those 2 cases we return an error.
	 */
	if (carrier == NEAR_CARRIER_WIFI && !remote_carrier) {
		if (local_carrier &&
				type == RECORD_TYPE_WKT_HANDOVER_REQUEST) {
			g_free(local_carrier);
			return -EINVAL;
		}

		if (!local_carrier &&
				type == RECORD_TYPE_WKT_HANDOVER_SELECT)
			return -EINVAL;

		if (!local_carrier &&
				type == RECORD_TYPE_WKT_HANDOVER_REQUEST) {
			*cfg = near_ndef_prepare_cfg_message(mime_type,
							NULL, 0,
							&cdr, sizeof(cdr));
			*ac = near_ndef_prepare_ac_message(CPS_UNKNOWN, &cdr,
								sizeof(cdr));

			return 0;
		}
	}

	if (!local_carrier) {
		DBG("Unable to retrieve local carrier %s data", carrier_string);
		return -ESRCH;
	}

	*cfg = near_ndef_prepare_cfg_message(mime_type, local_carrier->data,
							local_carrier->size,
							&cdr, sizeof(cdr));
	*ac = near_ndef_prepare_ac_message(local_carrier->state,
							&cdr, sizeof(cdr));

	g_free(local_carrier);

	if (!*cfg || !*ac) {
		near_ndef_msg_free(*ac);
		near_ndef_msg_free(*cfg);

		return -ENOMEM;
	}

	return 0;
}

static void free_ndef_list(gpointer data)
{
	struct near_ndef_message *msg = data;

	near_ndef_msg_free(msg);
}

static struct near_ndef_message *prepare_handover_message_header(char *type,
					uint32_t msg_len, uint32_t payload_len)
{
	struct near_ndef_message *ho_msg;

	ho_msg = ndef_message_alloc(type, msg_len);
	if (!ho_msg)
		return NULL;

	/*
	 * The handover payload length is not the *real* length.
	 * The PL refers to the NDEF record, not the extra ones.
	 * So, we have to fix the payload length in the header.
	 */
	ho_msg->data[NDEF_PAYLOAD_LENGTH_OFFSET] = payload_len;
	near_ndef_set_mb_me(ho_msg->data, true, false);

	/* Add version */
	ho_msg->data[ho_msg->offset++] = HANDOVER_VERSION;

	return ho_msg;
}

static uint32_t ndef_message_list_length(GList *list)
{
	struct near_ndef_message *msg;
	uint32_t length = 0;

	if (!list)
		return 0;

	while (list) {
		msg = list->data;
		length += msg->length;
		list = list->next;
	}

	return length;
}

static void copy_ac_records(struct near_ndef_message *ho, GList *acs)
{
	GList *temp = acs;
	struct near_ndef_message *ac;

	if (!ho || !temp)
		return;

	while (temp) {
		ac = temp->data;
		memcpy(ho->data + ho->offset, ac->data, ac->length);
		/*
		 * AC records are part of handover message payload,
		 * so modifying offset.
		 */
		ho->offset += ac->length;
		temp = temp->next;
	}
}

static void copy_cfg_records(struct near_ndef_message *ho, GList *cfgs)
{
	GList *temp = cfgs;
	struct near_ndef_message *cfg;
	uint32_t offset;

	if (!ho || !temp)
		return;

	offset = ho->offset;

	while (temp) {
		cfg = temp->data;
		memcpy(ho->data + offset, cfg->data, cfg->length);
		/*
		 * Configuration records (e.g. bt or wifi) records are not part
		 * of handover payload, they are consecutive ndef msgs. So
		 * here we are not modifying ho->offset.
		 */
		offset += cfg->length;
		temp = temp->next;
	}
}

static void set_mb_me_to_false(gpointer data, gpointer user_data)
{
	struct near_ndef_message *msg = data;

	near_ndef_set_mb_me(msg->data, false, false);
}

static struct near_ndef_message *near_ndef_prepare_empty_hs_message(void)
{
	struct near_ndef_message *hs_msg;
	struct near_ndef_message *ac_msg;
	char cdr = 0x00;
	uint32_t hs_length;

	DBG("");

	ac_msg = near_ndef_prepare_ac_message(CPS_UNKNOWN, &cdr, sizeof(cdr));
	if (!ac_msg)
		return NULL;

	hs_length = 1;
	hs_length += ac_msg->length;

	hs_msg = prepare_handover_message_header("Hs", hs_length, hs_length);
	if (!hs_msg)
		goto fail;

	near_ndef_set_mb_me(hs_msg->data, true, true);
	memcpy(hs_msg->data + hs_msg->offset, ac_msg->data, ac_msg->length);
	hs_msg->offset += ac_msg->length;

	if (hs_msg->offset > hs_msg->length)
		goto fail;

	near_ndef_msg_free(ac_msg);

	return hs_msg;

fail:
	near_ndef_msg_free(ac_msg);
	near_ndef_msg_free(hs_msg);

	return NULL;
}

static struct near_ndef_message *near_ndef_prepare_hs_reply(
					GSList *remote_mimes,
					GSList *remote_cfgs)
{
	struct near_ndef_message *hs_msg = NULL;
	struct near_ndef_message *ac_msg;
	struct near_ndef_message *cfg_msg;
	struct near_ndef_mime_payload *remote_mime;
	struct carrier_data *remote_cfg;
	GList *ac_msgs = NULL, *cfg_msgs = NULL, *temp;
	GSList *mime_iter, *cfg_iter;
	uint8_t hs_length, hs_pl_length, num_of_carriers;
	int ret = -EINVAL;

	DBG("");

	/*
	 * Preparing empty Hs message in case remote devices has zero
	 * alternative carries or unknown mime types or unknown
	 * configuration data.
	 */
	if ((!remote_mimes || !remote_cfgs))
		return near_ndef_prepare_empty_hs_message();

	mime_iter = remote_mimes;
	cfg_iter  = remote_cfgs;

	while (mime_iter) {
		remote_mime = mime_iter->data;
		remote_cfg  = cfg_iter->data;
		if (!remote_mime || !remote_cfg)
			goto fail;

		ret = near_ndef_prepare_ac_and_cfg_records(
					RECORD_TYPE_WKT_HANDOVER_SELECT,
					remote_mime->handover.carrier_type,
					&ac_msg, &cfg_msg,
					remote_mime, remote_cfg);
		if (ret == 0) {
			ac_msgs  = g_list_append(ac_msgs, ac_msg);
			cfg_msgs = g_list_append(cfg_msgs, cfg_msg);
		}

		mime_iter = mime_iter->next;
		cfg_iter  = cfg_iter->next;
	}

	if (g_list_length(ac_msgs) == 0) {
		DBG("no alternative carriers, so preparing empty Hs message");
		return near_ndef_prepare_empty_hs_message();
	}

	/* Prepare Hs message */
	hs_pl_length = 1;
	/* Alternative carriers are part of handover record payload length */
	hs_pl_length += ndef_message_list_length(ac_msgs);

	hs_length = hs_pl_length;
	/* Configuration records are part of handover message length */
	hs_length += ndef_message_list_length(cfg_msgs);

	hs_msg = prepare_handover_message_header("Hs", hs_length, hs_pl_length);
	if (!hs_msg)
		goto fail;

	num_of_carriers = g_list_length(ac_msgs);

	if (num_of_carriers == 1) {
		/* only one message */
		ac_msg = ac_msgs->data;
		near_ndef_set_mb_me(ac_msg->data, true, true);
	} else if (num_of_carriers > 1) {
		g_list_foreach(ac_msgs, set_mb_me_to_false, NULL);
		/* first message */
		temp = g_list_first(ac_msgs);
		ac_msg = temp->data;
		near_ndef_set_mb_me(ac_msg->data, true, false);
		/* last message */
		temp = g_list_last(ac_msgs);
		ac_msg = temp->data;
		near_ndef_set_mb_me(ac_msg->data, false, true);
	}

	g_list_foreach(cfg_msgs, set_mb_me_to_false, NULL);
	temp = g_list_last(cfg_msgs);
	cfg_msg = temp->data;
	near_ndef_set_mb_me(cfg_msg->data, false, true);

	/* copy acs */
	copy_ac_records(hs_msg, ac_msgs);
	if (hs_msg->offset > hs_msg->length)
		goto fail;

	/*
	 * copy cfgs, cfg (associated to the ac) records length
	 * (bt or wifi) is not part of Hs initial size.
	 */
	copy_cfg_records(hs_msg, cfg_msgs);

	DBG("Hs message preparation is done");

	g_list_free_full(ac_msgs, free_ndef_list);
	g_list_free_full(cfg_msgs, free_ndef_list);

	return hs_msg;

fail:
	near_error("handover Hs message preparation failed");

	g_list_free_full(ac_msgs, free_ndef_list);
	g_list_free_full(cfg_msgs, free_ndef_list);

	near_ndef_msg_free(hs_msg);

	return NULL;
}

static enum handover_carrier string2carrier(char *carrier)
{
	if (strcasecmp(carrier, NEAR_HANDOVER_AGENT_BLUETOOTH) == 0)
		return NEAR_CARRIER_BLUETOOTH;

	if (strcasecmp(carrier, NEAR_HANDOVER_AGENT_WIFI) == 0)
		return NEAR_CARRIER_WIFI;

	return NEAR_CARRIER_UNKNOWN;
}

static struct near_ndef_message *
near_ndef_prepare_ho_message(enum record_type type, GSList *carriers)
{
	struct near_ndef_message *ho_msg = NULL;
	struct near_ndef_message *cr_msg = NULL;
	struct near_ndef_message *ac_msg;
	struct near_ndef_message *cfg_msg;
	GList *ac_msgs = NULL, *cfg_msgs = NULL, *temp;
	uint16_t collision;
	uint8_t ho_length, ho_pl_length;
	int ret = -EINVAL;
	char *str_type;

	DBG("");

	switch (type) {
	case RECORD_TYPE_WKT_HANDOVER_REQUEST:
		str_type = "Hr";
		break;

	case RECORD_TYPE_WKT_HANDOVER_SELECT:
		str_type = "Hs";
		break;

	default:
		return NULL;
	}

	/* Hr message should have at least one carrier */
	while (carriers) {
		ret = near_ndef_prepare_ac_and_cfg_records(
				type,
				string2carrier(carriers->data),
				&ac_msg, &cfg_msg, NULL, NULL);
		if (ret == 0) {
			ac_msgs  = g_list_append(ac_msgs, ac_msg);
			cfg_msgs = g_list_append(cfg_msgs, cfg_msg);
		}

		carriers = carriers->next;
	}

	if (g_list_length(ac_msgs) == 0) {
		DBG("no alternative carriers to prepare Hr message");
		goto fail;
	}

	/* Prepare Hr message */
	ho_pl_length = 1;

	/* Prepare collision resolution record MB=1 ME=0, only for Hr */
	if (type == RECORD_TYPE_WKT_HANDOVER_REQUEST) {
		collision = GUINT16_TO_BE(g_random_int_range(0, G_MAXUINT16 + 1));
		cr_msg = near_ndef_prepare_cr_message(collision);
		if (!cr_msg)
			goto fail;

		near_ndef_set_mb_me(cr_msg->data, true, false);

		ho_pl_length += cr_msg->length;
	}

	/* Alternative carriers are part of handover record payload length */
	ho_pl_length += ndef_message_list_length(ac_msgs);

	ho_length = ho_pl_length;
	/* Configuration records are part of handover message length */
	ho_length += ndef_message_list_length(cfg_msgs);

	ho_msg = prepare_handover_message_header(str_type,
						ho_length, ho_pl_length);
	if (!ho_msg)
		goto fail;

	g_list_foreach(ac_msgs, set_mb_me_to_false, NULL);
	/* last message */
	temp = g_list_last(ac_msgs);
	ac_msg = temp->data;
	near_ndef_set_mb_me(ac_msg->data, false, true);

	/*
	 * Hs record payloads do not have collision recore, the first record
	 * will be the first ac record.
	 */
	if (type == RECORD_TYPE_WKT_HANDOVER_SELECT) {
		temp = g_list_first(ac_msgs);
		ac_msg = temp->data;
		near_ndef_set_mb_me(ac_msg->data, true, false);
	}

	g_list_foreach(cfg_msgs, set_mb_me_to_false, NULL);
	temp = g_list_last(cfg_msgs);
	cfg_msg = temp->data;
	near_ndef_set_mb_me(cfg_msg->data, false, true);

	if (type == RECORD_TYPE_WKT_HANDOVER_REQUEST) {
		/* copy cr */
		memcpy(ho_msg->data + ho_msg->offset,
				cr_msg->data, cr_msg->length);
		ho_msg->offset += cr_msg->length;

		if (ho_msg->offset > ho_msg->length)
			goto fail;
	}

	/* copy acs */
	copy_ac_records(ho_msg, ac_msgs);
	if (ho_msg->offset > ho_msg->length)
		goto fail;

	/*
	 * copy cfgs, cfg (associated to the ac) records length
	 * (bt or wifi) is not part of Hr initial size.
	 */
	copy_cfg_records(ho_msg, cfg_msgs);

	DBG("Handover message preparation is done");

	near_ndef_msg_free(cr_msg);
	g_list_free_full(ac_msgs, free_ndef_list);
	g_list_free_full(cfg_msgs, free_ndef_list);

	return ho_msg;

fail:
	near_error("handover record preparation failed");

	g_list_free_full(ac_msgs, free_ndef_list);
	g_list_free_full(cfg_msgs, free_ndef_list);
	near_ndef_msg_free(cr_msg);
	near_ndef_msg_free(ho_msg);

	return NULL;
}

/* Code to fill hr record structure from acs and mimes lists */
static int near_fill_ho_payload(struct near_ndef_ho_payload *ho,
					GSList *acs, GSList *mimes)
{
	int rec_count;
	int i;
	GSList *temp;

	rec_count = g_slist_length(acs);
	ho->ac_payloads = g_try_malloc0(rec_count *
			sizeof(struct near_ndef_ac_payload *));
	if (!ho->ac_payloads)
		goto fail;
	temp = acs;
	for (i = 0; i < rec_count; i++) {
		ho->ac_payloads[i] = temp->data;
		temp = temp->next;
	}
	ho->number_of_ac_payloads = rec_count;
	g_slist_free(acs);

	/* Same process for cfg mimes */
	rec_count = g_slist_length(mimes);
	ho->cfg_payloads = g_try_malloc0(rec_count *
			sizeof(struct near_ndef_mime_payload *));
	if (!ho->cfg_payloads)
		goto fail;
	temp = mimes;
	for (i = 0; i < rec_count; i++) {
		ho->cfg_payloads[i] = temp->data;
		temp = temp->next;
	}

	ho->number_of_cfg_payloads = rec_count;
	g_slist_free(mimes);

	return 0;
fail:
	g_free(ho->ac_payloads);
	g_free(ho->cfg_payloads);
	ho->ac_payloads = NULL;
	ho->cfg_payloads = NULL;
	return -ENOMEM;
}

/*
 * This function will parse an handover record payload, retrieving sub records
 * like (ac, cr, er) but it  will also get the associated ndefs
 * (eg: handover carrier record, mime type for BT)
 * In a handover frame, only the following types are expected:
 *     RECORD_TYPE_WKT_HANDOVER_CARRIER:
 *     RECORD_TYPE_WKT_COLLISION_RESOLUTION
 *     RECORD_TYPE_MIME_TYPE
 *     RECORD_TYPE_WKT_ALTERNATIVE_CARRIER
 */
static struct near_ndef_ho_payload *parse_ho_payload(enum record_type rec_type,
		uint8_t *payload, uint32_t ho_length, size_t frame_length,
		uint8_t ho_mb, uint8_t ho_me, struct near_ndef_message **reply)
{
	struct near_ndef_ho_payload *ho_payload = NULL;
	struct near_ndef_ac_payload *ac = NULL;
	struct near_ndef_mime_payload *mime = NULL;
	struct carrier_data *c_data;
	struct near_ndef_record *trec = NULL;
	GSList *acs = NULL, *mimes = NULL, *c_datas = NULL;
	uint8_t mb = 0, me = 0, i;
	uint32_t offset;
	int16_t count_ac = 0;
	bool action = false, status;

	DBG("");

	if (!payload)
		return NULL;
	offset = 0;

	/* Create the handover record payload */
	ho_payload = g_try_malloc0(sizeof(struct near_ndef_ho_payload));
	if (!ho_payload)
		return NULL;

	/* Version is the first mandatory field of hr payload */
	ho_payload->version = payload[offset];

	/* If major is different, reply with an empty Hs */
	if (HANDOVER_MAJOR(ho_payload->version) !=
	    HANDOVER_MAJOR(HANDOVER_VERSION)) {
		near_error("Unsupported version (%d)", ho_payload->version);
		/* Skip parsing and return an empty record */
		if (reply)
			*reply = near_ndef_prepare_empty_hs_message();

		return ho_payload;
	}

	offset = offset + 1;

	/* We should work on the whole frame */
	ho_length = frame_length;

	while (offset < ho_length) {
		/* Create local record for mime parsing */
		trec = g_try_malloc0(sizeof(struct near_ndef_record));
		if (!trec)
			return NULL;

		trec->header = parse_record_header(payload, offset, ho_length);

		if (!trec->header)
			goto fail;

		offset = trec->header->offset;

		switch (trec->header->rec_type) {
		case RECORD_TYPE_WKT_SMART_POSTER:
		case RECORD_TYPE_WKT_SIZE:
		case RECORD_TYPE_WKT_TEXT:
		case RECORD_TYPE_WKT_TYPE:
		case RECORD_TYPE_WKT_ACTION:
		case RECORD_TYPE_WKT_URI:
		case RECORD_TYPE_WKT_HANDOVER_REQUEST:
		case RECORD_TYPE_WKT_HANDOVER_SELECT:
		case RECORD_TYPE_WKT_ERROR:
		case RECORD_TYPE_EXT_AAR:
		case RECORD_TYPE_UNKNOWN:
		case RECORD_TYPE_ERROR:
			break;

		case RECORD_TYPE_WKT_HANDOVER_CARRIER:
			DBG("HANDOVER_CARRIER");
			/*
			 * TODO process Hc record too !!!
			 * Used for Wifi session
			 */
			break;

		case RECORD_TYPE_MIME_TYPE:
			DBG("TYPE_MIME_TYPE");

			/* check mb/me bits */
			if (validate_record_begin_and_end_bits(&ho_mb, &ho_me,
				trec->header->mb, trec->header->me) != 0) {
				DBG("validate mb me failed");
				goto fail;
			}

			/*
			 * In Handover, the mime type gives bluetooth handover
			 * or WiFi configuration data.
			 * If we initiated the session, the received Hs frame
			 * is the signal to launch the pairing.
			 */
			if (rec_type == RECORD_TYPE_WKT_HANDOVER_SELECT)
				action = true;
			else
				action = false;

			/* HO payload for reply creation */
			trec->ho = ho_payload;

			mime = parse_mime_type(trec, payload, frame_length,
					offset, trec->header->payload_len,
					&c_data);
			trec->ho = NULL;

			if (!mime || !c_data)
				goto fail;

			/* add the mime to the list */
			mimes = g_slist_append(mimes, mime);
			/* add the carrier data to the list */
			c_datas = g_slist_append(c_datas, c_data);

			count_ac--;
			if (count_ac == 0)
				offset = ho_length;
			break;

		case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
			DBG("COLLISION_RESOLUTION");

			/* check nested mb/me bits */
			if (validate_record_begin_and_end_bits(&mb, &me,
				trec->header->mb, trec->header->me) != 0) {
				DBG("validate mb me failed");
				goto fail;
			}

			ho_payload->collision_record =
					near_get_be16(payload + offset);

			break;

		case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
			DBG("ALTERNATIVE_CARRIER");

			/* check nested mb/me bits */
			if (validate_record_begin_and_end_bits(&mb, &me,
				trec->header->mb, trec->header->me) != 0) {
				DBG("validate mb me failed");
				goto fail;
			}

			ac = parse_ac_payload(payload + offset,
					trec->header->payload_len);
			if (!ac)
				goto fail;

			acs = g_slist_append(acs, ac);

			/* TODO check if adata are present */
			count_ac++;
			break;
		}

		offset += trec->header->payload_len;
		g_free(trec->header->il_field);
		g_free(trec->header->type_name);
		g_free(trec->header);
		trec->header = NULL;

		g_free(trec);
		trec = NULL;
	}

	/*
	 * In case of multiple carriers, handover with any carrier
	 * gets done then leave the loop.
	 */
	if (action) {
		status = false;
		count_ac = g_slist_length(mimes);

		for (i = 0; i < count_ac; i++) {
			if (process_mime_type(g_slist_nth_data(mimes, i),
					g_slist_nth_data(c_datas, i)) == 0) {
				status = true;
				break;
			}
		}

		if (!status) {
			DBG("could not process alternative carriers");
			goto fail;
		}
	} else if (reply) {
		/* This is a Hs with no cfg and no Ac: No reply and fail */
		if (rec_type == RECORD_TYPE_WKT_HANDOVER_SELECT &&
					g_slist_length(acs) == 0) {
			*reply = NULL;
			goto fail;
		}

		/* Prepare Hs, it depends upon Hr message carrier types */
		*reply = near_ndef_prepare_hs_reply(mimes, c_datas);
		if (!*reply) {
			DBG("error in preparing in HS record");
			goto fail;
		}
	}

	if ((!acs) || (!mimes))
		return ho_payload;

	/* Save the records */
	if (near_fill_ho_payload(ho_payload, acs, mimes) < 0)
		goto fail;

	DBG("handover payload parsing complete");

	g_slist_free_full(c_datas, g_free);

	return ho_payload;

fail:
	near_error("handover payload parsing failed");

	if (trec) {
		if (trec->header) {
			g_free(trec->header->type_name);
			g_free(trec->header->il_field);
			g_free(trec->header);
		}
		g_free(trec);
	}

	g_slist_free_full(c_datas, g_free);
	free_ho_payload(ho_payload);

	return NULL;
}

static struct near_ndef_aar_payload *
parse_aar_payload(uint8_t *payload, uint32_t length)
{
	struct near_ndef_aar_payload *aar_payload = NULL;

	DBG("");

	if (!payload)
		return NULL;

	aar_payload = g_try_malloc0(sizeof(struct near_ndef_uri_payload));
	if (!aar_payload)
		return NULL;

	aar_payload->package = g_strndup((char *)payload, length);
	if (!aar_payload->package) {
		near_error("AAR payload parsing failed");
		free_aar_payload(aar_payload);
		return NULL;
	}

	DBG("AAR package %s", aar_payload->package);

	return aar_payload;
}

int __near_ndef_record_register(struct near_ndef_record *record, char *path)
{
	switch (record->header->rec_type) {
	case RECORD_TYPE_WKT_TEXT:
	case RECORD_TYPE_WKT_URI:
	case RECORD_TYPE_WKT_SMART_POSTER:
	case RECORD_TYPE_WKT_HANDOVER_REQUEST:
	case RECORD_TYPE_WKT_HANDOVER_SELECT:
	case RECORD_TYPE_WKT_HANDOVER_CARRIER:
	case RECORD_TYPE_MIME_TYPE:
	case RECORD_TYPE_EXT_AAR:
		break;
	default:
		DBG("Unrecognized record type: %u", record->header->rec_type);
		return 0;
	}

	record->path = path;
	g_dbus_register_interface(connection, record->path,
						NFC_RECORD_INTERFACE,
						NULL, NULL, record_properties,
						record, NULL);

	return 0;
}

/*
 * These functions parse a specific type record (id or mime) to find the
 * associated string.
 */
bool near_ndef_record_cmp_id(struct near_ndef_record *rec1,
						struct near_ndef_record *rec2)
{
	DBG("");

	if ((!rec1) || (!rec2))
		return false;

	if ((!rec1->header) || (!rec2->header))
		return false;

	/* usual checks */
	if ((!rec1->header->il_field) ||
			(!rec2->header->il_field))
		return false;

	if (memcmp(rec1->header->il_field, rec2->header->il_field,
		(rec1->header->il_length) > (rec2->header->il_length)
					? (rec1->header->il_length) :
					(rec2->header->il_length)) != 0)
		return false;

	return true;
}

bool near_ndef_record_cmp_mime(struct near_ndef_record *rec1,
					struct near_ndef_record *rec2)
{

	DBG("");

	if ((!rec1) || (!rec2))
		return false;

	if ((!rec1->header) || (!rec2->header))
		return false;
	/* usual checks */
	if ((!rec1->mime) || (!rec2->mime))
		return false;

	if ((!rec1->mime->type) || (!rec2->mime->type))
		return false;

	if (strlen(rec1->mime->type) != strlen(rec2->mime->type))
		return false;

	if ((g_strcmp0(rec1->mime->type, rec2->mime->type) != 0))
		return false;

	return true;
}

/* helper to get the record data length */
size_t near_ndef_data_length(struct near_ndef_record *rec)
{
	if (!rec)
		return 0;
	else
		return rec->data_len;
}

/* helper to get the record data pointer */
uint8_t *near_ndef_data_ptr(struct near_ndef_record *rec)
{
	if (!rec)
		return NULL;
	else
		return rec->data;
}

GList *near_ndef_parse_msg(uint8_t *ndef_data, size_t ndef_length,
				struct near_ndef_message **reply)
{
	GList *records;
	uint8_t p_mb = 0, p_me = 0, *record_start;
	size_t offset = 0;
	struct near_ndef_record *record = NULL;
	struct carrier_data *c_data;

	DBG("");

	records = NULL;

	if (!ndef_data ||
		ndef_length < NDEF_MSG_MIN_LENGTH)
			goto fail;

	while (offset < ndef_length) {

		DBG("Record Header : 0x%X", ndef_data[offset]);

		record = g_try_malloc0(sizeof(struct near_ndef_record));
		if (!record)
			goto fail;

		record->header = parse_record_header(ndef_data, offset,
							ndef_length);
		if (!record->header)
			goto fail;

		if (validate_record_begin_and_end_bits(&p_mb, &p_me,
					record->header->mb,
					record->header->me) != 0) {
			DBG("validate mb me failed");
			goto fail;
		}

		record_start = ndef_data + offset;
		offset = record->header->offset;

		switch (record->header->rec_type) {
		case RECORD_TYPE_WKT_SIZE:
		case RECORD_TYPE_WKT_TYPE:
		case RECORD_TYPE_WKT_ACTION:
		case RECORD_TYPE_WKT_HANDOVER_CARRIER:
		case RECORD_TYPE_WKT_ALTERNATIVE_CARRIER:
		case RECORD_TYPE_WKT_COLLISION_RESOLUTION:
		case RECORD_TYPE_WKT_ERROR:
		case RECORD_TYPE_UNKNOWN:
			break;

		case RECORD_TYPE_WKT_HANDOVER_REQUEST:
		case RECORD_TYPE_WKT_HANDOVER_SELECT:
			/*
			 * Handover frame are a little bit special as the NDEF
			 * length (specified in the header) is not the real
			 * frame size. The complete frame includes extra NDEF
			 * following the initial handover NDEF
			 */
			record->ho = parse_ho_payload(record->header->rec_type,
					ndef_data + offset,
					record->header->payload_len,
					ndef_length - offset,
					record->header->mb, record->header->me,
					reply);
			if (!record->ho)
				goto fail;

			/* the complete frame is processed, break the loop */
			record->header->payload_len = ndef_length;
			break;

		case RECORD_TYPE_WKT_TEXT:
			record->text = parse_text_payload(ndef_data + offset,
						record->header->payload_len);

			if (!record->text)
				goto fail;

			break;

		case RECORD_TYPE_WKT_URI:
			record->uri = parse_uri_payload(ndef_data + offset,
						record->header->payload_len);

			if (!record->uri)
				goto fail;

			break;

		case RECORD_TYPE_WKT_SMART_POSTER:
			record->sp = parse_sp_payload(
						ndef_data + offset,
						record->header->payload_len);

			if (!record->sp)
				goto fail;

			break;

		case RECORD_TYPE_MIME_TYPE:
			record->mime = parse_mime_type(record, ndef_data,
						ndef_length, offset,
						record->header->payload_len,
						&c_data);
			if (!record->mime)
				goto fail;

			/* No carrier data, move on */
			if (!c_data)
				break;

			if (process_mime_type(record->mime, c_data) < 0) {
				g_free(c_data);
				c_data = NULL;
				goto fail;
			}

			g_free(c_data);
			c_data = NULL;
			break;

		case RECORD_TYPE_EXT_AAR:
			record->aar = parse_aar_payload(ndef_data + offset,
						record->header->payload_len);

			if (!record->aar)
				goto fail;

			break;
		case RECORD_TYPE_ERROR:
			goto fail;
		}

		record->data_len = record->header->header_len +
					record->header->payload_len;

		record->data = g_try_malloc0(record->data_len);
		if (!record->data)
			goto fail;

		memcpy(record->data, record_start, record->data_len);

		records = g_list_append(records, record);

		build_record_type_string(record);

		offset += record->header->payload_len;
	}

	return records;

fail:
	near_error("ndef parsing failed");
	free_ndef_record(record);

	return records;
}

void near_ndef_records_free(GList *records)
{
	GList *list;

	for (list = records; list; list = list->next) {
		struct near_ndef_record *record = list->data;

		__near_ndef_record_free(record);
	}

	g_list_free(records);
}

void near_ndef_msg_free(struct near_ndef_message *msg)
{
	if (!msg)
		return;

	g_free(msg->data);
	g_free(msg);
}

/*
 * Compute ndef records length, even though the submitted frame is incomplete.
 * This code is used in the handover read function, as we have to "guess" the
 * final frame size.
 *
 * Message size for SR=1 is:
 *  1 : ndef rec header (offset 0)
 *  x : record type length (offset 1)
 *  y : payload length (offset 2) 1 byte ONLY if SR=1
 *	if SR=0: (4bytes) 32 bits
 *  z : payload id length (offset 3) ONLY if il_length=1
 * */
int near_ndef_record_length(uint8_t *ndef_in, size_t ndef_in_length)
{
	int ndef_size;	 /* default size for NDEF hdr + rec typ len + payl */
	size_t offset;
	uint8_t hdr;

	DBG("");

	if (ndef_in_length < 3)
		return -EINVAL;

	ndef_size = 3;
	offset = 0;

	/* save header byte */
	hdr = ndef_in[offset];
	offset++;

	/* header->type_len */
	ndef_size += ndef_in[offset++];

	/* header->payload_len */
	if (RECORD_SR_BIT(hdr) == 1) {
		ndef_size += ndef_in[offset++];
	} else {
		ndef_size += near_get_be32(ndef_in + offset);
		offset += 4;

		if (offset >= ndef_in_length)
			return -ERANGE;
	}

	/* header->il */
	ndef_size += RECORD_IL_BIT(hdr);

	/* header->il_length */
	if (RECORD_IL_BIT(hdr) == 1)
		ndef_size += ndef_in[offset++];

	DBG("near_ndef_message_length is %d", ndef_size);

	return ndef_size;
}

int near_ndef_count_records(uint8_t *ndef_in, size_t ndef_in_length,
			uint8_t record_type)
{
	uint8_t p_mb = 0, p_me = 0;
	int err;
	size_t offset;
	struct near_ndef_record *record = NULL;
	int counted_records = 0;

	DBG("");

	offset = 0;

	if (!ndef_in ||	ndef_in_length < NDEF_MSG_MIN_LENGTH) {
		err = -EINVAL;
		goto fail;
	}

	while (offset < ndef_in_length) {
		record = g_try_malloc0(sizeof(struct near_ndef_record));
		if (!record) {
			err = -ENOMEM;
			goto fail;
		}

		/* Create a record */
		record->header = parse_record_header(ndef_in, offset,
							ndef_in_length);
		if (!record->header) {
			err = -EINVAL;
			goto fail;
		}

		/* Validate MB ME */
		if (validate_record_begin_and_end_bits(&p_mb, &p_me,
					record->header->mb,
					record->header->me) != 0) {
			DBG("validate mb me failed");
			err = -EINVAL;
			goto fail;
		}

		/* Is this what we want ? */
		if (record->header->rec_type == record_type)
			counted_records++;

		/* Jump to the next record */
		offset = record->header->offset + record->header->payload_len;

		free_ndef_record(record);
	}

	DBG("Type %d Records found: %d", record_type, counted_records);

	return counted_records;

fail:
	near_error("ndef counting failed");
	free_ndef_record(record);
	return err;
}

/* Possible encoding "UTF-8" or "UTF-16" */
struct near_ndef_message *near_ndef_prepare_text_record(char *encoding,
						char *language_code, char *text)
{
	struct near_ndef_message *msg;
	uint32_t text_len, payload_length;
	uint8_t  code_len, status = 0;

	DBG("");

	/* Validate input parameters*/
	if (((g_strcmp0(encoding, "UTF-8") != 0) &&
		 (g_strcmp0(encoding, "UTF-16") != 0)) ||
		 (!language_code) ||
		 (!text)) {
		return NULL;
	}

	code_len = strlen(language_code);
	text_len = strlen(text);
	payload_length = 1 + code_len + text_len;

	msg = ndef_message_alloc("T", payload_length);
	if (!msg)
		return NULL;

	if (g_strcmp0(encoding, "UTF-16") == 0)
		status |= NDEF_TEXT_RECORD_UTF16_STATUS;

	status = status | code_len;
	msg->data[msg->offset++] = status;

	if (code_len > 0)
		memcpy(msg->data + msg->offset, language_code, code_len);

	msg->offset += code_len;

	if (text_len > 0)
		memcpy(msg->data + msg->offset, text, text_len);

	msg->offset += text_len;

	if (msg->offset > msg->length)
		goto fail;

	return msg;

fail:
	near_error("text record preparation failed");
	near_ndef_msg_free(msg);

	return NULL;
}

struct near_ndef_message *near_ndef_prepare_uri_record(uint8_t identifier,
					uint32_t field_length, uint8_t *field)
{
	struct near_ndef_message *msg = NULL;
	uint32_t payload_length;

	DBG("");

	/* Validate input parameters*/
	if ((field_length == 0 && field) ||
		(field_length != 0 && !field)) {
		return NULL;
	}

	payload_length = field_length + 1;

	msg = ndef_message_alloc("U", payload_length);
	if (!msg)
		return NULL;

	msg->data[msg->offset++] = identifier;

	if (field_length > 0) {
		memcpy(msg->data + msg->offset, field, field_length);
		msg->offset += field_length;
	}

	if (msg->offset > msg->length)
		goto fail;

	return msg;

fail:
	near_error("uri record preparation failed");
	near_ndef_msg_free(msg);

	return NULL;
}

struct near_ndef_message *
near_ndef_prepare_smartposter_record(uint8_t uri_identifier,
					uint32_t uri_field_length,
					uint8_t *uri_field)
{
	struct near_ndef_message *msg = NULL, *uri = NULL;

	/* URI is mandatory in Smartposter */
	uri = near_ndef_prepare_uri_record(uri_identifier, uri_field_length,
								uri_field);
	if (!uri)
		goto fail;

	/* URI record length is equal to payload length of Sp record */
	msg = ndef_message_alloc("Sp", uri->length);
	if (!msg)
		goto fail;

	memcpy(msg->data + msg->offset, uri->data, uri->length);
	msg->offset += uri->length;

	if (msg->offset > msg->length)
		goto fail;

	near_ndef_msg_free(uri);

	return msg;

fail:
	near_error("smartposter record preparation failed");

	near_ndef_msg_free(uri);
	near_ndef_msg_free(msg);

	return NULL;
}

static char *get_text_field(DBusMessageIter *msg, char *text)
{
	DBusMessageIter arr_iter;
	char *uri = NULL;

	DBG("");

	if (!text)
		return NULL;

	dbus_message_iter_recurse(msg, &arr_iter);

	while (dbus_message_iter_get_arg_type(&arr_iter) !=
					DBUS_TYPE_INVALID) {
		const char *key;
		DBusMessageIter ent_iter;
		DBusMessageIter var_iter;

		dbus_message_iter_recurse(&arr_iter, &ent_iter);
		dbus_message_iter_get_basic(&ent_iter, &key);
		dbus_message_iter_next(&ent_iter);
		dbus_message_iter_recurse(&ent_iter, &var_iter);

		switch (dbus_message_iter_get_arg_type(&var_iter)) {
		case DBUS_TYPE_STRING:
			if (g_strcmp0(key, text) == 0)
				dbus_message_iter_get_basic(&var_iter, &uri);

			break;
		}

		dbus_message_iter_next(&arr_iter);
	}

	return uri;
}

static inline char *get_uri_field(DBusMessageIter *msg)
{
	return get_text_field(msg, "URI");
}

static GSList *get_carrier_field(DBusMessageIter *msg)
{
	char *carrier;
	char **arr;
	GSList *carriers = NULL;
	uint8_t num_of_carriers, i;

	DBG("");

	carrier = get_text_field(msg, "Carrier");
	if (!carrier)
		return NULL;

	arr = g_strsplit(carrier, ",", NEAR_CARRIER_MAX);
	num_of_carriers = g_strv_length(arr);

	for (i = 0; i < num_of_carriers; i++)
		carriers = g_slist_append(carriers, g_strdup(arr[i]));

	g_strfreev(arr);

	return carriers;
}

static struct near_ndef_message *build_text_record(DBusMessageIter *msg)
{
	DBusMessageIter arr_iter;
	char *cod = NULL, *lang = NULL, *rep = NULL;

	DBG("");

	dbus_message_iter_recurse(msg, &arr_iter);

	while (dbus_message_iter_get_arg_type(&arr_iter) !=
					DBUS_TYPE_INVALID) {
		const char *key;
		DBusMessageIter ent_iter;
		DBusMessageIter var_iter;

		dbus_message_iter_recurse(&arr_iter, &ent_iter);
		dbus_message_iter_get_basic(&ent_iter, &key);
		dbus_message_iter_next(&ent_iter);
		dbus_message_iter_recurse(&ent_iter, &var_iter);

		switch (dbus_message_iter_get_arg_type(&var_iter)) {
		case DBUS_TYPE_STRING:
			if (g_strcmp0(key, "Encoding") == 0)
				dbus_message_iter_get_basic(&var_iter, &cod);
			else if (g_strcmp0(key, "Language") == 0)
				dbus_message_iter_get_basic(&var_iter, &lang);
			else if (g_strcmp0(key, "Representation") == 0)
				dbus_message_iter_get_basic(&var_iter, &rep);

			break;
		}

		dbus_message_iter_next(&arr_iter);
	}

	return near_ndef_prepare_text_record(cod, lang, rep);
}

static struct near_ndef_message *build_uri_record(DBusMessageIter *msg)
{
	char *uri = NULL;
	const char *uri_prefix = NULL;
	uint8_t id_len, i, id;
	uint32_t uri_len;

	DBG("");

	uri = get_uri_field(msg);
	if (!uri)
		return NULL;

	id = 0;
	id_len = 0;

	for (i = 1; i <= NFC_MAX_URI_ID; i++) {
		uri_prefix = __near_ndef_get_uri_prefix(i);

		if (uri_prefix &&
		    g_str_has_prefix(uri, uri_prefix)) {
			id = i;
			id_len = strlen(uri_prefix);
			break;
		}
	}

	DBG("%d %d\n", i, id_len);

	uri_len = strlen(uri) - id_len;
	return near_ndef_prepare_uri_record(id, uri_len,
						(uint8_t *)(uri + id_len));
}

static struct near_ndef_message *build_sp_record(DBusMessageIter *msg)
{
	char *uri = NULL;
	const char *uri_prefix;
	uint8_t id_len, i;
	uint32_t uri_len;

	DBG("");

	/*
	 * Currently this function supports only mandatory URI record,
	 * TODO: Other records support.
	 */
	uri = get_uri_field(msg);
	if (!uri)
		return NULL;

	for (i = 1; i <= NFC_MAX_URI_ID; i++) {
		uri_prefix = __near_ndef_get_uri_prefix(i);

		if (uri_prefix &&
				g_str_has_prefix(uri, uri_prefix))
			break;
	}

	if (!uri_prefix) {
		i = 0;
		id_len = 0;
	} else
		id_len = strlen(uri_prefix);

	uri_len = strlen(uri) - id_len;
	return near_ndef_prepare_smartposter_record(i, uri_len,
						(uint8_t *)(uri + id_len));
}

static struct near_ndef_message *build_ho_record(enum record_type type,
							DBusMessageIter *msg)
{
	struct near_ndef_message *ho;
	GSList *carriers;

	DBG("");

	carriers = get_carrier_field(msg);
	ho = near_ndef_prepare_ho_message(type, carriers);
	g_slist_free_full(carriers, g_free);

	return ho;
}

static int fill_wifi_wsc_data(uint8_t *tlv, uint16_t id,
				uint16_t data_len, uint8_t *data)
{
	int offset = 0;

	if (!tlv || !data)
		return 0;

	fillb16(tlv, id);
	offset += WIFI_WSC_ID_LENGTH;

	fillb16(tlv + offset, data_len);
	offset += WIFI_WSC_ID_DATA_LENGTH;

	memcpy(tlv + offset, data, data_len);
	offset += data_len;

	return offset;
}

static void get_wsc_data(DBusMessageIter iter, char **ssid, char **pass)
{
	DBG("");

	*ssid = NULL;
	*pass = NULL;

	while (dbus_message_iter_get_arg_type(&iter) !=
					DBUS_TYPE_INVALID) {
		const char *key;
		DBusMessageIter ent_iter;
		DBusMessageIter var_iter;

		dbus_message_iter_recurse(&iter, &ent_iter);
		dbus_message_iter_get_basic(&ent_iter, &key);
		dbus_message_iter_next(&ent_iter);
		dbus_message_iter_recurse(&ent_iter, &var_iter);

		switch (dbus_message_iter_get_arg_type(&var_iter)) {
		case DBUS_TYPE_STRING:
			if (g_strcmp0(key, "SSID") == 0)
				dbus_message_iter_get_basic(&var_iter, ssid);
			else if (g_strcmp0(key, "Passphrase") == 0)
				dbus_message_iter_get_basic(&var_iter, pass);

			break;
		}

		dbus_message_iter_next(&iter);
	}
}

struct near_ndef_message *near_ndef_prepare_wsc_record(char *ssid,
							char *passphrase)
{
	uint16_t ssid_len, pass_len, key_type;
	uint8_t temp_key[2];
	uint8_t *tlv;
	uint32_t tlv_len, offset;
	struct near_ndef_message *mime;

	/* At least SSID is required in case of open network */
	if (!ssid)
		return NULL;

	DBG("SSID %s Passphrase %s", ssid, passphrase);

	/* Prepare TLV from ssid and passphrasse */
	ssid_len = strlen(ssid);

	if (passphrase) {
		pass_len = strlen(passphrase);
		key_type = WIFI_WSC_KEY_PSK;
	} else {
		pass_len = 0;
		key_type = WIFI_WSC_KEY_OPEN;
	}

	/* add ssid length */
	tlv_len = WIFI_WSC_ID_LENGTH + WIFI_WSC_ID_DATA_LENGTH + ssid_len;
	/* add authentication type length */
	tlv_len += WIFI_WSC_ID_LENGTH + WIFI_WSC_ID_DATA_LENGTH + 2;
	/* add network key length */
	if (passphrase)
		tlv_len += WIFI_WSC_ID_LENGTH +
				WIFI_WSC_ID_DATA_LENGTH + pass_len;

	tlv = g_try_malloc0(tlv_len);
	if (!tlv)
		return NULL;

	offset = 0;
	/* copy SSID */
	offset += fill_wifi_wsc_data(tlv, WIFI_WSC_ID_SSID,
					ssid_len, (uint8_t *) ssid);

	/*
	 * copy authentication type
	 * copy key type to temp key array to maintain endianess
	 * and fill wsc data
	 */
	fillb16(temp_key, key_type);
	offset += fill_wifi_wsc_data(tlv + offset, WIFI_WSC_ID_AUTH_TYPE,
						WIFI_WSC_ID_DATA_LENGTH,
						(uint8_t *) temp_key);

	/* copy Network Key */
	if (passphrase)
		offset += fill_wifi_wsc_data(tlv + offset, WIFI_WSC_ID_KEY,
						pass_len,
						(uint8_t *) passphrase);

	mime = ndef_message_alloc_complete(WIFI_WSC_MIME_STRING, tlv_len, NULL,
						0, RECORD_TNF_MIME, true,
					   true);
	if (!mime) {
		g_free(tlv);
		return NULL;
	}

	memcpy(mime->data + mime->offset, tlv, tlv_len);
	g_free(tlv);

	return mime;
}

static char *get_mime_payload_data(DBusMessageIter iter,
				char **payload, int *payload_len)
{
	DBG("");

	if (!payload || !payload_len) {
		near_error("Payload %p payload_len %p", payload, payload_len);
		return NULL;
	}

	while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) {
		const char *key;
		DBusMessageIter ent_iter;
		DBusMessageIter var_iter;
		DBusMessageIter arr_iter;

		dbus_message_iter_recurse(&iter, &ent_iter);
		dbus_message_iter_get_basic(&ent_iter, &key);
		dbus_message_iter_next(&ent_iter);
		dbus_message_iter_recurse(&ent_iter, &var_iter);

		if (g_strcmp0(key, "Payload") == 0 ||
				g_strcmp0(key, "MIMEPayload") == 0) {
			if (dbus_message_iter_get_arg_type(&var_iter) ==
							DBUS_TYPE_ARRAY &&
			    dbus_message_iter_get_element_type(&var_iter) ==
							DBUS_TYPE_BYTE) {
					dbus_message_iter_recurse(&var_iter,
								 &arr_iter);
					dbus_message_iter_get_fixed_array(
							&arr_iter, payload,
							payload_len);
			} else {
				near_error("Unexpected payload type");
				return NULL;
			}
		}
		dbus_message_iter_next(&iter);
	}

	return *payload;
}

static struct near_ndef_message *near_ndef_prepare_mime_payload_record(
				char *type, char *payload, int payload_len)
{
	struct near_ndef_message *mime;

	DBG("Payload %*s", payload_len, payload);
	mime = ndef_message_alloc_complete(type, payload_len, NULL, 0,
						RECORD_TNF_MIME, true, true);
	if (!mime) {
		near_error("Failed to alloc NDEF message");
		return NULL;
	}

	memcpy(mime->data + mime->offset, payload, payload_len);

	return mime;
}

static struct near_ndef_message *build_mime_record(DBusMessageIter *msg)
{
	DBusMessageIter iter, arr_iter;
	char *key, *mime_str, *ssid, *passphrase;

	DBG("");

	dbus_message_iter_recurse(msg, &arr_iter);

	while (dbus_message_iter_get_arg_type(&arr_iter) !=
					DBUS_TYPE_INVALID) {
		DBusMessageIter ent_iter;
		DBusMessageIter var_iter;

		dbus_message_iter_recurse(&arr_iter, &ent_iter);
		dbus_message_iter_get_basic(&ent_iter, &key);

		if (g_strcmp0(key, "MIME") == 0) {
			dbus_message_iter_next(&ent_iter);
			dbus_message_iter_recurse(&ent_iter, &var_iter);
			dbus_message_iter_get_basic(&var_iter, &mime_str);

			if (g_strcmp0(mime_str, WIFI_WSC_MIME_STRING) == 0) {
				struct near_ndef_message *mime;
				struct carrier_data *carrier;

				get_wsc_data(arr_iter, &ssid, &passphrase);
				if (ssid)
					return near_ndef_prepare_wsc_record(
							ssid, passphrase);

				/*
				 * If we did not get an SSID and optionally
				 * a passphrase from the DBus message, then
				 * we try to get one from the WiFi-WSC agent.
				 */
				carrier = __near_agent_handover_request_data(
							HO_AGENT_WIFI, NULL);
				if (!carrier)
					return NULL;

				mime = ndef_message_alloc_complete(
					WIFI_WSC_MIME_STRING, carrier->size,
					NULL, 0, RECORD_TNF_MIME, true, true);
				if (!mime) {
					g_free(carrier);
					return NULL;
				}

				memcpy(mime->data + mime->offset,
					carrier->data, carrier->size);

				g_free(carrier);

				return mime;
			} else {
				/*
				 * Expect data is set in the Payload field of
				 * message.
				 */
				DBusMessageIter payload_iter;
				char *payload;
				int payload_len;

				DBG("mime string %s", mime_str);
				dbus_message_iter_recurse(&iter, &payload_iter);
				if (!get_mime_payload_data(payload_iter,
							&payload, &payload_len))
					return NULL;

				return near_ndef_prepare_mime_payload_record(
					mime_str, payload, payload_len);
			}
		}

		dbus_message_iter_next(&arr_iter);
	}

	return NULL;
}

static gboolean near_dbus_message_iter_recurse_to_signature(
					DBusMessageIter *iter, char *sig)
{
	gboolean found = FALSE;
	int type;

	while ((type = dbus_message_iter_get_arg_type(iter))
			!= DBUS_TYPE_INVALID) {

		if (g_strcmp0(sig, dbus_message_iter_get_signature(iter))
				== 0) {
			found = TRUE;
			break;
		}

		if (dbus_type_is_container(type))
			dbus_message_iter_recurse(iter, iter);
		else if (dbus_message_iter_has_next(iter))
			dbus_message_iter_next(iter);
		else break;
	}

	DBG("sig %s %s", sig, found ? "found" : "not found");

	return found;
}

static gboolean near_dbus_message_with_multiple_records(DBusMessage *msg)
{
	DBusMessageIter iter;
	char *s;

	dbus_message_iter_init(msg, &iter);

	near_dbus_message_iter_recurse_to_signature(&iter, "s");

	dbus_message_iter_get_basic(&iter, &s);

	return g_strcmp0(s, "Records") == 0;
}

static gboolean near_dbus_message_get_records(DBusMessage *msg,
						DBusMessageIter *iter)
{
	gboolean found = FALSE;

	dbus_message_iter_init(msg, iter);
again:
	if (near_dbus_message_iter_recurse_to_signature(iter, "a{sv}")) {

		if (msg && near_dbus_message_with_multiple_records(msg)) {

			near_dbus_message_iter_recurse_to_signature(iter, "v");

			msg = NULL;

			goto again;
		}

		found = TRUE;
	}

	return found;
}

static struct near_ndef_message *build_multi_record_message(
				DBusMessageIter *iter, const char *type)
{
	struct near_ndef_message *ndef = NULL;

	DBG("");

	if (g_strcmp0(type, "SmartPoster") == 0)
		ndef = build_sp_record(iter);
	else if (g_strcmp0(type, "Handover") == 0)
		ndef = build_ho_record(RECORD_TYPE_WKT_HANDOVER_REQUEST, iter);
	else if (g_strcmp0(type, "StaticHandover") == 0)
		ndef = build_ho_record(RECORD_TYPE_WKT_HANDOVER_SELECT, iter);

	return ndef;
}

static struct near_ndef_message *ndef_build_from_record(DBusMessageIter *msg,
							bool begin, bool end)
{
	struct near_ndef_message *ndef = NULL;
	DBusMessageIter arr_iter;

	DBG("");

	dbus_message_iter_recurse(msg, &arr_iter);

	while (dbus_message_iter_get_arg_type(&arr_iter) !=
						DBUS_TYPE_INVALID) {
		const char *key, *value;
		DBusMessageIter ent_iter;
		DBusMessageIter var_iter;

		dbus_message_iter_recurse(&arr_iter, &ent_iter);
		dbus_message_iter_get_basic(&ent_iter, &key);

		if (g_strcmp0(key, "Type") != 0) {
			dbus_message_iter_next(&arr_iter);
			continue;
		}

		dbus_message_iter_next(&ent_iter);
		dbus_message_iter_recurse(&ent_iter, &var_iter);

		switch (dbus_message_iter_get_arg_type(&var_iter)) {
		case DBUS_TYPE_STRING:
			dbus_message_iter_get_basic(&var_iter, &value);

			if (g_strcmp0(value, "Text") == 0) {
				ndef = build_text_record(msg);
				break;
			} else if (g_strcmp0(value, "URI") == 0) {
				ndef = build_uri_record(msg);
				break;
			} else if (g_strcmp0(value, "MIME") == 0) {
				ndef = build_mime_record(msg);
				break;
			} else if ((begin && end) &&
					(g_strcmp0(value, "SmartPoster") == 0 ||
					g_strcmp0(value, "Handover") == 0 ||
					g_strcmp0(value, "StaticHandover")
						== 0)) {
				/*
				 * Currently SmartPoster and Handover messages
				 * are supported only in the single record mode
				 * of the tag.write() method.
				 */
				ndef = build_multi_record_message(msg, value);
				break;
			} else {
				near_error("%s not supported", value);
				ndef = NULL;
				break;
			}

			break;
		}

		dbus_message_iter_next(&arr_iter);
	}

	return ndef;
}

static gboolean ndef_message_append(struct near_ndef_message *to,
					struct near_ndef_message *from)
{
	gboolean ret = TRUE;
	uint8_t *data = g_try_realloc(to->data, to->length + from->length);

	if (!data) {
		near_error("Memory re-allocation failed (NDEF message data)");
		ret = FALSE;
		goto exit;
	}

	to->data = data;

	memcpy(to->data + to->length, from->data, from->length);

	to->offset = to->length + from->offset;

	to->length += from->length;
exit:
	return ret;
}

struct near_ndef_message *__ndef_build_from_message(DBusMessage *msg)
{
	struct near_ndef_message *ndef, *record;
	DBusMessageIter iter;
	bool begin = TRUE, end = FALSE;

	DBG("");

	if (!near_dbus_message_get_records(msg, &iter)) {
		near_error("No records found");
		ndef = NULL;
		goto exit;
	}

	for (ndef = NULL ;; dbus_message_iter_next(&iter)) {

		end = !dbus_message_iter_has_next(&iter);

		record = ndef_build_from_record(&iter, begin, end);
		if (!record)
			goto err;

		near_ndef_set_mb_me(record->data, begin, end);

		if (!ndef) {
			ndef = record;
			goto next;
		}

		if (!ndef_message_append(ndef, record))
			goto err;

		near_ndef_msg_free(record);
	next:
		if (end)
			break;

		begin = FALSE;
	}
exit:
	return ndef;
err:
	near_ndef_msg_free(ndef);
	near_ndef_msg_free(record);
	ndef = NULL;
	goto exit;
}

int __near_ndef_init(void)
{
	DBG("");

	connection = near_dbus_get_connection();

	return 0;
}

void __near_ndef_cleanup(void)
{
}
