mirror of https://github.com/ARMmbed/mbed-os.git
NFC: Add NDEF message framework.
parent
61445cc9af
commit
dcf38eec50
|
@ -0,0 +1,136 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NFC_COMMON_MIME_H_
|
||||||
|
#define NFC_COMMON_MIME_H_
|
||||||
|
|
||||||
|
#include "platform/Span.h"
|
||||||
|
#include <nfc/ndef/RecordParser.h>
|
||||||
|
#include <nfc/ndef/MessageBuilder.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup nfc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a mime object.
|
||||||
|
*/
|
||||||
|
class Mime {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Construct an empty Mime object.
|
||||||
|
*/
|
||||||
|
Mime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a mime object from its type and content
|
||||||
|
*
|
||||||
|
* @param mime_type The mime type of the object
|
||||||
|
* @param content The content of the object.
|
||||||
|
*/
|
||||||
|
Mime(
|
||||||
|
const Span<const uint8_t> &mime_type,
|
||||||
|
const Span<const uint8_t> &content
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy construct a Mime object.
|
||||||
|
* @param other The Mime object copied.
|
||||||
|
*/
|
||||||
|
Mime(const Mime &other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a Mime object.
|
||||||
|
*/
|
||||||
|
~Mime();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy asign a Mime object.
|
||||||
|
* @param to_copy The Mime object to copy.
|
||||||
|
* @return a reference to this object
|
||||||
|
*/
|
||||||
|
Mime& operator=(const Mime &other);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all attributes of a mime object.
|
||||||
|
* @param mime_type Type of the mime object.
|
||||||
|
* @param content Content of the mime object.
|
||||||
|
*/
|
||||||
|
void set_mime(
|
||||||
|
const Span<const uint8_t> &mime_type,
|
||||||
|
const Span<const uint8_t> &content
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the mime type.
|
||||||
|
* @return The mime type.
|
||||||
|
*/
|
||||||
|
Span<const uint8_t> get_mime_type() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the content of thr mime object.
|
||||||
|
* @return the content of the mime object.
|
||||||
|
*/
|
||||||
|
Span<const uint8_t> get_mime_content() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append into a message builder
|
||||||
|
*/
|
||||||
|
bool append_as_record(
|
||||||
|
ndef::MessageBuilder &message_builder,
|
||||||
|
bool is_last_record = false
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class MimeParser;
|
||||||
|
|
||||||
|
void move_data(
|
||||||
|
uint8_t *mime_record,
|
||||||
|
size_t mime_type_size,
|
||||||
|
size_t mime_content_size
|
||||||
|
);
|
||||||
|
|
||||||
|
size_t mime_size() const;
|
||||||
|
|
||||||
|
uint8_t *_mime;
|
||||||
|
size_t _type_size;
|
||||||
|
size_t _content_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a Mime payload.
|
||||||
|
*/
|
||||||
|
class MimeParser : public ndef::GenericRecordParser<MimeParser, Mime> {
|
||||||
|
public:
|
||||||
|
bool do_parse(const ndef::Record &record, Mime &mime);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
#endif /* NFC_COMMON_MIME_H_ */
|
|
@ -0,0 +1,167 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NFC_COMMON_SIMPLEMESSAGEPARSER_H_
|
||||||
|
#define NFC_COMMON_SIMPLEMESSAGEPARSER_H_
|
||||||
|
|
||||||
|
#include "platform/Span.h"
|
||||||
|
#include "nfc/ndef/MessageParser.h"
|
||||||
|
#include "nfc/ndef/RecordParser.h"
|
||||||
|
#include "URI.h"
|
||||||
|
#include "Text.h"
|
||||||
|
#include "Mime.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup nfc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic message parser that aggregates URIParser, TextParser and MimeParser.
|
||||||
|
*
|
||||||
|
* Custom parsers can be added at runtime as well.
|
||||||
|
*/
|
||||||
|
class SimpleMessageParser :
|
||||||
|
ndef::MessageParser::Delegate,
|
||||||
|
URIParser::Delegate,
|
||||||
|
TextParser::Delegate,
|
||||||
|
MimeParser::Delegate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Delegate invoked when the parser raise an event.
|
||||||
|
*/
|
||||||
|
struct Delegate {
|
||||||
|
/**
|
||||||
|
* Invoked when an error is present in the message.
|
||||||
|
* @param error The error present in the message.
|
||||||
|
*/
|
||||||
|
virtual void on_parsing_error(ndef::MessageParser::error_t error) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when parsing as started.
|
||||||
|
*/
|
||||||
|
virtual void on_parsing_started() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a text element has been parsed.
|
||||||
|
* @param text The text parsed.
|
||||||
|
*/
|
||||||
|
virtual void on_text_parsed(const Text &text, const ndef::RecordID &id) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a text element has been parsed.
|
||||||
|
* @param text The text parsed.
|
||||||
|
*/
|
||||||
|
virtual void on_uri_parsed(const URI &uri, const ndef::RecordID &id) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a mime element has been parsed.
|
||||||
|
* @param mime The mime object parsed.
|
||||||
|
*/
|
||||||
|
virtual void on_mime_parsed(const Mime &mime, const ndef::RecordID &id) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when an unknown record has been parsed.
|
||||||
|
* @param The record freshly parsed.
|
||||||
|
*/
|
||||||
|
virtual void on_unknown_record_parsed(const ndef::Record &record) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when parsing is over.
|
||||||
|
*/
|
||||||
|
virtual void on_parsing_terminated() { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~Delegate() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new CommonMessageParser.
|
||||||
|
*/
|
||||||
|
SimpleMessageParser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the handler that processes parsing events.
|
||||||
|
* @param delegate The parsing event handler.
|
||||||
|
*/
|
||||||
|
void set_delegate(Delegate *delegate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an NDEF Message.
|
||||||
|
*
|
||||||
|
* Records and errors are reported to the handler registered with
|
||||||
|
* set_event_handler.
|
||||||
|
*
|
||||||
|
* @param data_buffer The data buffer that contains the NDEF message.
|
||||||
|
*/
|
||||||
|
void parse(const Span<const uint8_t> &data_buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert a new parser in the parser chain.
|
||||||
|
* @param parser The parser to add in the parsing chain.
|
||||||
|
*/
|
||||||
|
void add_record_parser(ndef::RecordParser *parser);
|
||||||
|
|
||||||
|
private:
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Implementation of MessageParser::EventHandler
|
||||||
|
|
||||||
|
virtual void on_parsing_error(ndef::MessageParser::error_t error);
|
||||||
|
|
||||||
|
virtual void on_parsing_started();
|
||||||
|
|
||||||
|
virtual void on_record_parsed(const ndef::Record &record);
|
||||||
|
|
||||||
|
virtual void on_parsing_terminated();
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Implementation of URIParser::EventHandler
|
||||||
|
|
||||||
|
virtual void on_record_parsed(const URI &uri, const ndef::RecordID &id);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Implementation of TextParser::EventHandler
|
||||||
|
|
||||||
|
virtual void on_record_parsed(const Text &text, const ndef::RecordID &id);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Implementation of MimeParser::EventHandler
|
||||||
|
|
||||||
|
virtual void on_record_parsed(const Mime &mime, const ndef::RecordID &id);
|
||||||
|
|
||||||
|
ndef::MessageParser _message_parser;
|
||||||
|
ndef::RecordParserChain _record_parser_chain;
|
||||||
|
URIParser _uri_parser;
|
||||||
|
TextParser _text_parser;
|
||||||
|
MimeParser _mime_parser;
|
||||||
|
Delegate *_delegate;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
#endif /* NFC_COMMON_SIMPLEMESSAGEPARSER_H_ */
|
|
@ -0,0 +1,145 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NFC_COMMON_TEXT_H_
|
||||||
|
#define NFC_COMMON_TEXT_H_
|
||||||
|
|
||||||
|
#include "platform/Span.h"
|
||||||
|
#include <nfc/ndef/RecordParser.h>
|
||||||
|
#include <nfc/ndef/MessageBuilder.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup nfc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent the well known type text.
|
||||||
|
*/
|
||||||
|
class Text {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Encoding of the text.
|
||||||
|
*/
|
||||||
|
enum encoding_t {
|
||||||
|
UTF8 = 0,//!< UTF8
|
||||||
|
UTF16 = 1//!< UTF16
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an empty text element.
|
||||||
|
*/
|
||||||
|
Text();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a text element from a data buffer and an encoding.
|
||||||
|
* @param text_encoding The encoding of the text.
|
||||||
|
* @param language_code The string of the language code.
|
||||||
|
* @param text The text buffer.
|
||||||
|
*/
|
||||||
|
Text(
|
||||||
|
encoding_t text_encoding,
|
||||||
|
const Span<const uint8_t> &language_code,
|
||||||
|
const Span<const uint8_t> &text
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy construct a text element.
|
||||||
|
* @param to_copy
|
||||||
|
*/
|
||||||
|
Text(const Text &to_copy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a text element.
|
||||||
|
*/
|
||||||
|
~Text();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy assignment of another text element.
|
||||||
|
* @param to_copy The Text instance to copy
|
||||||
|
* @return a reference to this object.
|
||||||
|
*/
|
||||||
|
Text& operator=(const Text &to_copy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a text from an external buffer.
|
||||||
|
* @param language_code The language code of the text.
|
||||||
|
* @param text The text to copy.
|
||||||
|
*/
|
||||||
|
void set_text(
|
||||||
|
encoding_t text_encoding,
|
||||||
|
const Span<const uint8_t> &language_code,
|
||||||
|
const Span<const uint8_t> &text
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the encoding of the text.
|
||||||
|
* @return The encoding of the text.
|
||||||
|
*/
|
||||||
|
encoding_t get_encoding() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the language code.
|
||||||
|
* @return The language code.
|
||||||
|
*/
|
||||||
|
Span<const uint8_t> get_language_code() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the text contained in this object.
|
||||||
|
* @return The text contained in this object.
|
||||||
|
*/
|
||||||
|
Span<const uint8_t> get_text() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append into a message builder
|
||||||
|
*/
|
||||||
|
bool append_as_record(
|
||||||
|
ndef::MessageBuilder &message_builder,
|
||||||
|
bool is_last_record = false
|
||||||
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class TextParser;
|
||||||
|
|
||||||
|
void move_data(uint8_t *text, size_t size);
|
||||||
|
|
||||||
|
uint8_t *_text_record;
|
||||||
|
size_t _text_record_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a Text.
|
||||||
|
*/
|
||||||
|
class TextParser : public ndef::GenericRecordParser<TextParser, Text> {
|
||||||
|
public:
|
||||||
|
virtual bool do_parse(const ndef::Record &record, Text &text);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* NFC_COMMON_TEXT_H_ */
|
|
@ -0,0 +1,189 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NFC_COMMON_URI_H_
|
||||||
|
#define NFC_COMMON_URI_H_
|
||||||
|
|
||||||
|
#include <nfc/ndef/RecordParser.h>
|
||||||
|
#include <nfc/ndef/MessageBuilder.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <platform/Span.h>
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup nfc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model the well known type URI.
|
||||||
|
*/
|
||||||
|
class URI {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Identifier codes
|
||||||
|
*/
|
||||||
|
enum uri_identifier_code_t {
|
||||||
|
NA = 0x00, /// Not applicable
|
||||||
|
HTTP_WWW = 0x01, /// http://www.
|
||||||
|
HTTPS_WWW = 0x02, /// https://www.
|
||||||
|
HTTP = 0x03, /// http://
|
||||||
|
HTTPS = 0x04, /// https://
|
||||||
|
TEL = 0x05, /// tel:
|
||||||
|
MAILTO = 0x06, /// mailto:
|
||||||
|
FTP_ANONYMOUS = 0x07, /// ftp://anonymous:anonymous@
|
||||||
|
FTP_FTP = 0x08, /// ftp://ftp.
|
||||||
|
FTPS = 0x09, /// ftps://
|
||||||
|
SFTP = 0x0A, /// sftp://
|
||||||
|
SMB = 0x0B, /// smb://
|
||||||
|
NFS = 0x0C, /// nfs://
|
||||||
|
FTP = 0x0D, /// ftp://
|
||||||
|
DAV = 0x0E, /// dav://
|
||||||
|
NEWS = 0x0F, /// news:
|
||||||
|
TELNET = 0x10, /// telnet://
|
||||||
|
IMAP = 0x11, /// imap:
|
||||||
|
RSTP = 0x12, /// rstp://
|
||||||
|
URN = 0x13, /// urn:
|
||||||
|
POP = 0x14, /// pop:
|
||||||
|
SIP = 0x15, /// sip:
|
||||||
|
SIPS = 0x16, /// sips:
|
||||||
|
TFTP = 0x17, /// tftp:
|
||||||
|
BTSPP = 0x18, /// btspp://
|
||||||
|
BTL2CAP = 0x19, /// btl2cap://
|
||||||
|
BTGOEP = 0x1A, /// btgoep://
|
||||||
|
TCPOBEX = 0x1B, /// tcpobex://
|
||||||
|
IRDAOBEX = 0x1C, /// irdaobex://
|
||||||
|
FILE = 0x1D, /// file://
|
||||||
|
URN_EPC_ID = 0x1E, /// urn:epc:id:
|
||||||
|
URN_EPC_TAG = 0x1F, /// urn:epc:tag:
|
||||||
|
URN_EPC_PAT = 0x20, /// urn:epc:pat:
|
||||||
|
URN_EPC_RAW = 0x21, /// urn:epc:raw:
|
||||||
|
URN_EPC = 0x22, /// urn:epc:
|
||||||
|
URN_NFC = 0x23, /// urn:nfc:
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an empty URI object.
|
||||||
|
*/
|
||||||
|
URI();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a URI from an id and a uri field.
|
||||||
|
* @param id The code of the URI prefix.
|
||||||
|
* @param uri_field The URI itself.
|
||||||
|
* @param uri_field_size The size of the URI.
|
||||||
|
*/
|
||||||
|
URI(uri_identifier_code_t id, const Span<const uint8_t> &uri_field);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a URI from another URI.
|
||||||
|
* @param to_copy The uri copied.
|
||||||
|
*/
|
||||||
|
URI(const URI &to_copy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a URI object.
|
||||||
|
*/
|
||||||
|
~URI();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the content by the one of an existing URI.
|
||||||
|
* @param to_copy The URI to copy.
|
||||||
|
* @return a reference to this object
|
||||||
|
*/
|
||||||
|
URI& operator=(const URI &to_copy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace the value of the URI.
|
||||||
|
* @param id The ID of the URI
|
||||||
|
* @param uri_field A buffer containing the value of the URI field.
|
||||||
|
*/
|
||||||
|
void set_uri(
|
||||||
|
uri_identifier_code_t id,
|
||||||
|
const Span<const uint8_t> &uri_field
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the id of the uri.
|
||||||
|
* @return The id of the uri.
|
||||||
|
*/
|
||||||
|
uri_identifier_code_t get_id() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current value of the uri field.
|
||||||
|
* @return The value of the uri field.
|
||||||
|
*/
|
||||||
|
Span<const uint8_t> get_uri_field() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append into a message builder
|
||||||
|
*/
|
||||||
|
bool append_as_record(
|
||||||
|
ndef::MessageBuilder &message_builder,
|
||||||
|
bool is_last_record = false
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equal operator between two URIs
|
||||||
|
* @param lhs The URI on the left hand side
|
||||||
|
* @param rhs The URI on the right hand side
|
||||||
|
* @return true if lhs equals rhs or false.
|
||||||
|
*/
|
||||||
|
friend bool operator==(const URI &lhs, const URI &rhs)
|
||||||
|
{
|
||||||
|
if (lhs._uri_size != rhs._uri_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return memcmp(lhs._uri, rhs._uri, lhs._uri_size) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const URI &lhs, const URI &rhs)
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class URIParser;
|
||||||
|
|
||||||
|
void move_data(uint8_t *text, size_t size);
|
||||||
|
|
||||||
|
uint8_t *_uri;
|
||||||
|
size_t _uri_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser of a URI.
|
||||||
|
*/
|
||||||
|
class URIParser : public ndef::GenericRecordParser<URIParser, URI> {
|
||||||
|
public:
|
||||||
|
bool do_parse(const ndef::Record &record, URI &uri);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
#endif /* NFC_COMMON_URI_H_ */
|
|
@ -0,0 +1,138 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NFC_NDEF_MESSAGEBUILDER_H_
|
||||||
|
#define NFC_NDEF_MESSAGEBUILDER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "Record.h"
|
||||||
|
|
||||||
|
#include "platform/Span.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace ndef {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup nfc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a NDEF Message.
|
||||||
|
*/
|
||||||
|
class MessageBuilder {
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a new MessageBuilder that can be used to construct valid NDEF
|
||||||
|
* messages.
|
||||||
|
*
|
||||||
|
* @param buffer The data buffer that will contain the NDEF message.
|
||||||
|
*/
|
||||||
|
MessageBuilder(const Span<uint8_t> &buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a new record to the message being built.
|
||||||
|
*
|
||||||
|
* @param type The type of the record to insert.
|
||||||
|
* @param payload The payload of the record (optional).
|
||||||
|
* @param is_last_record true if the record to insert is the last record of
|
||||||
|
* the payload or false otherwise.
|
||||||
|
*
|
||||||
|
* @return true if the record has been successfully inserted or false
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* @note insertion can fail if the message is already complete or if the
|
||||||
|
* size remaining in the message buffer is not large enough to makes the
|
||||||
|
* record inserted fit.
|
||||||
|
*/
|
||||||
|
bool append_record(
|
||||||
|
const RecordType &type,
|
||||||
|
const RecordPayload &payload = RecordPayload(),
|
||||||
|
bool is_last_record = false
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append a new record to the message being built.
|
||||||
|
*
|
||||||
|
* @param record The record to insert.
|
||||||
|
*
|
||||||
|
* @return true if the record has been successfully inserted or false otherwise.
|
||||||
|
*
|
||||||
|
* @note insertion can fail if the message is already complete or if the
|
||||||
|
* size remaining in the message buffer is not large enough to makes the
|
||||||
|
* record inserted fit.
|
||||||
|
*/
|
||||||
|
bool append_record(const Record &record);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the builder state.
|
||||||
|
*/
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the builder state and assign a new buffer to it.
|
||||||
|
*/
|
||||||
|
void reset(const Span<uint8_t> &buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the message stored is complete and false otherwise.
|
||||||
|
*
|
||||||
|
* @return true if the message is complete or false.
|
||||||
|
*/
|
||||||
|
bool is_message_complete() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the buffer storing the data if the message is complete or an empty
|
||||||
|
* buffer if the message is not complete.
|
||||||
|
*
|
||||||
|
* @return The message built.
|
||||||
|
*/
|
||||||
|
Span<const uint8_t> get_message() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// append fields
|
||||||
|
void append_header(const Record &record);
|
||||||
|
void append_type_length(const Record &record);
|
||||||
|
void append_payload_length(const Record&);
|
||||||
|
void append_id_length(const Record&);
|
||||||
|
void append_type(const Record&);
|
||||||
|
void append_id(const Record&);
|
||||||
|
void append_payload(const Record&);
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
static size_t compute_record_size(const Record &record);
|
||||||
|
static bool is_short_payload(const Record &record);
|
||||||
|
|
||||||
|
// builder state.
|
||||||
|
Span<uint8_t> _message_buffer;
|
||||||
|
size_t _position;
|
||||||
|
bool _message_started;
|
||||||
|
bool _message_ended;
|
||||||
|
bool _in_chunk;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace ndef
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
#endif /* NFC_NDEF_MESSAGEBUILDER_H_ */
|
|
@ -0,0 +1,178 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NFC_NDEF_MESSAGEPARSER_H_
|
||||||
|
#define NFC_NDEF_MESSAGEPARSER_H_
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "platform/Span.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace ndef {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup nfc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Forward declaration
|
||||||
|
class Record;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event driven NDEF Message parser
|
||||||
|
*/
|
||||||
|
class MessageParser {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Error that can be reported by the parsing operation.
|
||||||
|
*/
|
||||||
|
enum error_t {
|
||||||
|
/**
|
||||||
|
* The message doesn't start with a message start tag.
|
||||||
|
*/
|
||||||
|
INVALID_MESSAGE_START,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There is not enough data left to pursue parsing of the message.
|
||||||
|
*/
|
||||||
|
INSUFICIENT_DATA,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type name of a record is invalid.
|
||||||
|
*/
|
||||||
|
INVALID_TYPE_NAME_FORMAT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An empty record is malformed.
|
||||||
|
*/
|
||||||
|
INVALID_EMPTY_RECORD,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record of unknown type embed a type length different than 0.
|
||||||
|
*/
|
||||||
|
INVALID_UNKNOWN_TYPE_LENGTH,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record of unchanged type contains a type.
|
||||||
|
*/
|
||||||
|
INVALID_UNCHANGED_TYPE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chunk record encountered.
|
||||||
|
*/
|
||||||
|
CHUNK_RECORD_NOT_SUPPORTED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message is not properly closed.
|
||||||
|
*/
|
||||||
|
MISSING_MESSAGE_END,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type is missing in a record expecting a type (well known type, media
|
||||||
|
* type, absolute uri or external type).
|
||||||
|
*/
|
||||||
|
MISSING_TYPE_VALUE
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report parsing event to the application.
|
||||||
|
*/
|
||||||
|
struct Delegate {
|
||||||
|
/**
|
||||||
|
* Invoked when parsing as started.
|
||||||
|
*/
|
||||||
|
virtual void on_parsing_started() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a record has been parsed.
|
||||||
|
*
|
||||||
|
* @param The record obtained from parsing.
|
||||||
|
*/
|
||||||
|
virtual void on_record_parsed(const Record &record) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when parsing is over.
|
||||||
|
*/
|
||||||
|
virtual void on_parsing_terminated() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when an error is present in the message.
|
||||||
|
* @param error The error present in the message.
|
||||||
|
*/
|
||||||
|
virtual void on_parsing_error(error_t error) { }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Protected non virtual destructor.
|
||||||
|
* Delegate is not meant to be destroyed in a polymorphic manner.
|
||||||
|
*/
|
||||||
|
~Delegate() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a message parser.
|
||||||
|
*/
|
||||||
|
MessageParser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the handler that processes parsing events.
|
||||||
|
* @param delegate The parsing event handler.
|
||||||
|
*/
|
||||||
|
void set_delegate(Delegate *delegate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an NDEF Message.
|
||||||
|
*
|
||||||
|
* Records and errors are reported to the handler registered with
|
||||||
|
* set_event_handler.
|
||||||
|
*
|
||||||
|
* @param data_buffer The data buffer that contains the NDEF message.
|
||||||
|
*/
|
||||||
|
void parse(const Span<const uint8_t> &data_buffer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct parsing_state_t;
|
||||||
|
|
||||||
|
// parser
|
||||||
|
bool parse_record(parsing_state_t &it);
|
||||||
|
|
||||||
|
static uint8_t compute_lengths_size(uint8_t header);
|
||||||
|
static uint8_t extract_type_length(parsing_state_t &s);
|
||||||
|
static uint32_t extract_payload_length(parsing_state_t &s, uint8_t header);
|
||||||
|
static uint8_t extract_id_length(parsing_state_t &s, uint8_t header);
|
||||||
|
|
||||||
|
// reporting
|
||||||
|
void report_parsing_started();
|
||||||
|
void report_record_parsed(const Record &record);
|
||||||
|
void report_parsing_terminated();
|
||||||
|
void report_parsing_error(error_t error, parsing_state_t& parsing_state);
|
||||||
|
|
||||||
|
Delegate *_delegate;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace ndef
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* NFC_NDEF_MESSAGEPARSER_H_ */
|
|
@ -0,0 +1,213 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NFC_NDEF_RECORD_H_
|
||||||
|
#define NFC_NDEF_RECORD_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "platform/Span.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace ndef {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup nfc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of constants of a record header
|
||||||
|
*/
|
||||||
|
struct Header {
|
||||||
|
static const uint8_t message_begin_bit = (1 << 7);
|
||||||
|
static const uint8_t message_end_bit = (1 << 6);
|
||||||
|
static const uint8_t chunk_flag_bit = (1 << 5);
|
||||||
|
static const uint8_t short_record_bit = (1 << 4);
|
||||||
|
static const uint8_t id_length_bit = (1 << 3);
|
||||||
|
static const uint8_t tnf_bits = (1 << 0) | (1 << 1) | (1 << 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a record type.
|
||||||
|
*
|
||||||
|
* A RecordType is composed of a type name format flag and an optional type
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
struct RecordType {
|
||||||
|
enum tnf_t {
|
||||||
|
/**
|
||||||
|
* empty type; value must be empty.
|
||||||
|
*/
|
||||||
|
empty = 0x00,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type defined by the NFC forum; value must be defined.
|
||||||
|
*/
|
||||||
|
well_known_type = 0x01,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mime type; value must be defined.
|
||||||
|
*/
|
||||||
|
media_type = 0x02,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Absolute URI; value must be defined.
|
||||||
|
*/
|
||||||
|
absolute_uri = 0x03,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type defined by vendors; value must be defined.
|
||||||
|
*/
|
||||||
|
external_type = 0x04,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unknown type; value must be empty.
|
||||||
|
*/
|
||||||
|
unknown = 0x05,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use for middle and terminating chunk record.
|
||||||
|
* value must be empty.
|
||||||
|
*/
|
||||||
|
unchanged = 0x06
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an unknown type.
|
||||||
|
*/
|
||||||
|
RecordType() : tnf(unknown), value() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a type with no value.
|
||||||
|
*
|
||||||
|
* @note Valid tnf are: empty, unknown and unchanged.
|
||||||
|
*
|
||||||
|
* @param tnf The type name format of the type.
|
||||||
|
*/
|
||||||
|
RecordType(tnf_t tnf) :
|
||||||
|
tnf(tnf), value()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a RecordType from a type name format and its associated value.
|
||||||
|
*
|
||||||
|
* @param tnf The type name format of the record type.
|
||||||
|
* @param value The value associated with the tnf.
|
||||||
|
*/
|
||||||
|
RecordType(tnf_t tnf, const Span<const uint8_t> &value) :
|
||||||
|
tnf(tnf), value(value)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type name format of the record type.
|
||||||
|
*/
|
||||||
|
tnf_t tnf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value associated with the record type. It can be empty.
|
||||||
|
*/
|
||||||
|
Span<const uint8_t> value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition of a Record payload.
|
||||||
|
*
|
||||||
|
* @note A payload can be empty.
|
||||||
|
*/
|
||||||
|
typedef Span<const uint8_t> RecordPayload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definition of a Record IR.
|
||||||
|
*
|
||||||
|
* @note ID's are optional and therefore it can be empty.
|
||||||
|
*/
|
||||||
|
typedef Span<const uint8_t> RecordID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent a record.
|
||||||
|
*/
|
||||||
|
struct Record {
|
||||||
|
/**
|
||||||
|
* Construct an empty record.
|
||||||
|
*/
|
||||||
|
Record() : type(), payload(), id(), chunk(false), last_record(false) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a record from its type, payload and id.
|
||||||
|
*
|
||||||
|
* The flags chunk and last_record can be added to indicate if the record
|
||||||
|
* is aprt of a chunk or the last one in a message.
|
||||||
|
*
|
||||||
|
* @param type The type of the record.
|
||||||
|
* @param payload The payload of the record.
|
||||||
|
* @param id The id associated with the record.
|
||||||
|
* @param chunk If true then this record is a chunk of a bigger record.
|
||||||
|
* @param last_record If true then this record is the last of the message
|
||||||
|
* containing it.
|
||||||
|
*/
|
||||||
|
Record(
|
||||||
|
RecordType type,
|
||||||
|
const RecordPayload &payload,
|
||||||
|
const RecordID &id,
|
||||||
|
bool chunk,
|
||||||
|
bool last_record
|
||||||
|
) :
|
||||||
|
type(type),
|
||||||
|
payload(payload),
|
||||||
|
id(id),
|
||||||
|
chunk(chunk),
|
||||||
|
last_record(last_record)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of the record.
|
||||||
|
*/
|
||||||
|
RecordType type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value of the payload.
|
||||||
|
*/
|
||||||
|
RecordPayload payload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the record.
|
||||||
|
*/
|
||||||
|
RecordID id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, this record is a chunked record.
|
||||||
|
*/
|
||||||
|
bool chunk:1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, this record is the last one of the payload containing it.
|
||||||
|
*/
|
||||||
|
bool last_record:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace ndef
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
#endif /* NFC_NDEF_RECORD_H_ */
|
|
@ -0,0 +1,168 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NFC_NDEF_RECORDPARSER_H_
|
||||||
|
#define NFC_NDEF_RECORDPARSER_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "Record.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace ndef {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @addtogroup nfc
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a record.
|
||||||
|
*/
|
||||||
|
struct RecordParser {
|
||||||
|
/**
|
||||||
|
* Construct a record parser.
|
||||||
|
*/
|
||||||
|
RecordParser() : _next_parser(NULL) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the record in input.
|
||||||
|
* @param record The NDEF record to parse.
|
||||||
|
* @return true if decoding has succeeded and false otherwise.
|
||||||
|
*/
|
||||||
|
virtual bool parse(const Record &record) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Protected non virtual destructor.
|
||||||
|
* RecordParser subclasses are not meant to be destroyed as RecordParser's.
|
||||||
|
*/
|
||||||
|
~RecordParser() { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class RecordParserChain;
|
||||||
|
RecordParser *_next_parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GenericRecordParser.
|
||||||
|
*
|
||||||
|
* @tparam ParserImplementation the implementation type of the parser.
|
||||||
|
* It must provides A decoding function named do_parse that accept a const
|
||||||
|
* reference to a record and a reference to the type parsed and return a boolean
|
||||||
|
* status that indicates the result of the parsing operation.
|
||||||
|
*
|
||||||
|
* @tparam ParsingResult Type produced by the parsing operation when successful.
|
||||||
|
*/
|
||||||
|
template<typename ParserImplementation, typename ParsingResult>
|
||||||
|
struct GenericRecordParser : public RecordParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle that receives parsed values.
|
||||||
|
*/
|
||||||
|
struct Delegate {
|
||||||
|
/**
|
||||||
|
* Called when a record has been parsed and converted into a value_type.
|
||||||
|
* @param record The record in its parsed form.
|
||||||
|
*/
|
||||||
|
virtual void on_record_parsed(const ParsingResult &record, const RecordID &id) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~Delegate() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a record parser.
|
||||||
|
*/
|
||||||
|
GenericRecordParser() : _delegate(NULL) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see RecordParser::parse
|
||||||
|
*/
|
||||||
|
virtual bool parse(const Record &record)
|
||||||
|
{
|
||||||
|
ParsingResult parsed_value;
|
||||||
|
if (static_cast<ParserImplementation*>(this)->do_parse(record, parsed_value)) {
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_record_parsed(parsed_value, record.id);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the handler that processes record parser.
|
||||||
|
* @param handler
|
||||||
|
*/
|
||||||
|
void set_delegate(Delegate *delegate)
|
||||||
|
{
|
||||||
|
_delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Protected non virtual destructor.
|
||||||
|
*/
|
||||||
|
~GenericRecordParser() { }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Delegate *_delegate;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record parser chain.
|
||||||
|
*/
|
||||||
|
struct RecordParserChain {
|
||||||
|
/**
|
||||||
|
* Construct a parser chain.
|
||||||
|
*/
|
||||||
|
RecordParserChain() : _parsers(NULL) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a parser chain.
|
||||||
|
*/
|
||||||
|
~RecordParserChain() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a record.
|
||||||
|
* @param record The record to parse.
|
||||||
|
* @return true if the record has been parsed and false otherwise.
|
||||||
|
*/
|
||||||
|
bool parse(const Record &record);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a parser at the end of the parser list.
|
||||||
|
* @param parser The parser to add.
|
||||||
|
*/
|
||||||
|
void set_next_parser(RecordParser *parser);
|
||||||
|
|
||||||
|
private:
|
||||||
|
RecordParser *_parsers;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
} // namespace ndef
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* NFC_NDEF_RECORDPARSER_H_ */
|
|
@ -0,0 +1,153 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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 <cstring>
|
||||||
|
|
||||||
|
#include "Mime.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
Mime::Mime() :
|
||||||
|
_mime(NULL),
|
||||||
|
_type_size(0),
|
||||||
|
_content_size(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Mime::Mime(
|
||||||
|
const Span<const uint8_t> &mime_type,
|
||||||
|
const Span<const uint8_t> &content
|
||||||
|
) : _mime(new uint8_t[mime_type.size() + content.size()]),
|
||||||
|
_type_size(mime_type.size()),
|
||||||
|
_content_size(content.size())
|
||||||
|
{
|
||||||
|
memcpy(_mime, mime_type.data(), mime_type.size());
|
||||||
|
memcpy(_mime + mime_type.size(), content.data(), content.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Mime::Mime(const Mime &to_copy) :
|
||||||
|
_mime(new uint8_t[to_copy.mime_size()]),
|
||||||
|
_type_size(to_copy._type_size),
|
||||||
|
_content_size(to_copy._content_size)
|
||||||
|
{
|
||||||
|
memcpy(_mime, to_copy._mime, to_copy.mime_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Mime::~Mime()
|
||||||
|
{
|
||||||
|
delete[] _mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mime& Mime::operator=(const Mime &to_copy)
|
||||||
|
{
|
||||||
|
if (this == &to_copy) {
|
||||||
|
return * this;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] _mime;
|
||||||
|
|
||||||
|
_mime = new uint8_t[to_copy.mime_size()];
|
||||||
|
memcpy(_mime, to_copy._mime, to_copy.mime_size());
|
||||||
|
_type_size = to_copy._type_size;
|
||||||
|
_content_size = to_copy._content_size;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mime::set_mime(
|
||||||
|
const Span<const uint8_t> &mime_type,
|
||||||
|
const Span<const uint8_t> &content
|
||||||
|
) {
|
||||||
|
delete[] _mime;
|
||||||
|
|
||||||
|
_mime = new uint8_t[mime_type.size() + content.size()];
|
||||||
|
memcpy(_mime, mime_type.data(), mime_type.size());
|
||||||
|
memcpy(_mime + mime_type.size(), content.data(), content.size());
|
||||||
|
_type_size = mime_type.size();
|
||||||
|
_content_size = content.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<const uint8_t> Mime::get_mime_type() const
|
||||||
|
{
|
||||||
|
return make_const_Span(_mime, _type_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<const uint8_t> Mime::get_mime_content() const
|
||||||
|
{
|
||||||
|
return make_const_Span(_mime + _type_size, _content_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mime::append_as_record(
|
||||||
|
ndef::MessageBuilder &message_builder,
|
||||||
|
bool is_last_record
|
||||||
|
) {
|
||||||
|
return message_builder.append_record(
|
||||||
|
ndef::RecordType(
|
||||||
|
ndef::RecordType::media_type,
|
||||||
|
get_mime_type()
|
||||||
|
),
|
||||||
|
get_mime_content(),
|
||||||
|
is_last_record
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mime::move_data(
|
||||||
|
uint8_t *mime_record,
|
||||||
|
size_t mime_type_size,
|
||||||
|
size_t mime_content_size
|
||||||
|
) {
|
||||||
|
delete[] _mime;
|
||||||
|
_mime = mime_record;
|
||||||
|
_type_size = mime_type_size;
|
||||||
|
_content_size = mime_content_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Mime::mime_size() const
|
||||||
|
{
|
||||||
|
return _type_size + _content_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MimeParser::do_parse(const ndef::Record &record, Mime &mime)
|
||||||
|
{
|
||||||
|
if (record.type.tnf != ndef::RecordType::media_type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A type and a payload should be present
|
||||||
|
if (record.type.value.empty() || record.payload.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the buffer
|
||||||
|
size_t type_size = record.type.value.size();
|
||||||
|
size_t content_size = record.payload.size();
|
||||||
|
uint8_t *mime_buffer = new uint8_t[type_size + content_size];
|
||||||
|
|
||||||
|
// copy type
|
||||||
|
memcpy(mime_buffer, record.type.value.data(), type_size);
|
||||||
|
|
||||||
|
// copy content
|
||||||
|
memcpy(mime_buffer + type_size, record.payload.data(), content_size);
|
||||||
|
|
||||||
|
mime.move_data(mime_buffer, type_size, content_size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
|
@ -0,0 +1,119 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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 "SimpleMessageParser.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
SimpleMessageParser::SimpleMessageParser() :
|
||||||
|
_message_parser(),
|
||||||
|
_record_parser_chain(),
|
||||||
|
_uri_parser(),
|
||||||
|
_text_parser(),
|
||||||
|
_mime_parser(),
|
||||||
|
_delegate(NULL)
|
||||||
|
{
|
||||||
|
// setup the parser chain
|
||||||
|
_record_parser_chain.set_next_parser(&_uri_parser);
|
||||||
|
_record_parser_chain.set_next_parser(&_text_parser);
|
||||||
|
_record_parser_chain.set_next_parser(&_mime_parser);
|
||||||
|
|
||||||
|
// wire event handling
|
||||||
|
_message_parser.set_delegate(this);
|
||||||
|
_uri_parser.set_delegate(this);
|
||||||
|
_text_parser.set_delegate(this);
|
||||||
|
_mime_parser.set_delegate(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::set_delegate(Delegate *delegate)
|
||||||
|
{
|
||||||
|
_delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::parse(const Span<const uint8_t> &data_buffer)
|
||||||
|
{
|
||||||
|
_message_parser.parse(data_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::add_record_parser(ndef::RecordParser *parser)
|
||||||
|
{
|
||||||
|
_record_parser_chain.set_next_parser(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::on_parsing_error(ndef::MessageParser::error_t error)
|
||||||
|
{
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_parsing_error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::on_parsing_started()
|
||||||
|
{
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_parsing_started();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::on_record_parsed(const ndef::Record &record)
|
||||||
|
{
|
||||||
|
bool parsed = _record_parser_chain.parse(record);
|
||||||
|
|
||||||
|
if (!parsed && _delegate) {
|
||||||
|
_delegate->on_unknown_record_parsed(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::on_parsing_terminated()
|
||||||
|
{
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_parsing_terminated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::on_record_parsed(
|
||||||
|
const URI &uri,
|
||||||
|
const ndef::RecordID &id
|
||||||
|
) {
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_uri_parsed(uri, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::on_record_parsed(
|
||||||
|
const Text &text,
|
||||||
|
const ndef::RecordID &id
|
||||||
|
) {
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_text_parsed(text, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SimpleMessageParser::on_record_parsed(
|
||||||
|
const Mime &mime,
|
||||||
|
const ndef::RecordID &id
|
||||||
|
) {
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_mime_parsed(mime, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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 <cstring>
|
||||||
|
|
||||||
|
#include "Text.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static const uint8_t utf16_encoding_bit = (1 << 7);
|
||||||
|
static const uint8_t language_code_size_mask = 0x3F;
|
||||||
|
static const uint8_t header_index = 0;
|
||||||
|
static const uint8_t language_code_index = 1;
|
||||||
|
static const uint8_t header_size = 1;
|
||||||
|
static const uint8_t text_record_type_value[] = { 'T' };
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
Text::Text() :
|
||||||
|
_text_record(NULL),
|
||||||
|
_text_record_size(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Text::Text(const Text &other) :
|
||||||
|
_text_record(other._text_record ? new uint8_t[other._text_record_size] : NULL),
|
||||||
|
_text_record_size(other._text_record_size)
|
||||||
|
{
|
||||||
|
memcpy(_text_record, other._text_record, _text_record_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Text::Text(
|
||||||
|
encoding_t text_encoding,
|
||||||
|
const Span<const uint8_t> &language_code,
|
||||||
|
const Span<const uint8_t> &text
|
||||||
|
) : _text_record(NULL),
|
||||||
|
_text_record_size(0)
|
||||||
|
{
|
||||||
|
set_text(text_encoding, language_code, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
Text::~Text()
|
||||||
|
{
|
||||||
|
delete[] _text_record;
|
||||||
|
}
|
||||||
|
|
||||||
|
Text& Text::operator=(const Text &other)
|
||||||
|
{
|
||||||
|
if (this == &other) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_text_record_size = other._text_record_size;
|
||||||
|
|
||||||
|
delete[] _text_record;
|
||||||
|
if (!other._text_record) {
|
||||||
|
_text_record = NULL;
|
||||||
|
} else {
|
||||||
|
_text_record = new uint8_t[_text_record_size];
|
||||||
|
memcpy(_text_record, other._text_record, _text_record_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::set_text(
|
||||||
|
encoding_t text_encoding,
|
||||||
|
const Span<const uint8_t> &language_code,
|
||||||
|
const Span<const uint8_t> &text
|
||||||
|
) {
|
||||||
|
delete[] _text_record;
|
||||||
|
|
||||||
|
_text_record_size = header_size + language_code.size() + text.size();
|
||||||
|
_text_record = new uint8_t[_text_record_size];
|
||||||
|
|
||||||
|
// build the header
|
||||||
|
_text_record[header_index] = 0;
|
||||||
|
if (text_encoding == UTF16) {
|
||||||
|
_text_record[header_index] |= utf16_encoding_bit;
|
||||||
|
}
|
||||||
|
_text_record[header_index] |= language_code.size();
|
||||||
|
|
||||||
|
// language code
|
||||||
|
memcpy(_text_record + language_code_index, language_code.data(), language_code.size());
|
||||||
|
|
||||||
|
// actual text
|
||||||
|
memcpy(_text_record + language_code_index + language_code.size(), text.data(), text.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Text::encoding_t Text::get_encoding() const
|
||||||
|
{
|
||||||
|
return (_text_record[header_index] & utf16_encoding_bit) ? UTF16 : UTF8;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<const uint8_t> Text::get_language_code() const {
|
||||||
|
return make_const_Span(
|
||||||
|
_text_record + language_code_index,
|
||||||
|
_text_record[header_index] & language_code_size_mask
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<const uint8_t> Text::get_text() const {
|
||||||
|
size_t language_code_size = get_language_code().size();
|
||||||
|
|
||||||
|
return make_const_Span(
|
||||||
|
_text_record + header_size + language_code_size,
|
||||||
|
_text_record_size - header_size - language_code_size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::move_data(uint8_t *text, size_t size) {
|
||||||
|
delete[] _text_record;
|
||||||
|
_text_record = text;
|
||||||
|
_text_record_size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Text::append_as_record(
|
||||||
|
ndef::MessageBuilder &message_builder,
|
||||||
|
bool is_last_record
|
||||||
|
) {
|
||||||
|
// Build the record type
|
||||||
|
ndef::RecordType type(
|
||||||
|
ndef::RecordType::well_known_type,
|
||||||
|
text_record_type_value
|
||||||
|
);
|
||||||
|
|
||||||
|
// build the record payload
|
||||||
|
ndef::RecordPayload payload(_text_record, _text_record_size);
|
||||||
|
return message_builder.append_record(type, payload, is_last_record);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextParser::do_parse(const ndef::Record &record, Text &text)
|
||||||
|
{
|
||||||
|
if (record.type.tnf != ndef::RecordType::well_known_type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the record type value should be equal to `T`
|
||||||
|
if (record.type.value != make_const_Span(text_record_type_value) ||
|
||||||
|
record.payload.empty()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the buffer
|
||||||
|
size_t text_record_size = record.payload.size();
|
||||||
|
uint8_t *text_record = new uint8_t[text_record_size];
|
||||||
|
memcpy(text_record, record.payload.data(), text_record_size);
|
||||||
|
|
||||||
|
text.move_data(text_record, text_record_size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
|
@ -0,0 +1,162 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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 <cstring>
|
||||||
|
#include "URI.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static const uint8_t uri_id_code_size = 1;
|
||||||
|
static const uint8_t uri_id_index = 0;
|
||||||
|
static const uint8_t uri_field_index = 1;
|
||||||
|
static const uint8_t uri_record_type_value[] = { 'U' } ;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace common {
|
||||||
|
|
||||||
|
URI::URI() :
|
||||||
|
_uri(NULL),
|
||||||
|
_uri_size(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
URI::URI(uri_identifier_code_t id, const Span<const uint8_t> &uri_field) :
|
||||||
|
_uri(uri_field.size() ? new uint8_t[uri_id_code_size + uri_field.size()] : NULL),
|
||||||
|
_uri_size(uri_id_code_size + uri_field.size())
|
||||||
|
{
|
||||||
|
_uri[uri_id_index] = id;
|
||||||
|
memcpy(_uri + uri_field_index, uri_field.data(), uri_field.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
URI::URI(const URI &other) :
|
||||||
|
_uri(other._uri ? new uint8_t[other._uri_size] : NULL),
|
||||||
|
_uri_size(other._uri_size)
|
||||||
|
{
|
||||||
|
memcpy(_uri, other._uri, other._uri_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
URI::~URI()
|
||||||
|
{
|
||||||
|
delete[] _uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
URI& URI::operator=(const URI &other)
|
||||||
|
{
|
||||||
|
delete[] _uri;
|
||||||
|
|
||||||
|
if (!other._uri) {
|
||||||
|
_uri = NULL;
|
||||||
|
_uri_size = 0;
|
||||||
|
} else {
|
||||||
|
_uri = new uint8_t[other._uri_size];
|
||||||
|
_uri_size = other._uri_size;
|
||||||
|
memcpy(_uri, other._uri, other._uri_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void URI::set_uri(
|
||||||
|
uri_identifier_code_t id,
|
||||||
|
const Span<const uint8_t> &uri_field
|
||||||
|
) {
|
||||||
|
delete[] _uri;
|
||||||
|
|
||||||
|
if (uri_field.empty()) {
|
||||||
|
_uri = NULL;
|
||||||
|
_uri_size = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_uri = new uint8_t[uri_id_code_size + uri_field.size()];
|
||||||
|
_uri_size = uri_id_code_size + uri_field.size();
|
||||||
|
_uri[uri_id_index] = id;
|
||||||
|
memcpy(_uri + uri_field_index, uri_field.data(), uri_field.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
URI::uri_identifier_code_t URI::get_id() const
|
||||||
|
{
|
||||||
|
if (!_uri) {
|
||||||
|
return NA;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<uri_identifier_code_t>(_uri[uri_id_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<const uint8_t> URI::get_uri_field() const
|
||||||
|
{
|
||||||
|
if (!_uri) {
|
||||||
|
return Span<const uint8_t>();
|
||||||
|
}
|
||||||
|
return make_const_Span(
|
||||||
|
_uri + uri_field_index,
|
||||||
|
_uri_size - uri_id_code_size
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool URI::append_as_record(ndef::MessageBuilder &message_builder, bool is_last_record)
|
||||||
|
{
|
||||||
|
if (!_uri) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the record type
|
||||||
|
ndef::RecordType type(
|
||||||
|
ndef::RecordType::well_known_type,
|
||||||
|
uri_record_type_value
|
||||||
|
);
|
||||||
|
|
||||||
|
// build the record payload
|
||||||
|
ndef::RecordPayload payload(_uri, _uri_size);
|
||||||
|
|
||||||
|
return message_builder.append_record(type, payload, is_last_record);
|
||||||
|
}
|
||||||
|
|
||||||
|
void URI::move_data(uint8_t *new_uri, size_t new_uri_size)
|
||||||
|
{
|
||||||
|
delete[] _uri;
|
||||||
|
_uri = new_uri;
|
||||||
|
_uri_size = new_uri_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool URIParser::do_parse(const ndef::Record &record, URI &uri)
|
||||||
|
{
|
||||||
|
if (record.type.tnf != ndef::RecordType::well_known_type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the record type value should be equal to `U`
|
||||||
|
if (record.type.value != make_const_Span(uri_record_type_value) ||
|
||||||
|
record.payload.empty()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the buffer
|
||||||
|
size_t uri_record_size = record.payload.size();
|
||||||
|
uint8_t *uri_record = new uint8_t[uri_record_size];
|
||||||
|
memcpy(uri_record, record.payload.data(), uri_record_size);
|
||||||
|
|
||||||
|
uri.move_data(uri_record, uri_record_size);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace common
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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 "MessageBuilder.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace ndef {
|
||||||
|
|
||||||
|
MessageBuilder::MessageBuilder(const Span<uint8_t> &buffer) :
|
||||||
|
_message_buffer(buffer),
|
||||||
|
_position(0),
|
||||||
|
_message_started(false),
|
||||||
|
_message_ended(false),
|
||||||
|
_in_chunk(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool MessageBuilder::append_record(
|
||||||
|
const RecordType &type,
|
||||||
|
const RecordPayload &payload,
|
||||||
|
bool is_last_record
|
||||||
|
) {
|
||||||
|
Record record(
|
||||||
|
type,
|
||||||
|
payload,
|
||||||
|
/* id */ RecordID(),
|
||||||
|
/* chunk */ false,
|
||||||
|
is_last_record
|
||||||
|
);
|
||||||
|
|
||||||
|
return append_record(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageBuilder::append_record(const Record &record)
|
||||||
|
{
|
||||||
|
if (_message_ended) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.type.value.size() > 255) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.id.size() > 255) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!record.id.empty() && _in_chunk) {
|
||||||
|
if (record.chunk) {
|
||||||
|
// middle chunk
|
||||||
|
return false;
|
||||||
|
} else if (record.type.tnf == RecordType::unchanged) {
|
||||||
|
// terminating chunk
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_in_chunk && record.type.tnf != RecordType::unchanged) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_in_chunk && record.chunk && record.type.tnf == RecordType::unchanged) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.type.tnf == RecordType::empty) {
|
||||||
|
if (!record.type.value.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!record.id.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!record.payload.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.type.tnf == RecordType::well_known_type ||
|
||||||
|
record.type.tnf == RecordType::media_type ||
|
||||||
|
record.type.tnf == RecordType::absolute_uri ||
|
||||||
|
record.type.tnf == RecordType::external_type
|
||||||
|
) {
|
||||||
|
if (record.type.value.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.type.tnf == RecordType::unknown && !record.type.value.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t record_size = compute_record_size(record);
|
||||||
|
if (record_size > (_message_buffer.size() - _position)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
append_header(record);
|
||||||
|
append_type_length(record);
|
||||||
|
append_payload_length(record);
|
||||||
|
append_id_length(record);
|
||||||
|
append_type(record);
|
||||||
|
append_id(record);
|
||||||
|
append_payload(record);
|
||||||
|
|
||||||
|
if (record.chunk) {
|
||||||
|
_in_chunk = true;
|
||||||
|
} else if (record.type.tnf == RecordType::unchanged) {
|
||||||
|
// last chunk reached
|
||||||
|
_in_chunk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::reset()
|
||||||
|
{
|
||||||
|
_position = 0;
|
||||||
|
_message_started = false;
|
||||||
|
_message_ended = false;
|
||||||
|
_in_chunk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::reset(const Span<uint8_t> &buffer)
|
||||||
|
{
|
||||||
|
_message_buffer = buffer;
|
||||||
|
_position = 0;
|
||||||
|
_message_started = false;
|
||||||
|
_message_ended = false;
|
||||||
|
_in_chunk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageBuilder::is_message_complete() const
|
||||||
|
{
|
||||||
|
return _message_ended;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<const uint8_t> MessageBuilder::get_message() const
|
||||||
|
{
|
||||||
|
if (is_message_complete()) {
|
||||||
|
return _message_buffer.first(_position);
|
||||||
|
} else {
|
||||||
|
return Span<const uint8_t>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MessageBuilder::compute_record_size(const Record &record)
|
||||||
|
{
|
||||||
|
size_t record_size = 0;
|
||||||
|
record_size = 1; /* header */
|
||||||
|
record_size += 1; /* type length */
|
||||||
|
record_size += is_short_payload(record) ? 1 : 4;
|
||||||
|
|
||||||
|
if (!record.id.empty()) {
|
||||||
|
record_size += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
record_size += record.type.value.size();
|
||||||
|
record_size += record.id.size();
|
||||||
|
record_size += record.payload.size();
|
||||||
|
|
||||||
|
return record_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::append_header(const Record &record)
|
||||||
|
{
|
||||||
|
uint8_t header = 0;
|
||||||
|
if (!_message_started) {
|
||||||
|
header |= Header::message_begin_bit;
|
||||||
|
_message_started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.last_record) {
|
||||||
|
header |= Header::message_end_bit;
|
||||||
|
_message_ended = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.chunk) {
|
||||||
|
header |= Header::chunk_flag_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_short_payload(record)) {
|
||||||
|
header |= Header::short_record_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (record.id.size()) {
|
||||||
|
header |= Header::id_length_bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
header |= record.type.tnf;
|
||||||
|
_message_buffer[_position++] = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::append_type_length(const Record &record)
|
||||||
|
{
|
||||||
|
_message_buffer[_position++] = record.type.value.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::append_payload_length(const Record &record)
|
||||||
|
{
|
||||||
|
if (record.payload.empty()) {
|
||||||
|
_message_buffer[_position++] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_short_payload(record)) {
|
||||||
|
_message_buffer[_position++] = record.payload.size();
|
||||||
|
} else {
|
||||||
|
// TODO: proper host to network
|
||||||
|
uint32_t size = record.payload.size();
|
||||||
|
_message_buffer[_position++] = (size >> 24) & 0xFF;
|
||||||
|
_message_buffer[_position++] = (size >> 16) & 0xFF;
|
||||||
|
_message_buffer[_position++] = (size >> 8) & 0xFF;
|
||||||
|
_message_buffer[_position++] = size & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::append_id_length(const Record &record)
|
||||||
|
{
|
||||||
|
if (record.id.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_message_buffer[_position++] = record.id.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::append_type(const Record &record)
|
||||||
|
{
|
||||||
|
if (record.type.value.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(
|
||||||
|
_message_buffer.data() + _position,
|
||||||
|
record.type.value.data(),
|
||||||
|
record.type.value.size()
|
||||||
|
);
|
||||||
|
_position += record.type.value.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::append_id(const Record &record)
|
||||||
|
{
|
||||||
|
if (record.id.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(
|
||||||
|
_message_buffer.data() + _position,
|
||||||
|
record.id.data(),
|
||||||
|
record.id.size()
|
||||||
|
);
|
||||||
|
_position += record.id.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBuilder::append_payload(const Record &record)
|
||||||
|
{
|
||||||
|
if (record.payload.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(
|
||||||
|
_message_buffer.data() + _position,
|
||||||
|
record.payload.data(),
|
||||||
|
record.payload.size()
|
||||||
|
);
|
||||||
|
_position += record.payload.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageBuilder::is_short_payload(const Record &record)
|
||||||
|
{
|
||||||
|
if (record.payload.size() <= 255) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ndef
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
|
@ -0,0 +1,313 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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 <cstring>
|
||||||
|
|
||||||
|
#include "MessageParser.h"
|
||||||
|
#include "Record.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct buffer_iterator_t {
|
||||||
|
buffer_iterator_t(const mbed::Span<const uint8_t> &buffer) :
|
||||||
|
buffer(buffer),
|
||||||
|
position(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
uint8_t operator*()
|
||||||
|
{
|
||||||
|
return buffer[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_iterator_t &operator++()
|
||||||
|
{
|
||||||
|
++position;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_iterator_t operator++(int)
|
||||||
|
{
|
||||||
|
buffer_iterator_t previous = *this;
|
||||||
|
++*this;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_iterator_t &operator+=(size_t increment)
|
||||||
|
{
|
||||||
|
position += increment;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return (position >= buffer.size()) ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t remaining_size() const
|
||||||
|
{
|
||||||
|
return buffer.size() - position;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_le(uint8_t *dest, size_t size)
|
||||||
|
{
|
||||||
|
memcpy(dest, buffer.data() + position, size);
|
||||||
|
position += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_be(uint8_t *dest, size_t size)
|
||||||
|
{
|
||||||
|
// TODO: Needs proper network to host function
|
||||||
|
std::reverse_copy(
|
||||||
|
buffer.data() + position,
|
||||||
|
buffer.data() + position + size,
|
||||||
|
dest
|
||||||
|
);
|
||||||
|
position += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
mbed::Span<const uint8_t> get_underlying_buffer() const
|
||||||
|
{
|
||||||
|
return buffer.last(buffer.size() - position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mbed::Span<const uint8_t> buffer;
|
||||||
|
mbed::Span<const uint8_t>::index_type position;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end of anonymous namespace
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace ndef {
|
||||||
|
|
||||||
|
struct MessageParser::parsing_state_t {
|
||||||
|
parsing_state_t(const Span<const uint8_t> &data_buffer) :
|
||||||
|
it(data_buffer),
|
||||||
|
first_record_parsed(false),
|
||||||
|
last_record_parsed(false),
|
||||||
|
error(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
buffer_iterator_t it;
|
||||||
|
bool first_record_parsed:1;
|
||||||
|
bool last_record_parsed:1;
|
||||||
|
bool error:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
MessageParser::MessageParser() :
|
||||||
|
_delegate(NULL)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void MessageParser::set_delegate(Delegate *delegate)
|
||||||
|
{
|
||||||
|
_delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageParser::parse(const Span<const uint8_t> &data_buffer)
|
||||||
|
{
|
||||||
|
parsing_state_t parsing_state(data_buffer);
|
||||||
|
report_parsing_started();
|
||||||
|
while(parsing_state.it && parse_record(parsing_state));
|
||||||
|
if (!parsing_state.error && !parsing_state.last_record_parsed) {
|
||||||
|
report_parsing_error(MISSING_MESSAGE_END, parsing_state);
|
||||||
|
}
|
||||||
|
report_parsing_terminated();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageParser::parse_record(parsing_state_t &s)
|
||||||
|
{
|
||||||
|
if (s.error || s.last_record_parsed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure that the header can be extracted
|
||||||
|
if (s.it.remaining_size() < 1) {
|
||||||
|
report_parsing_error(INSUFICIENT_DATA, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t header = *s.it++;
|
||||||
|
|
||||||
|
// NOTE: report an error until the chunk parsing design is sorted out
|
||||||
|
if (header & Header::chunk_flag_bit) {
|
||||||
|
report_parsing_error(CHUNK_RECORD_NOT_SUPPORTED, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle first record cases
|
||||||
|
if (s.first_record_parsed == false) {
|
||||||
|
if (header & Header::message_begin_bit) {
|
||||||
|
s.first_record_parsed = true;
|
||||||
|
} else {
|
||||||
|
report_parsing_error(INVALID_MESSAGE_START, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (header & Header::message_begin_bit) {
|
||||||
|
report_parsing_error(INVALID_MESSAGE_START, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle last record
|
||||||
|
if (header & Header::message_end_bit) {
|
||||||
|
s.last_record_parsed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure their is enough space to contain the type length, payload
|
||||||
|
// length and id length
|
||||||
|
uint8_t lengths_size = compute_lengths_size(header);
|
||||||
|
if (s.it.remaining_size() < lengths_size) {
|
||||||
|
report_parsing_error(INSUFICIENT_DATA, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract the various length from the message
|
||||||
|
uint8_t type_length = extract_type_length(s);
|
||||||
|
uint32_t payload_length = extract_payload_length(s, header);
|
||||||
|
uint8_t id_length = extract_id_length(s, header);
|
||||||
|
|
||||||
|
// there should be enough bytes left in the buffer
|
||||||
|
if (s.it.remaining_size() < (type_length + id_length + payload_length)) {
|
||||||
|
report_parsing_error(INSUFICIENT_DATA, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate the Type Name Format of the header
|
||||||
|
switch (header & Header::tnf_bits) {
|
||||||
|
case RecordType::empty:
|
||||||
|
if (type_length || payload_length || id_length) {
|
||||||
|
report_parsing_error(INVALID_EMPTY_RECORD, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RecordType::well_known_type:
|
||||||
|
case RecordType::media_type:
|
||||||
|
case RecordType::absolute_uri:
|
||||||
|
case RecordType::external_type:
|
||||||
|
if (!type_length) {
|
||||||
|
report_parsing_error(MISSING_TYPE_VALUE, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RecordType::unknown:
|
||||||
|
if (type_length) {
|
||||||
|
report_parsing_error(INVALID_UNKNOWN_TYPE_LENGTH, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RecordType::unchanged:
|
||||||
|
// shouldn't be handled outside of chunk handling
|
||||||
|
report_parsing_error(INVALID_UNCHANGED_TYPE, s);
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
report_parsing_error(INVALID_TYPE_NAME_FORMAT, s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the record
|
||||||
|
Record record;
|
||||||
|
|
||||||
|
// flags
|
||||||
|
record.last_record = header & Header::message_end_bit;
|
||||||
|
|
||||||
|
// type
|
||||||
|
record.type.tnf = static_cast<RecordType::tnf_t>(header & Header::tnf_bits);
|
||||||
|
if (type_length) {
|
||||||
|
record.type.value = s.it.get_underlying_buffer().first(type_length);
|
||||||
|
s.it += type_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// id
|
||||||
|
if (id_length) {
|
||||||
|
record.id = s.it.get_underlying_buffer().first(id_length);
|
||||||
|
s.it += id_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// payload
|
||||||
|
if (payload_length) {
|
||||||
|
record.payload = s.it.get_underlying_buffer().first(payload_length);
|
||||||
|
s.it += payload_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.it += payload_length;
|
||||||
|
|
||||||
|
report_record_parsed(record);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MessageParser::compute_lengths_size(uint8_t header)
|
||||||
|
{
|
||||||
|
return 1 /* type_length size */ +
|
||||||
|
((header & Header::short_record_bit) ? 1 : 4) /* payload length */ +
|
||||||
|
((header & Header::id_length_bit) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MessageParser::extract_type_length(parsing_state_t &s)
|
||||||
|
{
|
||||||
|
return *s.it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MessageParser::extract_payload_length(parsing_state_t &s, uint8_t header)
|
||||||
|
{
|
||||||
|
uint32_t payload_length = 0;
|
||||||
|
if (header & Header::short_record_bit) {
|
||||||
|
payload_length = *s.it++;
|
||||||
|
} else {
|
||||||
|
s.it.read_be(
|
||||||
|
reinterpret_cast<uint8_t*>(&payload_length),
|
||||||
|
sizeof(payload_length)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return payload_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t MessageParser::extract_id_length(parsing_state_t &s, uint8_t header)
|
||||||
|
{
|
||||||
|
return (header & Header::id_length_bit) ? *s.it++ : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageParser::report_parsing_started() {
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_parsing_started();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageParser::report_record_parsed(const Record &record)
|
||||||
|
{
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_record_parsed(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageParser::report_parsing_terminated()
|
||||||
|
{
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_parsing_terminated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageParser::report_parsing_error(error_t error, parsing_state_t &parsing_state)
|
||||||
|
{
|
||||||
|
parsing_state.error = true;
|
||||||
|
if (_delegate) {
|
||||||
|
_delegate->on_parsing_error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ndef
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* mbed Microcontroller Library
|
||||||
|
* Copyright (c) 2018 ARM Limited
|
||||||
|
*
|
||||||
|
* 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 "RecordParser.h"
|
||||||
|
|
||||||
|
namespace mbed {
|
||||||
|
namespace nfc {
|
||||||
|
namespace ndef {
|
||||||
|
|
||||||
|
bool RecordParserChain::parse(const Record &record)
|
||||||
|
{
|
||||||
|
RecordParser *current_parser = _parsers;
|
||||||
|
while (current_parser) {
|
||||||
|
if (current_parser->parse(record)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current_parser = current_parser->_next_parser;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordParserChain::set_next_parser(RecordParser *parser)
|
||||||
|
{
|
||||||
|
if (!_parsers) {
|
||||||
|
_parsers = parser;
|
||||||
|
} else {
|
||||||
|
RecordParser *current_parser = _parsers;
|
||||||
|
while (current_parser->_next_parser) {
|
||||||
|
current_parser = current_parser->_next_parser;
|
||||||
|
}
|
||||||
|
current_parser->_next_parser = parser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ndef
|
||||||
|
} // namespace nfc
|
||||||
|
} // namespace mbed
|
Loading…
Reference in New Issue