in HAP/HAPIPAccessoryServer.c [851:1282]
static void handle_characteristic_write_request(
HAPIPSessionDescriptor* session,
const HAPCharacteristic* characteristic,
const HAPService* service,
const HAPAccessory* accessory,
HAPIPWriteContextRef* context,
HAPIPByteBuffer* dataBuffer) {
HAPPrecondition(session);
HAPPrecondition(session->server);
HAPPrecondition(session->securitySession.type == kHAPIPSecuritySessionType_HAP);
HAPPrecondition(session->securitySession.isOpen);
HAPPrecondition(session->securitySession.isSecured || kHAPIPAccessoryServer_SessionSecurityDisabled);
HAPPrecondition(!HAPSessionIsTransient(&session->securitySession._.hap));
HAPPrecondition(characteristic);
HAPPrecondition(service);
HAPPrecondition(accessory);
HAPPrecondition(context);
HAPPrecondition(dataBuffer);
HAPError err;
const HAPBaseCharacteristic* baseCharacteristic = characteristic;
HAPIPWriteContext* writeContext = (HAPIPWriteContext*) context;
HAPAssert(baseCharacteristic->iid == writeContext->iid);
if ((writeContext->type == kHAPIPWriteValueType_None) &&
(writeContext->ev == kHAPIPEventNotificationState_Undefined)) {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
return;
}
if (writeContext->ev != kHAPIPEventNotificationState_Undefined) {
if (HAPCharacteristicReadRequiresAdminPermissions(baseCharacteristic) &&
!HAPSessionControllerIsAdmin(&session->securitySession._.hap)) {
writeContext->status = kHAPIPAccessoryServerStatusCode_InsufficientPrivileges;
} else if (!baseCharacteristic->properties.supportsEventNotification) {
writeContext->status = kHAPIPAccessoryServerStatusCode_NotificationNotSupported;
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_Success;
HAPAssert(session->numEventNotifications <= session->maxEventNotifications);
size_t i = 0;
while ((i < session->numEventNotifications) &&
((((HAPIPEventNotification*) &session->eventNotifications[i])->aid != writeContext->aid) ||
(((HAPIPEventNotification*) &session->eventNotifications[i])->iid != writeContext->iid))) {
i++;
}
HAPAssert(
(i == session->numEventNotifications) ||
((i < session->numEventNotifications) &&
(((HAPIPEventNotification*) &session->eventNotifications[i])->aid == writeContext->aid) &&
(((HAPIPEventNotification*) &session->eventNotifications[i])->iid == writeContext->iid)));
if (i == session->numEventNotifications) {
if (writeContext->ev == kHAPIPEventNotificationState_Enabled) {
if (i == session->maxEventNotifications) {
writeContext->status = kHAPIPAccessoryServerStatusCode_OutOfResources;
} else {
((HAPIPEventNotification*) &session->eventNotifications[i])->aid = writeContext->aid;
((HAPIPEventNotification*) &session->eventNotifications[i])->iid = writeContext->iid;
((HAPIPEventNotification*) &session->eventNotifications[i])->flag = false;
session->numEventNotifications++;
handle_characteristic_subscribe_request(session, characteristic, service, accessory);
}
}
} else if (writeContext->ev == kHAPIPEventNotificationState_Disabled) {
session->numEventNotifications--;
if (((HAPIPEventNotification*) &session->eventNotifications[i])->flag) {
HAPAssert(session->numEventNotificationFlags > 0);
session->numEventNotificationFlags--;
}
while (i < session->numEventNotifications) {
HAPRawBufferCopyBytes(
&session->eventNotifications[i],
&session->eventNotifications[i + 1],
sizeof session->eventNotifications[i]);
i++;
}
HAPAssert(i == session->numEventNotifications);
handle_characteristic_unsubscribe_request(session, characteristic, service, accessory);
}
}
}
if (writeContext->type != kHAPIPWriteValueType_None) {
if (HAPCharacteristicWriteRequiresAdminPermissions(baseCharacteristic) &&
!HAPSessionControllerIsAdmin(&session->securitySession._.hap)) {
writeContext->status = kHAPIPAccessoryServerStatusCode_InsufficientPrivileges;
return;
}
if ((baseCharacteristic->properties.ip.supportsWriteResponse || writeContext->response) &&
HAPCharacteristicReadRequiresAdminPermissions(baseCharacteristic) &&
!HAPSessionControllerIsAdmin(&session->securitySession._.hap)) {
writeContext->status = kHAPIPAccessoryServerStatusCode_InsufficientPrivileges;
return;
}
if (baseCharacteristic->properties.writable) {
writeContext->status = kHAPIPAccessoryServerStatusCode_Success;
const void* authorizationDataBytes = NULL;
size_t numAuthorizationDataBytes = 0;
if (writeContext->authorizationData.bytes) {
int r = util_base64_decode(
writeContext->authorizationData.bytes,
writeContext->authorizationData.numBytes,
writeContext->authorizationData.bytes,
writeContext->authorizationData.numBytes,
&writeContext->authorizationData.numBytes);
if (r == 0) {
authorizationDataBytes = writeContext->authorizationData.bytes;
numAuthorizationDataBytes = writeContext->authorizationData.numBytes;
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
}
if (writeContext->status == kHAPIPAccessoryServerStatusCode_Success) {
switch (baseCharacteristic->format) {
case kHAPCharacteristicFormat_Data: {
if (writeContext->type == kHAPIPWriteValueType_String) {
HAPAssert(writeContext->value.stringValue.bytes);
int r = util_base64_decode(
writeContext->value.stringValue.bytes,
writeContext->value.stringValue.numBytes,
writeContext->value.stringValue.bytes,
writeContext->value.stringValue.numBytes,
&writeContext->value.stringValue.numBytes);
if (r == 0) {
HAPAssert(writeContext->value.stringValue.bytes);
err = HAPDataCharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPDataCharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPDataCharacteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
HAPNonnull(writeContext->value.stringValue.bytes),
writeContext->value.stringValue.numBytes,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_Bool: {
if ((writeContext->type == kHAPIPWriteValueType_UInt) &&
(writeContext->value.unsignedIntValue <= 1)) {
err = HAPBoolCharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPBoolCharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPBoolCharacteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
(bool) writeContext->value.unsignedIntValue,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_UInt8: {
if ((writeContext->type == kHAPIPWriteValueType_UInt) &&
(writeContext->value.unsignedIntValue <= UINT8_MAX)) {
err = HAPUInt8CharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPUInt8CharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPUInt8Characteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
(uint8_t) writeContext->value.unsignedIntValue,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_UInt16: {
if ((writeContext->type == kHAPIPWriteValueType_UInt) &&
(writeContext->value.unsignedIntValue <= UINT16_MAX)) {
err = HAPUInt16CharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPUInt16CharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPUInt16Characteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
(uint16_t) writeContext->value.unsignedIntValue,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_UInt32: {
if ((writeContext->type == kHAPIPWriteValueType_UInt) &&
(writeContext->value.unsignedIntValue <= UINT32_MAX)) {
err = HAPUInt32CharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPUInt32CharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPUInt32Characteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
(uint32_t) writeContext->value.unsignedIntValue,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_UInt64: {
if (writeContext->type == kHAPIPWriteValueType_UInt) {
err = HAPUInt64CharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPUInt64CharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPUInt64Characteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
writeContext->value.unsignedIntValue,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_Int: {
if ((writeContext->type == kHAPIPWriteValueType_UInt) &&
(writeContext->value.unsignedIntValue <= INT32_MAX)) {
writeContext->value.intValue = (int32_t) writeContext->value.unsignedIntValue;
writeContext->type = kHAPIPWriteValueType_Int;
}
if (writeContext->type == kHAPIPWriteValueType_Int) {
err = HAPIntCharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPIntCharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPIntCharacteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
writeContext->value.intValue,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_Float: {
if ((writeContext->type == kHAPIPWriteValueType_Int) &&
(writeContext->value.intValue >= -FLT_MAX) && (writeContext->value.intValue <= FLT_MAX)) {
writeContext->value.floatValue = (float) writeContext->value.intValue;
writeContext->type = kHAPIPWriteValueType_Float;
}
if ((writeContext->type == kHAPIPWriteValueType_UInt) &&
(writeContext->value.unsignedIntValue <= FLT_MAX)) {
writeContext->value.floatValue = (float) writeContext->value.unsignedIntValue;
writeContext->type = kHAPIPWriteValueType_Float;
}
if (writeContext->type == kHAPIPWriteValueType_Float) {
err = HAPFloatCharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPFloatCharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPFloatCharacteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
writeContext->value.floatValue,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_String: {
if ((writeContext->type == kHAPIPWriteValueType_String) &&
(writeContext->value.stringValue.numBytes <= 256)) {
HAPAssert(writeContext->value.stringValue.bytes);
HAPAssert(dataBuffer->data);
HAPAssert(dataBuffer->position <= dataBuffer->limit);
HAPAssert(dataBuffer->limit <= dataBuffer->capacity);
if (writeContext->value.stringValue.numBytes >= dataBuffer->limit - dataBuffer->position) {
writeContext->status = kHAPIPAccessoryServerStatusCode_OutOfResources;
} else {
HAPRawBufferCopyBytes(
&dataBuffer->data[dataBuffer->position],
HAPNonnull(writeContext->value.stringValue.bytes),
writeContext->value.stringValue.numBytes);
dataBuffer->data[dataBuffer->position + writeContext->value.stringValue.numBytes] =
'\0';
err = HAPStringCharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPStringCharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPStringCharacteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
&dataBuffer->data[dataBuffer->position],
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
}
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
} break;
case kHAPCharacteristicFormat_TLV8: {
if (writeContext->type == kHAPIPWriteValueType_String) {
HAPAssert(writeContext->value.stringValue.bytes);
int r = util_base64_decode(
writeContext->value.stringValue.bytes,
writeContext->value.stringValue.numBytes,
writeContext->value.stringValue.bytes,
writeContext->value.stringValue.numBytes,
&writeContext->value.stringValue.numBytes);
if (r == 0) {
HAPTLVReaderRef tlvReader;
HAPTLVReaderCreate(
&tlvReader,
writeContext->value.stringValue.bytes,
writeContext->value.stringValue.numBytes);
err = HAPTLV8CharacteristicHandleWrite(
HAPNonnull(session->server),
&(const HAPTLV8CharacteristicWriteRequest) {
.transportType = kHAPTransportType_IP,
.session = &session->securitySession._.hap,
.characteristic = (const HAPTLV8Characteristic*) baseCharacteristic,
.service = service,
.accessory = accessory,
.remote = writeContext->remote,
.authorizationData = { .bytes = authorizationDataBytes,
.numBytes = numAuthorizationDataBytes } },
&tlvReader,
HAPAccessoryServerGetClientContext(HAPNonnull(session->server)));
writeContext->status = ConvertCharacteristicWriteErrorToStatusCode(err);
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_InvalidValueInWrite;
}
}
} break;
}
if (writeContext->status == kHAPIPAccessoryServerStatusCode_Success) {
if (baseCharacteristic->properties.ip.supportsWriteResponse) {
HAPIPByteBuffer dataBufferSnapshot;
HAPRawBufferCopyBytes(&dataBufferSnapshot, dataBuffer, sizeof dataBufferSnapshot);
HAPIPReadContext readContext;
HAPRawBufferZero(&readContext, sizeof readContext);
readContext.aid = writeContext->aid;
readContext.iid = writeContext->iid;
handle_characteristic_read_request(
session,
characteristic,
service,
accessory,
(HAPIPReadContextRef*) &readContext,
dataBuffer);
writeContext->status = readContext.status;
if (writeContext->status == kHAPIPAccessoryServerStatusCode_Success) {
if (writeContext->response) {
switch (baseCharacteristic->format) {
case kHAPCharacteristicFormat_Bool:
case kHAPCharacteristicFormat_UInt8:
case kHAPCharacteristicFormat_UInt16:
case kHAPCharacteristicFormat_UInt32:
case kHAPCharacteristicFormat_UInt64: {
writeContext->value.unsignedIntValue = readContext.value.unsignedIntValue;
} break;
case kHAPCharacteristicFormat_Int: {
writeContext->value.intValue = readContext.value.intValue;
} break;
case kHAPCharacteristicFormat_Float: {
writeContext->value.floatValue = readContext.value.floatValue;
} break;
case kHAPCharacteristicFormat_Data:
case kHAPCharacteristicFormat_String:
case kHAPCharacteristicFormat_TLV8: {
writeContext->value.stringValue.bytes = readContext.value.stringValue.bytes;
writeContext->value.stringValue.numBytes =
readContext.value.stringValue.numBytes;
} break;
}
} else {
// Ignore value of read operation and revert possible changes to data buffer.
HAPRawBufferCopyBytes(dataBuffer, &dataBufferSnapshot, sizeof *dataBuffer);
}
}
} else if (writeContext->response) {
writeContext->status = kHAPIPAccessoryServerStatusCode_ReadFromWriteOnlyCharacteristic;
}
}
}
} else {
writeContext->status = kHAPIPAccessoryServerStatusCode_WriteToReadOnlyCharacteristic;
}
}
}