HAPError HAPBLECharacteristicParseAndWriteValue()

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