<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#pragma once

/**
 * @file mavlink_ftp.h
 *
 * MAVLink remote file server.
 *
 * Messages are wrapped in ENCAPSULATED_DATA messages. Every message includes
 * a session ID and sequence number.
 *
 * A limited number of requests (currently 2) may be outstanding at a time.
 * Additional messages will be discarded.
 *
 * Messages consist of a fixed header, followed by a data area.
 *
 */
 
#include <dirent.h>
#include <queue.h>

#include <nuttx/wqueue.h>
#include <systemlib/err.h>

#include "mavlink_messages.h"

class MavlinkFTP
{
public:
	MavlinkFTP();

	static MavlinkFTP	*getServer();

	// static interface
	void			handle_message(Mavlink* mavlink,
					mavlink_message_t *msg);

private:

	static const unsigned	kRequestQueueSize = 2;

	static MavlinkFTP	*_server;

	struct RequestHeader
	{
		uint8_t		magic;
		uint8_t		session;
		uint8_t		opcode;
		uint8_t		size;
		uint32_t	crc32;
		uint32_t	offset;
		uint8_t		data[];
	};

	enum Opcode : uint8_t
	{
		kCmdNone,	// ignored, always acked
		kCmdTerminate,	// releases sessionID, closes file
		kCmdReset,	// terminates all sessions
		kCmdList,	// list files in <path> from <offset>
		kCmdOpen,	// opens <path> for reading, returns <session>
		kCmdRead,	// reads <size> bytes from <offset> in <session>
		kCmdCreate,	// creates <path> for writing, returns <session>
		kCmdWrite,	// appends <size> bytes at <offset> in <session>
		kCmdRemove,	// remove file (only if created by server?)

		kRspAck,
		kRspNak
	};

	enum ErrorCode : uint8_t
	{
		kErrNone,
		kErrNoRequest,
		kErrNoSession,
		kErrSequence,
		kErrNotDir,
		kErrNotFile,
		kErrEOF,
		kErrNotAppend,
		kErrTooBig,
		kErrIO,
		kErrPerm
	};

	class Session
	{
	public:
		Session() : _fd(-1) {}

		static int	allocate();
		static Session	*get(unsigned index);
		static bool	terminate(unsigned index);
		static void	reset();

		void		terminate();
		bool		open(const char *path, bool create);
		int		read(off_t offset, uint8_t *buf, uint8_t count);
		int		append(off_t offset, uint8_t *buf, uint8_t count);

	private:
		static const unsigned kMaxSession = 2;
		static Session	_sessions[kMaxSession];

		int		_fd;
	};

	class Request
	{
	public:
		union {
			dq_entry_t	entry;
			work_s		work;
		};

		bool		decode(Mavlink *mavlink, mavlink_message_t *fromMessage) {
			if (fromMessage->msgid == MAVLINK_MSG_ID_ENCAPSULATED_DATA) {
				_mavlink = mavlink;
				mavlink_msg_encapsulated_data_decode(fromMessage, &_message);
				return true;
			}
			return false;
		}

		void		reply() {

			// XXX the proper way would be an IOCTL / uORB call, rather than exploiting the
			// flat memory architecture, as we're operating between threads here.
			mavlink_message_t msg;
			msg.checksum = 0;
			unsigned len = mavlink_msg_encapsulated_data_pack_chan(_mavlink->get_system_id(), _mavlink->get_component_id(),
				_mavlink->get_channel(), &msg, sequence(), rawData());
			// unsigned len = mavlink_msg_system_time_pack_chan(_mavlink->get_system_id(), _mavlink->get_component_id(),
			// 	_mavlink->get_channel(), &msg, 255, 255);

			if (!_mavlink->message_buffer_write(&msg, len+2)) {
				warnx("FTP TX ERR");
			} else {
				warnx("wrote: sys: %d, comp: %d, chan: %d, len: %d, checksum: %d",
					_mavlink->get_system_id(),
					_mavlink->get_component_id(),
					_mavlink->get_channel(),
					len,
					msg.checksum);
			}
		}

		uint8_t		*rawData() { return &_message.data[0]; }
		RequestHeader 	*header()  { return reinterpret_cast<RequestHeader *>(&_message.data[0]); }
		uint8_t         *requestData() { return &(header()->data[0]); }
		unsigned	dataSize() { return header()->size + sizeof(RequestHeader); }
		uint16_t	sequence() const { return _message.seqnr; }
		mavlink_channel_t channel() { return _mavlink->get_channel(); }

		char		*dataAsCString();

