Remove dependencies to NdefMessage library

This commit is contained in:
nicolas.dorier 2024-12-03 21:40:04 +09:00
parent 0a1ec8f530
commit b2440ad0f1
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
7 changed files with 905 additions and 9 deletions

View File

@ -31,7 +31,6 @@
<ItemGroup>
<PackageReference Include="NdefLibrary" Version="4.1.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup>

View 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();
}

View 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) { }
}
}

View 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();
}
}
}

View 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;
}
}
}

View 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));
}
}
}

View File

@ -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;