static void handle_characteristic_write_request()

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;
        }
    }
}