| /* |
| * |
| * 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. |
| */ |
| |
| #include "Weave/Support/logging/WeaveLogging.h" |
| |
| #include "nlweavebdxserver.h" |
| #include <unistd.h> |
| #include <fcntl.h> |
| |
| #if HAVE_CURL_CURL_H |
| #include <curl/curl.h> |
| #endif |
| |
| #if HAVE_CURL_EASY_H |
| #include <curl/easy.h> |
| #endif |
| |
| int DownloadFile(char *aFileDeisgnator); |
| size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream); |
| size_t read_data(char *ptr, size_t size, size_t n, FILE *stream); |
| |
| namespace nl { |
| namespace Weave { |
| namespace Profiles { |
| |
| using namespace nl::Weave::Logging; |
| |
| BulkDataTransferServer::BulkDataTransferServer() |
| : mpExchangeMgr(NULL) |
| , mpAppState(NULL) |
| , OnBDXReceiveInitRequestReceived(NULL) |
| , OnBDXSendInitRequestReceived(NULL) |
| , OnBDXBlockQueryRequestReceived(NULL) |
| , OnBDXBlockSendReceived(NULL) |
| , OnBDXBlockEOFAckReceived(NULL) |
| , OnBDXTransferFailed(NULL) |
| , OnBDXTransferSucceeded(NULL) |
| , mpDelegate(NULL) |
| , mHostedFileName(NULL) |
| , mBDXDownloadCanRun(false) |
| { |
| memset(mTransferPool, 0, sizeof(mTransferPool)); |
| } |
| |
| BulkDataTransferServer::~BulkDataTransferServer() |
| { |
| Shutdown(); |
| } |
| |
| void |
| BulkDataTransferServer::SetDelegate(BulkDataTransferServerDelegate *pDelegate) |
| { |
| mpDelegate = pDelegate; |
| } |
| |
| BulkDataTransferServerDelegate * |
| BulkDataTransferServer::GetDelegate() |
| { |
| return mpDelegate; |
| } |
| |
| void BulkDataTransferServer::AllowBDXServerToRun(bool aEnable) |
| { |
| mBDXDownloadCanRun = aEnable; |
| } |
| |
| bool BulkDataTransferServer::CanBDXServerRun() |
| { |
| return mBDXDownloadCanRun; |
| } |
| |
| WEAVE_ERROR BulkDataTransferServer::Init(WeaveExchangeManager *exchangeMgr, void *appState) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| // |
| // Error if already initialized. |
| VerifyOrExit(mpExchangeMgr == NULL, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| mpExchangeMgr = exchangeMgr; |
| mpAppState = appState; |
| mHostedFileName = NULL; |
| |
| // Initialize connection pool |
| memset(mTransferPool, 0, sizeof(mTransferPool)); |
| for (int i = 0; i < MAX_NUM_BDX_TRANSFERS; i++) |
| { |
| //TODO: call a BDXTransfer.Init() function to set defaults? block counter, etc. |
| mTransferPool[i].FD = NULL; |
| } |
| |
| // Register to receive unsolicited ReceiveInitiation messages from the exchange manager. |
| mpExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_BDX, kMsgType_ReceiveInit, HandleReceiveInitRequest, this); |
| mpExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_BDX, kMsgType_SendInit, HandleSendInitRequest, this); |
| |
| exit: |
| return err; |
| } |
| |
| WEAVE_ERROR BulkDataTransferServer::Shutdown() |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX Shutdown entering\n"); |
| |
| if (mpExchangeMgr != NULL) |
| { |
| // Shutdown actions to perform only if BDX server initialized: |
| |
| mpExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_BDX, kMsgType_ReceiveInit); |
| mpExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_BDX, kMsgType_SendInit); |
| mpExchangeMgr = NULL; |
| |
| // Explicitly shut down transfers to free any held Weave resources |
| for (int i = 0; i < MAX_NUM_BDX_TRANSFERS; i++) |
| { |
| ShutdownTransfer(&mTransferPool[i]); |
| } |
| } |
| |
| // Shutdown actions to perform even if BDX server uninitialized: |
| |
| mpAppState = NULL; |
| OnBDXReceiveInitRequestReceived = NULL; |
| OnBDXSendInitRequestReceived = NULL; |
| OnBDXBlockQueryRequestReceived = NULL; |
| OnBDXBlockSendReceived = NULL; |
| OnBDXBlockEOFAckReceived = NULL; |
| OnBDXTransferFailed = NULL; |
| OnBDXTransferSucceeded = NULL; |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "1 BDX Shutdown exiting\n"); |
| return WEAVE_NO_ERROR; |
| } |
| |
| BulkDataTransferServer::BDXTransfer *BulkDataTransferServer::NewTransfer() |
| { |
| for (int i = 0; i < MAX_NUM_BDX_TRANSFERS; i++) |
| { |
| //TODO: verify this is thread-safe?? |
| if (!mTransferPool[i].BdxApp) |
| { |
| mTransferPool[i].BdxApp = this; |
| return &mTransferPool[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| void BulkDataTransferServer::ShutdownTransfer(BDXTransfer *xfer) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| VerifyOrExit(xfer->BdxApp != NULL, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX ShutdownTransfer entering\n"); |
| uint64_t peerNodeId = kNodeIdNotSpecified; |
| IPAddress peerAddr = IPAddress::Any; |
| |
| // Get values to send application callback |
| if (xfer->EC && xfer->EC->Con) |
| { |
| peerNodeId = xfer->EC->Con->PeerNodeId; |
| peerAddr = xfer->EC->Con->PeerAddr; |
| } |
| |
| // Fire application callback |
| if (!xfer->CompletedSuccessfully) |
| { |
| if (OnBDXTransferFailed) |
| { |
| OnBDXTransferFailed(peerNodeId, peerAddr, mpAppState); |
| } |
| } |
| else |
| { |
| if (OnBDXTransferSucceeded) |
| { |
| OnBDXTransferSucceeded(peerNodeId, peerAddr, mpAppState); |
| } |
| } |
| |
| // Release Weave resources |
| if (xfer->EC) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "1 BDX ShutdownTransfer closing EC\n"); |
| |
| if (xfer->EC->Con) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "2 BDX ShutdownTransfer closing Con\n"); |
| xfer->EC->Con->Close(); |
| xfer->EC->Con = NULL; |
| } |
| |
| xfer->EC->Close(); |
| xfer->EC = NULL; |
| } |
| |
| // Close file |
| if (xfer->FD) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "4 BDX ShutdownTransfer closing FD\n"); |
| if (fclose(xfer->FD) == EOF) { |
| Log(kLogModule_BDX, kLogCategory_Error, "4.5 BDX ShutdownTransfer error closing file!\n"); |
| } |
| |
| xfer->FD = NULL; |
| } |
| |
| // Reset and release transfer object |
| xfer->mMaxBlockSize = 0; |
| xfer->CompletedSuccessfully = false; |
| xfer->BdxApp = NULL; |
| |
| exit: |
| if (err != WEAVE_NO_ERROR) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "BDX ShutdownTransfer exiting with error: %d", err); |
| } |
| else |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "5 BDX ShutdownTransfer exiting"); |
| } |
| } |
| |
| void BulkDataTransferServer::HandleReceiveInitRequest(ExchangeContext *ec, const IPPacketInfo *packetInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, |
| uint8_t msgType, PacketBuffer *payloadReceiveInit) |
| { |
| // We're guaranteed of the right message profile and type by the mpExchangeMgr. |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX HandleReceiveInitRequest entering\n"); |
| |
| WEAVE_ERROR ret = WEAVE_NO_ERROR; |
| const uint8_t BDX_SERVER_TRANSFER_MODE = kMode_ReceiverDrive; |
| BulkDataTransferServer *bdxApp = NULL; |
| BDXTransfer *xfer = NULL; |
| PacketBuffer *payload = NULL; |
| char *fileDesignator = NULL; |
| int retval = 0; |
| |
| ReceiveAccept receiveAccept; |
| ReceiveReject receiveReject; |
| ReceiveInit receiveInit; |
| |
| VerifyOrExit(ec != NULL, |
| Log(kLogModule_BDX, kLogCategory_Error, "0.5 BDX HandleReceiveInitRequest failed, null EC\n")); |
| bdxApp = static_cast<BulkDataTransferServer *>(ec->AppState); |
| |
| VerifyOrExit(bdxApp->CanBDXServerRun(), |
| Log(kLogModule_BDX, kLogCategory_Error, "0.5 BDX HandleReceiveInitRequest failed, can't run!\n")); |
| |
| // Parse init request and discard payload buffer |
| ret = ReceiveInit::parse(payloadReceiveInit, receiveInit); |
| VerifyOrExit(ret == WEAVE_NO_ERROR, |
| Log(kLogModule_BDX, kLogCategory_Error, "0.5 BDX HandleReceiveInitRequest failed, error parsing\n")); |
| PacketBuffer::Free(payloadReceiveInit); |
| payloadReceiveInit = NULL; |
| |
| // Grab BDXTransfer object for this transfer |
| xfer = bdxApp->NewTransfer(); |
| |
| VerifyOrExit(xfer, Log(kLogModule_BDX, |
| kLogCategory_Error, |
| "1 BDX HandleReceiveInitRequest (transfer alloc failed)\n"); |
| SendTransferError(ec, kWeaveProfile_Common, kStatus_OutOfMemory)); |
| |
| // Hang new BDXTransfer on exchange context |
| ec->AppState = xfer; |
| |
| // Initialize xfer struct (move to init function?) |
| xfer->EC = ec; |
| xfer->FD = NULL; // memset(0) doesn't set us up for ShutdownTransfer() |
| |
| if (receiveInit.mMaxBlockSize <= 0) { |
| Log(kLogModule_BDX, kLogCategory_Error, "2 BDX HandleReceiveInitRequest (maxBlockSize <= 0)\n"); |
| |
| // Send rejection status message |
| receiveReject.init(kWeaveProfile_Common, kStatus_BadRequest); |
| payload = PacketBuffer::New(); |
| receiveReject.pack(payload); |
| ret = ec->SendMessage(kWeaveProfile_Common, kMsgType_ReceiveReject, payload); |
| if (ret != WEAVE_NO_ERROR) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "3 BDX HandleReceiveInitRequest err=%d\n", ret); |
| } |
| |
| payload = NULL; |
| goto exit; |
| } |
| xfer->mMaxBlockSize = receiveInit.mMaxBlockSize; |
| |
| if (receiveInit.mFileDesignator.theLength <= 0) { |
| Log(kLogModule_BDX, kLogCategory_Error, "4 BDX HandleReceiveInitRequest (bad FileDesignator)\n"); |
| SendTransferError(ec, kWeaveProfile_Common, kStatus_LengthTooShort); |
| goto exit; |
| } |
| |
| // // Copy file name onto C-string |
| // // NOTE: the original string is not NUL terminated, but we know its length. |
| fileDesignator = (char*)malloc(receiveInit.mFileDesignator.theLength + 1); |
| memcpy(fileDesignator, receiveInit.mFileDesignator.theString, receiveInit.mFileDesignator.theLength); |
| fileDesignator[receiveInit.mFileDesignator.theLength] = '\0'; |
| |
| #if BUILD_FEATURE_IMAGE_CACHE |
| // // TODO Validate requested file path with value from nlhlfirmware.plist |
| // // Future: delegate path security validation |
| // // nlclient will open() this path as root, so we must be conservative in our validation. |
| // if (0 != strcmp(fileDesignator, bdxApp->mHostedFileName)) |
| // { |
| // Log(kLogModule_BDX, kLogCategory_Error, "5 BDX HandleReceiveInitRequest (forbidden FileDesignator)\n"); |
| // SendTransferError(ec, kWeaveProfile_Common, kStatus_UnknownFile); //TODO add 'forbidden' Weave status code |
| // free(fileDesignator); |
| // fileDesignator = NULL; |
| // goto exit; |
| // } |
| #else |
| Log(kLogModule_BDX, kLogCategory_Detail, "BDX: Download URI : %s\n", fileDesignator); |
| |
| /** |
| * Download file |
| */ |
| retval = DownloadFile(fileDesignator); |
| |
| if (retval != CURLE_OK && fileDesignator != NULL) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "BDX: Unable to download the file :%d\n", retval); |
| free(fileDesignator); |
| fileDesignator = NULL; |
| |
| receiveReject.init(kWeaveProfile_BDX, kStatus_UnknownFile); |
| payload = PacketBuffer::New(); |
| receiveReject.pack(payload); |
| ret = ec->SendMessage(kWeaveProfile_BDX, kMsgType_ReceiveReject, payload); |
| if (ret != WEAVE_NO_ERROR) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "8 BDX HandleReceiveInitRequest err=%d\n", ret); |
| } |
| |
| payload = NULL; |
| |
| goto exit; |
| } |
| #endif |
| // Open file to send |
| xfer->FD = fopen(fileDesignator, "r"); |
| if (xfer->FD == NULL) |
| { |
| SendTransferError(ec, kWeaveProfile_Common, kStatus_InternalServerProblem); |
| goto exit; |
| } |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "7 BDX HandleReceiveInitRequest validated request\n"); |
| |
| // Fire application callback once we've validated the request (TODO: call earlier? feels like semantic abuse) |
| if (bdxApp->OnBDXReceiveInitRequestReceived) |
| { |
| bdxApp->OnBDXReceiveInitRequestReceived(ec->PeerNodeId, ec->PeerAddr, payloadReceiveInit, bdxApp->mpAppState); |
| } |
| |
| // Set up response timeout and connection closed handler |
| ec->Con->AppState = xfer; |
| ec->Con->OnConnectionClosed = HandleBDXConnectionClosed; |
| ec->OnResponseTimeout = HandleResponseTimeout; |
| ec->ResponseTimeout = BDX_RESPONSE_TIMEOUT_MS; |
| |
| // Set ourselves up to handle first BlockQueryRequest. |
| ec->OnMessageReceived = HandleBlockQueryRequest; |
| |
| // Send a ReceiveAccept response back to the receiver. |
| ret = receiveAccept.init(BDX_SERVER_TRANSFER_MODE, receiveInit.mMaxBlockSize, receiveInit.mLength, NULL); |
| VerifyOrExit(ret == WEAVE_NO_ERROR, Log(kLogModule_BDX, kLogCategory_Progress, "7.5 BDX HandleReceiveInitRequest error initializing ReceiveAccept\n")); |
| |
| payload = PacketBuffer::New(); |
| ret = receiveAccept.pack(payload); |
| VerifyOrExit(ret == WEAVE_NO_ERROR, |
| Log(kLogModule_BDX, kLogCategory_Progress, "7.5 BDX HandleReceiveInitRequest packing err=%d\n", ret)); |
| |
| ret = ec->SendMessage(kWeaveProfile_BDX, kMsgType_ReceiveAccept, payload, ExchangeContext::kSendFlag_ExpectResponse); |
| payload = NULL; |
| if (ret != WEAVE_NO_ERROR) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "8 BDX HandleReceiveInitRequest err=%d\n", ret); |
| goto exit; |
| } |
| |
| free(fileDesignator); |
| fileDesignator = NULL; |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "9 BDX HandleReceiveInitRequest exiting (success)\n"); |
| return; |
| |
| exit: |
| Log(kLogModule_BDX, kLogCategory_Error, "10 BDX HandleReceiveInitRequest exiting (failure on code %d)\n", ret); |
| |
| if (fileDesignator) |
| { |
| free(fileDesignator); |
| fileDesignator = NULL; |
| } |
| |
| if (payloadReceiveInit) |
| { |
| PacketBuffer::Free(payloadReceiveInit); |
| } |
| |
| if (payload) |
| { |
| PacketBuffer::Free(payload); |
| } |
| |
| if (xfer) |
| { |
| bdxApp->ShutdownTransfer(xfer); |
| } |
| else |
| { |
| // Transfer object uninitialized, so we do this manually |
| if (ec) |
| { |
| if (ec->Con) |
| { |
| ec->Con->Close(); |
| ec->Con = NULL; |
| } |
| |
| ec->Close(); |
| ec = NULL; |
| } |
| } |
| } |
| |
| void BulkDataTransferServer::HandleSendInitRequest(ExchangeContext *ec, const IPPacketInfo *packetInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX HandleSendInitRequest entering\n"); |
| |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| BulkDataTransferServer *bdxApp = NULL; |
| BDXTransfer *xfer = NULL; |
| |
| SendInit sendInit; |
| SendAccept sendAccept; |
| SendReject sendReject; |
| |
| char *filename = NULL; |
| PacketBuffer *SendInitResponsePayload; |
| |
| VerifyOrExit(ec != NULL, |
| Log(kLogModule_BDX, kLogCategory_Error, "HandleSendInitRequest failed: NULL EC!")); |
| |
| bdxApp = static_cast<BulkDataTransferServer *>(ec->AppState); |
| |
| xfer = bdxApp->NewTransfer(); |
| //TODO: move this other initialization logic to NewTransfer()? |
| //xfer->mBlockCounter = 0; |
| xfer->EC = ec; |
| xfer->CompletedSuccessfully = false; |
| // Hang the Transfer handle on the EC now instead of the whole app |
| ec->AppState = (void*)xfer; |
| |
| VerifyOrExit(profileId == kWeaveProfile_BDX, |
| Log(kLogModule_BDX, kLogCategory_Error, "HandleSendInit failed: incorrect ProfileId")); |
| VerifyOrExit(msgType == kMsgType_SendInit, |
| Log(kLogModule_BDX, kLogCategory_Error, "HandleSendInit failed: Incorrect msgType")); |
| |
| //TODO? |
| //if (ec->Con == NULL) |
| //xfer->mSendFlags = ExchangeContext::kSendFlag_ExpectResponse | ExchangeContext::kSendFlag_UseWRMP; |
| //else |
| //xfer->mSendFlags = ExchangeContext::kSendFlag_ExpectResponse; |
| |
| /** |
| * Parse Send Init. Request |
| */ |
| err = SendInit::parse(payload, sendInit); |
| xfer->mMaxBlockSize = sendInit.mMaxBlockSize; |
| VerifyOrExit(err == WEAVE_NO_ERROR, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleSendInit: Unable to parse Send Init. request: %d", err)); |
| PacketBuffer::Free(payload); |
| payload = NULL; |
| |
| /** |
| * Allocate PacketBuffer |
| */ |
| SendInitResponsePayload = PacketBuffer::New(); |
| VerifyOrExit(SendInitResponsePayload != NULL, |
| Log(kLogModule_BDX, kLogCategory_Progress, "Error: HandleSendInit: Unable to allocate PacketBuffer: %d", err)); |
| |
| /** |
| * Extract file name and open it for writing |
| */ |
| VerifyOrExit(sendInit.mFileDesignator.theLength > 0, |
| Log(kLogModule_BDX, kLogCategory_Progress, "Error: HandleSendInit: No file name provided")); |
| |
| // build up the filename, which will be stored under /tmp, then open the file |
| filename = (char*) malloc (sendInit.mFileDesignator.theLength + 1 + strlen(TEMP_FILE_LOCATION)); |
| strcpy(filename, TEMP_FILE_LOCATION); |
| strncat(filename, sendInit.mFileDesignator.theString, sendInit.mFileDesignator.theLength); |
| Log(kLogModule_BDX, kLogCategory_Detail, "Opening file %s for writing...", filename); |
| |
| xfer->FD = fopen(filename, "w"); |
| |
| if (xfer->FD == NULL) // Unable to open file for writing. Already exists? TODO |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "Couldn't open file %s for writing...", filename); |
| fclose(xfer->FD); |
| free(filename); |
| filename = NULL; |
| |
| sendReject.init(kWeaveProfile_BDX, kStatus_UnknownFile); |
| sendReject.pack(SendInitResponsePayload); |
| |
| err = ec->SendMessage(kWeaveProfile_BDX, kMsgType_SendReject, SendInitResponsePayload, ExchangeContext::kSendFlag_ExpectResponse); |
| SendInitResponsePayload = NULL; |
| VerifyOrExit(err == WEAVE_NO_ERROR, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleSendInit: Failed to send reject message: %d", err)); |
| VerifyOrExit(false, |
| Log(kLogModule_BDX, kLogCategory_Progress, "Send Init. Request rejected")); |
| } |
| |
| free(filename); |
| filename = NULL; |
| |
| // Determine transfer mode (TODO: non-hard coded?) |
| xfer->mTransferMode = kMode_SenderDrive; |
| VerifyOrExit(sendInit.mSenderDriveSupported, |
| Log(kLogModule_BDX, kLogCategory_Progress, "SendInitResponse error: SenderDrive mode not supported on client!")); |
| |
| // Finish up configuring and then send the SendInitResponse message |
| sendAccept.init(xfer->mTransferMode, xfer->mMaxBlockSize, NULL); |
| sendAccept.pack(SendInitResponsePayload); |
| |
| ec->OnMessageReceived = HandleBlockSend; |
| |
| err = ec->SendMessage(kWeaveProfile_BDX, kMsgType_SendAccept, SendInitResponsePayload, |
| ExchangeContext::kSendFlag_ExpectResponse); |
| SendInitResponsePayload = NULL; |
| VerifyOrExit(err == WEAVE_NO_ERROR, |
| Log(kLogModule_BDX, kLogCategory_Progress, "SendInitResponse error sending accept message: %d", err)); |
| |
| return; |
| |
| exit: |
| |
| Log(kLogModule_BDX, kLogCategory_Error, "10 BDX HandleSendInitRequest exiting (failure)\n"); |
| |
| if (xfer) |
| { |
| xfer->BdxApp->ShutdownTransfer(xfer); |
| } |
| else |
| { |
| // Transfer object uninitialized, so we do this manually |
| if (ec) |
| { |
| if (ec->Con) |
| { |
| ec->Con->Close(); |
| ec->Con = NULL; |
| } |
| |
| ec->Close(); |
| ec = NULL; |
| } |
| } |
| } |
| |
| void BulkDataTransferServer::HandleBlockQueryRequest(ExchangeContext *ec, const IPPacketInfo *packetInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payloadBlockQuery) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX HandleBlockQueryRequest entering\n"); |
| WEAVE_ERROR ret = WEAVE_NO_ERROR; |
| char *block = NULL; |
| int len; |
| |
| BDXTransfer *xfer = (BDXTransfer* ) ec->AppState; |
| BulkDataTransferServer *bdxApp = xfer->BdxApp; |
| PacketBuffer *responseBuf = NULL; |
| |
| // Free unused query payload. |
| PacketBuffer::Free(payloadBlockQuery); |
| payloadBlockQuery = NULL; |
| |
| if (kWeaveProfile_BDX != profileId || kMsgType_BlockQuery != msgType) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "1 BDX HandleBlockQueryRequest bad msg type (%d, %d)\n", profileId, msgType); |
| SendTransferError(ec, kWeaveProfile_Common, kStatus_BadRequest); |
| goto exit; |
| } |
| |
| //FIXME: should this be freed? Or is setting to NULL good enough? |
| if ((responseBuf = PacketBuffer::New()) == NULL) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "2 BDX HandleBlockQueryRequest (PacketBuffer alloc failed)\n"); |
| SendTransferError(ec, kWeaveProfile_Common, kStatus_InternalServerProblem); |
| goto exit; |
| } |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "3 BDX HandleBlockQueryRequest"); |
| |
| block = (char *) responseBuf->Start(); |
| len = read_data(block, 1, xfer->mMaxBlockSize, xfer->FD); |
| responseBuf->SetDataLength((uint16_t) len); |
| |
| // EOF case first |
| // TODO: we don't actually pack the payload using BlockSend/EOF objects |
| // as we currently don't transmit block number |
| if (len < xfer->mMaxBlockSize && len >= 0) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "6 BDX HandleBlockQueryRequest (BlockEOF, len = %d)\n", len); |
| |
| // Prepare to handle BlockEOF ACK. |
| ec->OnMessageReceived = HandleBlockEOFAck; |
| |
| // Send a BlockEOF Response back to the sender. |
| ret = ec->SendMessage(kWeaveProfile_BDX, kMsgType_BlockEOF, responseBuf, ExchangeContext::kSendFlag_ExpectResponse); |
| responseBuf = NULL; |
| if (ret != WEAVE_NO_ERROR) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "7 BDX HandleBlockQueryRequest\n"); |
| goto exit; |
| } |
| |
| } |
| else if (len > 0) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "4 BDX HandleBlockQueryRequest (len = %d)\n", len); |
| |
| // Prepare to handle next BlockQueryRequest. |
| ec->OnMessageReceived = HandleBlockQueryRequest; |
| |
| // Send a BlockSend Response back to the sender. |
| ret = ec->SendMessage(kWeaveProfile_BDX, kMsgType_BlockSend, responseBuf, ExchangeContext::kSendFlag_ExpectResponse); |
| responseBuf = NULL; |
| if (ret != WEAVE_NO_ERROR) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "5 BDX HandleBlockQueryRequest (SendMessage failed, err=%d)\n", ret); |
| goto exit; |
| } |
| |
| } |
| else |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "8 BDX HandleBlockQueryRequest read failed (len < 0)\n"); |
| // read() failed |
| SendTransferError(ec, kWeaveProfile_Common, kStatus_InternalServerProblem); |
| goto exit; |
| } |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "9 BDX HandleBlockQueryRequest exiting (success)\n"); |
| return; |
| |
| exit: |
| Log(kLogModule_BDX, kLogCategory_Error, "10 BDX HandleBlockQueryRequest exiting (failure)\n"); |
| bdxApp->ShutdownTransfer(xfer); |
| if (responseBuf) |
| { |
| PacketBuffer::Free(responseBuf); |
| responseBuf = NULL; |
| } |
| } |
| |
| void BulkDataTransferServer::HandleBlockSend(ExchangeContext *ec, const IPPacketInfo *packetInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX HandleBlockSend entering\n"); |
| |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| const char *errMsg = NULL; |
| |
| int len = 0; |
| |
| BlockSend blockSend; |
| BDXTransfer *xfer = static_cast<BDXTransfer *>(ec->AppState); |
| |
| VerifyOrExit(ec->Con != NULL, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: Connection is NULL!")); |
| |
| VerifyOrExit(profileId == kWeaveProfile_BDX, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: Incorrect ProfileId")); |
| |
| VerifyOrExit(msgType == kMsgType_BlockSend or msgType == kMsgType_BlockEOF, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: Incorrect MsgType")); |
| |
| //DumpMemory(payload->Start(), payload->DataLength(), "--> ", 16); |
| |
| // Parse message data to get the block counter later |
| err = BlockSend::parse(payload, blockSend); |
| VerifyOrExit(err == WEAVE_NO_ERROR, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: Error parsing BlockSend")); |
| |
| //block = (uint8_t*) malloc (xfer->mMaxBlockSize); |
| |
| VerifyOrExit(xfer->FD != NULL, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: File handle is NULL!")); |
| //NOTE: we skip over the block counter so it doesn't appear in the file |
| len = write_data(blockSend.mData + sizeof(blockSend.mBlockCounter), 1, |
| blockSend.mLength - sizeof(blockSend.mBlockCounter), xfer->FD); |
| VerifyOrExit(len >= 0, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: Unable to read image into block")); |
| |
| PacketBuffer::Free(payload); |
| payload = NULL; |
| |
| // Always need to ACK a BlockEOF |
| if (msgType == kMsgType_BlockEOF) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "Sending BlockEOFAck"); |
| |
| BlockEOFAck blockEOFAck; |
| //TODO: deal with the block counter not being sent optionally |
| blockEOFAck.init(blockSend.mBlockCounter-1); //final ack uses same block-counter of last block-query request |
| PacketBuffer* blockEOFAckPayload = PacketBuffer::New(); |
| VerifyOrExit(blockEOFAckPayload != NULL, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: no memory")); |
| blockEOFAck.pack(blockEOFAckPayload); |
| |
| err = ec->SendMessage(kWeaveProfile_BDX, kMsgType_BlockEOFAck, blockEOFAckPayload); |
| blockEOFAckPayload = NULL; |
| VerifyOrExit(err == WEAVE_NO_ERROR, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: Failed to send message: err=%d", err)); |
| |
| xfer->BdxApp->ShutdownTransfer(xfer); |
| } |
| |
| // currently we only support synchronous mode, so send BlockAck |
| else { |
| Log(kLogModule_BDX, kLogCategory_Detail, "Sending BlockAck"); |
| |
| BlockAck blockAck; |
| blockAck.init(blockSend.mBlockCounter); |
| PacketBuffer* blockAckPayload = PacketBuffer::New(); |
| VerifyOrExit(blockAckPayload != NULL, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: no memory")); |
| blockAck.pack(blockAckPayload); |
| |
| err = ec->SendMessage(kWeaveProfile_BDX, kMsgType_BlockAck, blockAckPayload); |
| blockAckPayload = NULL; |
| VerifyOrExit(err == WEAVE_NO_ERROR, |
| Log(kLogModule_BDX, kLogCategory_Error, "Error: HandleBlockSend: Failed to send message: err=%d", err)); |
| } |
| |
| exit: |
| |
| if (err != WEAVE_NO_ERROR) |
| { |
| Log(kLogModule_BDX, kLogCategory_Progress, errMsg); |
| xfer->BdxApp->ShutdownTransfer(xfer); |
| } |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "HandleBlockSend exiting"); |
| return; |
| } |
| |
| void BulkDataTransferServer::HandleBlockEOFAck(ExchangeContext *ec, const IPPacketInfo *packetInfo, |
| const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX HandleBlockEOFAck entering\n"); |
| BDXTransfer *xfer = (BDXTransfer *) ec->AppState; |
| BulkDataTransferServer *bdxApp = xfer->BdxApp; |
| |
| // Free unused query payload. |
| PacketBuffer::Free(payload); |
| payload = NULL; |
| |
| if (kWeaveProfile_BDX != profileId || kMsgType_BlockEOFAck != msgType) |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "1 BDX HandleBlockEOFAck bad msg type (%d, %d)\n", profileId, msgType); |
| SendTransferError(ec, kWeaveProfile_Common, kStatus_BadRequest); |
| } |
| else |
| { |
| // Set flag for connection closed handler |
| xfer->CompletedSuccessfully = true; |
| |
| // Fire application callback |
| if (bdxApp->OnBDXBlockEOFAckReceived) |
| { |
| bdxApp->OnBDXBlockEOFAckReceived(ec->PeerNodeId, ec->PeerAddr, payload, bdxApp->mpAppState); |
| } |
| } |
| |
| // Either way it's the end of the line |
| bdxApp->ShutdownTransfer(xfer); |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "2 BDX HandleBlockEOFAck exiting\n"); |
| } |
| |
| void BulkDataTransferServer::HandleBDXConnectionClosed(WeaveConnection *con, WEAVE_ERROR conErr) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX HandleBDXConnectionClosed entering (conErr = %d)\n", conErr); |
| BDXTransfer *xfer = (BDXTransfer *) con->AppState; |
| BulkDataTransferServer *bdxApp = xfer->BdxApp; |
| bdxApp->ShutdownTransfer(xfer); |
| Log(kLogModule_BDX, kLogCategory_Detail, "1 BDX HandleBDXConnectionClosed exiting\n"); |
| } |
| |
| void BulkDataTransferServer::HandleResponseTimeout(ExchangeContext *ec) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 BDX HandleResponseTimeout entering\n"); |
| BDXTransfer *xfer = (BDXTransfer *) ec->AppState; |
| BulkDataTransferServer *bdxApp = xfer->BdxApp; |
| bdxApp->ShutdownTransfer(xfer); |
| Log(kLogModule_BDX, kLogCategory_Detail, "1 BDX HandleResponseTimeout exiting\n"); |
| } |
| |
| void BulkDataTransferServer::SendTransferError(ExchangeContext *ec, uint32_t aProfileId, uint16_t aStatusCode) |
| { |
| TransferError transferError; |
| transferError.init(aProfileId, aStatusCode); |
| PacketBuffer* payloadTransferError = PacketBuffer::New(); |
| transferError.pack(payloadTransferError); |
| ec->SendMessage(kWeaveProfile_BDX, kMsgType_TransferError, payloadTransferError); |
| payloadTransferError = NULL; |
| } |
| |
| } // namespace Profiles |
| } // namespace Weave |
| } // namespace nl |
| |
| #if !defined(BUILD_FEATURE_IMAGE_CACHE) |
| size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) |
| { |
| size_t written = fwrite(ptr, size, nmemb, stream); |
| return written; |
| } |
| |
| // Unclear as to why we need the while loop in here. Perhaps for |
| // handling streams that are being added to as we consume from them? |
| size_t read_data(char *ptr, size_t size, size_t n, FILE *stream) |
| { |
| int nread, left = n; |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "0 read_n entering\n"); |
| |
| while (left > 0) |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "1 read_n (left: %d)\n", left); |
| |
| if ((nread = fread(ptr, size, n, stream)) > 0) |
| { |
| left -= nread; |
| ptr += nread; |
| Log(kLogModule_BDX, kLogCategory_Detail, "2 read_n (nread: %d, left: %d)\n", nread, left); |
| } |
| else |
| { |
| Log(kLogModule_BDX, kLogCategory_Detail, "3 read_n (nread: 0, left: %d)\n", left); |
| return n - left; |
| } |
| } |
| |
| Log(kLogModule_BDX, kLogCategory_Detail, "4 read_n (n: %d, left: %d) exiting\n", n, left); |
| return n; |
| } |
| |
| |
| int DownloadFile(char *aFileDesignator) |
| { |
| CURL *curl; |
| FILE *fp; |
| CURLcode res = CURLE_FAILED_INIT; |
| char *pch = NULL, *file_name = NULL; |
| char *download_url = (char *) malloc(1 + strlen(aFileDesignator)); |
| |
| strcpy(download_url, aFileDesignator); |
| |
| // Extract the file name out of the download URL |
| pch = strtok(aFileDesignator,"/"); |
| while (pch != NULL) |
| { |
| file_name = pch; |
| pch = strtok(NULL, "/"); |
| } |
| |
| char outfilename[FILENAME_MAX] = TEMP_FILE_LOCATION; |
| strcat(outfilename, file_name); |
| |
| if (access(outfilename, F_OK) != -1) |
| { |
| strcpy(aFileDesignator, outfilename); |
| return 0; |
| } |
| else |
| { |
| curl = curl_easy_init(); |
| if (curl) |
| { |
| fp = fopen(outfilename,"wb"); |
| Log(kLogModule_BDX, kLogCategory_Error, "BDX: Downloading Image : |%s|\n", download_url); |
| curl_easy_setopt(curl, CURLOPT_URL, download_url); |
| curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); |
| curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); |
| res = curl_easy_perform(curl); |
| |
| /* always cleanup */ |
| curl_easy_cleanup(curl); |
| fclose(fp); |
| |
| if (res != CURLE_OK) |
| { |
| remove(outfilename); |
| aFileDesignator = NULL; |
| } |
| else |
| { |
| /*TODO: Cleanup old Pinna image files : SUN-918 */ |
| strcpy(aFileDesignator, outfilename); |
| } |
| } |
| else |
| { |
| Log(kLogModule_BDX, kLogCategory_Error, "BDX: Failed to initialize curl\n"); |
| aFileDesignator = NULL; |
| } |
| } |
| |
| free(download_url); |
| download_url = NULL; |
| |
| return res; |
| } |
| |
| #endif |