blob: 86fe579d3b747839bab54a24efa631ea465540c1 [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file contains implementations for the definitions
* contained and described in BulkDataTransfer.h
*
*/
#include <Weave/Core/WeaveCore.h>
#include <Weave/Core/WeaveMessageLayer.h>
#include <Weave/Profiles/ProfileCommon.h>
#include <Weave/Support/CodeUtils.h>
#include "BulkDataTransfer.h"
using namespace ::nl::Weave;
using namespace ::nl::Weave::Profiles;
using namespace ::nl::Weave::Profiles::StatusReporting;
using namespace ::nl::Weave::Profiles::WeaveMakeManagedNamespaceIdentifier(BDX, kWeaveManagedNamespaceDesignation_Current);
/*
* -- definitions for SendInit and its supporting classes --
*
* the no-arg constructor with defaults for the SendInit message.
* note that the defaults here are set up for sleepy 802.15.4 devices.
* in another context they should be changed on initialization.
*/
SendInit::SendInit()
{
senderDriveSupported=true;
receiverDriveSupported=false;
asynchronousModeSupported=false;
definiteLength=true;
startOffsetPresent=false;
wideRange=false;
theMaxBlockSize=32;
theStartOffset=0;
theLength=0;
}
/*
* initialize a "wide" send inititiation
* parameters:
* - bool aSenderDrive, true if it's OK for the sender to drive, false otherwise
* - bool aReceiverDrive, true if it's OK for the receiver to drive, false otherwise
* - bool asynchMode, true if the device supports asynchronous mode, false otherwsie
* - uint16_t aMaxBlockSize, proposes a maximum block size for the upcomign transfer
* - uint64_t aStartOffset, a start-offset in the file at which to start. if the given
* value is 0 then there's no offset.
* - uint64_t aLength, the length of the file to be transferred. if the given value is 0
* the tranfer is of indefinite length.
* - ReferencedString &aFileDesignator, a string that identifies the data to be transferred
* - ReferencedTLVData *aMetaData, (optional) additional data in TLV format.
* return: error/status
*/
WEAVE_ERROR SendInit::init(bool aSenderDrive, bool aReceiverDrive, bool asynchMode,
uint16_t aMaxBlockSize, uint64_t aStartOffset, uint64_t aLength,
ReferencedString &aFileDesignator, ReferencedTLVData *aMetaData=NULL)
{
wideRange=true;
senderDriveSupported=aSenderDrive;
receiverDriveSupported=aReceiverDrive;
asynchronousModeSupported=asynchMode;
theMaxBlockSize=aMaxBlockSize;
theStartOffset=aStartOffset;
if (theStartOffset==0) startOffsetPresent=false;
theLength=aLength;
if (theLength==0) definiteLength=false;
theFileDesignator=aFileDesignator;
if (aMetaData!=NULL) theMetaData=*aMetaData;
return WEAVE_NO_ERROR;
}
/*
* initialize a "non-wide" send inititiation
* parameters: (same as above except)
* - uint32_t aStartOffset, a 32-bit start offset
* - uint32_t aLength, a 32-bit length
* return error/status
*/
WEAVE_ERROR SendInit::init(bool aSenderDrive, bool aReceiverDrive, bool asynchMode,
uint16_t aMaxBlockSize, uint32_t aStartOffset, uint32_t aLength,
ReferencedString &aFileDesignator, ReferencedTLVData *aMetaData=NULL)
{
init(aSenderDrive, aReceiverDrive, asynchMode, aMaxBlockSize,
(uint64_t)aStartOffset, (uint64_t)aLength, aFileDesignator, aMetaData);
wideRange=false;
return WEAVE_NO_ERROR;
}
/*
* parameter: PacketBuffer *aBuffer, a packet into which the pack the request
* return: /error/status
*/
WEAVE_ERROR SendInit::pack(PacketBuffer *aBuffer)
{
MessageIterator i(aBuffer);
i.append();
// pack the transfer control byte
uint8_t xferCtl=0;
if (senderDriveSupported) xferCtl|=kMode_SenderDrive;
if (receiverDriveSupported) xferCtl|=kMode_ReceiverDrive;
if (asynchronousModeSupported) xferCtl|=kMode_Asynchronous;
TRY(i.writeByte(xferCtl));
// pack the range control byte
uint8_t rangeCtl=0;
if (definiteLength) rangeCtl|=kRangeCtl_DefiniteLength;
if (startOffsetPresent) rangeCtl|=kRangeCtl_StartOffsetPresent;
if (wideRange) rangeCtl|=kRangeCtl_WideRange;
TRY(i.writeByte(rangeCtl));
TRY(i.write16(theMaxBlockSize));
if (startOffsetPresent) {
if (wideRange) {
TRY(i.write64(theStartOffset));
}
else {
TRY(i.write32((uint32_t)theStartOffset));
}
}
if (definiteLength) {
if (wideRange) {
TRY(i.write64(theLength));
}
else {
TRY(i.write32((uint32_t)theLength));
}
}
theFileDesignator.pack(i);
theMetaData.pack(i);
return WEAVE_NO_ERROR;
}
/*
* the packed length, the packed length
*/
uint16_t SendInit::packedLength()
{
// <xfer cctl>+<range ctl>+<max block>+<start offset (optional)>+<length (optional)>+<designator>+<metadata (optional)>
uint16_t startOffsetLength=startOffsetPresent?(wideRange?8:4):0;
uint16_t lengthLength=definiteLength?(wideRange?8:4):0;
return 1+1+2+startOffsetLength+lengthLength+(2+theFileDesignator.theLength)+theMetaData.packedLength();
}
/*
* parameters:
* - PacketBuffer *aBuffer, a pointer to a packet from which to parse the query
* - SendInit &aRequest, a pointer to an object in which to put the result
* return: error/status
*/
WEAVE_ERROR SendInit::parse(PacketBuffer *aBuffer, SendInit &aRequest)
{
MessageIterator i(aBuffer);
// get the xfer ctl field and unpack it
uint8_t xferCtl;
uint32_t tmpUint32;
TRY(i.readByte(&xferCtl));
aRequest.senderDriveSupported=((xferCtl&kMode_SenderDrive)!=0);
aRequest.receiverDriveSupported=((xferCtl&kMode_ReceiverDrive)!=0);
aRequest.asynchronousModeSupported=((xferCtl&kMode_Asynchronous)!=0);
// now the range ctl field and do the same
uint8_t rangeCtl;
TRY(i.readByte(&rangeCtl));
aRequest.definiteLength=(rangeCtl&kRangeCtl_DefiniteLength)!=0;
aRequest.startOffsetPresent=(rangeCtl&kRangeCtl_StartOffsetPresent)!=0;
aRequest.wideRange=(rangeCtl&kRangeCtl_WideRange)!=0;
// now everything else
TRY(i.read16(&aRequest.theMaxBlockSize));
if (aRequest.startOffsetPresent) {
if (aRequest.wideRange) {
TRY(i.read64(&aRequest.theStartOffset));
}
else {
TRY(i.read32(&tmpUint32));
aRequest.theStartOffset = tmpUint32;
}
}
if (aRequest.definiteLength) {
if (aRequest.wideRange) {
TRY(i.read64(&aRequest.theLength));
}
else {
TRY(i.read32(&tmpUint32));
aRequest.theLength = tmpUint32;
}
}
TRY(ReferencedString::parse(i, aRequest.theFileDesignator));
ReferencedTLVData::parse(i, aRequest.theMetaData);
return WEAVE_NO_ERROR;
}
/*
* check everything to see if two of these are equal
*/
bool SendInit::operator == (const SendInit &another) const
{
return(senderDriveSupported==another.senderDriveSupported &&
receiverDriveSupported==another.receiverDriveSupported &&
asynchronousModeSupported==another.asynchronousModeSupported &&
definiteLength==another.definiteLength &&
startOffsetPresent==another.startOffsetPresent &&
asynchronousModeSupported==another.asynchronousModeSupported &&
theMaxBlockSize==another.theMaxBlockSize &&
theStartOffset==another.theStartOffset &&
theFileDesignator==another.theFileDesignator &&
theMetaData==another.theMetaData);
}
/*
* -- definitions for SendAccept and its supporting classes --
*
* the no-arg constructor with defaults for the send accept message
*/
SendAccept::SendAccept()
{
theTransferMode=kMode_SenderDrive;
theMaxBlockSize=0;
}
/*
* parameters:
* - uint8_t aTransferMode, a transfer mode value. must be at MOST one of the
* flag values for this field.
* - uiunt16_t aMaxBlockSize, the maximum allowable block sixe for this exchange
* - ReferencedTLVData *aMetaData, optional metadata
* - return error/status
*/
WEAVE_ERROR SendAccept::init(uint8_t aTransferMode, uint16_t aMaxBlockSize, ReferencedTLVData *aMetaData=NULL)
{
if (aTransferMode!=kMode_SenderDrive &&
aTransferMode!=kMode_ReceiverDrive &&
aTransferMode!=kMode_Asynchronous)
return WEAVE_ERROR_INVALID_TRANSFER_MODE;
theTransferMode=aTransferMode;
theMaxBlockSize=aMaxBlockSize;
if (aMetaData!=NULL) theMetaData=*aMetaData;
return WEAVE_NO_ERROR;
}
/*
* parameter: PacketBuffer *aBuffer, a buffer in which to pack the response
* return: error/status
*/
WEAVE_ERROR SendAccept::pack(PacketBuffer *aBuffer)
{
MessageIterator i(aBuffer);
i.append();
TRY(i.writeByte(theTransferMode));
TRY(i.write16(theMaxBlockSize));
theMetaData.pack(i);
return WEAVE_NO_ERROR;
}
/*
* return the length of this message if you bothered to stuff it in a packet
*/
uint16_t SendAccept::packedLength()
{
// <transfer mode>+<max block size>+<meta data (optional)>
return 1+2+theMetaData.packedLength();
}
/*
* parameters:
* - PacketBuffer *aBuffer, a buffer from which to parse the response
* - SendAccept &aResponse, a place to put the result
* return: error/status
*/
WEAVE_ERROR SendAccept::parse(PacketBuffer *aBuffer, SendAccept &aResponse)
{
MessageIterator i(aBuffer);
TRY(i.readByte(&aResponse.theTransferMode));
TRY(i.read16(&aResponse.theMaxBlockSize));
ReferencedTLVData::parse(i, aResponse.theMetaData);
return WEAVE_NO_ERROR;
}
/*
* are they equal or not... that is the big question
*/
bool SendAccept::operator== (const SendAccept &another) const
{
return(theTransferMode==another.theTransferMode &&
theMaxBlockSize==another.theMaxBlockSize &&
theMetaData==another.theMetaData);
}
/*
* -- definitions for ReceiveInit and its supporting classes --
*
* the no-arg constructor with defaults for the ReceiveInit message.
* note that the defaults here are set up for sleepy 802.15.4 devices.
* in another context they should be changed on initialization.
*/
ReceiveInit::ReceiveInit()
{
senderDriveSupported=false;
receiverDriveSupported=true;
asynchronousModeSupported=false;
definiteLength=true;
startOffsetPresent=false;
wideRange=false;
theMaxBlockSize=32;
theStartOffset=0;
theLength=0;
}
/*
* -- definitions for ReceiveAccept and its supporting classes --
*
* the no-arg constructor with defaults for the send accept message
*/
ReceiveAccept::ReceiveAccept()
{
theTransferMode=kMode_ReceiverDrive;
definiteLength=true;
wideRange=false;
theMaxBlockSize=0;
theLength=0;
}
/*
* initialze a "wide" recevie accept frame
* parameters:
* - uint8_t aTransferMode, a transfer mode value. must be at MOST one of the
* flag values for this field.
* - uiunt16_t aMaxBlockSize, the maximum allowable block sixe for this exchange
* - uint64_t aLength, the length of the file to be transferred (if known)
* - ReferencedTLVData *aMetaData, optional metadata
* - return error/status
*/
WEAVE_ERROR ReceiveAccept::init(uint8_t aTransferMode, uint16_t aMaxBlockSize, uint64_t aLength, ReferencedTLVData *aMetaData=NULL)
{
definiteLength=(aLength!=0);
wideRange=true;
theLength=aLength;
return SendAccept::init(aTransferMode, aMaxBlockSize, aMetaData);
}
/*
* initialze a "narrow" recevie accept frame
* parameters:
* - uint8_t aTransferMode, a transfer mode value. must be at MOST one of the
* flag values for this field.
* - uiunt16_t aMaxBlockSize, the maximum allowable block sixe for this exchange
* - uint
* - uint32_t aLength, the length of the file to be transferred (if known)
* - ReferencedTLVData *aMetaData, optional metadata
* - return error/status
*/
WEAVE_ERROR ReceiveAccept::init(uint8_t aTransferMode, uint16_t aMaxBlockSize, uint32_t aLength, ReferencedTLVData *aMetaData=NULL)
{
definiteLength=(aLength!=0);
wideRange=false;
theLength=(uint64_t)aLength;
return SendAccept::init(aTransferMode, aMaxBlockSize, aMetaData);
}
/*
* parameter: PacketBuffer *aBuffer, a buffer in which to pack the response
* return: error/status
*/
WEAVE_ERROR ReceiveAccept::pack(PacketBuffer *aBuffer)
{
MessageIterator i(aBuffer);
i.append();
TRY(i.writeByte(theTransferMode));
// format and pack the range control field
uint8_t rangeCtl=0;
if (definiteLength) rangeCtl|=kRangeCtl_DefiniteLength;
if (wideRange) rangeCtl|=kRangeCtl_WideRange;
TRY(i.writeByte(rangeCtl));
TRY(i.write16(theMaxBlockSize));
// and the length, if any
if (definiteLength) {
if (wideRange) {
TRY(i.write64(theLength));
}
else {
TRY(i.write32((uint32_t)theLength));
}
}
theMetaData.pack(i);
return WEAVE_NO_ERROR;
}
/*
* return the length of this message if you bothered to stuff it in a packet
*/
uint16_t ReceiveAccept::packedLength()
{
// <transfer mode>+<range control>+<max block size>+<length (optional)>+<meta data (optional)>
return 1+1+2+(definiteLength?(wideRange?8:4):0)+theMetaData.packedLength();
}
/*
* parameters:
* - PacketBuffer *aBuffer, a buffer from which to parse the response
* - ReceiveAccept &aResponse, a place to put the result
* return: error/status
*/
WEAVE_ERROR ReceiveAccept::parse(PacketBuffer *aBuffer, ReceiveAccept &aResponse)
{
MessageIterator i(aBuffer);
uint32_t tmpUint32Value;
TRY(i.readByte(&aResponse.theTransferMode));
// unpack the range control byte
uint8_t rangeCtl;
TRY(i.readByte(&rangeCtl));
aResponse.definiteLength=((rangeCtl&kRangeCtl_DefiniteLength)!=0);
aResponse.wideRange=((rangeCtl&kRangeCtl_WideRange)!=0);
TRY(i.read16(&aResponse.theMaxBlockSize));
if (aResponse.definiteLength) {
if (aResponse.wideRange) {
TRY(i.read64(&aResponse.theLength));
}
else {
TRY(i.read32(&tmpUint32Value));
aResponse.theLength = tmpUint32Value;
}
}
ReferencedTLVData::parse(i, aResponse.theMetaData);
return WEAVE_NO_ERROR;
}
/*
* are they equal or not... that is the big question
*/
bool ReceiveAccept::operator== (const ReceiveAccept &another) const
{
return(theTransferMode==another.theTransferMode &&
definiteLength==another.definiteLength &&
wideRange==another.wideRange &&
theMaxBlockSize==another.theMaxBlockSize &&
theLength==another.theLength &&
theMetaData==another.theMetaData);
}
/*
* -- definitions for BlockQuery and its supporting classes --
*
* the no-arg constructor with defaults for the block query message
*/
BlockQuery::BlockQuery()
{
theBlockCounter=0;
}
/*
* parameter: uint8_t aCounter, a block counter value
* return: error/status
*/
WEAVE_ERROR BlockQuery::init(uint8_t aCounter)
{
theBlockCounter=aCounter;
return WEAVE_NO_ERROR;
}
/*
* parameter: PacketBuffer *aBuffer, a buffer in which to pack the query
* return: error/status
*/
WEAVE_ERROR BlockQuery::pack(PacketBuffer *aBuffer)
{
MessageIterator i(aBuffer);
i.append();
TRY(i.writeByte(theBlockCounter));
return WEAVE_NO_ERROR;
}
/*
* return the length of this message if you bothered to stuff it in a packet
*/
uint16_t BlockQuery::packedLength()
{
// just the counter
return 1;
}
/*
* parameters:
* - PacketBuffer *aBuffer, a buffer from which to parse the query
* - ReceiveAccept &aQuery, a place to put the result
* return: error/status
*/
WEAVE_ERROR BlockQuery::parse(PacketBuffer *aBuffer, BlockQuery &aQuery)
{
MessageIterator i(aBuffer);
TRY(i.readByte(&aQuery.theBlockCounter));
return WEAVE_NO_ERROR;
}
/*
* liberte, equality, fraternite
*/
bool BlockQuery::operator== (const BlockQuery &another) const
{
return theBlockCounter==another.theBlockCounter;
}
/*
* -- definitions for BlockSend and its supporting classes --
*
* the no-arg constructor with defaults for the block send message
*/
BlockSend::BlockSend(void) :
RetainedPacketBuffer()
{
theBlockCounter=0;
theLength=0;
theData=NULL;
}
/*
* parameters:
* - uint8_t aCounter, a block counter value for this block
* - uint64_t aLength, the length of the block
* - uint8_t *aData, a pointer to the data, this may be in a message
* buffer or elsewhere depending on whether it's a recevied or a sent message.
* return: error/status
*/
WEAVE_ERROR BlockSend::init(uint8_t aCounter, uint64_t aLength, uint8_t *aData)
{
theBlockCounter=aCounter;
theLength=aLength;
theData=aData;
return WEAVE_NO_ERROR;
}
/*
* parameter: PacketBuffer *aBuffer, a buffer in which to pack the response
* return: error/status
*/
WEAVE_ERROR BlockSend::pack(PacketBuffer *aBuffer)
{
MessageIterator i(aBuffer);
i.append();
TRY(i.writeByte(theBlockCounter));
/*
* note that we have a problem here. the 64-bit length field here implies
* that we may want to have truly whopping block lengths but we have been,
* up 'til now, assuming that it all fits in a single message buffer. for
* the time being i'll just cast it but eventually we'll have to figure
* this out.
*/
uint8_t *p=theData;
for (uint64_t j=0; j<theLength; j++) TRY(i.writeByte(*p++));
return WEAVE_NO_ERROR;
}
/*
* the length OTM
*/
uint16_t BlockSend::packedLength()
{
// <block counter>+<length>
return 1+theLength;
}
/*
* parameters:
* - PacketBuffer *aBuffer, a buffer from which to parse the response
* - ReceiveAccept &aResponse, a place to put the result
* return: error/status
*/
WEAVE_ERROR BlockSend::parse(PacketBuffer *aBuffer, BlockSend &aResponse)
{
MessageIterator i(aBuffer);
TRY(i.readByte(&aResponse.theBlockCounter));
aResponse.theLength=(uint64_t)aBuffer->DataLength();
aResponse.theData=aBuffer->Start();
/*
* we're holding onto this buffer and, while we might not want to
* write anything after the data in it we want to:
* - move the message iterator's insertion point past the data we've
* just "parsed".
* - set the private buffer pointer data member to point to it
* - increment the reference count on the inet buffer.
*/
i.thePoint+=aResponse.theLength;
aResponse.Retain(aBuffer);
return WEAVE_NO_ERROR;
}
/*
* testing, testing
*/
bool BlockSend::operator== (const BlockSend &another) const
{
return(theBlockCounter==another.theBlockCounter &&
theLength==another.theLength &&
theData==another.theData);
}
/*
* here are the method definitions for the WeaveBdxClient class
*
* NOTE!!!! for now we're just defining enough stuff here to make
* sensor data and log upload work.
*
* the no-arg constructor with defaulting for BDX clients
*/
WeaveBdxClient::WeaveBdxClient()
{
isInitiated=false;
isAccepted=false;
isDone=false;
amInitiator=false;
amSender=false;
amDriver=true;
isAsynch=false;
isWideRange=false;
theMaxBlockSize=0;
theStartOffset=0;
theLength=0;
theBlockCounter=0;
theFabricState = NULL;
theExchangeMgr = NULL;
theConnection = NULL;
theEncryptionType = kWeaveEncryptionType_None;
theKeyId = WeaveKeyId::kNone;
theSendInitHandler=NULL;
theReceiveInitHandler=NULL;
theSendAcceptHandler=NULL;
theReceiveAcceptHandler=NULL;
theRejectHandler=NULL;
theGetBlockHandler=NULL;
thePutBlockHandler=NULL;
theXferErrorHandler=NULL;
theXferDoneHandler=NULL;
theErrorHandler=NULL;
theExchangeCtx = NULL;
theAppState = NULL;
}
/*
* parameters:
* - WeaveExchangeManager &anExchangeMgr, an exhcnage manager to use
* for this bulk transfer operation.
* - ReferencedString &aFileDesignator,
* - uint16_t aMaxBlockSize,
* - uint64_t aStartOffset,
* - uint64_t aLength,
* - bool aWideRange,
return: error/status
*/
WEAVE_ERROR WeaveBdxClient::initClient(WeaveExchangeManager *anExchangeMgr,
void *aAppState,
ReferencedString &aFileDesignator,
uint16_t aMaxBlockSize,
uint64_t aStartOffset,
uint64_t aLength,
bool aWideRange)
{
// Error if already initialized.
if (theExchangeMgr!=NULL) return WEAVE_ERROR_INCORRECT_STATE;
theExchangeMgr=anExchangeMgr;
theFabricState=theExchangeMgr->FabricState;
theConnection=NULL;
theEncryptionType=kWeaveEncryptionType_None;
theKeyId=WeaveKeyId::kNone;
theExchangeCtx=NULL;
// set up the application-specific state
theFileDesignator=aFileDesignator;
theMaxBlockSize=aMaxBlockSize;
theStartOffset=aStartOffset;
theLength=aLength;
theBlockCounter=0;
isWideRange=aWideRange;
theAppState = aAppState;
return WEAVE_NO_ERROR;
}
/*
* return: error/status
*/
WEAVE_ERROR WeaveBdxClient::shutdownClient()
{
return shutdownClient(WEAVE_NO_ERROR);
}
/*
* return: error/status
*/
WEAVE_ERROR WeaveBdxClient::shutdownClient(WEAVE_ERROR aErr)
{
if (theExchangeCtx!=NULL) {
theExchangeCtx->Close();
theExchangeCtx=NULL;
}
if (theConnection!=NULL) {
if (WEAVE_NO_ERROR == aErr)
{
theConnection->Close();
}
else
{
theConnection->Abort();
}
theConnection=NULL;
}
theExchangeMgr=NULL;
theFabricState=NULL;
// clear application-specific state
isInitiated=false;
isAccepted=false;
amInitiator=false;
amSender=false;
amDriver=false;
isAsynch=false;
isWideRange=false;
theFileDesignator.Release();
theMaxBlockSize=0;
theStartOffset=0;
theLength=0;
theBlockCounter=0;
return WEAVE_NO_ERROR;
}
/*
* parameters:
* - bool iCanDrive, true if the initiator should propose that it drive,
* false otherwise
* - ReceiveAcceptHandler anAcceptHandler, a handler for the case where the transfer
* is accepted
* - RejectHandler aRejectHandler, a hander for the case where the transfer is
* rejected
* - PutBlockHandler aBlockHandler, a handler that is supposed to save the block
* that was received
* - XferErrorHandler anErrorHandler, a handler for fatal errors that occur in the
* middle of the transfer
* - XferDoneHandler aDoneHandler, a handler for when we're done transferring
* - ErrorHandler anErrorHandler, a handler for fatal errors that occur in the
* middle of the transfer
* return: error/status
*/
WEAVE_ERROR WeaveBdxClient::initBdxReceive(bool iCanDrive,
ReceiveAcceptHandler anAcceptHandler, RejectHandler aRejectHandler,
PutBlockHandler aBlockHandler, XferErrorHandler aXferErrorHandler,
XferDoneHandler aDoneHandler, ErrorHandler anErrorHandler)
{
// Discard any existing exchange context. Effectively we can only have one transfer with
// a single node at any one time.
if (theExchangeCtx!=NULL) {
theExchangeCtx->Close();
theExchangeCtx = NULL;
}
// Create a new exchange context.
if (theConnection!=NULL)
theExchangeCtx=theExchangeMgr->NewContext(theConnection, this);
else
return WEAVE_ERROR_INCORRECT_STATE;
// get out if this didn't work
if (theExchangeCtx==NULL) return WEAVE_ERROR_NO_MEMORY; // TODO: better error code
// Configure the encryption and signature types to be used to send the request.
theExchangeCtx->EncryptionType=theEncryptionType;
theExchangeCtx->KeyId=theKeyId;
// set up the application-specific state
isInitiated=true;
isAccepted=false;
amInitiator=true;
amSender=false;
// Arrange for messages in this exchange to go to our response handler.
theExchangeCtx->OnMessageReceived=handleResponse;
// Handle exchange failures due to the connection being closed.
theExchangeCtx->OnConnectionClosed=handleConnectionClosed;
// install application-specifc handlers
theReceiveAcceptHandler=anAcceptHandler;
theRejectHandler=aRejectHandler;
thePutBlockHandler=aBlockHandler;
theXferErrorHandler=aXferErrorHandler;
theErrorHandler=anErrorHandler;
theXferDoneHandler=aDoneHandler;
WEAVE_ERROR err;
ReceiveInit msg;
// Send an BDX Request message. Discard the exchange context if the send fails.
PacketBuffer *buffer=PacketBuffer::New();
if (buffer==NULL) {
err=WEAVE_ERROR_NO_MEMORY;
goto cleanup;
}
if (isWideRange) {
RESCUE(err, msg.init(!iCanDrive/*SenderDrive*/, iCanDrive/*ReceiverDrive*/, false/*aSynchOK*/, theMaxBlockSize, theStartOffset, theLength, theFileDesignator), cleanup);
}
else {
RESCUE(err, msg.init(!iCanDrive/*SenderDrive*/, iCanDrive/*ReceiverDrive*/, false/*aSynchOK*/, theMaxBlockSize, (uint32_t)theStartOffset, (uint32_t)theLength, theFileDesignator), cleanup);
}
RESCUE(err, msg.pack(buffer), cleanup);
err=theExchangeCtx->SendMessage(kWeaveProfile_BDX, kMsgType_ReceiveInit, buffer);
buffer = NULL;
if (err==WEAVE_NO_ERROR) return err;
cleanup:
DispatchErrorHandler(err);
if (buffer!=NULL) PacketBuffer::Free(buffer);
shutdownClient(err);
return err;
}
/*
* parameters:
* - bool iCanDrive, true if the initiator should propose that it drive,
* false otherwise
* - bool uCanDrive, true if the initiator should propose that its counterpart drive,
* false otherwise
* - bool aSynchOK, true if the initiator can do asynchronous mode, false otherwise
* - SendAcceptHandler anAcceptHandler, a handler for the case where the transfer
* is accepted
* - RejectHandler aRejectHandler, a hander for the case where the transfer is
* rejected
* - GetBlockHandler aBlockHandler, a handler that is supposed to get "the next"
* block to be transfered
* - XferErrorHandler anErrorHandler, a handler for fatal errors that occur in the
* middle of the transfer
* - XferDoneHandler aDoneHandler, a handler for when we're done transferring
* - ReferencedTLVData *aMetaData=NULL, (optional) metadata
* return: error/status
*/
WEAVE_ERROR WeaveBdxClient::initBdxSend(bool iCanDrive, bool uCanDrive, bool aSynchOK,
SendAcceptHandler anAcceptHandler, RejectHandler aRejectHandler,
GetBlockHandler aBlockHandler, XferErrorHandler aXferErrorHandler,
XferDoneHandler aDoneHandler, ErrorHandler anErrorHandler,
ReferencedTLVData *aMetaData)
{
// Discard any existing exchange context. Effectively we can only have one transfer with
// a single node at any one time.
if (theExchangeCtx!=NULL) {
theExchangeCtx->Close();
theExchangeCtx = NULL;
}
// Create a new exchange context.
if (theConnection!=NULL)
theExchangeCtx=theExchangeMgr->NewContext(theConnection, this);
else
return WEAVE_ERROR_INCORRECT_STATE;
// get out if this didn't work
if (theExchangeCtx==NULL) return WEAVE_ERROR_NO_MEMORY; // TODO: better error code
// Configure the encryption and signature types to be used to send the request.
theExchangeCtx->EncryptionType=theEncryptionType;
theExchangeCtx->KeyId=theKeyId;
// set up the application-specific state
isInitiated=true;
isAccepted=false;
amInitiator=true;
amSender=true;
// Arrange for messages in this exchange to go to our response handler.
theExchangeCtx->OnMessageReceived=handleResponse;
// Handle exchange failures due to the connection being closed.
theExchangeCtx->OnConnectionClosed=handleConnectionClosed;
// install application-specifc handlers
theSendAcceptHandler=anAcceptHandler;
theRejectHandler=aRejectHandler;
theGetBlockHandler=aBlockHandler;
theXferErrorHandler=aXferErrorHandler;
theErrorHandler=anErrorHandler;
theXferDoneHandler=aDoneHandler;
WEAVE_ERROR err;
SendInit msg;
// Send an BDX Request message. Discard the exchange context if the send fails.
PacketBuffer *buffer=PacketBuffer::New();
if (buffer==NULL) {
err=WEAVE_ERROR_NO_MEMORY;
goto cleanup;
}
if (isWideRange) {
RESCUE(err, msg.init(iCanDrive, uCanDrive, aSynchOK, theMaxBlockSize, theStartOffset, theLength, theFileDesignator, aMetaData), cleanup);
}
else {
RESCUE(err, msg.init(iCanDrive, uCanDrive, aSynchOK, theMaxBlockSize, (uint32_t)theStartOffset, (uint32_t)theLength, theFileDesignator, aMetaData), cleanup);
}
RESCUE(err, msg.pack(buffer), cleanup);
err=theExchangeCtx->SendMessage(kWeaveProfile_BDX, kMsgType_SendInit, buffer);
buffer = NULL;
if (err==WEAVE_NO_ERROR) return err;
cleanup:
DispatchErrorHandler(err);
if (buffer!=NULL) PacketBuffer::Free(buffer);
shutdownClient(err);
return err;
}
/*
*
WEAVE_ERROR WeaveBdxClient::awaitBdxInit(uint64_t) {} //TBD
*/
/*
*
WEAVE_ERROR WeaveBdxClient::awaitBdxInit(uint64_t, IPAddress) {} //TBD
*/
/*
* here's a utility function for sending a block query
* parameter: WeaveBdxClient bdxApp,
* return: error/status
*/
WEAVE_ERROR sendBlockQuery(WeaveBdxClient *bdxApp)
{
PacketBuffer *outBuffer=PacketBuffer::New();
if (outBuffer==NULL) return WEAVE_ERROR_NO_MEMORY;
BlockQuery outMsg;
TRY(outMsg.init(bdxApp->theBlockCounter));
TRY(outMsg.pack(outBuffer));
TRY(bdxApp->theExchangeCtx->SendMessage(kWeaveProfile_BDX, kMsgType_BlockQuery, outBuffer));
outBuffer = NULL;
return WEAVE_NO_ERROR;
}
/*
* here's a utility function for sending a block EOF ack
* parameter: WeaveBdxClient bdxApp,
* return: error/status
*/
WEAVE_ERROR sendBlockEOFAck(WeaveBdxClient *bdxApp)
{
PacketBuffer *outBuffer=PacketBuffer::New();
if (outBuffer==NULL) return WEAVE_ERROR_NO_MEMORY;
BlockEOFAck outMsg;
TRY(outMsg.init(bdxApp->theBlockCounter));
TRY(outMsg.pack(outBuffer));
TRY(bdxApp->theExchangeCtx->SendMessage(kWeaveProfile_BDX, kMsgType_BlockEOFAck, outBuffer));
outBuffer = NULL;
return WEAVE_NO_ERROR;
}
/*
* here's a utility function for sending a block
* parameter: WeaveBdxClient bdxApp,
* return: error/status
*/
WEAVE_ERROR sendBlock(WeaveBdxClient *bdxApp)
{
uint64_t length;
uint8_t *data = NULL;
bool isLast;
uint8_t msgType;
PacketBuffer *outBuffer = NULL;
WEAVE_ERROR err = WEAVE_NO_ERROR;
// This check ensures DispatchGetBlockHandler has a registered handler
// to dispatch to, and hence data, length, and isLast are properly initialized.
// Note that return value from the callback theGetBlockHandler would make this
// code more robust, but it would require changes to the public API.
VerifyOrExit(bdxApp->theGetBlockHandler != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
bdxApp->DispatchGetBlockHandler(&length, &data, &isLast);
VerifyOrExit(data != NULL, err = WEAVE_ERROR_INCORRECT_STATE);
outBuffer = PacketBuffer::New();
VerifyOrExit(outBuffer != NULL, err = WEAVE_ERROR_NO_MEMORY);
if (isLast) {
bdxApp->isDone=true;
BlockEOF outMsg;
msgType=kMsgType_BlockEOF;
TRY(outMsg.init(bdxApp->theBlockCounter, length, data));
TRY(outMsg.pack(outBuffer));
}
else {
BlockSend outMsg;
msgType=kMsgType_BlockSend;
TRY(outMsg.init(bdxApp->theBlockCounter, length, data));
TRY(outMsg.pack(outBuffer));
}
TRY(bdxApp->theExchangeCtx->SendMessage(kWeaveProfile_BDX, msgType, outBuffer));
exit:
return err;
}
/*
* this is the main handle for BDX exchanges.
* parameters:
* - ExchangeContext *anExchangeCtx, the exhcnage context in case we need it
* - WeaveMessageInfo *aWeaveMsgInfo, the message information for the message
* - uint32_t aProfileId, the ID of the profile underwhihc the message is defined
* - uint8_t aMessageType, the message type with that profile
* - PacketBuffer *aPacketBuffer, the packed message itself
*/
void WeaveBdxClient::handleResponse(ExchangeContext *anExchangeCtx, const IPPacketInfo *pktInfo, const WeaveMessageInfo *aWeaveMsgInfo,
uint32_t aProfileId, uint8_t aMessageType, PacketBuffer *aPacketBuffer)
{
WEAVE_ERROR err=WEAVE_NO_ERROR; // we'll use this later
// extract the BDX client object from the exchange context
WeaveBdxClient *bdxApp = (WeaveBdxClient *)anExchangeCtx->AppState;
// Verify that the message is a BDX message and get out if it's not
if (aProfileId!=kWeaveProfile_BDX)
err=WEAVE_ERROR_INVALID_PROFILE_ID;
else {
if (bdxApp->isInitiated) {
/*
* here's the case where the transfer has been initiated.
* on the initiator, this will always be true when we get
* here. on a listener it won't. the two possibilites here
* are that the transfer has also been accepted or it hasn't.
*/
if (bdxApp->isAccepted) {
/*
* at this point the transfer has been accepted and we're
* just moving the bits back and forth. what happens here
* depends on whether we're sending or receving, driving
* or not.
*/
if (bdxApp->amSender) {
/*
* the transfer has been started and i'm the sender. this
* means, basically, that i should expect an ACK or a
* query depending on who's driving.
*/
if (bdxApp->amDriver) {
/*
* i'm sending and driving. this means that it should expect
* an ACK from my counterpart and should send the next block.
* a transfer error is possible here as well.
*/
switch (aMessageType) {
case kMsgType_BlockAck:
{
BlockAck ack;
BlockAck::parse(aPacketBuffer, ack);
uint8_t rcvdCounter=ack.theBlockCounter;
if (rcvdCounter==bdxApp->theBlockCounter) {
// everything's OK update the counter and send the next block
bdxApp->theBlockCounter++;
err=sendBlock(bdxApp);
}
else if (rcvdCounter<bdxApp->theBlockCounter) {
// just ignore the packet
}
else { // bad scene we've somehow fallen behind
}
}
break;
case kMsgType_BlockEOFAck:
bdxApp->DispatchXferDoneHandler();
break;
case kMsgType_TransferError:
{
TransferError inMsg;
TransferError::parse(aPacketBuffer, inMsg);
bdxApp->DispatchXferErrorHandler(&inMsg);
}
break;
default:
bdxApp->DispatchErrorHandler(WEAVE_ERROR_INVALID_MESSAGE_TYPE);
}
}
else { // not driving
/*
* i'm sending and my counterpart is driving. this means
* i wait for a query and send the reqwuested block. there
* is an optional ACK here and a transfer error is also a
* possibiliity.
*/
switch (aMessageType) {
case kMsgType_BlockQuery:
break;
case kMsgType_BlockAck:
break;
case kMsgType_BlockEOFAck:
break;
case kMsgType_TransferError:
break;
default:
break;
}
}
}
else { // receiving (not sending)
/*
* otherwise, i'm the receiver. i should expect to get
* a block here and then, if i'm driving, send out a
* query for the next block otherwise just send an ACK.
*/
if (bdxApp->amDriver) {
//send out a query for the next block
switch (aMessageType) {
case kMsgType_BlockSend:
{
BlockSend blockSend;
BlockSend::parse(aPacketBuffer, blockSend);
bdxApp->DispatchPutBlockHandler(blockSend.theLength, blockSend.theData, false/*bool isLastBlock*/);
//update the block counter and send the next block query
bdxApp->theBlockCounter++;
err=sendBlockQuery(bdxApp);
}
break;
case kMsgType_BlockEOF:
{//TODO: add a consistency check on the overall size of the file received
//i.e. the sum of all received blocks should match the expected file size
BlockEOF blockEOF;
BlockEOF::parse(aPacketBuffer, blockEOF);
bdxApp->DispatchPutBlockHandler(blockEOF.theLength, blockEOF.theData, true/*bool isLastBlock*/);
//maintain the same block counter used to send the previous block query
//and send the final block EOF ack
err=sendBlockEOFAck(bdxApp);
bdxApp->DispatchXferDoneHandler();
}
break;
case kMsgType_TransferError:
{
TransferError inMsg;
TransferError::parse(aPacketBuffer, inMsg);
bdxApp->DispatchXferErrorHandler(&inMsg);
}
break;
default:
bdxApp->DispatchErrorHandler(WEAVE_ERROR_INVALID_MESSAGE_TYPE);
}
}
else { // not driving
// TODO
}
}
}
else { // not accepted
/*
* here the tranfer hasn't been accepted yet, and we're waiting
* either for an accept or a reject message.
*/
switch (aMessageType) {
case kMsgType_TransferError:
{
TransferError inMsg;
TransferError::parse(aPacketBuffer, inMsg);
bdxApp->DispatchXferErrorHandler(&inMsg);
break;
}
case kMsgType_SendAccept:
/*
* the send is accepted. now we figure out who's driving and,
* if necessary, kick it off.
*/
{
SendAccept inMsg;
err=SendAccept::parse(aPacketBuffer, inMsg);
if (err==WEAVE_NO_ERROR) {
bdxApp->DispatchSendAccept(&inMsg);
switch (inMsg.theTransferMode) {
case kMode_SenderDrive:
bdxApp->amDriver=true;
bdxApp->isAccepted=true;
bdxApp->theMaxBlockSize=inMsg.theMaxBlockSize;
// try and send the first block
err=sendBlock(bdxApp);
break;
case kMode_ReceiverDrive:
// TODO
break;
case kMode_Asynchronous:
// we don't currently support asynch mode
default:
err=WEAVE_ERROR_INVALID_TRANSFER_MODE;
}
}
}
break;
case kMsgType_SendReject:
/*
* the send is rejected, callback the reject handler.
*/
bdxApp->DispatchRejectHandler(NULL);
break;
case kMsgType_ReceiveAccept:
/*
* the receive is accepted. now we figure out who's driving and,
* if necessary, kick it off.
*/
{
ReceiveAccept inMsg;
err=ReceiveAccept::parse(aPacketBuffer, inMsg);
if (err==WEAVE_NO_ERROR) {
bdxApp->DispatchReceiveAccept(&inMsg);
switch (inMsg.theTransferMode) {
case kMode_ReceiverDrive:
bdxApp->amDriver=true;
bdxApp->isAccepted=true;
bdxApp->theMaxBlockSize=inMsg.theMaxBlockSize;
// try and send the first block
bdxApp->theBlockCounter = 0;
err=sendBlockQuery(bdxApp);
break;
case kMode_SenderDrive:
// TODO
break;
case kMode_Asynchronous:
// we don't currently support asynch mode
default:
err=WEAVE_ERROR_INVALID_TRANSFER_MODE;
}
}
}
break;
case kMsgType_ReceiveReject:
/*
* the receive is rejected, callback the reject handler.
*/
bdxApp->DispatchRejectHandler(NULL);
break;
default:
err = WEAVE_ERROR_INVALID_MESSAGE_TYPE;
break;
}
}
}
else { // not initiated
/*
* OK. so here we were listening and we get something,
* which must be an initiation or else.
*/
switch (aMessageType) {
case kMsgType_SendInit:
// TODO
break;
case kMsgType_ReceiveInit:
// TODO
break;
default:
// TODO
break;
}
}
}
if (err!=WEAVE_NO_ERROR) {
bdxApp->DispatchErrorHandler(err);
bdxApp->shutdownClient(err);
}
PacketBuffer::Free(aPacketBuffer);
}
void WeaveBdxClient::handleConnectionClosed(ExchangeContext *anExchangeCtx, WeaveConnection *con, WEAVE_ERROR conErr)
{
WeaveBdxClient *bdxClient = (WeaveBdxClient *)anExchangeCtx->AppState;
// If the other end closed the connection, pass WEAVE_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY to the application.
if (conErr == WEAVE_NO_ERROR) conErr = WEAVE_ERROR_CONNECTION_CLOSED_UNEXPECTEDLY;
// Clear the client state. Note this also closes the exchange context, meaning that anExchangeCtx is no longer valid.
bdxClient->shutdownClient(conErr);
// Forward the error to the app's error handler.
if (bdxClient->theErrorHandler)
{
bdxClient->theErrorHandler(bdxClient->theAppState, conErr);
}
}
void WeaveBdxClient::DispatchReceiveAccept(ReceiveAccept *aReceiveAcceptMsg)
{
if (theReceiveAcceptHandler)
{
theReceiveAcceptHandler(aReceiveAcceptMsg);
}
}
void WeaveBdxClient::DispatchSendAccept(SendAccept *aSendAcceptMsg)
{
if (theSendAcceptHandler)
{
theSendAcceptHandler(theAppState, aSendAcceptMsg);
}
}
void WeaveBdxClient::DispatchRejectHandler(StatusReport *aReport)
{
if (theRejectHandler)
{
theRejectHandler(theAppState, aReport);
}
}
void WeaveBdxClient::DispatchPutBlockHandler(uint64_t length,
uint8_t *dataBlock,
bool isLastBlock)
{
if (thePutBlockHandler)
{
thePutBlockHandler(length, dataBlock, isLastBlock);
}
}
void WeaveBdxClient::DispatchGetBlockHandler(uint64_t *pLength,
uint8_t **aDataBlock,
bool *isLastBlock)
{
if (theGetBlockHandler)
{
theGetBlockHandler(theAppState, pLength, aDataBlock, isLastBlock);
}
}
void WeaveBdxClient::DispatchXferErrorHandler(StatusReport *aXferError)
{
if (theXferErrorHandler)
{
theXferErrorHandler(theAppState, aXferError);
}
}
void WeaveBdxClient::DispatchXferDoneHandler()
{
if (theXferDoneHandler)
{
theXferDoneHandler(theAppState);
}
}
void WeaveBdxClient::DispatchErrorHandler(WEAVE_ERROR anErrorCode)
{
if (theErrorHandler)
{
theErrorHandler(theAppState, anErrorCode);
}
}