	private:<commit_msg>mavlink-ftp: Remove two extra bytes we don not need<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

#pragma once

/**
 * @file mavlink_ftp.h
 *
 * MAVLink remote file server.
 *
 * Messages are wrapped in ENCAPSULATED_DATA messages. Every message includes
 * a session ID and sequence number.
 *
 * A limited number of requests (currently 2) may be outstanding at a time.
 * Additional messages will be discarded.
 *
 * Messages consist of a fixed header, followed by a data area.
 *
 */
 
#include <dirent.h>
#include <queue.h>

#include <nuttx/wqueue.h>
#include <systemlib/err.h>

#include "mavlink_messages.h"

class MavlinkFTP
{
public:
	MavlinkFTP();

	static MavlinkFTP	*getServer();

	// static interface
	void			handle_message(Mavlink* mavlink,
					mavlink_message_t *msg);

private:

	static const unsigned	kRequestQueueSize = 2;

	static MavlinkFTP	*_server;

	struct RequestHeader
	{
		uint8_t		magic;
		uint8_t		session;
		uint8_t		opcode;
		uint8_t		size;
		uint32_t	crc32;
		uint32_t	offset;
		uint8_t		data[];
	};

	enum Opcode : uint8_t
	{
		kCmdNone,	// ignored, always acked
		kCmdTerminate,	// releases sessionID, closes file
		kCmdReset,	// terminates all sessions
		kCmdList,	// list files in <path> from <offset>
		kCmdOpen,	// opens <path> for reading, returns <session>
		kCmdRead,	// reads <size> bytes from <offset> in <session>
		kCmdCreate,	// creates <path> for writing, returns <session>
		kCmdWrite,	// appends <size> bytes at <offset> in <session>
		kCmdRemove,	// remove file (only if created by server?)

		kRspAck,
		kRspNak
	};

	enum ErrorCode : uint8_t
	{
		kErrNone,
		kErrNoRequest,
		kErrNoSession,
		kErrSequence,
		kErrNotDir,
		kErrNotFile,
		kErrEOF,
		kErrNotAppend,
		kErrTooBig,
		kErrIO,
		kErrPerm
	};

	class Session
	{
	public:
		Session() : _fd(-1) {}

		static int	allocate();
		static Session	*get(unsigned index);
		static bool	terminate(unsigned index);
		static void	reset();

		void		terminate();
		bool		open(const char *path, bool create);
		int		read(off_t offset, uint8_t *buf, uint8_t count);
		int		append(off_t offset, uint8_t *buf, uint8_t count);

	private:
		static const unsigned kMaxSession = 2;
		static Session	_sessions[kMaxSession];

		int		_fd;
	};

	class Request
	{
	public:
		union {
			dq_entry_t	entry;
			work_s		work;
		};

		bool		decode(Mavlink *mavlink, mavlink_message_t *fromMessage) {
			if (fromMessage->msgid == MAVLINK_MSG_ID_ENCAPSULATED_DATA) {
				_mavlink = mavlink;
				mavlink_msg_encapsulated_data_decode(fromMessage, &_message);
				return true;
			}
			return false;
		}

		void		reply() {

			// XXX the proper way would be an IOCTL / uORB call, rather than exploiting the
			// flat memory architecture, as we're operating between threads here.
			mavlink_message_t msg;
			msg.checksum = 0;
			unsigned len = mavlink_msg_encapsulated_data_pack_chan(_mavlink->get_system_id(), _mavlink->get_component_id(),
				_mavlink->get_channel(), &msg, sequence(), rawData());
			// unsigned len = mavlink_msg_system_time_pack_chan(_mavlink->get_system_id(), _mavlink->get_component_id(),
			// 	_mavlink->get_channel(), &msg, 255, 255);

			if (!_mavlink->message_buffer_write(&msg, len)) {
				warnx("FTP TX ERR");
			} else {
				warnx("wrote: sys: %d, comp: %d, chan: %d, len: %d, checksum: %d",
					_mavlink->get_system_id(),
					_mavlink->get_component_id(),
					_mavlink->get_channel(),
					len,
					msg.checksum);
			}
		}

		uint8_t		*rawData() { return &_message.data[0]; }
		RequestHeader 	*header()  { return reinterpret_cast<RequestHeader *>(&_message.data[0]); }
		uint8_t         *requestData() { return &(header()->data[0]); }
		unsigned	dataSize() { return header()->size + sizeof(RequestHeader); }
		uint16_t	sequence() const { return _message.seqnr; }
		mavlink_channel_t channel() { return _mavlink->get_channel(); }

		char		*dataAsCString();

	private:
{'fraction_non_alphanumeric': 0.12110570862596795, 'fraction_numerical': 0.00801368629569602, 'mean_word_length': 3.9762544802867383, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6592848', 'n_tokens_mistral': 4001, 'n_tokens_neox': 3525, 'n_words': 1157}