diff options
| -rw-r--r-- | hcid/dbus-sdp.c | 1125 | 
1 files changed, 588 insertions, 537 deletions
| diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c index 2cfd687c..fe3ad04a 100644 --- a/hcid/dbus-sdp.c +++ b/hcid/dbus-sdp.c @@ -50,18 +50,17 @@  #include "hcid.h"  #include "textfile.h" -#define SDP_UUID_SEQ_SIZE 256 -#define SDP_MAX_ATTR_LEN 65535 - +#define MAX_IDENTIFIER_LEN	24  struct service_provider {  	char *owner;	/* null for remote services or unique name if local */ -	bdaddr_t prov;	/* remote Bluetooth address or local address */ +	char *prov;	/* remote Bluetooth address thar provides the service  */ +	int ttl;	/* time to live */  	struct slist *lrec;  };  struct service_record { -	uint32_t identifier; +	int id;  	sdp_record_t *record;  }; @@ -69,59 +68,114 @@ struct pending_connect {  	DBusConnection *conn;  	DBusMessage *rq;  	char *svc; -	bdaddr_t dba; +	char *dst;  };  struct transaction_context {  	DBusConnection *conn;  	DBusMessage *rq; -	char *svc;  	sdp_session_t *session; -	sdp_cstate_t *cstate; -	uint8_t *reqbuf; -	bdaddr_t dba; -	sdp_buf_t rspbuf; -	uint32_t reqsize; -	int attr_list_len; +}; +/* FIXME:  move to a common file */ +typedef struct { +	char            *name; +	uint16_t        class; +	char            *info_name; +} sdp_service_t; + +/* FIXME:  move to a common file */ +sdp_service_t sdp_service[] = { +	{ "vcp",	VIDEO_CONF_SVCLASS_ID,		NULL			}, +	{ "map",	0,				NULL			}, +	{ "pbap",	0,				NULL			}, +	{ "sap",	SAP_SVCLASS_ID,			"SIM Access"		}, +	{ "ftp",	OBEX_FILETRANS_SVCLASS_ID,	"OBEX File Transfer"	}, +	{ "bpp",	DIRECT_PRINTING_SVCLASS_ID,	"Direct Printing"	}, +	{ "bip",	0,				NULL			}, +	{ "synch",	0,				NULL			}, +	{ "dun",	DIALUP_NET_SVCLASS_ID,		"Dial-Up Networking"	}, +	{ "opp",	OBEX_OBJPUSH_SVCLASS_ID,	"OBEX Object Push"	}, +	{ "fax",	FAX_SVCLASS_ID,			"Fax"			}, +	{ "spp",	SERIAL_PORT_SVCLASS_ID,		"Serial Port"		}, +	{ NULL }  }; -/* FIXME: store the arguments or just the pointer to the message */ +/* FIXME:  move to a common file */ +uint16_t sdp_str2svclass(const char *str) +{ +	sdp_service_t *s; -/* list of remote and local service records - * FIXME: free the cache when the local sock(sdpd) is closed - */ +	for (s = sdp_service; s->name; s++) { +		if (strcasecmp(s->name, str) == 0) +			return s->class; +	} -static struct slist *sdp_cache = NULL; -static struct slist *pending_connects  = NULL; +	return 0; +} -static const char *ecode2str(uint16_t ecode) +/* FIXME:  move to a common file */ +const char* sdp_svclass2str(uint16_t class)  { -	switch (ecode) { -	case 0x0000: -		return "Reserved"; -	case 0x0001: -		return "Invalid/Unsupported SDP version"; -	case 0x0002: -		return "Invalid Service Record Handle"; -	case 0x0003: -		return "Invalid request syntax"; -	case 0x0004: -		return "Invalid PDU size"; -	case 0x0005: -		return "Invalid Continuation State"; -	case 0x0006: -		return "Insufficient Resources to satisfy Request"; -	default: -		return "Reserved"; +	sdp_service_t *s; + +	for (s = sdp_service; s->name; s++) { +		if (s->class == class) +			return s->name;  	} + +	return NULL;  } +/* FIXME: stub for bluez-libs changes */ +static inline sdp_session_t *sdp_session_new(int sk) +{ +	errno = ENOSYS; +	return NULL; +} + +/* FIXME: stub for bluez-libs changes */ +static inline int sdp_service_search_handle(sdp_session_t *session) +{ +	return ENODATA; +} + +/* FIXME: stub for bluez-libs changes */ +typedef void sdp_transaction_cb_t(sdp_list_t *rsp, void *udata, int err); +static inline int sdp_service_search_attr_req_async(sdp_session_t *session, const sdp_list_t *search, +		sdp_transaction_cb_t *f, void *udata) +{ +	return 0; +} + +/* FIXME: stub for service registration. Shared with sdptool */ +static inline sdp_record_t *sdp_service_register(const char *name, bdaddr_t *interface, +		                                   uint8_t channel, int *err) +{ +	if (err) +		*err = ENOSYS; + +	return NULL; +} + +/* FIXME: stub for service registration. Shared with sdptool */ +static inline int sdp_service_unregister(bdaddr_t *interface, sdp_record_t *rec, int *err) +{ +	if (err) +		*err = ENOSYS; + +	return -1; +} + +/* list of remote and local service records */ +static struct slist *sdp_cache = NULL; +static struct slist *pending_connects  = NULL; +  static struct pending_connect *pending_connect_new(DBusConnection *conn, DBusMessage *msg, -							const bdaddr_t *bda, const char *svc) +							const char *dst, const char *svc)  {  	struct pending_connect *c; -	if (!bda) +	if (!dst)  		return NULL;  	c = malloc(sizeof(*c)); @@ -131,18 +185,26 @@ static struct pending_connect *pending_connect_new(DBusConnection *conn, DBusMes  	if (svc) {  		c->svc = strdup(svc);  		if (!c->svc) -			goto failed; +			goto fail; +	} +	if (dst) { +		c->dst = strdup(dst); +		if (!c->dst) +			goto fail;  	} -	bacpy(&c->dba, bda);  	c->conn = dbus_connection_ref(conn);  	c->rq = dbus_message_ref(msg);  	return c; -failed: -	if (c) -		free(c); +fail: +	if (c->svc) +		free(c->svc); +	if (c->dst) +		free(c->dst); +	free(c); +  	return NULL;  } @@ -163,237 +225,120 @@ static void pending_connect_free(struct pending_connect *c)  	free(c);  } -static struct pending_connect *find_pending_connect(const bdaddr_t *bda) +static struct pending_connect *find_pending_connect(const char *dst)  {  	struct slist *l;  	for (l = pending_connects; l != NULL; l = l->next) {  		struct pending_connect *pending = l->data; -		if (!bacmp(bda, &pending->dba)) +		if (!strcmp(dst, pending->dst))  			return pending;  	}  	return NULL;  } -/* FIXME: duplicated function. Make this function public on bluez-libs */ -static int gen_dataseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dtd) -{ -	sdp_data_t *dataseq; -	void **types, **values; -	sdp_buf_t buf; -	int i, seqlen = sdp_list_len(seq); - -	// Fill up the value and the dtd arrays -	memset(&buf, 0, sizeof(sdp_buf_t)); -	buf.data = malloc(SDP_UUID_SEQ_SIZE); -	buf.buf_size = SDP_UUID_SEQ_SIZE; - -	types = malloc(seqlen * sizeof(void *)); -	values = malloc(seqlen * sizeof(void *)); -	for (i = 0; i < seqlen; i++) { -		void *data = seq->data; -		types[i] = &dtd; -		if (SDP_IS_UUID(dtd)) -			data = &((uuid_t *)data)->value; -		values[i] = data; -		seq = seq->next; -	} - -	dataseq = sdp_seq_alloc(types, values, seqlen); -	seqlen = sdp_gen_pdu(&buf, dataseq); -	memcpy(dst, buf.data, buf.data_size); -	sdp_data_free(dataseq); - -	free(types); -	free(values); -	free(buf.data); -	return seqlen; -} - -/* FIXME: duplicated function */ -static int gen_searchseq_pdu(uint8_t *dst, const sdp_list_t *seq) -{ -	uuid_t *uuid = (uuid_t *) seq->data; -	return gen_dataseq_pdu(dst, seq, uuid->type); -} - -/* FIXME: duplicated function */ -static int gen_attridseq_pdu(uint8_t *dst, const sdp_list_t *seq, uint8_t dataType) -{ -	return gen_dataseq_pdu(dst, seq, dataType); -} - -struct transaction_context *transaction_context_new(DBusConnection *conn, DBusMessage *msg, bdaddr_t *dba, -							const char *svc, int sock, uint32_t flags) -{ -	struct transaction_context *ctxt; -	sdp_pdu_hdr_t *reqhdr; -	sdp_list_t *pattern = NULL; -	sdp_list_t *attrids = NULL; -	uint8_t *pdata; -	uuid_t uuid; -	uint32_t range = 0x0000ffff; -	int seqlen; - -	ctxt = malloc(sizeof(*ctxt)); -	if (!ctxt) -		return NULL; - -	memset(ctxt, 0, sizeof(*ctxt)); - -	if (svc) { -		ctxt->svc = strdup(svc); -		if (!ctxt->svc) -			goto failed; -	} - -	if (dba) -		bacpy(&ctxt->dba, dba); - -	ctxt->session = malloc(sizeof(sdp_session_t)); -	if (!ctxt->session) -		goto failed; - -	memset(ctxt->session, 0, sizeof(sdp_session_t)); - -	ctxt->conn = dbus_connection_ref(conn); -	ctxt->rq = dbus_message_ref(msg); -	ctxt->session->sock = sock; -	ctxt->session->flags = flags; - -	ctxt->reqbuf = malloc(SDP_REQ_BUFFER_SIZE); -	if (!ctxt->reqbuf) -		goto failed; - -	memset(ctxt->reqbuf, 0, SDP_REQ_BUFFER_SIZE); - -	reqhdr = (sdp_pdu_hdr_t *) ctxt->reqbuf; - -	reqhdr->pdu_id = SDP_SVC_SEARCH_ATTR_REQ;  -	reqhdr->tid = 0; - -	// Generate PDU -	pdata = ctxt->reqbuf + sizeof(sdp_pdu_hdr_t); -	ctxt->reqsize = sizeof(sdp_pdu_hdr_t); - -	/* FIXME: it should be generic to handle other kind of search requests */ -	sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); -	pattern = sdp_list_append(0, &uuid); -	attrids = sdp_list_append(0, &range); - -	seqlen = gen_searchseq_pdu(pdata, pattern); -	 -	// set the length and increment the pointer -	ctxt->reqsize += seqlen; -	pdata +=seqlen; - -	bt_put_unaligned(htons(SDP_MAX_ATTR_LEN), (uint16_t *) pdata); -	ctxt->reqsize += sizeof(uint16_t); -	pdata += sizeof(uint16_t); - -	if (attrids) { -		seqlen = gen_attridseq_pdu(pdata, attrids, SDP_UINT32); -		if (seqlen == -1) -			goto failed; - -	} -	if (pattern) -		sdp_list_free(pattern, 0); -	if (attrids) -		sdp_list_free(attrids, 0); - -	pdata += seqlen; -	ctxt->reqsize += seqlen; - -	reqhdr->plen = htons(ctxt->reqsize - sizeof(sdp_pdu_hdr_t)); - -	return ctxt; - -failed: -	if (ctxt->session) -		free(ctxt->session); -	if (ctxt->reqbuf) -		free(ctxt->reqbuf); -	free(ctxt); -	 -	return NULL; -} - -void transaction_context_free(struct transaction_context *ctxt) +static int str2identifier(const char *identifier, char *address, int *id)  { -	if (!ctxt) -		return; - -	if (ctxt->conn) -		dbus_connection_unref(ctxt->conn); - -	if (ctxt->rq) -		dbus_message_unref(ctxt->rq); - -	if (ctxt->svc) -		free(ctxt->svc); +	if (!identifier || !address) +		return -1; -	if (ctxt->session) -		free(ctxt->session); +	if (strlen(identifier) < 19) +		return -1; -	if (ctxt->reqbuf) -		free(ctxt->reqbuf); +	memset(address, 0, 18); -	if (ctxt->rspbuf.data) -		free(ctxt->rspbuf.data); +	snprintf(address, 18, "%s", identifier); -	free(ctxt); +	return (sscanf(identifier + 18, "%d", id) > 0? 0: -1);  } -/* FIXME: generate the pseudo random id */ -static uint32_t gen_next_id(const bdaddr_t *prov, uint32_t handle) +static inline int gen_next_record_id()  { -	static uint32_t id; -	return ++id; +	static int id = 0; +	return id++;  } -static struct service_record *service_record_new(const bdaddr_t *prov, sdp_record_t *rec) +static struct service_record *service_record_new(sdp_record_t *rec)  {  	struct service_record *r; -	 -	if (!prov) -		return NULL;  	r = malloc(sizeof(*r));  	if (!r)  		return NULL;  	memset(r, 0, sizeof(*r)); -	r->identifier = gen_next_id(prov, rec->handle); +	r->id = gen_next_record_id();  	r->record = rec;  	return r;  } -static void service_record_free(struct service_record *r, void *data) +static void service_record_free(void *data, void *udata)  { +	struct service_record *r = data; +  	if (!r)  		return; -	sdp_record_free(r->record); +	if (r->record) +		sdp_record_free(r->record); +  	free(r);  } -static void service_provider_free(struct service_provider *p) +/* + * This function doesn't check service record pattern + */ +static int service_record_cmp(const void *data, const void *udata)  { -	if (p->owner) -		free(p->owner); +	const struct service_record *a = data; +	const struct service_record *b = udata; +	if (b->id >= 0) { +		if (a->id != b->id) +			return -1; +	} -	if (p->lrec) { -		slist_foreach(p->lrec, (slist_func_t)service_record_free, NULL); -		slist_free(p->lrec); +	if (b->record) { +		if (b->record->handle != 0xffffffff && +			b->record->handle != a->record->handle) +				return -1;  	} -	free(p); +	return 0;  } -static struct service_provider *service_provider_new(const char *owner, const bdaddr_t *prov) +static void service_provider_free(void *data, void *udata) +{ +	struct service_provider *p1 = data; +	struct service_provider *p2 = udata; + +	if (!p1) +		return; + +	/* Check if the provider match */ +	if (p2) { +		if (p2->owner && strcmp(p2->owner, p1->owner)) +			return; +		if (p2->prov && strcmp(p2->prov, p1->prov)) +			return; +	} + +	if (p1->owner) +		free(p1->owner); + +	if (p1->prov) +		free(p1->prov); + +	if (p1->lrec) { +		slist_foreach(p1->lrec, service_record_free, NULL); +		slist_free(p1->lrec); +	} + +	free(p1); +} + +static struct service_provider *service_provider_new(const char *owner, const char *prov)  {  	struct service_provider *p; @@ -411,19 +356,25 @@ static struct service_provider *service_provider_new(const char *owner, const bd  			goto fail;  	} -	bacpy(&p->prov, prov); +	if (prov) { +		p->prov = strdup(prov); +		if (!p->prov) +			goto fail; +	}  	return p;  fail: -	service_provider_free(p); +	service_provider_free(p, NULL);  	return NULL;  } -static int service_provider_cmp(const struct service_provider *a, const struct service_provider *b) +static int service_provider_cmp(const void *data, const void *udata)  { +	const struct service_provider *a = data; +	const struct service_provider *b = udata;  	int ret; -	 +  	if (b->owner) {  		if (!a->owner)  			return -1; @@ -432,10 +383,10 @@ static int service_provider_cmp(const struct service_provider *a, const struct s  			return ret;  	} -	if (bacmp(&b->prov, BDADDR_ANY)) { -		if (!bacmp(&a->prov, BDADDR_ANY)) +	if (b->prov) { +		if (!a->prov)  			return -1; -		ret = bacmp(&a->prov, &b->prov); +		ret = strcmp(a->prov, b->prov);  		if (ret)  			return ret;  	} @@ -443,158 +394,98 @@ static int service_provider_cmp(const struct service_provider *a, const struct s  	return 0;  } -static uint32_t sdp_cache_append(const char *owner, const bdaddr_t *prov, sdp_record_t *rec) +static int sdp_cache_append(const char *owner, const char *prov, sdp_record_t *rec)  { -	struct slist *l; +	struct slist *lp, *lr;  	struct service_provider *p; -	struct service_provider *ref; +	struct service_provider psearch;  	struct service_record *r; -	 -	if (!prov || !rec) -		return 0; -	ref = service_provider_new(owner, prov); -	if (!ref) -		return 0; +	if (!prov || !rec) +		return -1; -	l = slist_find(sdp_cache, (const void*)ref, (cmp_func_t)service_provider_cmp); -	if (!l) { +	memset(&psearch, 0, sizeof(psearch)); +	psearch.owner = (char *)owner; +	psearch.prov = (char *)prov; +	 +	lp = slist_find(sdp_cache, &psearch, service_provider_cmp); +	if (!lp) {  		p = service_provider_new(owner, prov);  		sdp_cache = slist_append(sdp_cache, p);  	} else -		p = l->data; - -	r = service_record_new(prov, rec); -	p->lrec = slist_append(p->lrec, r); +		p = lp->data; -	if (ref) -		service_provider_free(ref); +	/* check if there is service record already belongs to the cache */ +	r = malloc(sizeof(*r)); +	memset(r, 0, sizeof(*r)); +	r->id = -1; +	r->record = sdp_record_alloc(); +	r->record->handle = rec->handle; +	lr = slist_find(p->lrec, r, service_record_cmp); +	service_record_free(r, NULL); + +	if (lr) { +		/* overwrite the record instead of compare */ +		r = lr->data; +		sdp_record_free(r->record); +		r->record = rec; +	} else { +		/* create a new entry */ +		r = service_record_new(rec); +		p->lrec = slist_append(p->lrec, r); +	} -	return r->identifier; +	return r->id;  }  static void owner_exited(const char *owner, struct hci_dbus_data *dbus_data)  { -	struct slist *cur, *next; - +	struct slist *lp, *next, *lr; +	struct service_provider *p; +	bdaddr_t sba; +	int err = 0;  	debug("SDP provider owner %s exited", owner); -	for (cur = sdp_cache; cur != NULL; cur = next) { -		struct service_provider *p = cur->data; - -		next = cur->next; +	for (lp = sdp_cache; lp; lp = next) { -		if(!p->owner) -			continue; - -		if (strcmp(p->owner, owner)) +		next = lp->next; +		p = lp->data; +		 +		if (!p->owner || strcmp(p->owner, owner))  			continue; -		sdp_cache = slist_remove(sdp_cache, p); -		service_provider_free(p); -	} -} - -/* FIXME: duplicated function */ -static int copy_cstate(uint8_t *pdata, const sdp_cstate_t *cstate) -{ -	if (cstate) { -		*pdata++ = cstate->length; -		memcpy(pdata, cstate->data, cstate->length); -		return cstate->length + 1; -	} -	*pdata = 0; -	return 1; -} - -static int sdp_send_req(struct transaction_context *ctxt, int *err) -{ -	sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) ctxt->reqbuf; -	uint32_t sent = 0; -	uint32_t reqsize; - -	reqhdr->tid = htons(sdp_gen_tid(ctxt->session)); -	reqsize = ctxt->reqsize + copy_cstate(ctxt->reqbuf + ctxt->reqsize, ctxt->cstate); -	reqhdr->plen = htons(reqsize - sizeof(sdp_pdu_hdr_t)); - -	while (sent < reqsize) { -		int n = send(ctxt->session->sock, ctxt->reqbuf + sent, reqsize - sent, 0); -		if (n < 0) { -			*err = errno; -			return -1; +		/* +		 * Unregister all service records related to this owner. +		 * One owner can use multiple local adapter(provider) +		 */ +		str2ba(dbus_data->address, &sba); +		for (lr = p->lrec; lr; lr = lr->next) { +			struct service_record *r = lr->data; +			if (sdp_service_unregister(&sba, r->record, &err) < 0) +				error("unregister error: %s (%d)", strerror(err), err); +			else +				/* free inside the library */ +				r->record = NULL;  		} -		sent += n; -	} -	return 0; -} - -static DBusMessage *parse_response(struct transaction_context *ctxt) -{ -	DBusMessage *reply = NULL; -	DBusMessageIter iter; -	DBusMessageIter array_iter; -	int scanned, seqlen; -	uint8_t dataType; -	uint8_t *pdata; -	const char *owner; - -	owner = dbus_message_get_sender(ctxt->rq); - -	reply = dbus_message_new_method_return(ctxt->rq); - -	if ((ctxt->attr_list_len <= 0) || (ctxt->rspbuf.data_size == 0)) -		return dbus_message_new_error(ctxt->rq, ERROR_INTERFACE ".DoesNotExist", -						"Record does not exist"); - -	dbus_message_iter_init_append(reply, &iter); -	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, -			DBUS_TYPE_UINT32_AS_STRING, &array_iter); - -	pdata = ctxt->rspbuf.data; - -	scanned = sdp_extract_seqtype(pdata, &dataType, &seqlen); - -	if (scanned && seqlen) { -		pdata += scanned; -		do { -			uint32_t id; -			int recsize = 0; -			sdp_record_t *rec = sdp_extract_pdu(pdata, &recsize); -			if (rec == NULL) -				break; - -			if (!recsize) { -				sdp_record_free(rec); -				break; -			} - -			scanned += recsize; -			pdata += recsize; - -			id = sdp_cache_append(owner, &ctxt->dba, rec); -			dbus_message_iter_append_basic(&array_iter, -					DBUS_TYPE_UINT32, &id); +		/* remove from the cache */ +		sdp_cache = slist_remove(sdp_cache, p); -		} while (scanned < ctxt->attr_list_len); +		service_provider_free(p, NULL);  	} - -	dbus_message_iter_close_container(&iter, &array_iter); - -	return reply;  } -static gboolean svc_search_attr_req_cb(GIOChannel *chan, GIOCondition cond, struct transaction_context *ctxt) +static gboolean search_session_handle_cb(GIOChannel *chan, GIOCondition cond, void *udata)  { -	int sk, err, n; -	uint32_t rsp_count; -	gboolean ret_val = FALSE; +	struct transaction_context *ctxt = udata; +	int sk, err = 0;  	socklen_t len; -	uint8_t cstate_len; -	uint8_t *pdata; -	uint8_t *rsp = NULL; -	sdp_pdu_hdr_t *reqhdr = (sdp_pdu_hdr_t *) ctxt->reqbuf; -	sdp_pdu_hdr_t *rsphdr; +	const char *dst; +	int retval = FALSE; + +	dbus_message_get_args(ctxt->rq, NULL, +			DBUS_TYPE_STRING, &dst, +			DBUS_TYPE_INVALID);  	sk = g_io_channel_unix_get_fd(chan); @@ -602,84 +493,96 @@ static gboolean svc_search_attr_req_cb(GIOChannel *chan, GIOCondition cond, stru  	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {  		error("getsockopt(): %s, (%d)", strerror(errno), errno); -		goto failed; +		error_failed(ctxt->conn, ctxt->rq, errno); +		goto fail;  	}  	if (err != 0) { -		error("connect(): %s(%d)", strerror(err), err); -		error_connection_attempt_failed(ctxt->conn, ctxt->rq, err); -		goto failed; +		error_failed(ctxt->conn, ctxt->rq, err); +		error("sock error:s(%d)", strerror(err), err); +		goto fail;  	} +	if (!sdp_service_search_handle(ctxt->session)) +		return TRUE; -	rsp = malloc(SDP_RSP_BUFFER_SIZE); -	memset(rsp, 0, SDP_RSP_BUFFER_SIZE); +fail: +	g_io_channel_close(chan); +	return retval; +} -	n = recv(sk, rsp, SDP_RSP_BUFFER_SIZE, 0); -	if (n <= 0) { -		err = errno; -		goto failed; -	} +static void search_transaction_completed_cb(sdp_list_t *rsp, struct transaction_context *ctxt, int err) +{ +	char identifier[MAX_IDENTIFIER_LEN]; +	const char *ptr = identifier; +	DBusMessage *reply; +	DBusMessageIter iter, array_iter; +	sdp_list_t *next; +	const char *dst; +	int id; -	rsphdr = (sdp_pdu_hdr_t *)rsp; -	if (n == 0 || reqhdr->tid != rsphdr->tid) { -		err = EPROTO; -		goto failed; +	if (!ctxt) +		return; +		 +	if (err) { +		error_failed(ctxt->conn, ctxt->rq, err); +		return;  	} -	pdata = rsp + sizeof(sdp_pdu_hdr_t); +	dbus_message_get_args(ctxt->rq, NULL, +			DBUS_TYPE_STRING, &dst, +			DBUS_TYPE_INVALID); -	if (rsphdr->pdu_id == SDP_ERROR_RSP) { -		uint16_t ecode = ntohs(bt_get_unaligned((uint16_t *) pdata)); -		error("Received SDP error response PDU: %s (%d)", ecode2str(ecode), ecode); -		err = EPROTO; -		goto failed; -	} +	reply = dbus_message_new_method_return(ctxt->rq); +	dbus_message_iter_init_append(reply, &iter); +	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, +			DBUS_TYPE_STRING_AS_STRING, &array_iter); -	rsp_count = ntohs(bt_get_unaligned((uint16_t *) pdata)); -	ctxt->attr_list_len += rsp_count; -	pdata += sizeof(uint16_t); +	/* add in the cache */ +	for ( ;rsp; rsp = next) { +		sdp_record_t *rec = (sdp_record_t *) rsp->data; -	// if continuation state set need to re-issue request before parsing -	cstate_len = *(uint8_t *) (pdata + rsp_count); +		id = sdp_cache_append(NULL, dst, rec); +		snprintf(identifier, MAX_IDENTIFIER_LEN, "%s/%d", dst, id); +		dbus_message_iter_append_basic(&array_iter, +				DBUS_TYPE_STRING, &ptr); -	if (rsp_count > 0) { -		uint8_t *targetPtr = NULL; +		next = rsp->next; +		free(rsp); +		/* Don't free the record */ +	} -		ctxt->cstate = cstate_len > 0 ? (sdp_cstate_t *) (pdata + rsp_count) : 0; +	dbus_message_iter_close_container(&iter, &array_iter); +	send_reply_and_unref(ctxt->conn, reply); +} -		// build concatenated response buffer -		ctxt->rspbuf.data = realloc(ctxt->rspbuf.data, ctxt->rspbuf.data_size + rsp_count); -		ctxt->rspbuf.buf_size = ctxt->rspbuf.data_size + rsp_count; -		targetPtr = ctxt->rspbuf.data + ctxt->rspbuf.data_size; -		memcpy(targetPtr, pdata, rsp_count); -		ctxt->rspbuf.data_size += rsp_count; -	} +static void transaction_context_free(void *udata) +{ +	struct transaction_context *ctxt = udata; -	if (ctxt->cstate) { -		if (!sdp_send_req(ctxt, &err)) -			ret_val = TRUE; -	} else { -		/* parse the response PDU */ -		send_reply_and_unref(ctxt->conn, parse_response(ctxt)); -	} -failed: -	if (rsp) -		free(rsp); +	if (!ctxt) +		return; -	if (err) { -		error_failed(ctxt->conn, ctxt->rq, err); -		error("SDP transaction error: %s (%d)", strerror(err), err); -	} +	if (ctxt->conn) +		dbus_connection_unref(ctxt->conn); + +	if (ctxt->rq) +		dbus_message_unref(ctxt->rq); -	return ret_val; +	if (ctxt->session) +		sdp_close(ctxt->session); + +	free(ctxt);  } -static gboolean sdp_client_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *c) +static gboolean sdp_client_connect_cb(GIOChannel *chan, GIOCondition cond, void *udata)  { -	int err, sk = 0; +	struct pending_connect *c = udata; +	struct transaction_context *ctxt = NULL; +	sdp_list_t *search = NULL; +	uint16_t class; +	int err = 0, sk = 0;  	socklen_t len; -	GIOChannel *tchan; -	struct transaction_context *ctxt; +	uuid_t uuid;  	sk = g_io_channel_unix_get_fd(chan); @@ -687,33 +590,62 @@ static gboolean sdp_client_connect_cb(GIOChannel *chan, GIOCondition cond, struc  	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {  		error("getsockopt(): %s, (%d)", strerror(errno), errno); -		goto failed; +		error_connection_attempt_failed(c->conn, c->rq, errno); +		goto fail;  	}  	if (err != 0) {  		error("connect(): %s(%d)", strerror(err), err);  		error_connection_attempt_failed(c->conn, c->rq, err); -		goto failed; +		goto fail;  	} -	ctxt = transaction_context_new(c->conn, c->rq, &c->dba, c->svc, sk, 0); - +	ctxt = malloc(sizeof(*ctxt));  	if (!ctxt) {  		error_failed(c->conn, c->rq, ENOMEM); -		goto failed; +		goto fail;  	} +	memset(ctxt, 0, sizeof(*ctxt)); -	tchan = g_io_channel_unix_new(sk); +	/* FIXME: the sdp search list pattern should be created outside */ +	if (!c->svc) { +		sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); +		search = sdp_list_append(0, &uuid); +	} else { +		class = sdp_str2svclass(c->svc); +		sdp_uuid16_create(&uuid, class); +		search = sdp_list_append(0, &uuid); +	} -	g_io_add_watch_full(tchan, 0, G_IO_IN, (GIOFunc)svc_search_attr_req_cb, -			ctxt, (GDestroyNotify)transaction_context_free); +	ctxt->conn = dbus_connection_ref(c->conn); +	ctxt->rq = dbus_message_ref(c->rq); +	ctxt->session = sdp_session_new(sk); +	if (!ctxt->session) { +		error("error: %s (%d)", strerror(errno), errno); +		error_failed(c->conn, c->rq, errno); +		goto fail; +	} -	if (sdp_send_req(ctxt, &err) < 0) { -		error("Can't send PDU: %s (%d)", strerror(err), err); -		error_failed(c->conn, c->rq, err); -		goto failed; +	/* Create/send the search request and set the callback to indicate the request completion */ +	if (sdp_service_search_attr_req_async(ctxt->session, search, +			(sdp_transaction_cb_t *)search_transaction_completed_cb, ctxt) < 0) { +		error("send request failed: %s (%d)", strerror(errno), errno); +		error_failed(c->conn, c->rq, errno); +		goto fail;  	} -failed: + +	/* set the callback responsible for update the transaction data */ +	g_io_add_watch_full(chan, 0, G_IO_IN, search_session_handle_cb, +				ctxt, transaction_context_free); +	goto done; + +fail: +	if (ctxt) +		transaction_context_free(ctxt); +done: +	if (search) +		sdp_list_free(search, 0); +  	pending_connects = slist_remove(pending_connects, c);  	pending_connect_free(c); @@ -725,11 +657,11 @@ static int search_request(DBusConnection *conn, DBusMessage *msg, uint16_t dev_i  {  	struct pending_connect *c = NULL;  	GIOChannel *chan = NULL; -	bdaddr_t sba;  	struct sockaddr_l2 sa;  	int sk, watch = 0;  	// create L2CAP connection +	/* FIXME: use set_nonblocking */  	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);  	if (sk < 0) {  		if (err) @@ -743,21 +675,18 @@ static int search_request(DBusConnection *conn, DBusMessage *msg, uint16_t dev_i  	sa.l2_family = AF_BLUETOOTH;  	sa.l2_psm = 0; -	hci_devba(dev_id, &sba); +	hci_devba(dev_id, &sa.l2_bdaddr); -	if (bacmp(&sba, BDADDR_ANY) != 0) { -		bacpy(&sa.l2_bdaddr, &sba); -		if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) { -			if (err) -				*err = errno; -			goto fail; -		} +	if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) { +		if (err) +			*err = errno; +		goto fail;  	}  	sa.l2_psm = htobs(SDP_PSM);  	str2ba(dst, &sa.l2_bdaddr); -	c = pending_connect_new(conn, msg, &sa.l2_bdaddr, svc); +	c = pending_connect_new(conn, msg, dst, svc);  	if (!c) {  		if (err)  			*err = ENOMEM; @@ -766,7 +695,7 @@ static int search_request(DBusConnection *conn, DBusMessage *msg, uint16_t dev_i  	fcntl(sk, F_SETFL, fcntl(sk, F_GETFL, 0)|O_NONBLOCK);  	if (connect(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) { -		if ( !(errno == EAGAIN || errno == EINPROGRESS)) { +		if (!(errno == EAGAIN || errno == EINPROGRESS)) {  			if (err)  				*err = errno;  			error("connect() failed:%s (%d)", strerror(errno), errno); @@ -774,7 +703,7 @@ static int search_request(DBusConnection *conn, DBusMessage *msg, uint16_t dev_i  		}  		watch = g_io_add_watch(chan, G_IO_OUT, -				(GIOFunc)sdp_client_connect_cb, c); +					sdp_client_connect_cb, c);  		pending_connects = slist_append(pending_connects, c);  	} else {  		sdp_client_connect_cb(chan, G_IO_OUT, c); @@ -796,60 +725,59 @@ static DBusHandlerResult get_identifiers(DBusConnection *conn,  {  	char filename[PATH_MAX + 1];  	struct hci_dbus_data *dbus_data = data; -	struct service_provider *p; -	struct slist *l;  	const char *dst;  	char *str; -	DBusMessage *reply; -	DBusMessageIter iter, array_iter; -	bdaddr_t dba;  	int err = 0;  	if (!dbus_message_get_args(msg, NULL,  			DBUS_TYPE_STRING, &dst,  			DBUS_TYPE_INVALID))  		return error_invalid_arguments(conn, msg); -	 -	/* FIXME: validate Bluetooth address(dst) */ - -	str2ba(dst, &dba); -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	if (find_pending_connect(dst)) +		return error_service_search_in_progress(conn, msg); -	p = service_provider_new(NULL, &dba); -	if (!p) { -		dbus_message_unref(reply); -		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	} -	 -	l = slist_find(sdp_cache, p, (cmp_func_t)service_provider_cmp); -	service_provider_free(p); +	/* check if it is a unknown address */ +	snprintf(filename, PATH_MAX, "%s/%s/lastseen", STORAGEDIR, dbus_data->address); -	if (l) { -		struct slist *lr; -		struct service_record *r; +	str = textfile_get(filename, dst); +	if (!str) +		return error_unknown_address(conn, msg); -		/* check the cache */ -		dbus_message_iter_init_append(reply, &iter); -		dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, -				DBUS_TYPE_UINT32_AS_STRING, &array_iter); +	free(str); -		p = l->data; -		for (lr = p->lrec; lr; lr = lr->next) { -			r = lr->data; -			dbus_message_iter_append_basic(&array_iter, -					DBUS_TYPE_UINT32, &r->identifier); -		} +	if (search_request(conn, msg, dbus_data->dev_id, dst, NULL, &err) < 0) { +		error("Search request failed: %s (%d)", strerror(err), err); +		return error_failed(conn, msg, err); +	} -		dbus_message_iter_close_container(&iter, &array_iter); +	return DBUS_HANDLER_RESULT_HANDLED; +} -		return send_reply_and_unref(conn, reply); -	} +static DBusHandlerResult get_identifiers_by_service(DBusConnection *conn, +						DBusMessage *msg, void *data) +{ +	char filename[PATH_MAX + 1]; +	struct hci_dbus_data *dbus_data = data; +	DBusMessage *reply; +	DBusMessageIter iter, array_iter; +	struct slist *lp; +	struct slist *lr; +	struct service_provider *p; +	struct service_record *r; +	char identifier[MAX_IDENTIFIER_LEN]; +	const char *ptr = identifier; +	const char *dst, *svc; +	char *str; +	int err = 0, nrec = 0; +	uint32_t class; +	uuid_t uuid; -	if (find_pending_connect(&dba)) -		return error_service_search_in_progress(conn, msg); +	if (!dbus_message_get_args(msg, NULL, +			DBUS_TYPE_STRING, &dst, +			DBUS_TYPE_STRING, &svc, +			DBUS_TYPE_INVALID)) +		return error_invalid_arguments(conn, msg);  	/* check if it is a unknown address */  	create_name(filename, PATH_MAX, STORAGEDIR, dbus_data->address, "lastseen"); @@ -860,21 +788,74 @@ static DBusHandlerResult get_identifiers(DBusConnection *conn,  	free(str); -	if (search_request(conn, msg, dbus_data->dev_id, dst, NULL, &err) < 0) { +	class = sdp_str2svclass(svc); +	if (!class) { +		error("Invalid service class name"); +		return error_invalid_arguments(conn, msg); +	} + +	sdp_uuid16_create(&uuid, class); + +	p = service_provider_new(NULL, dst); +	if (!p) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; + +	/* FIXME: return cache entry or query again? */ +	lp = slist_find(sdp_cache, p, service_provider_cmp); +	service_provider_free(p, NULL); + +	if (!lp) +		goto search_request; + +	reply = dbus_message_new_method_return(msg); +	dbus_message_iter_init_append(reply, &iter); +	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, +			DBUS_TYPE_STRING_AS_STRING, &array_iter); + +	p = lp->data; +	for (lr = p->lrec; lr; lr = lr->next) { +		sdp_list_t *ls; +		uuid_t *puuid; +		r = lr->data; +		/* check if the pattern match */ +		if (sdp_get_service_classes(r->record, &ls)) +			continue; + +		puuid = (uuid_t *)ls->data; + +		if (sdp_uuid16_cmp(puuid, &uuid) == 0) { +			snprintf(identifier, MAX_IDENTIFIER_LEN, "%s/%d", p->prov, r->id); +			dbus_message_iter_append_basic(&array_iter, +					DBUS_TYPE_STRING, &ptr); +			nrec++; +		} + +		sdp_list_free(ls, free); + +	} + +	dbus_message_iter_close_container(&iter, &array_iter); + +	if (nrec > 0) +		return send_reply_and_unref(conn, reply); + +	/* no record found: request search */ +	dbus_message_unref(reply); + +search_request: +	if (find_pending_connect(dst)) +		return error_service_search_in_progress(conn, msg); + +	if (search_request(conn, msg, dbus_data->dev_id, dst, svc, &err) < 0) {  		error("Search request failed: %s (%d)", strerror(err), err);  		return error_failed(conn, msg, err);  	}  	return DBUS_HANDLER_RESULT_HANDLED; -} -static DBusHandlerResult get_identifiers_by_service(DBusConnection *conn, -						DBusMessage *msg, void *data) -{ -	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;  } -sdp_record_t *find_record(uint32_t identifier) +static sdp_record_t *find_record(const char *address, int id)  {  	struct slist *lp, *lr;  	struct service_provider *p; @@ -882,9 +863,12 @@ sdp_record_t *find_record(uint32_t identifier)  	for (lp = sdp_cache; lp; lp = lp->next) {  		p = lp->data; +		if (strcmp(p->prov, address)) +			continue; +  		for (lr = p->lrec; lr; lr = lr->next) {  			r = lr->data; -			if (r->identifier == identifier) +			if (r->id == id)  				return r->record;  		}  	} @@ -896,18 +880,23 @@ static DBusHandlerResult get_uuid(DBusConnection *conn,  					 DBusMessage *msg, void *data)  {  	char uuid_str[MAX_LEN_UUID_STR]; -	sdp_list_t *l; +	char address[18]; +	sdp_list_t *ls;  	DBusMessage *reply;  	sdp_record_t *rec;  	char *ptr = uuid_str; -	uint32_t identifier; +	const char *identifier; +	int id;  	if (!dbus_message_get_args(msg, NULL, -				DBUS_TYPE_UINT32, &identifier, +				DBUS_TYPE_STRING, &identifier,  				DBUS_TYPE_INVALID))  		return error_invalid_arguments(conn, msg); -	rec = find_record(identifier); +	if (str2identifier(identifier, address, &id) != 0) +		return error_invalid_arguments(conn, msg); + +	rec = find_record(address, id);  	if (!rec)  		return error_record_does_not_exist(conn, msg); @@ -915,11 +904,16 @@ static DBusHandlerResult get_uuid(DBusConnection *conn,  	reply = dbus_message_new_method_return(msg); -	if (sdp_get_profile_descs(rec, &l) == 0) { -		sdp_profile_desc_t *desc = (sdp_profile_desc_t *)l->data; +	if (sdp_get_service_classes(rec, &ls) == 0) { +		char tmp_str[MAX_LEN_UUID_STR]; +		uuid_t *uuid = (uuid_t *)ls->data; -		sdp_uuid2strn(&desc->uuid, uuid_str, MAX_LEN_UUID_STR); -		sdp_list_free(l, free); +		if (sdp_uuid2strn(uuid, tmp_str, MAX_LEN_UUID_STR) != 0) +			error("Can't convert UUID to string!"); +		else +			snprintf(uuid_str, MAX_LEN_UUID_STR, "0x%s", tmp_str); + +		sdp_list_free(ls, free);  	}  	dbus_message_append_args(reply, @@ -932,27 +926,43 @@ static DBusHandlerResult get_uuid(DBusConnection *conn,  static DBusHandlerResult get_name(DBusConnection *conn,  		DBusMessage *msg, void *data)  { +	char address[18];  	DBusMessage *reply;  	sdp_record_t *rec; -	sdp_data_t *d; +	sdp_list_t *ls;  	char name[] = ""; -	char *ptr = name; -	uint32_t identifier; +	const char *ptr = name; +	const char *identifier; +	uuid_t *puuid; +	int id;  	if (!dbus_message_get_args(msg, NULL, -				DBUS_TYPE_UINT32, &identifier, +				DBUS_TYPE_STRING, &identifier,  				DBUS_TYPE_INVALID))  		return error_invalid_arguments(conn, msg); -	rec = find_record(identifier); +	if (str2identifier(identifier, address, &id) != 0) +		return error_invalid_arguments(conn, msg); + +	rec = find_record(address, id);  	if (!rec)  		return error_record_does_not_exist(conn, msg); -	reply = dbus_message_new_method_return(msg); -	d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY); -	if (d && d->val.str) -		ptr = d->val.str; +	if ((sdp_get_service_classes(rec, &ls)) < 0) { +		return error_failed(conn, msg, errno); +	} + +	puuid = (uuid_t *)ls->data; +	ptr = sdp_svclass2str(puuid->value.uuid16); +	sdp_list_free(ls, free); + +	/* return empty string for non supported services */ +	if (!ptr) +		ptr = name; + +	/* FIXME: it should return the service name attribute instead of the short service name */ +	reply = dbus_message_new_method_return(msg);  	dbus_message_append_args(reply,  			DBUS_TYPE_STRING, &ptr,  			DBUS_TYPE_INVALID); @@ -964,11 +974,14 @@ static DBusHandlerResult register_rfcomm(DBusConnection *conn,  		DBusMessage *msg, void *data)  {  	struct hci_dbus_data *dbus_data = data; -	struct service_provider ref; +	struct service_provider psearch;  	DBusMessage *reply; +	sdp_record_t *rec;  	const char *owner, *name; +	char identifier[MAX_IDENTIFIER_LEN]; +	const char *ptr = identifier;  	bdaddr_t sba; -	uint32_t identifier = 0, handle = 0; +	int id = 0, err = 0;  	uint8_t channel;  	owner = dbus_message_get_sender(msg); @@ -979,39 +992,51 @@ static DBusHandlerResult register_rfcomm(DBusConnection *conn,  			DBUS_TYPE_INVALID))  		return error_invalid_arguments(conn, msg); -	/* FIXME: register the service */ - -	hci_devba(dbus_data->dev_id, &sba); -	identifier = gen_next_id(&sba, handle); -  	reply = dbus_message_new_method_return(msg);  	if (!reply)  		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	dbus_message_append_args(msg, -			DBUS_TYPE_UINT32, &identifier, -			DBUS_TYPE_INVALID); +	str2ba(dbus_data->address, &sba); +	/* register service */ +	if (!(rec = sdp_service_register(name, &sba, channel, &err))) { +		dbus_message_unref(reply); +		error("service register error: %s (%d)", strerror(err), err); +		if (err == EINVAL) +			return error_invalid_arguments(conn, msg); +		else +			return error_failed(conn, msg, err); +	}  	/* Only add a D-Bus unique name listener if there isn't one already registered */ -	memset(&ref, 0, sizeof(ref)); -	bacpy(&ref.prov, BDADDR_ANY); +	memset(&psearch, 0, sizeof(psearch)); +	psearch.owner = (char *)owner; -	if (!slist_find(sdp_cache, &ref, (cmp_func_t)service_provider_cmp)) +	if (!slist_find(sdp_cache, &psearch, service_provider_cmp))  		name_listener_add(conn, owner, (name_cb_t)owner_exited, dbus_data); -	/* FIXME: register the RFCOMM service */ -	 +	/* add record in the cache */ +	id = sdp_cache_append(owner, dbus_data->address, rec); + +	snprintf(identifier, MAX_IDENTIFIER_LEN, "%s/%d", dbus_data->address, id); +	dbus_message_append_args(reply, +			DBUS_TYPE_STRING, &ptr, +			DBUS_TYPE_INVALID); +  	return send_reply_and_unref(conn, reply);  }  static DBusHandlerResult unregister_rfcomm(DBusConnection *conn,  						DBusMessage *msg, void *data)  { +	char address[18];  	struct hci_dbus_data *dbus_data = data; -	struct service_provider *p, ref; -	struct slist *match; +	struct service_provider *p, psearch; +	struct service_record rsearch, *r; +	struct slist *lp, *lr;  	DBusMessage *reply;  	const char *owner, *identifier; +	bdaddr_t sba; +	int id = 0, err = 0;  	owner = dbus_message_get_sender(msg); @@ -1020,34 +1045,55 @@ static DBusHandlerResult unregister_rfcomm(DBusConnection *conn,  			DBUS_TYPE_INVALID))  		return error_invalid_arguments(conn, msg); -	memset(&ref, 0, sizeof(ref)); -	 -	hci_devba(dbus_data->dev_id, &ref.prov); -	ref.owner = (char *) owner; +	if (str2identifier(identifier, address, &id) != 0) +		return error_invalid_arguments(conn, msg); + +	/* check if the local adapter match */ +	if (strcmp(address, dbus_data->address)) +		return error_not_authorized(conn, msg); -	match = slist_find(sdp_cache, &ref, (cmp_func_t)service_provider_cmp); -	if (!match) +	memset(&psearch, 0, sizeof(psearch)); + +	psearch.prov = address; +	psearch.owner = (char *)owner; + +	lp = slist_find(sdp_cache, &psearch, service_provider_cmp); +	if (!lp)  		return error_service_does_not_exist(conn, msg); -	/* FIXME: find the RFCOMM UUID in the list */ -	p = match->data; -	 -	if (strcmp(p->owner, owner)) -		return error_not_authorized(conn, msg); +	p = lp->data; + +	memset(&rsearch, 0, sizeof(rsearch)); +	rsearch.id = id; +	str2ba(dbus_data->address, &sba); +	lr = slist_find(p->lrec, &rsearch, service_record_cmp); +	if (!lr) +		return error_service_does_not_exist(conn, msg);  	reply = dbus_message_new_method_return(msg);  	if (!reply)  		return DBUS_HANDLER_RESULT_NEED_MEMORY; -	/* FIXME: unregister the service */ +	r = lr->data; +	if (sdp_service_unregister(&sba, r->record, &err) < 0) +		error("service unregister error: %s (%d)", strerror(err), err); +	else +		r->record = NULL; -	sdp_cache = slist_remove(sdp_cache, p); -	service_provider_free(p); +	/* Remove the service record */ +	service_record_free(r, NULL); +	p->lrec = slist_remove(p->lrec, r); -	bacpy(&ref.prov, BDADDR_ANY); +	/* if the service record is empty remove the provider */ +	if (!p->lrec) { +		sdp_cache = slist_remove(sdp_cache, p); +		service_provider_free(p, NULL); +	} + +	psearch.prov = NULL;  	/* Only remove the D-Bus unique name listener if there are no more record using this name */ -	if (!slist_find(sdp_cache, &ref, (cmp_func_t)service_provider_cmp)) +	if (!slist_find(sdp_cache, &psearch, service_provider_cmp))  		name_listener_remove(conn, owner, (name_cb_t)owner_exited, dbus_data);  	return send_reply_and_unref(conn, reply); @@ -1077,3 +1123,8 @@ DBusHandlerResult handle_sdp_method(DBusConnection *conn, DBusMessage *msg, void  	return error_unknown_method(conn, msg);  } + +void dbus_sdp_cache_free() +{ +	slist_foreach(sdp_cache, service_provider_free, NULL); +} | 
