You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libiec61850/src/mms/iso_mms/server/mms_file_service.c

1074 lines
33 KiB
C

/*
* mms_file_service.c
*
* Copyright 2013, 2014 Michael Zillgith
*
* This file is part of libIEC61850.
*
* libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
#include "libiec61850_platform_includes.h"
#include "mms_server_internal.h"
#include "mms_client_internal.h"
#if (MMS_FILE_SERVICE == 1)
#include "hal_filesystem.h"
#include "conversions.h"
#define CONFIG_MMS_FILE_SERVICE_MAX_FILENAME_LENGTH 256
static void
createNullResponseExtendedTag(uint32_t invokeId, ByteBuffer* response, uint8_t tag)
{
uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2;
uint32_t confirmedResponsePDUSize = invokeIdSize + 3 ;
uint8_t* buffer = response->buffer;
int bufPos = 0;
bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
buffer[bufPos++] = 0x9f; /* Context specific / primitive / extended */
bufPos = BerEncoder_encodeTL(tag, 0, buffer, bufPos);
response->size = bufPos;
}
static MmsFileReadStateMachine*
getFreeFrsm(MmsServerConnection connection)
{
int i;
MmsFileReadStateMachine* freeFrsm = NULL;
for (i = 0; i < CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION; i++) {
if (connection->frsms[i].fileHandle == NULL) {
freeFrsm = &(connection->frsms[i]);
break;
}
}
return freeFrsm;
}
static MmsFileReadStateMachine*
getFrsm(MmsServerConnection connection, int32_t frsmId)
{
int i;
MmsFileReadStateMachine* frsm = NULL;
for (i = 0; i < CONFIG_MMS_MAX_NUMBER_OF_OPEN_FILES_PER_CONNECTION; i++) {
if (connection->frsms[i].fileHandle != NULL) {
if (connection->frsms[i].frsmId == frsmId) {
frsm = &(connection->frsms[i]);
break;
}
}
}
return frsm;
}
static int32_t
getNextFrsmId(MmsServerConnection connection)
{
uint32_t nextFrsmId = connection->nextFrsmId;
connection->nextFrsmId++;
return nextFrsmId;
}
static int
encodeFileAttributes(uint8_t tag, uint32_t fileSize, char* gtString, uint8_t* buffer, int bufPos)
{
uint32_t sizeOfFileSize = BerEncoder_UInt32determineEncodedSize(fileSize);
uint32_t gtStringSize = strlen(gtString);
uint32_t fileAttributesSize = 1 + BerEncoder_determineLengthSize(sizeOfFileSize) + sizeOfFileSize
+ 2 + gtStringSize;
if (buffer == NULL) {
return fileAttributesSize;
}
else {
bufPos = BerEncoder_encodeTL(tag, fileAttributesSize, buffer, bufPos); /* file attributes */
bufPos = BerEncoder_encodeTL(0x80, sizeOfFileSize, buffer, bufPos); /* file size */
bufPos = BerEncoder_encodeUInt32(fileSize, buffer, bufPos);
bufPos = BerEncoder_encodeOctetString(0x81, (uint8_t*) gtString, gtStringSize, buffer, bufPos);
return bufPos;
}
}
static void
createExtendedFilename(char* extendedFileName, char* fileName)
{
strcpy(extendedFileName, CONFIG_VIRTUAL_FILESTORE_BASEPATH);
strncat(extendedFileName, fileName, sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256);
}
static bool
getFileInfo(char* filename, uint32_t* fileSize, uint64_t* lastModificationTimestamp)
{
char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256];
createExtendedFilename(extendedFileName, filename);
return FileSystem_getFileInfo(extendedFileName, fileSize, lastModificationTimestamp);
}
static FileHandle
openFile(char* fileName, bool readWrite)
{
char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256];
createExtendedFilename(extendedFileName, fileName);
return FileSystem_openFile(extendedFileName, readWrite);
}
static DirectoryHandle
openDirectory(char* directoryName)
{
char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256];
createExtendedFilename(extendedFileName, directoryName);
return FileSystem_openDirectory(extendedFileName);
}
static bool
renameFile(char* oldFilename, char* newFilename) {
char extendedOldFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256];
char extendedNewFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256];
createExtendedFilename(extendedOldFileName, oldFilename);
createExtendedFilename(extendedNewFileName, newFilename);
return FileSystem_renameFile(extendedOldFileName, extendedNewFileName);
}
static bool
deleteFile(char* fileName) {
char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256];
createExtendedFilename(extendedFileName, fileName);
return FileSystem_deleteFile(extendedFileName);
}
void
mmsMsg_createFileOpenResponse(uint32_t invokeId, ByteBuffer* response, char* fullPath, MmsFileReadStateMachine* frsm)
{
uint64_t msTime;
getFileInfo(fullPath, &(frsm->fileSize), &msTime);
char gtString[30];
Conversions_msTimeToGeneralizedTime(msTime, (uint8_t*) gtString);
uint32_t fileAttributesSize = encodeFileAttributes(0xa1, frsm->fileSize, gtString, NULL, 0) + 2;
uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2;
uint32_t frsmIdSize = BerEncoder_UInt32determineEncodedSize(frsm->frsmId) + 2;
uint32_t openFileResponseSize = fileAttributesSize + frsmIdSize;
uint32_t confirmedResponsePDUSize = invokeIdSize + 2 + BerEncoder_determineLengthSize(openFileResponseSize)
+ openFileResponseSize;
uint8_t* buffer = response->buffer;
int bufPos = 0;
bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
buffer[bufPos++] = 0xbf;
bufPos = BerEncoder_encodeTL(0x48, openFileResponseSize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0x80, frsmIdSize - 2, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32(frsm->frsmId, buffer, bufPos);
bufPos = encodeFileAttributes(0xa1, frsm->fileSize, gtString, buffer, bufPos);
response->size = bufPos;
}
static bool
parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos , uint32_t invokeId, ByteBuffer* response)
{
uint8_t tag = buffer[(*bufPos)++];
int length;
if (tag != 0x19) {
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
return false;
}
*bufPos = BerDecoder_decodeLength(buffer, &length, *bufPos, maxBufPos);
if (*bufPos < 0) {
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
return false;
}
if (length > 255) {
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
return false;
}
memcpy(filename, buffer + *bufPos, length);
filename[length] = 0;
*bufPos += length;
return true;
}
void
mmsServer_handleFileDeleteRequest(
MmsServerConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response)
{
if (buffer[bufPos++] != 0x19)
goto exit_reject_invalid_pdu;
int length;
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
char filename[256];
memcpy(filename, buffer + bufPos, length);
filename[length] = 0;
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: mms_file_service.c: Delete file (%s)\n", filename);
if (!getFileInfo(filename, NULL, NULL)) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: mms_file_service.c: File (%s) not found\n", filename);
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
return;
}
if (!deleteFile(filename)) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: mms_file_service.c: Delete file (%s) failed\n", filename);
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_ACCESS_DENIED);
return;
}
createNullResponseExtendedTag(invokeId, response, 0x4c);
return;
exit_reject_invalid_pdu:
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
}
void
mmsServer_handleFileOpenRequest(
MmsServerConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response)
{
char filename[256];
bool hasFileName = false;
uint32_t filePosition = 0;
while (bufPos < maxBufPos) {
uint8_t tag = buffer[bufPos++];
int length;
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if (bufPos < 0) goto exit_reject_invalid_pdu;
switch(tag) {
case 0xa0: /* filename */
if (!parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response))
return;
hasFileName = true;
break;
case 0x81: /* initial position */
filePosition = BerDecoder_decodeUint32(buffer, length, bufPos);
bufPos += length;
break;
default: /* unrecognized parameter */
bufPos += length;
goto exit_reject_invalid_pdu;
}
}
if (hasFileName) {
MmsFileReadStateMachine* frsm = getFreeFrsm(connection);
if (frsm != NULL) {
FileHandle fileHandle = openFile(filename, false);
if (fileHandle != NULL) {
frsm->fileHandle = fileHandle;
frsm->readPosition = filePosition;
frsm->frsmId = getNextFrsmId(connection);
mmsMsg_createFileOpenResponse(invokeId, response, filename, frsm);
}
else
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
}
else
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
}
else
goto exit_invalid_parameter;
return;
exit_invalid_parameter:
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
return;
exit_reject_invalid_pdu:
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
}
#if (MMS_OBTAIN_FILE_SERVICE == 1)
static void /* Confirmed service error (ServiceError) */
createServiceErrorObtainFileError(uint32_t invokeId, ByteBuffer* response,
MmsError errorType, uint32_t value)
{
uint8_t buffer[8];
int size = BerEncoder_encodeUInt32WithTL(0x80, value, buffer, 0);
mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType,
buffer, size);
}
static void
createObtainFileResponse(uint32_t invokeId, ByteBuffer* response)
{
createNullResponseExtendedTag(invokeId, response, 0x2e);
}
void
mmsServer_fileUploadTask(MmsServer self, MmsObtainFileTask task)
{
//printf("mmsServer_fileUploadTask: state: %i\n", task->state);
switch (task->state) {
case MMS_FILE_UPLOAD_STATE_NOT_USED:
break;
case MMS_FILE_UPLOAD_STATE_FILE_OPEN_SENT:
{
if (Hal_getTimeInMs() > task->nextTimeout) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: file open timeout!\n");
task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE;
FileSystem_closeFile(task->fileHandle);
deleteFile(task->destinationFilename);
}
}
break;
case MMS_FILE_UPLOAD_STATE_SEND_FILE_READ:
{
ByteBuffer* request = MmsServer_reserveTransmitBuffer(self);
task->lastRequestInvokeId = MmsServerConnection_getNextRequestInvokeId(task->connection);
mmsClient_createFileReadRequest(task->lastRequestInvokeId, request, task->frmsId);
IsoConnection_sendMessage(task->connection->isoConnection, request, false);
MmsServer_releaseTransmitBuffer(self);
task->nextTimeout = Hal_getTimeInMs() + 2000; /* timeout 2000 ms */
task->state = MMS_FILE_UPLOAD_STATE_FILE_READ_SENT;
}
break;
case MMS_FILE_UPLOAD_STATE_FILE_READ_SENT:
if (Hal_getTimeInMs() > task->nextTimeout) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: file read timeout!\n");
task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE;
FileSystem_closeFile(task->fileHandle);
deleteFile(task->destinationFilename);
}
break;
case MMS_FILE_UPLOAD_STATE_SEND_FILE_CLOSE:
{
ByteBuffer* request = MmsServer_reserveTransmitBuffer(self);
task->lastRequestInvokeId = MmsServerConnection_getNextRequestInvokeId(task->connection);
mmsClient_createFileCloseRequest(task->lastRequestInvokeId, request, task->frmsId);
IsoConnection_sendMessage(task->connection->isoConnection, request, false);
MmsServer_releaseTransmitBuffer(self);
task->nextTimeout = Hal_getTimeInMs() + 2000; /* timeout 2000 ms */
task->state = MMS_FILE_UPLOAD_STATE_FILE_CLOSE_SENT;
}
break;
case MMS_FILE_UPLOAD_STATE_FILE_CLOSE_SENT:
if (Hal_getTimeInMs() > task->nextTimeout) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: file close timeout!\n");
task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE;
FileSystem_closeFile(task->fileHandle);
deleteFile(task->destinationFilename);
}
break;
case MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_SOURCE:
{
/* send ObtainFileError */
ByteBuffer* response = MmsServer_reserveTransmitBuffer(self);
createServiceErrorObtainFileError(task->obtainFileRequestInvokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT, 0);
IsoConnection_sendMessage(task->connection->isoConnection, response, false);
MmsServer_releaseTransmitBuffer(self);
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: ObtainFile service: failed to open file from client\n");
task->state = MMS_FILE_UPLOAD_STATE_NOT_USED;
}
break;
case MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_DESTINATION:
{
/* send ObtainFileError */
ByteBuffer* response = MmsServer_reserveTransmitBuffer(self);
createServiceErrorObtainFileError(task->obtainFileRequestInvokeId, response, MMS_ERROR_FILE_OTHER, 1);
IsoConnection_sendMessage(task->connection->isoConnection, response, false);
MmsServer_releaseTransmitBuffer(self);
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: ObtainFile service: failed to create local file\n");
task->state = MMS_FILE_UPLOAD_STATE_NOT_USED;
}
break;
case MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_RESPONSE:
{
ByteBuffer* response = MmsServer_reserveTransmitBuffer(self);
createObtainFileResponse(task->obtainFileRequestInvokeId, response);
IsoConnection_sendMessage(task->connection->isoConnection, response, false);
MmsServer_releaseTransmitBuffer(self);
if (self->getFileCompleteHandler)
self->getFileCompleteHandler(self->getFileCompleteHandlerParameter, task->connection, task->destinationFilename);
task->state = MMS_FILE_UPLOAD_STATE_NOT_USED;
}
break;
}
}
void
mmsServer_handleObtainFileRequest(
MmsServerConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response)
{
char sourceFilename[256];
bool hasSourceFileName = false;
char destinationFilename[256];
bool hasDestinationFilename = false;
while (bufPos < maxBufPos) {
uint8_t tag = buffer[bufPos++];
int length;
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if (bufPos < 0) goto exit_reject_invalid_pdu;
switch(tag) {
case 0xa1: /* source filename */
if (!parseFileName(sourceFilename, buffer, &bufPos, bufPos + length, invokeId, response))
return;
hasSourceFileName = true;
break;
case 0xa2: /* destination filename */
if (!parseFileName(destinationFilename, buffer, &bufPos, bufPos + length, invokeId, response))
return;
hasDestinationFilename = true;
break;
default: /* unrecognized parameter */
bufPos += length;
goto exit_reject_invalid_pdu;
}
}
if (hasSourceFileName && hasDestinationFilename) {
/* call callback to check if access is allowed */
if (connection->server->obtainFileHandler)
if (connection->server->obtainFileHandler(connection->server->obtainFileHandlerParameter, connection, sourceFilename, destinationFilename) == false)
goto exit_access_denied;
/* check if destination file already exists. If exists return error message */
char extendedFileName[sizeof(CONFIG_VIRTUAL_FILESTORE_BASEPATH) + 256];
createExtendedFilename(extendedFileName, destinationFilename);
if (FileSystem_getFileInfo(extendedFileName, NULL, NULL)) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: obtainFile - file already exists on server\n");
goto exit_file_already_exists;
}
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: Start download file %s from client to local file %s...\n", sourceFilename, destinationFilename);
MmsObtainFileTask task = MmsServer_getObtainFileTask(connection->server);
if (task != NULL) {
FileHandle fileHandle = openFile(destinationFilename, true);
if (fileHandle == NULL) {
task->state = MMS_FILE_UPLOAD_STATE_SEND_OBTAIN_FILE_ERROR_DESTINATION;
}
else {
/* send file open request */
task->lastRequestInvokeId = MmsServerConnection_getNextRequestInvokeId(connection);
task->connection = connection;
task->fileHandle = fileHandle;
task->obtainFileRequestInvokeId = invokeId;
strcpy(task->destinationFilename, destinationFilename);
ByteBuffer* request = MmsServer_reserveTransmitBuffer(connection->server);
mmsClient_createFileOpenRequest(task->lastRequestInvokeId, request, sourceFilename, 0);
IsoConnection_sendMessage(task->connection->isoConnection, request, false);
MmsServer_releaseTransmitBuffer(connection->server);
task->nextTimeout = Hal_getTimeInMs() + 2000; /* timeout 2000 ms */
task->state = MMS_FILE_UPLOAD_STATE_FILE_OPEN_SENT;
}
}
else
goto exit_unavailable;
}
else
goto exit_invalid_parameter;
return;
exit_invalid_parameter:
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
return;
exit_access_denied:
createServiceErrorObtainFileError(invokeId, response, MMS_ERROR_FILE_FILE_ACCESS_DENIED, 1);
return;
exit_file_already_exists:
createServiceErrorObtainFileError(invokeId, response, MMS_ERROR_FILE_DUPLICATE_FILENAME, 1);
return;
exit_unavailable:
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE);
return;
exit_reject_invalid_pdu:
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
}
#endif /* (MMS_OBTAIN_FILE_SERVICE == 1) */
void
mmsMsg_createFileReadResponse(int maxPduSize, uint32_t invokeId,
ByteBuffer* response, MmsFileReadStateMachine* frsm)
{
/* determine remaining bytes in file */
uint32_t bytesLeft = frsm->fileSize - frsm->readPosition;
uint32_t fileChunkSize = 0;
uint32_t maxFileChunkSize = maxPduSize - 20;
uint32_t fileReadResponseSize = 1; /* for tag */
bool moreFollows = true;
if (bytesLeft > maxFileChunkSize) {
fileChunkSize = maxFileChunkSize;
}
else {
fileChunkSize = bytesLeft;
moreFollows = false;
fileReadResponseSize += 3; /* for moreFollows */
}
fileReadResponseSize += fileChunkSize;
fileReadResponseSize += BerEncoder_determineLengthSize(fileChunkSize);
frsm->readPosition += fileChunkSize;
uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2;
uint32_t confirmedResponsePDUSize = invokeIdSize + 2 + BerEncoder_determineLengthSize(fileReadResponseSize)
+ fileReadResponseSize;
uint8_t* buffer = response->buffer;
int bufPos = 0;
bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
buffer[bufPos++] = 0xbf;
bufPos = BerEncoder_encodeTL(0x49, fileReadResponseSize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0x80, fileChunkSize, buffer, bufPos);
FileSystem_readFile(frsm->fileHandle, buffer + bufPos, fileChunkSize);
bufPos += fileChunkSize;
if (!moreFollows)
bufPos = BerEncoder_encodeBoolean(0x81, false, buffer, bufPos);
response->size = bufPos;
}
void
mmsServer_handleFileReadRequest(
MmsServerConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response)
{
int32_t frsmId = (int32_t) BerDecoder_decodeUint32(buffer, maxBufPos - bufPos, bufPos);
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: mmsServer_handleFileReadRequest read request for frsmId: %i\n", frsmId);
MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId);
if (frsm != NULL)
mmsMsg_createFileReadResponse(connection->maxPduSize, invokeId, response, frsm);
else
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_OTHER);
}
void
mmsMsg_createFileCloseResponse(uint32_t invokeId, ByteBuffer* response)
{
createNullResponseExtendedTag(invokeId, response, 0x4a);
}
void
mmsServer_handleFileCloseRequest(
MmsServerConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response)
{
int32_t frsmId = (int32_t) BerDecoder_decodeUint32(buffer, maxBufPos - bufPos, bufPos);
MmsFileReadStateMachine* frsm = getFrsm(connection, frsmId);
FileSystem_closeFile(frsm->fileHandle);
frsm->fileHandle = NULL;
frsm->frsmId = 0;
mmsMsg_createFileCloseResponse(invokeId, response);
}
static int //TODO remove redundancy - same as in client code!
encodeFileSpecification(uint8_t tag, char* fileSpecification, uint8_t* buffer, int bufPos)
{
uint32_t fileNameStringSize = strlen(fileSpecification);
uint32_t fileNameSeqSize = 1 + BerEncoder_determineLengthSize(fileNameStringSize) + fileNameStringSize;
if (buffer != NULL) {
bufPos = BerEncoder_encodeTL(tag, fileNameSeqSize, buffer, bufPos);
bufPos = BerEncoder_encodeOctetString(0x19, (uint8_t*) fileSpecification, fileNameStringSize, buffer, bufPos);
return bufPos;
}
else {
return fileNameSeqSize + 1 + BerEncoder_determineLengthSize(fileNameSeqSize);
}
}
static int
addFileEntriesToResponse(uint8_t* buffer, int bufPos, int maxBufSize, char* directoryName, char** continueAfterFileName, bool* moreFollows)
{
int directoryNameLength = strlen(directoryName);
DirectoryHandle directory = openDirectory(directoryName);
if (directory != NULL) {
bool isDirectory;
char* fileName = FileSystem_readDirectory(directory, &isDirectory);
while (fileName != NULL) {
directoryName[directoryNameLength] = 0;
if (directoryNameLength > 0) {
if (directoryName[directoryNameLength - 1] != '/')
strcat(directoryName, "/");
}
strcat(directoryName, fileName);
bufPos = addFileEntriesToResponse(buffer, bufPos, maxBufSize, directoryName, continueAfterFileName, moreFollows);
if (*moreFollows == true)
break;
fileName = FileSystem_readDirectory(directory, &isDirectory);
}
FileSystem_closeDirectory(directory);
}
else {
if (*continueAfterFileName != NULL) {
if (strcmp(*continueAfterFileName, directoryName) == 0) {
*continueAfterFileName = NULL;
}
}
else {
uint64_t msTime;
uint32_t fileSize;
if (getFileInfo(directoryName, &fileSize, &msTime)) {
char gtString[30];
Conversions_msTimeToGeneralizedTime(msTime, (uint8_t*) gtString);
int fileAttributesSize = encodeFileAttributes(0xa1, fileSize, gtString, NULL, 0);
int filenameSize = encodeFileSpecification(0xa0, directoryName, NULL, 0);
int dirEntrySize = 2 + fileAttributesSize + filenameSize;
int overallEntrySize = 1 + BerEncoder_determineLengthSize(dirEntrySize) + dirEntrySize;
int bufferSpaceLeft = maxBufSize - bufPos;
if (overallEntrySize > bufferSpaceLeft) {
*moreFollows = true;
}
else {
bufPos = BerEncoder_encodeTL(0x30, dirEntrySize, buffer, bufPos); /* SEQUENCE (DirectoryEntry) */
bufPos = encodeFileSpecification(0xa0, directoryName, buffer, bufPos); /* fileName */
bufPos = encodeFileAttributes(0xa1, fileSize, gtString, buffer, bufPos); /* file attributes */
}
}
else
bufPos = -1;
}
}
directoryName[directoryNameLength] = 0;
return bufPos;
}
static void
createFileDirectoryResponse(uint32_t invokeId, ByteBuffer* response, int maxPduSize, char* directoryName, char* continueAfterFileName)
{
int maxSize = maxPduSize - 3; /* reserve space for moreFollows */
uint8_t* buffer = response->buffer;
bool moreFollows = false;
int tempStartPos = 30; /* estimated header part with safety margin */
int tempCurPos = tempStartPos;
int tempEncoded = 0;
if (continueAfterFileName != NULL) {
if (strlen(continueAfterFileName) == 0)
continueAfterFileName = NULL;
}
tempCurPos = addFileEntriesToResponse(buffer, tempCurPos, maxSize, directoryName, &continueAfterFileName, &moreFollows);
if (tempCurPos < 0) {
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: Error opening directory!\n");
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
return;
}
tempEncoded = tempCurPos - tempStartPos;
uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2;
uint32_t listOfDirectoryEntrySeqSize = tempEncoded;
uint32_t listOfDirectoryEntrySize = 1 + BerEncoder_determineLengthSize(tempEncoded) + tempEncoded;
uint32_t fileDirectoryResponseSize = 1 + BerEncoder_determineLengthSize(listOfDirectoryEntrySize)
+ listOfDirectoryEntrySize;
if (moreFollows)
fileDirectoryResponseSize += 3; /* for moreFollows */
uint32_t confirmedResponsePDUSize = invokeIdSize + 2 + BerEncoder_determineLengthSize(fileDirectoryResponseSize)
+ fileDirectoryResponseSize;
int bufPos = 0;
bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
bufPos = BerEncoder_encodeUInt32((uint32_t) invokeId, buffer, bufPos);
buffer[bufPos++] = 0xbf;
bufPos = BerEncoder_encodeTL(0x4d, fileDirectoryResponseSize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0xa0, listOfDirectoryEntrySize, buffer, bufPos);
bufPos = BerEncoder_encodeTL(0x30, listOfDirectoryEntrySeqSize, buffer, bufPos); /* SEQUENCE OF (DirectoryEntry) */
/* memmove is required instead of memcpy since memory areas overlap */
memmove(buffer + bufPos, buffer + tempStartPos, tempEncoded);
bufPos += tempEncoded;
if (moreFollows)
bufPos = BerEncoder_encodeBoolean(0x81, moreFollows, buffer, bufPos);
response->size = bufPos;
}
void
mmsServer_handleFileRenameRequest(
MmsServerConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response)
{
char currentFileName[256] = "";
char newFileName[256] = "";
while (bufPos < maxBufPos) {
uint8_t tag = buffer[bufPos++];
int length;
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if (bufPos < 0) {
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
return;
}
switch(tag) {
case 0xa0: /* currentFilename */
if (!parseFileName(currentFileName, buffer, &bufPos, bufPos + length, invokeId, response))
return;
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: currentFileName: (%s)\n", currentFileName);
break;
case 0xa1: /* newFilename */
if (!parseFileName(newFileName, buffer, &bufPos, bufPos + length, invokeId, response))
return;
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: newFileName: (%s)\n", newFileName);
break;
default: /* ignore unknown tag */
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: unknown tag: (%02x)\n", tag);
bufPos += length;
break;
}
}
if ((strlen(currentFileName) != 0) && (strlen(newFileName) != 0)) {
if (renameFile(currentFileName, newFileName)){
/* send positive response */
createNullResponseExtendedTag(invokeId, response, 0x4b);
}
else
{
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: rename file failed!\n");
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_OTHER);
}
}
else
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
}
void
mmsServer_handleFileDirectoryRequest(
MmsServerConnection connection,
uint8_t* buffer, int bufPos, int maxBufPos,
uint32_t invokeId,
ByteBuffer* response)
{
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: handleFileDirectoryRequest bufPos:%i, maxBufPus:%i\n", bufPos, maxBufPos);
char filename[256] = "";
char continueAfterFileName[256];
char* continueAfter = NULL;
while (bufPos < maxBufPos) {
uint8_t tag = buffer[bufPos++];
int length;
bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos);
if (bufPos < 0) {
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
return;
}
switch(tag) {
case 0xa0: /* filename */
if (!parseFileName(filename, buffer, &bufPos, bufPos + length, invokeId, response))
return;
/* check for wildcard character(*) */
if ((strcmp(filename, "*") == 0) || (strcmp(filename, "/") == 0) || (strcmp(filename, "\\") == 0))
filename[0] = 0;
break;
case 0xa1: /* continue-after */
if (!parseFileName(continueAfterFileName, buffer, &bufPos, bufPos + length, invokeId, response))
return;
continueAfter = continueAfterFileName;
break;
default: /* unrecognized parameter */
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: handleFileDirectoryRequest: unrecognized parameter\n");
mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
return;
}
}
int maxPduSize = connection->maxPduSize;
createFileDirectoryResponse(invokeId, response, maxPduSize, filename, continueAfter);
}
#endif /* MMS_FILE_SERVICE == 1 */