Remove dependencies to NdefMessage library
This commit is contained in:
parent
0a1ec8f530
commit
b2440ad0f1
@ -31,7 +31,6 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NdefLibrary" Version="4.1.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
24
src/BTCPayServer.NTag424/NDEFMessage.cs
Normal file
24
src/BTCPayServer.NTag424/NDEFMessage.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NdefLibrary.Ndef;
|
||||
|
||||
namespace BTCPayServer.NTag424;
|
||||
public class NDEFMessage
|
||||
{
|
||||
internal readonly NdefMessage _Message;
|
||||
public bool IsEmpty => _Message.Count == 0;
|
||||
|
||||
public NDEFMessage(byte[] bytes)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(bytes);
|
||||
_Message = NdefLibrary.Ndef.NdefMessage.FromByteArray(bytes);
|
||||
}
|
||||
internal NDEFMessage(NdefMessage message)
|
||||
{
|
||||
_Message = message;
|
||||
}
|
||||
public byte[] ToBytes() => _Message.ToByteArray();
|
||||
}
|
||||
43
src/BTCPayServer.NTag424/NdefLibrary/Ndef/NdefException.cs
Normal file
43
src/BTCPayServer.NTag424/NdefLibrary/Ndef/NdefException.cs
Normal file
@ -0,0 +1,43 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012-2018 Andreas Jakl - https://www.nfcinteractor.com/
|
||||
** All rights reserved.
|
||||
**
|
||||
** Extension to the NDEF handling classes.
|
||||
**
|
||||
** Created by Andreas Jakl (2012).
|
||||
** More information: https://andijakl.github.io/ndef-nfc/
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 3 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 3 requirements will be met:
|
||||
** http://www.gnu.org/licenses/lgpl.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace NdefLibrary.Ndef
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception occured when parsing or assembling NDEF messages, records and their
|
||||
/// respective payloads.
|
||||
/// </summary>
|
||||
internal class NdefException : FormatException
|
||||
{
|
||||
public NdefException() { }
|
||||
public NdefException(string message) : base(message) { }
|
||||
public NdefException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
}
|
||||
343
src/BTCPayServer.NTag424/NdefLibrary/Ndef/NdefMessage.cs
Normal file
343
src/BTCPayServer.NTag424/NdefLibrary/Ndef/NdefMessage.cs
Normal file
@ -0,0 +1,343 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012-2018 Andreas Jakl - https://www.nfcinteractor.com/
|
||||
** Original version copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
**
|
||||
** This file is based on the respective class of the Connectivity module
|
||||
** of Qt Mobility (http://qt.gitorious.org/qt-mobility).
|
||||
**
|
||||
** Ported to C# by Andreas Jakl (2012)
|
||||
** More information: https://andijakl.github.io/ndef-nfc/
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 3 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 3 requirements will be met:
|
||||
** http://www.gnu.org/licenses/lgpl.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NdefLibrary.Ndef
|
||||
{
|
||||
/// <summary>
|
||||
/// An NDEF message is composed of one or more NDEF records.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is essentially just a list of records, and provides
|
||||
/// the necessary methods to convert all the records into a single
|
||||
/// byte array that can be written to a tag and has the correct
|
||||
/// flags set (e.g., message begin / end).
|
||||
/// NdefMessage can also parse a byte array into an NDEF message,
|
||||
/// separating and creating all the individual records out of the array.
|
||||
///
|
||||
/// From the NFC Forum specification document:
|
||||
/// NFC Forum Data Exchange Format is a lightweight binary message
|
||||
/// format designed to encapsulate one or more application-defined
|
||||
/// payloads into a single message construct.
|
||||
///
|
||||
/// An NDEF message contains one or more NDEF records, each carrying
|
||||
/// a payload of arbitrary type and up to (2^32)-1 octets in size.
|
||||
/// Records can be chained together to support larger payloads.
|
||||
///
|
||||
/// An NDEF record carries three parameters for describing its payload:
|
||||
/// the payload length, the payload type, and an optional payload identifier.
|
||||
/// </remarks>
|
||||
internal class NdefMessage : List<NdefRecord>
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the NDEF message parsed from the contents of <paramref name="message"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <paramref name="message"/> parameter is interpreted as the raw message format
|
||||
/// defined in the NFC Forum specifications.
|
||||
/// </remarks>
|
||||
/// <param name="message">Raw byte array containing the NDEF message, which consists
|
||||
/// of 0 or more NDEF records.</param>
|
||||
/// <exception cref="NdefException">Thrown if there is an error parsing the NDEF
|
||||
/// message out of the byte array.</exception>
|
||||
/// <returns>If parsing was successful, the NDEF message containing 0 or more NDEF
|
||||
/// records.</returns>
|
||||
public static NdefMessage FromByteArray(byte[] message)
|
||||
{
|
||||
var result = new NdefMessage();
|
||||
|
||||
var seenMessageBegin = false;
|
||||
var seenMessageEnd = false;
|
||||
|
||||
var partialChunk = new MemoryStream();
|
||||
var record = new NdefRecord();
|
||||
|
||||
uint i = 0;
|
||||
while (i < message.Length)
|
||||
{
|
||||
//Debug.WriteLine("Parsing byte[] to NDEF message. New record starts at {0}", i);
|
||||
|
||||
// Parse flags out of NDEF message header
|
||||
|
||||
// The MB flag is a 1-bit field that when set indicates the start of an NDEF message.
|
||||
bool messageBegin = (message[i] & 0x80) != 0;
|
||||
// The ME flag is a 1-bit field that when set indicates the end of an NDEF message.
|
||||
// Note, that in case of a chunked payload, the ME flag is set only in the terminating record chunk of that chunked payload.
|
||||
bool messageEnd = (message[i] & 0x40) != 0;
|
||||
// The CF flag is a 1-bit field indicating that this is either the first record chunk or a middle record chunk of a chunked payload.
|
||||
bool cf = (message[i] & 0x20) != 0;
|
||||
// The SR flag is a 1-bit field indicating, if set, that the PAYLOAD_LENGTH field is a single octet.
|
||||
bool sr = (message[i] & 0x10) != 0;
|
||||
// The IL flag is a 1-bit field indicating, if set, that the ID_LENGTH field is present in the header as a single octet.
|
||||
// If the IL flag is zero, the ID_LENGTH field is omitted from the record header and the ID field is also omitted from the record.
|
||||
bool il = (message[i] & 0x08) != 0;
|
||||
var typeNameFormat = (NdefRecord.TypeNameFormatType)(message[i] & 0x07);
|
||||
|
||||
//Debug.WriteLine("ShortRecord: " + (sr ? "yes" : "no"));
|
||||
//Debug.WriteLine("Id Length present: " + (il ? "yes" : "no"));
|
||||
|
||||
if (messageBegin && seenMessageBegin)
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Message Begin flag present after first record.");
|
||||
}
|
||||
else if (!messageBegin && !seenMessageBegin)
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Expected Message Begin missing in first record.");
|
||||
}
|
||||
else if (messageBegin && !seenMessageBegin)
|
||||
{
|
||||
seenMessageBegin = true;
|
||||
}
|
||||
|
||||
if (messageEnd && seenMessageEnd)
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Message End flag present after last record.");
|
||||
}
|
||||
else if (messageEnd && !seenMessageEnd)
|
||||
{
|
||||
seenMessageEnd = true;
|
||||
}
|
||||
|
||||
if (cf && (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged) && partialChunk.Length > 0)
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Partial chunk not empty or typeNameFormat not 0x06 as expected.");
|
||||
}
|
||||
|
||||
// Header length
|
||||
int headerLength = 1;
|
||||
headerLength += (sr) ? 1 : 4;
|
||||
headerLength += (il) ? 1 : 0;
|
||||
|
||||
if (i + headerLength >= message.Length)
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Unexpected end of message.");
|
||||
}
|
||||
|
||||
// Type length
|
||||
byte typeLength = message[++i];
|
||||
|
||||
if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (typeLength != 0))
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Invalid chunked data, Type Length != 0.");
|
||||
}
|
||||
|
||||
// Payload length (short record?)
|
||||
uint payloadLength;
|
||||
if (sr)
|
||||
{
|
||||
// Short record - payload length is a single octet
|
||||
payloadLength = message[++i];
|
||||
}
|
||||
else
|
||||
{
|
||||
// No short record - payload length is four octets representing a 32 bit unsigned integer (MSB-first)
|
||||
payloadLength = (uint)((message[++i]) << 24);
|
||||
payloadLength |= (uint)((message[++i]) << 16);
|
||||
payloadLength |= (uint)((message[++i]) << 8);
|
||||
payloadLength |= (uint)((message[++i]) << 0);
|
||||
}
|
||||
|
||||
// ID length
|
||||
byte idLength;
|
||||
idLength = (byte)(il ? message[++i] : 0);
|
||||
|
||||
// Total length of content (= type + payload + ID)
|
||||
uint contentLength = typeLength + payloadLength + idLength;
|
||||
if (i + contentLength >= message.Length)
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Unexpected end of message.");
|
||||
}
|
||||
|
||||
|
||||
if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (idLength != 0))
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Invalid chunked data, Id Length != 0.");
|
||||
}
|
||||
|
||||
if (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged)
|
||||
{
|
||||
record.TypeNameFormat = typeNameFormat;
|
||||
}
|
||||
|
||||
// Read type
|
||||
if (typeLength > 0)
|
||||
{
|
||||
record.Type = new byte[typeLength];
|
||||
Array.Copy(message, (int)(++i), record.Type, 0, typeLength);
|
||||
i += (uint)typeLength - 1;
|
||||
}
|
||||
|
||||
// Read ID
|
||||
if (idLength > 0)
|
||||
{
|
||||
record.Id = new byte[idLength];
|
||||
Array.Copy(message, (int)(++i), record.Id, 0, idLength);
|
||||
i += (uint)idLength - 1;
|
||||
}
|
||||
|
||||
// Read payload
|
||||
if (payloadLength > 0)
|
||||
{
|
||||
var payload = new byte[payloadLength];
|
||||
Array.Copy(message, (int)(++i), payload, 0, (int)payloadLength);
|
||||
|
||||
if (cf)
|
||||
{
|
||||
// chunked payload, except last
|
||||
partialChunk.Write(payload, 0, payload.Length);
|
||||
}
|
||||
else if (typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged)
|
||||
{
|
||||
// last chunk of chunked payload
|
||||
partialChunk.Write(payload, 0, payload.Length);
|
||||
record.Payload = partialChunk.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-chunked payload
|
||||
record.Payload = payload;
|
||||
}
|
||||
|
||||
i += payloadLength - 1;
|
||||
}
|
||||
|
||||
if (!cf)
|
||||
{
|
||||
// Add record to the message and create a new record for the next loop iteration
|
||||
result.Add(record);
|
||||
record = new NdefRecord();
|
||||
}
|
||||
|
||||
if (!cf && seenMessageEnd)
|
||||
break;
|
||||
|
||||
// move to start of next record
|
||||
++i;
|
||||
}
|
||||
|
||||
|
||||
if (!seenMessageBegin && !seenMessageEnd)
|
||||
{
|
||||
throw new NdefException("Ndef parse error: Malformed NDEF Message, missing begin or end.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert all the NDEF records currently stored in the NDEF message to a byte
|
||||
/// array suitable for writing to a tag or sending to another device.
|
||||
/// </summary>
|
||||
/// <returns>The NDEF record(s) converted to an NDEF message.</returns>
|
||||
public byte[] ToByteArray()
|
||||
{
|
||||
// Empty message: single empty record
|
||||
if (Count == 0)
|
||||
{
|
||||
var msg = new NdefMessage { new NdefRecord() };
|
||||
return msg.ToByteArray();
|
||||
}
|
||||
|
||||
var m = new MemoryStream();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
var record = this[i];
|
||||
|
||||
var flags = (byte)record.TypeNameFormat;
|
||||
|
||||
// Message begin / end flags. If there is only one record in the message,
|
||||
// both flags are set.
|
||||
if (i == 0)
|
||||
flags |= 0x80; // MB (message begin = first record in the message)
|
||||
if (i == Count - 1)
|
||||
flags |= 0x40; // ME (message end = last record in the message)
|
||||
|
||||
// cf (chunked records) not supported yet
|
||||
|
||||
// SR (Short Record)?
|
||||
if (record.Payload == null || record.Payload.Length < 255)
|
||||
flags |= 0x10;
|
||||
|
||||
// ID present?
|
||||
if (record.Id != null && record.Id.Length > 0)
|
||||
flags |= 0x08;
|
||||
|
||||
m.WriteByte(flags);
|
||||
|
||||
// Type length
|
||||
if (record.Type != null) m.WriteByte((byte)record.Type.Length); else m.WriteByte(0);
|
||||
|
||||
// Payload length 1 byte (SR) or 4 bytes
|
||||
if (record.Payload == null)
|
||||
m.WriteByte(0);
|
||||
else
|
||||
{
|
||||
if ((flags & 0x10) != 0)
|
||||
{
|
||||
// SR
|
||||
m.WriteByte((byte)record.Payload.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No SR (Short Record)
|
||||
var payloadLength = (uint)record.Payload.Length;
|
||||
m.WriteByte((byte)(payloadLength >> 24));
|
||||
m.WriteByte((byte)(payloadLength >> 16));
|
||||
m.WriteByte((byte)(payloadLength >> 8));
|
||||
m.WriteByte((byte)(payloadLength & 0x000000ff));
|
||||
}
|
||||
}
|
||||
|
||||
// ID length
|
||||
if (record.Id != null && (flags & 0x08) != 0)
|
||||
m.WriteByte((byte)record.Id.Length);
|
||||
|
||||
// Type length
|
||||
if (record.Type != null && record.Type.Length > 0)
|
||||
m.Write(record.Type, 0, record.Type.Length);
|
||||
|
||||
// ID data
|
||||
if (record.Id != null && record.Id.Length > 0)
|
||||
m.Write(record.Id, 0, record.Id.Length);
|
||||
|
||||
// Payload data
|
||||
if (record.Payload != null && record.Payload.Length > 0)
|
||||
m.Write(record.Payload, 0, record.Payload.Length);
|
||||
|
||||
}
|
||||
|
||||
return m.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
264
src/BTCPayServer.NTag424/NdefLibrary/Ndef/NdefRecord.cs
Normal file
264
src/BTCPayServer.NTag424/NdefLibrary/Ndef/NdefRecord.cs
Normal file
@ -0,0 +1,264 @@
|
||||
#nullable disable
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012-2018 Andreas Jakl - https://www.nfcinteractor.com/
|
||||
** Original version copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
**
|
||||
** This file is based on the respective class of the Connectivity module
|
||||
** of Qt Mobility (http://qt.gitorious.org/qt-mobility).
|
||||
**
|
||||
** Ported to C# by Andreas Jakl (2012)
|
||||
** More information: https://andijakl.github.io/ndef-nfc/
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 3 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 3 requirements will be met:
|
||||
** http://www.gnu.org/licenses/lgpl.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace NdefLibrary.Ndef
|
||||
{
|
||||
/// <summary>
|
||||
/// An NDEF record contains a payload described by a type, a length, and an optional identifier.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is generic and can hold the data of any kind of record.
|
||||
/// It follows the specification from the NFC forum for the data format.
|
||||
///
|
||||
/// Ndef Records should usually be placed within an Ndef Message, which will
|
||||
/// make sure that for example the message begin / end flags are set correctly.
|
||||
///
|
||||
/// While the NdefRecord class only offers access to the payload as a byte array,
|
||||
/// you should rather use specialized sub classes, which offer convenient ways
|
||||
/// to handle data stored in the payload through easy access methods.
|
||||
/// Such classes are provided for several standardized types.
|
||||
/// </remarks>
|
||||
internal class NdefRecord
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Standardized type name formats, as defined by the NDEF record
|
||||
/// specification from the Nfc Forum.
|
||||
/// </summary>
|
||||
public enum TypeNameFormatType
|
||||
{
|
||||
/// <summary>
|
||||
/// Empty indicates that no type or payload is associated with this record.
|
||||
/// </summary>
|
||||
Empty = 0x00,
|
||||
/// <summary>
|
||||
/// The NFC Forum well-known type follows the RTD type name format defined in the NFC Forum RTD specification.
|
||||
/// </summary>
|
||||
NfcRtd = 0x01,
|
||||
/// <summary>
|
||||
/// The media type indicates that the TYPE field contains a value that follows the media-type BNF construct defined by RFC 2046.
|
||||
/// </summary>
|
||||
Mime = 0x02,
|
||||
/// <summary>
|
||||
/// Absolute-URI indicates that the TYPE field contains a value that follows the absolute-URI BNF construct defined by RFC 3986.
|
||||
/// </summary>
|
||||
Uri = 0x03,
|
||||
/// <summary>
|
||||
/// NFC Forum external type indicates that the TYPE field contains a value that follows the type name format defined in [NFC RTD] for external type names.
|
||||
/// </summary>
|
||||
ExternalRtd = 0x04,
|
||||
/// <summary>
|
||||
/// Unknown SHOULD be used to indicate that the type of the payload is unknown. This is similar to the "application/octet-stream" media type defined by MIME.
|
||||
/// When used, the TYPE_LENGTH field MUST be zero and thus the TYPE field is omitted from the NDEF record.
|
||||
/// </summary>
|
||||
Unknown = 0x05,
|
||||
/// <summary>
|
||||
/// Unchanged MUST be used in all middle record chunks and the terminating record chunk used in chunked payloads.
|
||||
/// </summary>
|
||||
Unchanged = 0x06,
|
||||
/// <summary>
|
||||
/// Any other type name format; should be treated as unknown.
|
||||
/// </summary>
|
||||
Reserved = 0x07
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the structure of the value of the TYPE field.
|
||||
/// </summary>
|
||||
public TypeNameFormatType TypeNameFormat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Byte array storing raw contents of the type.
|
||||
/// Use Type property to access it whenever possible.
|
||||
/// </summary>
|
||||
/// <remarks>Direct byte array access provided as virtual
|
||||
/// property can't be accessed from constructor.</remarks>
|
||||
protected byte[] _type;
|
||||
/// <summary>
|
||||
/// An identifier describing the type of the payload.
|
||||
/// </summary>
|
||||
public virtual byte[] Type
|
||||
{
|
||||
get { return _type; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
_type = null;
|
||||
return;
|
||||
}
|
||||
_type = new byte[value.Length];
|
||||
Array.Copy(value, _type, value.Length);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _id;
|
||||
/// <summary>
|
||||
/// An identifier in the form of a URI reference.
|
||||
/// </summary>
|
||||
public byte[] Id
|
||||
{
|
||||
get { return _id; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
_id = null;
|
||||
return;
|
||||
}
|
||||
_id = new byte[value.Length];
|
||||
Array.Copy(value, _id, value.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Byte array storing raw contents of the payload.
|
||||
/// Use Payload property to access it whenever possible.
|
||||
/// </summary>
|
||||
/// <remarks>Direct byte array access provided as virtual
|
||||
/// property can't be accessed from constructor.</remarks>
|
||||
protected byte[] _payload;
|
||||
/// <summary>
|
||||
/// The application data carried within an NDEF record.
|
||||
/// </summary>
|
||||
public virtual byte[] Payload
|
||||
{
|
||||
get { return _payload; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
_payload = null;
|
||||
return;
|
||||
}
|
||||
_payload = new byte[value.Length];
|
||||
Array.Copy(value, _payload, value.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty record, not setting any information.
|
||||
/// </summary>
|
||||
public NdefRecord()
|
||||
{
|
||||
TypeNameFormat = TypeNameFormatType.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new record with the specified type name format and type.
|
||||
/// Doesn't set the payload and ID.
|
||||
/// </summary>
|
||||
/// <param name="tnf">Type name format to use, based on the tnf's standardized
|
||||
/// by the Nfc Forum.</param>
|
||||
/// <param name="type">Type string.</param>
|
||||
public NdefRecord(TypeNameFormatType tnf, byte[] type)
|
||||
{
|
||||
TypeNameFormat = tnf;
|
||||
if (type != null)
|
||||
{
|
||||
// Can't call Type property set method from constructor, as it's virtual
|
||||
_type = new byte[type.Length];
|
||||
Array.Copy(type, _type, type.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new record, copying the information of the record sent through the parameter.
|
||||
/// </summary>
|
||||
/// <param name="other">Record to copy.</param>
|
||||
public NdefRecord(NdefRecord other)
|
||||
{
|
||||
TypeNameFormat = other.TypeNameFormat;
|
||||
if (other.Type != null)
|
||||
{
|
||||
// Can't call Type property set method from constructor, as it's virtual
|
||||
_type = new byte[other.Type.Length];
|
||||
Array.Copy(other.Type, _type, other.Type.Length);
|
||||
}
|
||||
if (other.Id != null) Id = other.Id;
|
||||
if (other.Payload != null)
|
||||
{
|
||||
// Can't call Type property set method from constructor, as it's virtual
|
||||
_payload = new byte[other.Payload.Length];
|
||||
Array.Copy(other.Payload, _payload, other.Payload.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the contents of the record are valid; throws an exception if
|
||||
/// a problem is found, containing a textual description of the issue.
|
||||
/// </summary>
|
||||
/// <exception cref="NdefException">Thrown if no valid NDEF record can be
|
||||
/// created based on the record's current contents. The exception message
|
||||
/// contains further details about the issue.</exception>
|
||||
/// <returns>True if the record contents are valid, or throws an exception
|
||||
/// if an issue is found.</returns>
|
||||
public virtual bool CheckIfValid()
|
||||
{
|
||||
// Check Type (according to TNF)
|
||||
if (TypeNameFormat == TypeNameFormatType.Unchanged ||
|
||||
TypeNameFormat == TypeNameFormatType.Unknown)
|
||||
{
|
||||
// Unknown and unchanged TNF must have a type length of 0
|
||||
if (!(Type == null || Type.Length == 0))
|
||||
{
|
||||
throw new NdefException("Record with TNF Unchanged must have an empty type name.");
|
||||
}
|
||||
}
|
||||
else if (TypeNameFormat == TypeNameFormatType.Empty)
|
||||
{
|
||||
// The value 0x00 (Empty) indicates that there is no type or payload associated
|
||||
// with this record. When used, the TYPE_LENGTH, ID_LENGTH, and PAYLOAD_LENGTH
|
||||
// fields MUST be zero and the TYPE, ID, and PAYLOAD fields are thus omitted from the record.
|
||||
if (Type != null || Payload != null)
|
||||
{
|
||||
throw new NdefException("The Empty record must not have a payload or a type.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// All other TNF should have a type set
|
||||
// Proximity APIs unstable in some cases when no Type name is set
|
||||
if (Type == null || Type.Length == 0)
|
||||
{
|
||||
throw new NdefException("No Type set for record.");
|
||||
}
|
||||
}
|
||||
// Check ID
|
||||
// Middle and terminating record chunks MUST not have an ID field
|
||||
if (TypeNameFormat == TypeNameFormatType.Unchanged && !(Id == null || Id.Length == 0))
|
||||
throw new NdefException("Id must not be set for middle or terminating record (TNF = Unchanged).");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
223
src/BTCPayServer.NTag424/NdefLibrary/Ndef/NdefUriRecord.cs
Normal file
223
src/BTCPayServer.NTag424/NdefLibrary/Ndef/NdefUriRecord.cs
Normal file
@ -0,0 +1,223 @@
|
||||
#nullable disable
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012-2018 Andreas Jakl - https://www.nfcinteractor.com/
|
||||
** Original version copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
**
|
||||
** This file is based on the respective class of the Connectivity module
|
||||
** of Qt Mobility (http://qt.gitorious.org/qt-mobility).
|
||||
**
|
||||
** Ported to C# by Andreas Jakl (2012)
|
||||
** More information: https://andijakl.github.io/ndef-nfc/
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 3 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 3 requirements will be met:
|
||||
** http://www.gnu.org/licenses/lgpl.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NdefLibrary.Ndef
|
||||
{
|
||||
/// <summary>
|
||||
/// The URI record as specified by the NFC Forum URI record type definition.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The record stores a URI and can be stored on a tag or sent to another device.
|
||||
/// Several of the most common URI headers are automatically abbreviated in order
|
||||
/// to keep the record as small as possible. URIs will be encoded using UTF-8.
|
||||
///
|
||||
/// This record can either be used stand alone, or as part of another record
|
||||
/// </remarks>
|
||||
internal class NdefUriRecord : NdefRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of the NDEF Text record (well-known, type 'U').
|
||||
/// </summary>
|
||||
public static readonly byte[] UriType = { (byte)'U' };
|
||||
|
||||
/// <summary>
|
||||
/// URI abbreviations, as defined in NDEF URI record specifications.
|
||||
/// </summary>
|
||||
private static readonly string[] Abbreviations =
|
||||
{
|
||||
string.Empty,
|
||||
"http://www.",
|
||||
"https://www.",
|
||||
"http://",
|
||||
"https://",
|
||||
"tel:",
|
||||
"mailto:",
|
||||
"ftp://anonymous:anonymous@",
|
||||
"ftp://ftp.",
|
||||
"ftps://",
|
||||
"sftp://",
|
||||
"smb://",
|
||||
"nfs://",
|
||||
"ftp://",
|
||||
"dav://",
|
||||
"news:",
|
||||
"telnet://",
|
||||
"imap:",
|
||||
"rtsp://",
|
||||
"urn:",
|
||||
"pop:",
|
||||
"sip:",
|
||||
"sips:",
|
||||
"tftp:",
|
||||
"btspp://",
|
||||
"btl2cap://",
|
||||
"btgoep://",
|
||||
"tcpobex://",
|
||||
"irdaobex://",
|
||||
"file://",
|
||||
"urn:epc:id:",
|
||||
"urn:epc:tag:",
|
||||
"urn:epc:pat:",
|
||||
"urn:epc:raw:",
|
||||
"urn:epc:",
|
||||
"urn:nfc:"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Get the raw URI as stored in this record, excluding any abbreviations.
|
||||
/// </summary>
|
||||
/// <remarks>Gets the raw contents of the URI, exclusive the first byte of the
|
||||
/// record's payload that would contain the abbreviation code.
|
||||
/// If the URI has been abbreviated, this method returns retuns the actual URI
|
||||
/// text as stored on the tag. To get the full URI that has been expanded with
|
||||
/// the abbreviated URI scheme, use the normal Uri accessor.</remarks>
|
||||
public byte[] RawUri
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Payload == null || Payload.Length == 0)
|
||||
return null;
|
||||
|
||||
var code = Payload.ElementAt(0);
|
||||
|
||||
var rawUri = new byte[Payload.Length - 1];
|
||||
Array.Copy(Payload, 1, rawUri, 0, Payload.Length - 1);
|
||||
return rawUri;
|
||||
}
|
||||
set
|
||||
{
|
||||
Payload = new byte[value.Length + 1];
|
||||
Payload[0] = 0;
|
||||
Array.Copy(value, 0, Payload, 1, value.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URI stored in this record.
|
||||
/// The abbreviation will be handled behind the scenes - getting and
|
||||
/// setting this property will always work on the full URI.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note that this property / class does not escape the URI data
|
||||
/// string to avoid double-escaping and to allow storing unescaped
|
||||
/// URIs if allowed by the protocol.
|
||||
/// For generic URLs, it's recommended to escape the URL string when
|
||||
/// sending it to this class, e.g., with System.Uri.EscapeUriString().
|
||||
/// </remarks>
|
||||
public string Uri
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Payload == null || Payload.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
var encoding = Encoding.UTF8;
|
||||
byte code = Payload.ElementAt(0);
|
||||
if (code >= Abbreviations.Length)
|
||||
{
|
||||
code = 0;
|
||||
}
|
||||
var uri = new byte[Payload.Length - 1];
|
||||
Array.Copy(Payload, 1, uri, 0, Payload.Length - 1);
|
||||
return (Abbreviations[code] + encoding.GetString(uri, 0, uri.Length));
|
||||
}
|
||||
set
|
||||
{
|
||||
var uriString = value;
|
||||
var encoding = Encoding.UTF8;
|
||||
var useAbbreviation = 0;
|
||||
for (var i = 1; i < Abbreviations.Length; i++)
|
||||
{
|
||||
if (uriString.StartsWith(Abbreviations[i]))
|
||||
{
|
||||
useAbbreviation = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Can abbreviate the URI
|
||||
var abbrevLength = Abbreviations[useAbbreviation].Length;
|
||||
var encodedLength = encoding.GetByteCount(uriString.ToCharArray(), abbrevLength,
|
||||
uriString.Length - abbrevLength);
|
||||
Payload = new byte[encodedLength + 1];
|
||||
Payload[0] = (byte) useAbbreviation;
|
||||
|
||||
encoding.GetBytes(uriString, abbrevLength, uriString.Length - abbrevLength, Payload, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty URI record.
|
||||
/// </summary>
|
||||
public NdefUriRecord()
|
||||
: base(TypeNameFormatType.NfcRtd, UriType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a URI record based on the record passed through the argument.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Internalizes and parses the payload of the original record.
|
||||
/// The original record has to be a URI record as well.
|
||||
/// </remarks>
|
||||
/// <param name="other">Uri record to copy into this record.</param>
|
||||
/// <exception cref="NdefException">Thrown if attempting to create a Uri record
|
||||
/// based on an incompatible record type.</exception>
|
||||
public NdefUriRecord(NdefRecord other)
|
||||
: base(other)
|
||||
{
|
||||
if (!IsRecordType(this))
|
||||
throw new NdefException("Attempted to instantiate new record from incompatible record.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the record sent via the parameter is indeed a URI record.
|
||||
/// Only checks the type and type name format, doesn't analyze if the
|
||||
/// payload is valid.
|
||||
/// </summary>
|
||||
/// <param name="record">Record to check.</param>
|
||||
/// <returns>True if the record has the correct type and type name format
|
||||
/// to be a URI record, false if it's a different record.</returns>
|
||||
public static bool IsRecordType(NdefRecord record)
|
||||
{
|
||||
if (record?.Type == null) return false;
|
||||
return (record.TypeNameFormat == TypeNameFormatType.NfcRtd && record.Type.SequenceEqual(UriType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -285,9 +285,9 @@ retry:
|
||||
try
|
||||
{
|
||||
var message = await this.ReadNDef(cancellationToken);
|
||||
if (!NdefUriRecord.IsRecordType(message[0]))
|
||||
if (message.IsEmpty || !NdefUriRecord.IsRecordType(message._Message[0]))
|
||||
return null;
|
||||
var uri = new NdefUriRecord(message[0]).Uri;
|
||||
var uri = new NdefUriRecord(message._Message[0]).Uri;
|
||||
if (string.IsNullOrEmpty(uri))
|
||||
return null;
|
||||
return new Uri(uri, UriKind.Absolute);
|
||||
@ -298,7 +298,7 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<NdefMessage> ReadNDef(CancellationToken cancellationToken = default)
|
||||
public async Task<NDEFMessage> ReadNDef(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await IsoSelectFile(ISOLevel.Application, cancellationToken);
|
||||
await IsoSelectFile(DataFile.NDEF, cancellationToken);
|
||||
@ -318,7 +318,7 @@ retry:
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return new NdefMessage();
|
||||
return new NDEFMessage(new NdefMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -328,7 +328,7 @@ retry:
|
||||
P2 = 2,
|
||||
Le = size
|
||||
}, cancellationToken)).Data;
|
||||
return NdefMessage.FromByteArray(data);
|
||||
return new NDEFMessage(data);
|
||||
}
|
||||
}
|
||||
finally
|
||||
@ -373,9 +373,9 @@ retry:
|
||||
} };
|
||||
}
|
||||
|
||||
public async Task WriteNDef(NdefMessage message, CancellationToken cancellationToken = default)
|
||||
public async Task WriteNDef(NDEFMessage message, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var ndefMessageBytes = message.ToByteArray();
|
||||
var ndefMessageBytes = message.ToBytes();
|
||||
var content = new byte[220]; // Normally we have 256 bytes, but APDU has a size limit we need some margin
|
||||
content[0] = (byte)(ndefMessageBytes.Length >> 8);
|
||||
content[1] = (byte)ndefMessageBytes.Length;
|
||||
@ -516,7 +516,7 @@ retry:
|
||||
{
|
||||
new NdefUriRecord() { Uri = lnurlw }
|
||||
};
|
||||
await WriteNDef(ndef);
|
||||
await WriteNDef(new NDEFMessage(ndef));
|
||||
var ndefBytes = ndef.ToByteArray();
|
||||
var pIndex = Array.LastIndexOf(ndefBytes, (byte)'p') + 4;
|
||||
var cIndex = Array.LastIndexOf(ndefBytes, (byte)'c') + 4;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user