| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY comes with ABSOLUTELY NO WARRANTY. |
| * |
| * This is free software, placed under the terms of the |
| * GNU Lesser General Public License, as published by the Free Software |
| * Foundation; either version 2.1 of the License, or (at your option) any |
| * later version. Please see the file LICENSE-LGPL for details. |
| * |
| * Web Page: http://brltty.app/ |
| * |
| * This software is maintained by Dave Mielke <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <bluetooth/bluetooth.h> |
| #include <bluetooth/hci.h> |
| #include <bluetooth/hci_lib.h> |
| #include <bluetooth/l2cap.h> |
| #include <bluetooth/sdp.h> |
| #include <bluetooth/sdp_lib.h> |
| #include <bluetooth/rfcomm.h> |
| |
| #include "log.h" |
| #include "parameters.h" |
| #include "io_bluetooth.h" |
| #include "bluetooth_internal.h" |
| #include "async_handle.h" |
| #include "async_io.h" |
| #include "io_misc.h" |
| #include "timing.h" |
| |
| struct BluetoothConnectionExtensionStruct { |
| SocketDescriptor socketDescriptor; |
| struct sockaddr_rc localAddress; |
| struct sockaddr_rc remoteAddress; |
| AsyncHandle inputMonitor; |
| }; |
| |
| typedef union { |
| unsigned char hciEvent[HCI_MAX_EVENT_SIZE]; |
| |
| struct { |
| unsigned char type; |
| |
| union { |
| struct { |
| hci_event_hdr header; |
| |
| union { |
| evt_remote_name_req_complete rn; |
| evt_cmd_complete cc; |
| evt_cmd_status cs; |
| } data; |
| } PACKED hciEvent; |
| } data; |
| } PACKED fields; |
| } BluetoothPacket; |
| |
| static void |
| bthMakeAddress (bdaddr_t *address, uint64_t bda) { |
| unsigned int index; |
| |
| for (index=0; index<BDA_SIZE; index+=1) { |
| address->b[index] = bda & 0XFF; |
| bda >>= 8; |
| } |
| } |
| |
| BluetoothConnectionExtension * |
| bthNewConnectionExtension (uint64_t bda) { |
| BluetoothConnectionExtension *bcx; |
| |
| if ((bcx = malloc(sizeof(*bcx)))) { |
| memset(bcx, 0, sizeof(*bcx)); |
| |
| bcx->localAddress.rc_family = AF_BLUETOOTH; |
| bcx->localAddress.rc_channel = 0; |
| bacpy(&bcx->localAddress.rc_bdaddr, BDADDR_ANY); /* Any HCI. No support for explicit |
| * interface specification yet. |
| */ |
| |
| bcx->remoteAddress.rc_family = AF_BLUETOOTH; |
| bcx->remoteAddress.rc_channel = 0; |
| bthMakeAddress(&bcx->remoteAddress.rc_bdaddr, bda); |
| |
| bcx->socketDescriptor = INVALID_SOCKET_DESCRIPTOR; |
| return bcx; |
| } else { |
| logMallocError(); |
| } |
| |
| return NULL; |
| } |
| |
| static void |
| bthCancelInputMonitor (BluetoothConnectionExtension *bcx) { |
| if (bcx->inputMonitor) { |
| asyncCancelRequest(bcx->inputMonitor); |
| bcx->inputMonitor = NULL; |
| } |
| } |
| |
| void |
| bthReleaseConnectionExtension (BluetoothConnectionExtension *bcx) { |
| bthCancelInputMonitor(bcx); |
| closeSocket(&bcx->socketDescriptor); |
| free(bcx); |
| } |
| |
| static int |
| bthGetConnectLogLevel (int error) { |
| switch (error) { |
| case EHOSTUNREACH: |
| case EHOSTDOWN: |
| return LOG_CATEGORY(BLUETOOTH_IO); |
| |
| default: |
| return LOG_ERR; |
| } |
| } |
| |
| int |
| bthOpenChannel (BluetoothConnectionExtension *bcx, uint8_t channel, int timeout) { |
| bcx->remoteAddress.rc_channel = channel; |
| |
| if ((bcx->socketDescriptor = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) != -1) { |
| setCloseOnExec(bcx->socketDescriptor, 1); |
| |
| if (bind(bcx->socketDescriptor, (struct sockaddr *)&bcx->localAddress, sizeof(bcx->localAddress)) != -1) { |
| if (setBlockingIo(bcx->socketDescriptor, 0)) { |
| int connectResult = LINUX_BLUETOOTH_CHANNEL_CONNECT_ASYNCHRONOUS? |
| connectSocket(bcx->socketDescriptor, |
| (struct sockaddr *)&bcx->remoteAddress, |
| sizeof(bcx->remoteAddress), |
| timeout): |
| connect(bcx->socketDescriptor, |
| (struct sockaddr *)&bcx->remoteAddress, |
| sizeof(bcx->remoteAddress)); |
| |
| if (connectResult != -1) return 1; |
| logSystemProblem(bthGetConnectLogLevel(errno), "RFCOMM connect"); |
| } |
| } else { |
| logSystemError("RFCOMM bind"); |
| } |
| |
| setSocketNoLinger(bcx->socketDescriptor); |
| close(bcx->socketDescriptor); |
| bcx->socketDescriptor = INVALID_SOCKET_DESCRIPTOR; |
| } else { |
| logSystemError("RFCOMM socket"); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| bthFindChannel (uint8_t *channel, sdp_record_t *record) { |
| int foundChannel = 0; |
| int stopSearching = 0; |
| sdp_list_t *protocolsList; |
| |
| if (!(sdp_get_access_protos(record, &protocolsList))) { |
| sdp_list_t *protocolsElement = protocolsList; |
| |
| while (protocolsElement) { |
| sdp_list_t *protocolList = (sdp_list_t *)protocolsElement->data; |
| sdp_list_t *protocolElement = protocolList; |
| |
| while (protocolElement) { |
| sdp_data_t *dataList = (sdp_data_t *)protocolElement->data; |
| sdp_data_t *dataElement = dataList; |
| int uuidProtocol = 0; |
| |
| while (dataElement) { |
| if (SDP_IS_UUID(dataElement->dtd)) { |
| uuidProtocol = sdp_uuid_to_proto(&dataElement->val.uuid); |
| } else if (dataElement->dtd == SDP_UINT8) { |
| if (uuidProtocol == RFCOMM_UUID) { |
| *channel = dataElement->val.uint8; |
| foundChannel = 1; |
| stopSearching = 1; |
| } |
| } |
| |
| if (stopSearching) break; |
| dataElement = dataElement->next; |
| } |
| |
| if (stopSearching) break; |
| protocolElement = protocolElement->next; |
| } |
| |
| sdp_list_free(protocolList, NULL); |
| if (stopSearching) break; |
| protocolsElement = protocolsElement->next; |
| } |
| |
| sdp_list_free(protocolsList, NULL); |
| } else { |
| logSystemError("sdp_get_access_protos"); |
| } |
| |
| return foundChannel; |
| } |
| |
| static SocketDescriptor |
| bthNewL2capConnection (const bdaddr_t *address, int timeout) { |
| SocketDescriptor socketDescriptor = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); |
| |
| if (socketDescriptor != -1) { |
| setCloseOnExec(socketDescriptor, 1); |
| |
| if (setBlockingIo(socketDescriptor, 0)) { |
| struct sockaddr_l2 socketAddress = { |
| .l2_family = AF_BLUETOOTH, |
| .l2_bdaddr = *address, |
| .l2_psm = htobs(SDP_PSM) |
| }; |
| |
| int connectResult = connectSocket(socketDescriptor, |
| (struct sockaddr *)&socketAddress, |
| sizeof(socketAddress), |
| timeout); |
| |
| if (connectResult != -1) return socketDescriptor; |
| logSystemProblem(bthGetConnectLogLevel(errno), "L2CAP connect"); |
| } |
| |
| setSocketNoLinger(socketDescriptor); |
| close(socketDescriptor); |
| } else { |
| logSystemError("L2CAP socket"); |
| } |
| |
| return INVALID_SOCKET_DESCRIPTOR; |
| } |
| |
| typedef struct { |
| sdp_session_t *session; |
| int *found; |
| uint8_t *channel; |
| } BluetoothChannelDiscoveryData; |
| |
| static void |
| bthHandleChannelDiscoveryResponse ( |
| uint8_t type, uint16_t status, |
| uint8_t *response, size_t size, |
| void *data |
| ) { |
| BluetoothChannelDiscoveryData *bcd = data; |
| |
| switch (status) { |
| case 0: |
| switch (type) { |
| case SDP_SVC_SEARCH_ATTR_RSP: { |
| uint8_t *nextByte = response; |
| int bytesLeft = size; |
| |
| uint8_t dtd = 0; |
| int dataLeft = 0; |
| int headerLength = sdp_extract_seqtype(nextByte, bytesLeft, &dtd, &dataLeft); |
| |
| if (headerLength > 0) { |
| nextByte += headerLength; |
| bytesLeft -= headerLength; |
| |
| while (dataLeft > 0) { |
| int stop = 0; |
| int recordLength = 0; |
| sdp_record_t *record = sdp_extract_pdu(nextByte, bytesLeft, &recordLength); |
| |
| if (record) { |
| if (bthFindChannel(bcd->channel, record)) { |
| *bcd->found = 1; |
| stop = 1; |
| } |
| |
| nextByte += recordLength; |
| bytesLeft -= recordLength; |
| dataLeft -= recordLength; |
| |
| sdp_record_free(record); |
| } else { |
| logSystemError("sdp_extract_pdu"); |
| stop = 1; |
| } |
| |
| if (stop) break; |
| } |
| } |
| |
| break; |
| } |
| |
| default: |
| logMessage(LOG_ERR, "unexpected channel discovery response type: %u", type); |
| break; |
| } |
| break; |
| |
| case 0XFFFF: |
| errno = sdp_get_error(bcd->session); |
| if (errno < 0) errno = EINVAL; |
| logSystemError("channel discovery response"); |
| break; |
| |
| default: |
| logMessage(LOG_ERR, "unexpected channel discovery response status: %u", status); |
| break; |
| } |
| } |
| |
| int |
| bthDiscoverChannel ( |
| uint8_t *channel, BluetoothConnectionExtension *bcx, |
| const void *uuidBytes, size_t uuidLength, |
| int timeout |
| ) { |
| int foundChannel = 0; |
| |
| uuid_t uuid; |
| sdp_list_t *searchList; |
| |
| sdp_uuid128_create(&uuid, uuidBytes); |
| searchList = sdp_list_append(NULL, &uuid); |
| |
| if (searchList) { |
| uint32_t attributesRange = 0X0000FFFF; |
| sdp_list_t *attributesList = sdp_list_append(NULL, &attributesRange); |
| |
| if (attributesList) { |
| if (LINUX_BLUETOOTH_CHANNEL_DISCOVER_ASYNCHRONOUS) { |
| SocketDescriptor l2capSocket; |
| TimePeriod period; |
| startTimePeriod(&period, timeout); |
| |
| if ((l2capSocket = bthNewL2capConnection(&bcx->remoteAddress.rc_bdaddr, timeout)) != INVALID_SOCKET_DESCRIPTOR) { |
| sdp_session_t *session = sdp_create(l2capSocket, 0); |
| |
| if (session) { |
| BluetoothChannelDiscoveryData bcd = { |
| .session = session, |
| .found = &foundChannel, |
| .channel = channel |
| }; |
| |
| if (sdp_set_notify(session, bthHandleChannelDiscoveryResponse, &bcd) != -1) { |
| int queryStatus = sdp_service_search_attr_async(session, searchList, |
| SDP_ATTR_REQ_RANGE, attributesList); |
| |
| if (!queryStatus) { |
| long int elapsed; |
| |
| while (!afterTimePeriod(&period, &elapsed)) { |
| if (!awaitSocketInput(l2capSocket, (timeout - elapsed))) break; |
| if (sdp_process(session) == -1) break; |
| } |
| } else { |
| logSystemError("sdp_service_search_attr_async"); |
| } |
| } else { |
| logSystemError("sdp_set_notify"); |
| } |
| |
| sdp_close(session); |
| } else { |
| logSystemError("sdp_create"); |
| } |
| |
| close(l2capSocket); |
| } |
| } else { |
| sdp_session_t *session = sdp_connect(BDADDR_ANY, &bcx->remoteAddress.rc_bdaddr, SDP_RETRY_IF_BUSY); |
| |
| if (session) { |
| sdp_list_t *recordList = NULL; |
| int queryStatus = sdp_service_search_attr_req(session, searchList, |
| SDP_ATTR_REQ_RANGE, attributesList, |
| &recordList); |
| |
| if (!queryStatus) { |
| int stopSearching = 0; |
| sdp_list_t *recordElement = recordList; |
| |
| while (recordElement) { |
| sdp_record_t *record = (sdp_record_t *)recordElement->data; |
| |
| if (record) { |
| if (bthFindChannel(channel, record)) { |
| foundChannel = 1; |
| stopSearching = 1; |
| } |
| |
| sdp_record_free(record); |
| } else { |
| logMallocError(); |
| stopSearching = 1; |
| } |
| |
| if (stopSearching) break; |
| recordElement = recordElement->next; |
| } |
| |
| sdp_list_free(recordList, NULL); |
| } else { |
| logSystemError("sdp_service_search_attr_req"); |
| } |
| |
| sdp_close(session); |
| } else { |
| logSystemError("sdp_connect"); |
| } |
| } |
| |
| sdp_list_free(attributesList, NULL); |
| } else { |
| logMallocError(); |
| } |
| |
| sdp_list_free(searchList, NULL); |
| } else { |
| logMallocError(); |
| } |
| |
| return foundChannel; |
| } |
| |
| int |
| bthMonitorInput (BluetoothConnection *connection, AsyncMonitorCallback *callback, void *data) { |
| BluetoothConnectionExtension *bcx = connection->extension; |
| |
| bthCancelInputMonitor(bcx); |
| if (!callback) return 1; |
| return asyncMonitorSocketInput(&bcx->inputMonitor, bcx->socketDescriptor, callback, data); |
| } |
| |
| int |
| bthPollInput (BluetoothConnectionExtension *bcx, int timeout) { |
| return awaitSocketInput(bcx->socketDescriptor, timeout); |
| } |
| |
| ssize_t |
| bthGetData ( |
| BluetoothConnectionExtension *bcx, void *buffer, size_t size, |
| int initialTimeout, int subsequentTimeout |
| ) { |
| return readSocket(bcx->socketDescriptor, buffer, size, initialTimeout, subsequentTimeout); |
| } |
| |
| ssize_t |
| bthPutData (BluetoothConnectionExtension *bcx, const void *buffer, size_t size) { |
| return writeSocket(bcx->socketDescriptor, buffer, size); |
| } |
| |
| char * |
| bthObtainDeviceName (uint64_t bda, int timeout) { |
| char *name = NULL; |
| int device = hci_get_route(NULL); |
| |
| if (device >= 0) { |
| SocketDescriptor socketDescriptor = hci_open_dev(device); |
| |
| if (socketDescriptor >= 0) { |
| int obtained = 0; |
| bdaddr_t address; |
| char buffer[HCI_MAX_NAME_LENGTH]; |
| |
| bthMakeAddress(&address, bda); |
| memset(buffer, 0, sizeof(buffer)); |
| |
| if (LINUX_BLUETOOTH_NAME_OBTAIN_ASYNCHRONOUS) { |
| if (setBlockingIo(socketDescriptor, 0)) { |
| struct hci_filter oldFilter; |
| socklen_t oldLength = sizeof(oldFilter); |
| |
| if (getsockopt(socketDescriptor, SOL_HCI, HCI_FILTER, &oldFilter, &oldLength) != -1) { |
| uint16_t ogf = OGF_LINK_CTL; |
| uint16_t ocf = OCF_REMOTE_NAME_REQ; |
| uint16_t opcode = htobs(cmd_opcode_pack(ogf, ocf)); |
| struct hci_filter newFilter; |
| |
| hci_filter_clear(&newFilter); |
| hci_filter_set_ptype(HCI_EVENT_PKT, &newFilter); |
| hci_filter_set_event(EVT_CMD_STATUS, &newFilter); |
| hci_filter_set_event(EVT_CMD_COMPLETE, &newFilter); |
| hci_filter_set_event(EVT_REMOTE_NAME_REQ_COMPLETE, &newFilter); |
| hci_filter_set_opcode(opcode, &newFilter); |
| |
| if (setsockopt(socketDescriptor, SOL_HCI, HCI_FILTER, &newFilter, sizeof(newFilter)) != -1) { |
| remote_name_req_cp parameters; |
| |
| memset(¶meters, 0, sizeof(parameters)); |
| bacpy(¶meters.bdaddr, &address); |
| parameters.pscan_rep_mode = 0X02; |
| parameters.clock_offset = 0; |
| |
| if (hci_send_cmd(socketDescriptor, ogf, ocf, sizeof(parameters), ¶meters) != -1) { |
| long int elapsed = 0; |
| TimePeriod period; |
| startTimePeriod(&period, timeout); |
| |
| while (awaitSocketInput(socketDescriptor, (timeout - elapsed))) { |
| enum { |
| UNEXPECTED, |
| HANDLED, |
| DONE |
| } state = UNEXPECTED; |
| |
| BluetoothPacket packet; |
| int result = read(socketDescriptor, &packet, sizeof(packet)); |
| |
| if (result == -1) { |
| if (errno == EAGAIN) continue; |
| if (errno == EINTR) continue; |
| |
| logSystemError("read"); |
| break; |
| } |
| |
| switch (packet.fields.type) { |
| case HCI_EVENT_PKT: { |
| hci_event_hdr *header = &packet.fields.data.hciEvent.header; |
| |
| switch (header->evt) { |
| case EVT_REMOTE_NAME_REQ_COMPLETE: { |
| evt_remote_name_req_complete *rn = &packet.fields.data.hciEvent.data.rn; |
| |
| if (bacmp(&rn->bdaddr, &address) == 0) { |
| state = DONE; |
| |
| if (!rn->status) { |
| size_t length = header->plen; |
| |
| length -= rn->name - (unsigned char *)rn; |
| length = MIN(length, sizeof(rn->name)); |
| length = MIN(length, sizeof(buffer)-1); |
| |
| memcpy(buffer, rn->name, length); |
| buffer[length] = 0; |
| obtained = 1; |
| } |
| } |
| |
| break; |
| } |
| |
| case EVT_CMD_STATUS: { |
| evt_cmd_status *cs = &packet.fields.data.hciEvent.data.cs; |
| |
| if (cs->opcode == opcode) { |
| state = HANDLED; |
| |
| if (cs->status) { |
| } |
| } |
| |
| break; |
| } |
| |
| default: |
| logMessage(LOG_CATEGORY(BLUETOOTH_IO), "unexpected HCI event type: %u", header->evt); |
| break; |
| } |
| |
| break; |
| } |
| |
| default: |
| logMessage(LOG_CATEGORY(BLUETOOTH_IO), "unexpected Bluetooth packet type: %u", packet.fields.type); |
| break; |
| } |
| |
| if (state == DONE) break; |
| if (state == UNEXPECTED) logBytes(LOG_WARNING, "unexpected Bluetooth packet", &packet, result); |
| if (afterTimePeriod(&period, &elapsed)) break; |
| } |
| } else { |
| logSystemError("hci_send_cmd"); |
| } |
| |
| if (setsockopt(socketDescriptor, SOL_HCI, HCI_FILTER, &oldFilter, oldLength) == -1) { |
| logSystemError("setsockopt[SOL_HCI,HCI_FILTER]"); |
| } |
| } else { |
| logSystemError("setsockopt[SOL_HCI,HCI_FILTER]"); |
| } |
| } else { |
| logSystemError("getsockopt[SOL_HCI,HCI_FILTER]"); |
| } |
| } |
| } else { |
| int result = hci_read_remote_name(socketDescriptor, &address, sizeof(buffer), buffer, timeout); |
| |
| if (result >= 0) { |
| obtained = 1; |
| } else { |
| logSystemError("hci_read_remote_name"); |
| } |
| } |
| |
| if (obtained) { |
| if (!(name = strdup(buffer))) { |
| logMallocError(); |
| } |
| } |
| |
| close(socketDescriptor); |
| } else { |
| logSystemError("hci_open_dev"); |
| } |
| } else { |
| logSystemError("hci_get_route"); |
| } |
| |
| return name; |
| } |
| |
| #ifdef HAVE_PKG_DBUS |
| #include <dbus/dbus.h> |
| |
| static void |
| logDBusError (const char *action, const DBusError *error) { |
| const char *message = error->message; |
| int length = strlen(message); |
| |
| while (length > 0) { |
| char character = message[--length]; |
| if (character != '\n') break; |
| } |
| |
| logMessage(LOG_CATEGORY(BLUETOOTH_IO), |
| "DBus error: %s: %s: %.*s", |
| action, error->name, length, message); |
| } |
| #endif /* HAVE_PKG_DBUS */ |
| |
| void |
| bthProcessDiscoveredDevices ( |
| DiscoveredBluetoothDeviceTester *testDevice, void *data |
| ) { |
| #ifdef HAVE_PKG_DBUS |
| int found = 0; |
| |
| DBusError error; |
| DBusConnection *bus; |
| |
| dbus_error_init(&error); |
| bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error); |
| |
| if (dbus_error_is_set(&error)) { |
| logDBusError("get bus", &error); |
| dbus_error_free(&error); |
| } else if (!bus) { |
| logMallocError(); |
| } else { |
| DBusMessage *getManagedObjects = dbus_message_new_method_call( |
| "org.bluez", "/", |
| "org.freedesktop.DBus.ObjectManager", |
| "GetManagedObjects" |
| ); |
| |
| if (!getManagedObjects) { |
| logMallocError(); |
| } else { |
| DBusMessage *managedObjects = dbus_connection_send_with_reply_and_block( |
| bus, getManagedObjects, -1, &error |
| ); |
| |
| dbus_message_unref(getManagedObjects); |
| getManagedObjects = NULL; |
| |
| if (dbus_error_is_set(&error)) { |
| logDBusError("send message", &error); |
| dbus_error_free(&error); |
| } else if (!managedObjects) { |
| logMallocError(); |
| } else { |
| DBusMessageIter args; |
| |
| if (dbus_message_iter_init(managedObjects, &args) == FALSE) { |
| logMessage(LOG_ERR, "reply has no arguments"); |
| } else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) { |
| logMessage(LOG_ERR, "expecting an array"); |
| } else { |
| DBusMessageIter objects; |
| dbus_message_iter_recurse(&args, &objects); |
| |
| while (dbus_message_iter_get_arg_type(&objects) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter object; |
| dbus_message_iter_recurse(&objects, &object); |
| |
| if (dbus_message_iter_get_arg_type(&object) == DBUS_TYPE_OBJECT_PATH) { |
| // DBus path to talk to this object |
| dbus_message_iter_next(&object); |
| |
| if (dbus_message_iter_get_arg_type(&object) == DBUS_TYPE_ARRAY) { |
| DBusMessageIter interfaces; |
| dbus_message_iter_recurse(&object, &interfaces); |
| |
| while (dbus_message_iter_get_arg_type(&interfaces) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter interface; |
| dbus_message_iter_recurse(&interfaces, &interface); |
| |
| if (dbus_message_iter_get_arg_type(&interface) == DBUS_TYPE_STRING) { |
| const char *interfaceName; |
| dbus_message_iter_get_basic(&interface, &interfaceName); |
| |
| if (strcmp(interfaceName, "org.bluez.Device1") == 0) { |
| dbus_message_iter_next(&interface); |
| |
| if (dbus_message_iter_get_arg_type(&interface) == DBUS_TYPE_ARRAY) { |
| DiscoveredBluetoothDevice device; |
| memset(&device, 0, sizeof(device)); |
| |
| DBusMessageIter properties; |
| dbus_message_iter_recurse(&interface, &properties); |
| |
| while (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&properties)) { |
| DBusMessageIter property; |
| dbus_message_iter_recurse(&properties, &property); |
| |
| if (dbus_message_iter_get_arg_type(&property) == DBUS_TYPE_STRING) { |
| const char *propertyName; |
| |
| dbus_message_iter_get_basic(&property, &propertyName); |
| dbus_message_iter_next(&property); |
| |
| if (dbus_message_iter_get_arg_type(&property) == DBUS_TYPE_VARIANT) { |
| DBusMessageIter variant; |
| dbus_message_iter_recurse(&property, &variant); |
| |
| if ((strcmp(propertyName, "Address") == 0) && |
| (dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_STRING)) { |
| const char *address; |
| dbus_message_iter_get_basic(&variant, &address); |
| bthParseAddress(&device.address, address); |
| } else if ((strcmp(propertyName, "Name") == 0) && |
| (dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_STRING)) { |
| const char *name; |
| dbus_message_iter_get_basic(&variant, &name); |
| device.name = name; |
| } else if ((strcmp(propertyName, "Paired") == 0) && |
| (dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_BOOLEAN)) { |
| dbus_bool_t paired; |
| dbus_message_iter_get_basic(&variant, &paired); |
| device.paired = paired == TRUE; |
| } |
| } |
| } |
| |
| dbus_message_iter_next(&properties); |
| } |
| |
| if (device.address) { |
| if (testDevice(&device, data)) { |
| found = 1; |
| } |
| } |
| } |
| } |
| } |
| |
| if (found) break; |
| dbus_message_iter_next(&interfaces); |
| } |
| } |
| } |
| |
| if (found) break; |
| dbus_message_iter_next(&objects); |
| } |
| } |
| |
| dbus_message_unref(managedObjects); |
| managedObjects = NULL; |
| } |
| } |
| |
| dbus_connection_unref(bus); |
| bus = NULL; |
| } |
| #endif /* HAVE_PKG_DBUS */ |
| } |