in HAP/HAPBLECharacteristicParseAndWriteValue.c [220:593]
HAPError HAPBLECharacteristicParseAndWriteValue(
HAPAccessoryServerRef* server_,
HAPSessionRef* session,
const HAPCharacteristic* characteristic,
const HAPService* service,
const HAPAccessory* accessory,
HAPTLVReaderRef* requestReader,
const HAPTime* _Nullable timedWriteStartTime,
bool* hasExpired,
bool* hasReturnResponse) {
HAPPrecondition(server_);
HAPAccessoryServer* server = (HAPAccessoryServer*) server_;
HAPPrecondition(session);
HAPPrecondition(characteristic);
HAPPrecondition(service);
HAPPrecondition(accessory);
HAPPrecondition(requestReader);
HAPPrecondition(hasExpired);
HAPPrecondition(hasReturnResponse);
HAPError err;
*hasExpired = false;
*hasReturnResponse = false;
HAPCharacteristicValueTLV value = { .bytes = NULL, .numBytes = 0, .maxBytes = 0 };
bool remote = false;
const void* authDataBytes = NULL;
size_t numAuthDataBytes = 0;
uint8_t ttl = 0;
err = ParseRequest(
characteristic, requestReader, &value, &remote, &authDataBytes, &numAuthDataBytes, &ttl, hasReturnResponse);
if (err) {
HAPAssert(err == kHAPError_InvalidData);
return err;
}
// Handle Timed Write.
if (timedWriteStartTime) {
if (!ttl) {
HAPLog(&logObject, "Timed Write Request did not include valid TTL.");
return kHAPError_InvalidData;
}
HAPTime now = HAPPlatformClockGetCurrent();
*hasExpired = now >= ttl * 100 * HAPMillisecond && now - ttl * 100 * HAPMillisecond > *timedWriteStartTime;
if (*hasExpired) {
return kHAPError_None;
}
}
// The maximum length of an HAP characteristic value shall be 64000 bytes.
// See HomeKit Accessory Protocol Specification R14
// Section 7.4.1.7 Maximum Payload Size
if (value.numBytes > 64000) {
HAPLog(&logObject, "Value exceeds maximum allowed length of 64000 bytes.");
return kHAPError_InvalidData;
}
// Parse value and handle write.
uint8_t* bytes = value.bytes;
size_t numBytes = value.numBytes;
switch (*((const HAPCharacteristicFormat*) characteristic)) {
case kHAPCharacteristicFormat_Data: {
err = HAPDataCharacteristicHandleWrite(
server_,
&(const HAPDataCharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
bytes,
numBytes,
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_Bool: {
if (numBytes != sizeof(bool)) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Unexpected value length: %lu.",
(unsigned long) numBytes);
return kHAPError_InvalidData;
}
if (bytes[0] != 0 && bytes[0] != 1) {
HAPLogCharacteristic(
&logObject, characteristic, service, accessory, "Unexpected bool value: %u.", bytes[0]);
return kHAPError_InvalidData;
}
err = HAPBoolCharacteristicHandleWrite(
server_,
&(const HAPBoolCharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
bytes[0] != 0,
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_UInt8: {
if (numBytes != sizeof(uint8_t)) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Unexpected value length: %lu.",
(unsigned long) numBytes);
return kHAPError_InvalidData;
}
err = HAPUInt8CharacteristicHandleWrite(
server_,
&(const HAPUInt8CharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
bytes[0],
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_UInt16: {
if (numBytes != sizeof(uint16_t)) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Unexpected value length: %lu.",
(unsigned long) numBytes);
return kHAPError_InvalidData;
}
err = HAPUInt16CharacteristicHandleWrite(
server_,
&(const HAPUInt16CharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
HAPReadLittleUInt16(bytes),
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_UInt32: {
if (numBytes != sizeof(uint32_t)) {
HAPLogCharacteristic(&logObject, characteristic, service, accessory, "Unexpected value length.");
return kHAPError_InvalidData;
}
err = HAPUInt32CharacteristicHandleWrite(
server_,
&(const HAPUInt32CharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
HAPReadLittleUInt32(bytes),
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_UInt64: {
if (numBytes != sizeof(uint64_t)) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Unexpected value length: %lu.",
(unsigned long) numBytes);
return kHAPError_InvalidData;
}
err = HAPUInt64CharacteristicHandleWrite(
server_,
&(const HAPUInt64CharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
HAPReadLittleUInt64(bytes),
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_Int: {
if (numBytes != sizeof(int32_t)) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Unexpected value length: %lu.",
(unsigned long) numBytes);
return kHAPError_InvalidData;
}
err = HAPIntCharacteristicHandleWrite(
server_,
&(const HAPIntCharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
HAPReadLittleInt32(bytes),
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_Float: {
if (numBytes != sizeof(float)) {
HAPLogCharacteristic(
&logObject,
characteristic,
service,
accessory,
"Unexpected value length: %lu.",
(unsigned long) numBytes);
return kHAPError_InvalidData;
}
uint32_t bitPattern = HAPReadLittleUInt32(bytes);
err = HAPFloatCharacteristicHandleWrite(
server_,
&(const HAPFloatCharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
HAPFloatFromBitPattern(bitPattern),
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_String: {
if (numBytes != HAPStringGetNumBytes(value.bytes)) {
HAPLogSensitiveCharacteristicBuffer(
&logObject,
characteristic,
service,
accessory,
bytes,
numBytes,
"Unexpected string value (contains NULL bytes).");
return kHAPError_InvalidData;
}
if (!HAPUTF8IsValidData(bytes, numBytes)) {
HAPLogSensitiveCharacteristicBuffer(
&logObject,
characteristic,
service,
accessory,
bytes,
numBytes,
"Unexpected string value (invalid UTF-8 encoding).");
return kHAPError_InvalidData;
}
err = HAPStringCharacteristicHandleWrite(
server_,
&(const HAPStringCharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
(const char*) bytes,
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
case kHAPCharacteristicFormat_TLV8: {
HAPTLVReaderRef reader;
HAPTLVReaderCreateWithOptions(
&reader,
&(const HAPTLVReaderOptions) { .bytes = bytes, .numBytes = numBytes, .maxBytes = value.maxBytes });
err = HAPTLV8CharacteristicHandleWrite(
server_,
&(const HAPTLV8CharacteristicWriteRequest) {
.transportType = kHAPTransportType_BLE,
.session = session,
.characteristic = characteristic,
.service = service,
.accessory = accessory,
.remote = remote,
.authorizationData = { .bytes = authDataBytes, .numBytes = numAuthDataBytes } },
&reader,
server->context);
if (err) {
HAPAssert(
err == kHAPError_Unknown || err == kHAPError_InvalidState || err == kHAPError_InvalidData ||
err == kHAPError_OutOfResources || err == kHAPError_NotAuthorized || err == kHAPError_Busy);
return err;
}
}
return kHAPError_None;
}
HAPFatalError();
}