bump version and add go-ndef to vendor
This commit is contained in:
parent
f35c31a20a
commit
4baa54e4e6
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
|
@ -0,0 +1,96 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
package ndef
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// BytesToUint64 parses a byte slice to an uint64 (BigEndian). If the slice
|
||||
// is longer than 8 bytes, it's truncated. If it's shorter, they are considered
|
||||
// the less significant bits of the uint64.
|
||||
func bytesToUint64(b []byte) uint64 {
|
||||
// Make sure we are not parsing more than 8 bytes (uint64 size)
|
||||
byte8 := make([]byte, 8)
|
||||
if len(b) > 8 {
|
||||
copy(byte8, b[len(b)-8:]) // use the last 8 bytes
|
||||
} else {
|
||||
copy(byte8[8-len(b):], b) // copy to last positions of byte8
|
||||
}
|
||||
return binary.BigEndian.Uint64(byte8)
|
||||
}
|
||||
|
||||
// Uint64ToBytes converts a BigEndian uint64 into a byte slice of
|
||||
// desiredLen. For lengths under 8 bytes, the 8 byte result is
|
||||
// truncated (the most significant bytes are discarded
|
||||
func uint64ToBytes(n uint64, desiredLen int) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, binary.BigEndian, n)
|
||||
if desiredLen >= 8 {
|
||||
slice := make([]byte, desiredLen)
|
||||
copy(slice[desiredLen-8:], buf.Bytes())
|
||||
return slice
|
||||
}
|
||||
|
||||
return buf.Bytes()[8-desiredLen:]
|
||||
}
|
||||
|
||||
// func PrintBytes(bytes []byte, length int) {
|
||||
// for i := 0; i < length; i++ {
|
||||
// fmt.Printf("%02x ", bytes[i])
|
||||
// }
|
||||
// fmt.Println()
|
||||
// }
|
||||
|
||||
// FmtBytes receives a byte slice and a n value and returns
|
||||
// a string with first n hex-formatted values of the slice
|
||||
func fmtBytes(bytes []byte, n int) (str string) {
|
||||
if n > len(bytes) {
|
||||
n = len(bytes)
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
str += fmt.Sprintf("%02x ", bytes[i])
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Reads from the bytes buffer and panics with an error
|
||||
// if the buffer does not have the bytes to read that we want
|
||||
// This is meant as a replacement for byteslice[3:3+x] where
|
||||
// I don't have to constantly check the array length
|
||||
// but instead I throw a custom panic
|
||||
func getBytes(b *bytes.Buffer, n int) []byte {
|
||||
slice := make([]byte, n)
|
||||
nread, err := b.Read(slice)
|
||||
if err != nil || nread != n {
|
||||
panic(errors.New("unexpected end of data"))
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
// Same as above bug for a single byte
|
||||
func getByte(b *bytes.Buffer) byte {
|
||||
byte, err := b.ReadByte()
|
||||
if err != nil {
|
||||
panic(errors.New("unexpected end of data"))
|
||||
}
|
||||
return byte
|
||||
}
|
|
@ -0,0 +1,235 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
package ndef
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Message represents an NDEF Message, which is a collection of one or
|
||||
// more NDEF Records.
|
||||
//
|
||||
// Most common types of NDEF Messages (URI, Media) only have a single
|
||||
// record. However, others, like Smart Posters, have multiple ones.
|
||||
type Message struct {
|
||||
Records []*Record
|
||||
}
|
||||
|
||||
// NewMessage returns a new Message initialized with a single Record
|
||||
// with the TNF, Type, ID and Payload values.
|
||||
func NewMessage(tnf byte, rtype string, id string, payload RecordPayload) *Message {
|
||||
return &Message{
|
||||
Records: []*Record{NewRecord(tnf, rtype, id, payload)},
|
||||
}
|
||||
}
|
||||
|
||||
// NewMessageFromRecords returns a new Message containing several NDEF
|
||||
// Records. The MB and ME flags for the records are adjusted to
|
||||
// create a valid message.
|
||||
func NewMessageFromRecords(records ...*Record) *Message {
|
||||
n := len(records)
|
||||
if n == 0 {
|
||||
return &Message{}
|
||||
}
|
||||
|
||||
last := n - 1
|
||||
|
||||
for _, r := range records {
|
||||
r.SetMB(false)
|
||||
r.SetME(false)
|
||||
}
|
||||
|
||||
records[0].SetMB(true)
|
||||
records[last].SetME(true)
|
||||
|
||||
return &Message{
|
||||
Records: records,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTextMessage returns a new Message with a single Record
|
||||
// of WellKnownType T[ext].
|
||||
func NewTextMessage(textVal, language string) *Message {
|
||||
return &Message{
|
||||
[]*Record{NewTextRecord(textVal, language)},
|
||||
}
|
||||
}
|
||||
|
||||
// NewURIMessage returns a new Message with a single Record
|
||||
// of WellKnownType U[RI].
|
||||
func NewURIMessage(uriVal string) *Message {
|
||||
return &Message{
|
||||
[]*Record{NewURIRecord(uriVal)},
|
||||
}
|
||||
}
|
||||
|
||||
// NewSmartPosterMessage returns a new Message with a single Record
|
||||
// of WellKnownType Sp (Smart Poster).
|
||||
func NewSmartPosterMessage(msgPayload *Message) *Message {
|
||||
return &Message{
|
||||
[]*Record{NewSmartPosterRecord(msgPayload)},
|
||||
}
|
||||
}
|
||||
|
||||
// NewMediaMessage returns a new Message with a single Record
|
||||
// of Media (RFC-2046) type.
|
||||
//
|
||||
// mimeType is something like "text/json" or "image/jpeg".
|
||||
func NewMediaMessage(mimeType string, payload []byte) *Message {
|
||||
return &Message{
|
||||
[]*Record{NewMediaRecord(mimeType, payload)},
|
||||
}
|
||||
}
|
||||
|
||||
// NewAbsoluteURIMessage returns a new Message with a single Record
|
||||
// of AbsoluteURI type.
|
||||
//
|
||||
// AbsoluteURI means that the type of the payload for this record is
|
||||
// defined by an URI resource. It is not supposed to be used to
|
||||
// describe an URI. For that, use NewURIRecord().
|
||||
func NewAbsoluteURIMessage(typeURI string, payload []byte) *Message {
|
||||
return &Message{
|
||||
[]*Record{NewAbsoluteURIRecord(typeURI, payload)},
|
||||
}
|
||||
}
|
||||
|
||||
// NewExternalMessage returns a new Message with a single Record
|
||||
// of NFC Forum External type.
|
||||
func NewExternalMessage(extType string, payload []byte) *Message {
|
||||
return &Message{
|
||||
[]*Record{NewExternalRecord(extType, payload)},
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the string representation of each of the records in the message.
|
||||
func (m *Message) String() string {
|
||||
str := ""
|
||||
last := len(m.Records) - 1
|
||||
for i, r := range m.Records {
|
||||
str += r.String()
|
||||
if i != last {
|
||||
str += "\n"
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Inspect returns a string with information about the message and its records.
|
||||
func (m *Message) Inspect() string {
|
||||
str := fmt.Sprintf("NDEF Message with %d records.", len(m.Records))
|
||||
if len(m.Records) > 0 {
|
||||
str += "\n"
|
||||
for i, r := range m.Records {
|
||||
str += fmt.Sprintf("Record %d:\n", i)
|
||||
rIns := r.Inspect()
|
||||
rInsLines := strings.Split(rIns, "\n")
|
||||
for _, l := range rInsLines {
|
||||
str += " " + l + "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Unmarshal parses a byte slice into a Message. This is done by
|
||||
// parsing all Records in the slice, until there are no more to parse.
|
||||
//
|
||||
// Returns the number of bytes processed (message length), or an error
|
||||
// if something looks wrong with the message or its records.
|
||||
func (m *Message) Unmarshal(buf []byte) (rLen int, err error) {
|
||||
m.Records = []*Record{}
|
||||
rLen = 0
|
||||
for rLen < len(buf) {
|
||||
r := new(Record)
|
||||
recordLen, err := r.Unmarshal(buf[rLen:])
|
||||
rLen += recordLen
|
||||
if err != nil {
|
||||
return rLen, err
|
||||
}
|
||||
m.Records = append(m.Records, r)
|
||||
if r.ME() { // last record in message
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = m.check()
|
||||
return rLen, err
|
||||
}
|
||||
|
||||
// Marshal provides the byte slice representation of a Message,
|
||||
// which is the concatenation of the Marshaling of each of its records.
|
||||
//
|
||||
// Returns an error if something goes wrong.
|
||||
func (m *Message) Marshal() ([]byte, error) {
|
||||
err := m.check()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
for _, r := range m.Records {
|
||||
rBytes, err := r.Marshal()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = buffer.Write(rBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (m *Message) check() error {
|
||||
last := len(m.Records) - 1
|
||||
|
||||
if last < 0 {
|
||||
return errors.New(eNORECORDS)
|
||||
}
|
||||
|
||||
if !m.Records[0].MB() {
|
||||
return errors.New(eNOMB)
|
||||
}
|
||||
|
||||
if !m.Records[last].ME() {
|
||||
return errors.New(eNOME)
|
||||
}
|
||||
|
||||
for i, r := range m.Records {
|
||||
if i > 0 && r.MB() {
|
||||
return errors.New(eBADMB)
|
||||
}
|
||||
if i < last && r.ME() {
|
||||
return errors.New(eBADME)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check errors
|
||||
const (
|
||||
eNORECORDS = "NDEF Message Check: No records"
|
||||
eNOMB = "NDEF Message Check: first record has not the MessageBegin flag set"
|
||||
eNOME = "NDEF Message Check: last record has not the MessageEnd flag set"
|
||||
eBADMB = "NDEF Message Check: middle record has the MessageBegin flag set"
|
||||
eBADME = "NDEF Message Check: middle record has the MessageEnd flag set"
|
||||
)
|
|
@ -0,0 +1,36 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
// Package ndef provides an implementation of the NFC Data Exchange
|
||||
// Format (NDEF) specification:
|
||||
// - NFCForum-TS-NDEF_1.0
|
||||
// - NFCForum-TS-RTD_1.0
|
||||
// It allows to parse byte slices into a structured Message type,
|
||||
// as well as to turn an Message into a bytes again.
|
||||
package ndef
|
||||
|
||||
// Possible values for the TNF Field as defined in the specification.
|
||||
const (
|
||||
Empty = byte(iota)
|
||||
NFCForumWellKnownType
|
||||
MediaType
|
||||
AbsoluteURI
|
||||
NFCForumExternalType
|
||||
Unknown
|
||||
Unchanged
|
||||
Reserved
|
||||
)
|
|
@ -0,0 +1,342 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
package ndef
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hsanjuan/go-ndef/types/absoluteuri"
|
||||
"github.com/hsanjuan/go-ndef/types/ext"
|
||||
"github.com/hsanjuan/go-ndef/types/media"
|
||||
"github.com/hsanjuan/go-ndef/types/wkt/text"
|
||||
"github.com/hsanjuan/go-ndef/types/wkt/uri"
|
||||
)
|
||||
|
||||
// A Record is an NDEF Record. Multiple records can be
|
||||
// part of a single NDEF Message.
|
||||
type Record struct {
|
||||
chunks []*recordChunk
|
||||
}
|
||||
|
||||
// NewRecord returns a single-chunked record with the given options.
|
||||
// Use a generic.Payload if you want to use a custom byte-slice for payload.
|
||||
func NewRecord(tnf byte, typ string, id string, payload RecordPayload) *Record {
|
||||
var payloadBytes []byte
|
||||
if payload != nil {
|
||||
payloadBytes = payload.Marshal()
|
||||
}
|
||||
|
||||
chunk := newChunk(
|
||||
tnf,
|
||||
typ,
|
||||
id,
|
||||
payloadBytes,
|
||||
)
|
||||
return &Record{
|
||||
chunks: []*recordChunk{chunk},
|
||||
}
|
||||
}
|
||||
|
||||
// TNF returns the Type Name Format (3 bits) associated to this Record.
|
||||
func (r *Record) TNF() byte {
|
||||
if r.Empty() {
|
||||
return 0
|
||||
}
|
||||
return r.chunks[0].TNF
|
||||
}
|
||||
|
||||
// Type returns the declared Type for this record.
|
||||
func (r *Record) Type() string {
|
||||
if r.Empty() {
|
||||
return ""
|
||||
}
|
||||
return r.chunks[0].Type
|
||||
}
|
||||
|
||||
// ID returns the declared record ID for this record.
|
||||
func (r *Record) ID() string {
|
||||
if r.Empty() {
|
||||
return ""
|
||||
}
|
||||
return r.chunks[0].ID
|
||||
}
|
||||
|
||||
// Payload returns the RecordPayload for this record. It will use
|
||||
// one of the supported types, or otherwise a generic.Payload.
|
||||
func (r *Record) Payload() (RecordPayload, error) {
|
||||
if r.Empty() {
|
||||
return nil, errors.New("empty record")
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, chunk := range r.chunks {
|
||||
_, err := buf.Write(chunk.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return makeRecordPayload(r.TNF(), r.Type(), buf.Bytes()), nil
|
||||
}
|
||||
|
||||
// Empty returns true if this record has no chunks.
|
||||
func (r *Record) Empty() bool {
|
||||
return len(r.chunks) == 0
|
||||
}
|
||||
|
||||
// MB returns the value of the MessageBegin bit of the first chunk of this
|
||||
// record, signaling that this is the first record in an NDEF Message.
|
||||
// a NDEF Message.
|
||||
func (r *Record) MB() bool {
|
||||
if r.Empty() {
|
||||
return false
|
||||
}
|
||||
return r.chunks[0].MB
|
||||
}
|
||||
|
||||
// SetMB sets the MessageBegin bit of the first chunk of this Record.
|
||||
func (r *Record) SetMB(b bool) {
|
||||
if r.Empty() {
|
||||
return
|
||||
}
|
||||
r.chunks[0].MB = b
|
||||
}
|
||||
|
||||
// ME returns the value of the MessageEnd bit of the last chunk of this Record,
|
||||
// signaling that this is the last record in an NDEF Message.
|
||||
func (r *Record) ME() bool {
|
||||
if r.Empty() {
|
||||
return false
|
||||
}
|
||||
return r.chunks[len(r.chunks)-1].ME
|
||||
}
|
||||
|
||||
// SetME sets the MessageEnd bit of the last chunk of this record.
|
||||
func (r *Record) SetME(b bool) {
|
||||
if r.Empty() {
|
||||
return
|
||||
}
|
||||
r.chunks[len(r.chunks)-1].ME = b
|
||||
}
|
||||
|
||||
// NewTextRecord returns a new Record with a
|
||||
// Payload of Text [Well-Known] Type.
|
||||
func NewTextRecord(textVal, language string) *Record {
|
||||
pl := text.New(textVal, language)
|
||||
return NewRecord(NFCForumWellKnownType, "T", "", pl)
|
||||
}
|
||||
|
||||
// NewURIRecord returns a new Record with a
|
||||
// Payload of URI [Well-Known] Type.
|
||||
func NewURIRecord(uriVal string) *Record {
|
||||
pl := uri.New(uriVal)
|
||||
return NewRecord(NFCForumWellKnownType, "U", "", pl)
|
||||
}
|
||||
|
||||
// NewSmartPosterRecord creates a new Record representing a Smart Poster.
|
||||
// The Payload of a Smart Poster is an NDEF Message.
|
||||
func NewSmartPosterRecord(msg *Message) *Record {
|
||||
pl := NewSmartPosterPayload(msg)
|
||||
return NewRecord(NFCForumWellKnownType, "Sp", "", pl)
|
||||
}
|
||||
|
||||
// NewMediaRecord returns a new Record with a
|
||||
// Media type (per RFC-2046) as payload.
|
||||
//
|
||||
// mimeType is something like "text/json" or "image/jpeg".
|
||||
func NewMediaRecord(mimeType string, payload []byte) *Record {
|
||||
pl := media.New(mimeType, payload)
|
||||
return NewRecord(MediaType, mimeType, "", pl)
|
||||
}
|
||||
|
||||
// NewAbsoluteURIRecord returns a new Record with a
|
||||
// Payload of Absolute URI type.
|
||||
//
|
||||
// AbsoluteURI means that the type of the payload for this record is
|
||||
// defined by an URI resource. It is not supposed to be used to
|
||||
// describe an URI. For that, use NewURIRecord().
|
||||
func NewAbsoluteURIRecord(typeURI string, payload []byte) *Record {
|
||||
pl := absoluteuri.New(typeURI, payload)
|
||||
return NewRecord(AbsoluteURI, typeURI, "", pl)
|
||||
}
|
||||
|
||||
// NewExternalRecord returns a new Record with a
|
||||
// Payload of NFC Forum external type.
|
||||
func NewExternalRecord(extType string, payload []byte) *Record {
|
||||
pl := ext.New(extType, payload)
|
||||
return NewRecord(NFCForumExternalType, extType, "", pl)
|
||||
}
|
||||
|
||||
// String a string representation of the payload of the record, prefixed
|
||||
// by the URN of the resource.
|
||||
//
|
||||
// Note that not all NDEF Payloads are supported, and that custom types/payloads
|
||||
// are considered not printable. In those cases, a generic RecordPayload is
|
||||
// used and an explanatory message is returned instead.
|
||||
// See submodules under "types/" for a list of supported types.
|
||||
func (r *Record) String() string {
|
||||
pl, err := r.Payload()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return pl.Type() + ":" + pl.String()
|
||||
}
|
||||
|
||||
// Inspect provides a string with information about this record.
|
||||
// For a String representation of the contents use String().
|
||||
func (r *Record) Inspect() string {
|
||||
if r.Empty() {
|
||||
return "Empty record"
|
||||
}
|
||||
|
||||
pl, err := r.Payload()
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
var str string
|
||||
str += fmt.Sprintf("TNF: %d\n", r.TNF())
|
||||
str += fmt.Sprintf("Type: %s\n", r.Type())
|
||||
str += fmt.Sprintf("ID: %s\n", r.ID())
|
||||
str += fmt.Sprintf("MB: %t\n", r.MB())
|
||||
str += fmt.Sprintf("ME: %t\n", r.ME())
|
||||
str += fmt.Sprintf("Payload Length: %d", pl.Len())
|
||||
return str
|
||||
}
|
||||
|
||||
// Unmarshal parses a byte slice into a Record struct (the slice can
|
||||
// have extra bytes which are ignored). The Record is always reset before
|
||||
// parsing.
|
||||
//
|
||||
// It does this by parsing every record chunk until a chunk with the CF flag
|
||||
// cleared is read is read.
|
||||
//
|
||||
// Returns how many bytes were parsed from the slice (record length) or
|
||||
// an error if something went wrong.
|
||||
func (r *Record) Unmarshal(buf []byte) (rLen int, err error) {
|
||||
rLen = 0
|
||||
var chunks []*recordChunk
|
||||
for rLen < len(buf) {
|
||||
chunk := &recordChunk{}
|
||||
chunkSize, err := chunk.Unmarshal(buf[rLen:])
|
||||
rLen += chunkSize
|
||||
if err != nil {
|
||||
return rLen, err
|
||||
}
|
||||
chunks = append(chunks, chunk)
|
||||
|
||||
// the last chunk record of a chunked record
|
||||
// has the CF flag cleared.
|
||||
if !chunk.CF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
r.chunks = chunks
|
||||
err = r.check()
|
||||
return rLen, err
|
||||
}
|
||||
|
||||
// Marshal returns the byte representation of a Record. It does this
|
||||
// by producing a single record chunk.
|
||||
//
|
||||
// Note that if the original Record was unmarshaled from many chunks,
|
||||
// the recovery is not possible anymore.
|
||||
func (r *Record) Marshal() ([]byte, error) {
|
||||
err := r.check()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, chunk := range r.chunks {
|
||||
chunkBytes, err := chunk.Marshal()
|
||||
if err != nil {
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
_, err = buf.Write(chunkBytes)
|
||||
if err != nil {
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (r *Record) check() error {
|
||||
chunksLen := len(r.chunks)
|
||||
last := chunksLen - 1
|
||||
if chunksLen == 0 {
|
||||
return errors.New(eNOCHUNKS)
|
||||
}
|
||||
if chunksLen == 1 && r.chunks[0].CF {
|
||||
return errors.New(eFIRSTCHUNKED)
|
||||
}
|
||||
if r.chunks[0].CF && r.chunks[last].CF {
|
||||
return errors.New(eLASTCHUNKED)
|
||||
}
|
||||
|
||||
if chunksLen > 1 {
|
||||
chunksWithoutCF := 0
|
||||
chunksWithIL := 0
|
||||
chunksWithTypeLength := 0
|
||||
chunksWithoutUnchangedType := 0
|
||||
for i, r := range r.chunks {
|
||||
// Check CF in all but the last
|
||||
if !r.CF && i != last {
|
||||
chunksWithoutCF++
|
||||
}
|
||||
// Check IL in all but first
|
||||
if r.IL && i != 0 {
|
||||
chunksWithIL++
|
||||
}
|
||||
// TypeLength should be zero except in the first
|
||||
if r.TypeLength > 0 && i != 0 {
|
||||
chunksWithTypeLength++
|
||||
}
|
||||
// All but first chunk should have TNF to 0x06
|
||||
if r.TNF != Unchanged && i != 0 {
|
||||
chunksWithoutUnchangedType++
|
||||
}
|
||||
}
|
||||
if chunksWithoutCF > 0 {
|
||||
return errors.New(eCFMISSING)
|
||||
}
|
||||
if chunksWithIL > 0 {
|
||||
return errors.New(eBADIL)
|
||||
}
|
||||
if chunksWithTypeLength > 0 {
|
||||
return errors.New(eBADTYPELENGTH)
|
||||
}
|
||||
if chunksWithoutUnchangedType > 0 {
|
||||
return errors.New(eBADTNF)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set some short-hands for the errors that can happen on check().
|
||||
const (
|
||||
eNOCHUNKS = "NDEF Record Check: No chunks"
|
||||
eFIRSTCHUNKED = "NDEF Record Check: A single record cannot have the Chunk flag set"
|
||||
eLASTCHUNKED = "NDEF Record Check: Last record cannot have the Chunk flag set"
|
||||
eCFMISSING = "NDEF Record Check: Chunk Flag missing from some records"
|
||||
eBADIL = "NDEF Record Check: IL flag is set on a middle or final chunk"
|
||||
eBADTYPELENGTH = "NDEF Record Check: A middle or last chunk has TypeLength != 0"
|
||||
eBADTNF = "NDEF Record Check: A middle or last chunk TNF is not UNCHANGED"
|
||||
)
|
|
@ -0,0 +1,269 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
package ndef
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// RecordChunk represents how a Record is actually stored
|
||||
// We need this for parsing and checking the validity of a Record
|
||||
// before assembling them.
|
||||
type recordChunk struct {
|
||||
// First byte
|
||||
MB bool // Message begin
|
||||
ME bool // Message end
|
||||
CF bool // Chunk Flag
|
||||
SR bool // Short record
|
||||
IL bool // ID length field present
|
||||
TNF byte // Type name format (3 bits)
|
||||
TypeLength byte // Type Length
|
||||
IDLength byte // Length of the ID field
|
||||
PayloadLength uint64 // Length of the Payload.
|
||||
Type string // Type of the payload. Must follow TNF
|
||||
ID string // An URI (per RFC 3986)
|
||||
Payload []byte // Payload
|
||||
}
|
||||
|
||||
// Reset clears up all the fields of the Record and sets them to their
|
||||
// default values.
|
||||
func (r *recordChunk) Reset() {
|
||||
r.MB = false
|
||||
r.ME = false
|
||||
r.CF = false
|
||||
r.SR = false
|
||||
r.IL = false
|
||||
r.TNF = 0
|
||||
r.TypeLength = 0
|
||||
r.IDLength = 0
|
||||
r.PayloadLength = 0
|
||||
r.Type = ""
|
||||
r.ID = ""
|
||||
r.Payload = []byte{}
|
||||
}
|
||||
|
||||
// New returns a single RecordChunk with the given options. This chunk can be
|
||||
// used directly to make a single-chunk NDEF Record on a single-record NDEF
|
||||
// message: the MB and ME fields are set to true.
|
||||
func newChunk(tnf byte, typ string, id string, payload []byte) *recordChunk {
|
||||
chunk := &recordChunk{}
|
||||
chunk.Reset()
|
||||
chunk.MB = true // Message-begin
|
||||
chunk.ME = true // Message-end
|
||||
chunk.CF = false // not chunked
|
||||
chunk.IL = len(id) > 0 // only if ID field present
|
||||
chunk.TNF = tnf
|
||||
chunk.TypeLength = byte(len([]byte(typ)))
|
||||
chunk.Type = typ
|
||||
chunk.IDLength = byte(len([]byte(id)))
|
||||
chunk.ID = id
|
||||
|
||||
payloadLen := uint64(len(payload))
|
||||
if payloadLen > 4294967295 { //2^32-1. 4GB message max.
|
||||
payloadLen = 4294967295
|
||||
}
|
||||
chunk.SR = payloadLen < 256 // Short record vs. Long
|
||||
chunk.PayloadLength = payloadLen
|
||||
|
||||
// FIXME: If payload is greater than 2^32 - 1
|
||||
// we'll truncate without warning.
|
||||
chunk.Payload = payload[:payloadLen]
|
||||
return chunk
|
||||
}
|
||||
|
||||
// Provide a string with information about this record chunk.
|
||||
// Records' payload do not make sense without having compiled a whole Record
|
||||
// so they are not dealed with here.
|
||||
func (r *recordChunk) String() string {
|
||||
var str string
|
||||
str += fmt.Sprintf("MB: %t | ME: %t | CF: %t | SR: %t | IL: %t | TNF: %d\n",
|
||||
r.MB, r.ME, r.CF, r.SR, r.IL, r.TNF)
|
||||
str += fmt.Sprintf("TypeLength: %d", r.TypeLength)
|
||||
str += fmt.Sprintf(" | Type: %s\n", r.Type)
|
||||
str += fmt.Sprintf("Record Payload Length: %d",
|
||||
r.PayloadLength)
|
||||
if r.IL {
|
||||
str += fmt.Sprintf(" | IDLength: %d", r.IDLength)
|
||||
str += fmt.Sprintf(" | ID: %s", r.ID)
|
||||
}
|
||||
str += fmt.Sprintf("\n")
|
||||
return str
|
||||
}
|
||||
|
||||
// Unmarshal parses a byte slice into a single Record chunk struct (the slice
|
||||
// can have extra bytes which are ignored). The Record is always reset before
|
||||
// parsing.
|
||||
//
|
||||
// Returns how many bytes were parsed from the slice (record length) or
|
||||
// an error if something went wrong.
|
||||
func (r *recordChunk) Unmarshal(buf []byte) (rLen int, err error) {
|
||||
// Handle errors that are produced by getByte() and getBytes()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(runtime.Error); ok {
|
||||
panic(r)
|
||||
}
|
||||
err = r.(error)
|
||||
err = errors.New("Record.Unmarshal: " + err.Error())
|
||||
}
|
||||
}()
|
||||
r.Reset()
|
||||
bytesBuf := bytes.NewBuffer(buf)
|
||||
|
||||
firstByte := getByte(bytesBuf)
|
||||
r.MB = (firstByte >> 7 & 0x1) == 1
|
||||
r.ME = (firstByte >> 6 & 0x1) == 1
|
||||
r.CF = (firstByte >> 5 & 0x1) == 1
|
||||
r.SR = (firstByte >> 4 & 0x1) == 1
|
||||
r.IL = (firstByte >> 3 & 0x1) == 1
|
||||
r.TNF = firstByte & 0x7
|
||||
|
||||
r.TypeLength = getByte(bytesBuf)
|
||||
|
||||
if r.SR { //This is a short record
|
||||
r.PayloadLength = uint64(getByte(bytesBuf))
|
||||
} else { // Regular record
|
||||
r.PayloadLength = bytesToUint64(getBytes(bytesBuf, 4))
|
||||
}
|
||||
if r.IL {
|
||||
r.IDLength = getByte(bytesBuf)
|
||||
}
|
||||
r.Type = string(getBytes(bytesBuf, int(r.TypeLength)))
|
||||
if r.IL {
|
||||
r.ID = string(getBytes(bytesBuf, int(r.IDLength)))
|
||||
}
|
||||
r.Payload = getBytes(bytesBuf, int(r.PayloadLength))
|
||||
|
||||
rLen = len(buf) - bytesBuf.Len()
|
||||
err = r.Check()
|
||||
if err != nil {
|
||||
return rLen, err
|
||||
}
|
||||
return rLen, nil
|
||||
}
|
||||
|
||||
// Marshal returns the byte representation of a Record, or an error
|
||||
// if something went wrong
|
||||
func (r *recordChunk) Marshal() ([]byte, error) {
|
||||
err := r.Check()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
firstByte := byte(0)
|
||||
if r.MB {
|
||||
firstByte |= 0x1 << 7
|
||||
}
|
||||
if r.ME {
|
||||
firstByte |= 0x1 << 6
|
||||
}
|
||||
if r.CF {
|
||||
firstByte |= 0x1 << 5
|
||||
}
|
||||
if r.SR {
|
||||
firstByte |= 0x1 << 4
|
||||
}
|
||||
if r.IL {
|
||||
firstByte |= 0x1 << 3
|
||||
}
|
||||
firstByte |= (r.TNF & 0x7) //Last 3 bits are from TNF
|
||||
buffer.WriteByte(firstByte)
|
||||
// TypeLength byte
|
||||
buffer.WriteByte(r.TypeLength)
|
||||
|
||||
// Payload Length byte (for SR) or 4 bytes for the regular case
|
||||
if r.SR {
|
||||
buffer.WriteByte(byte(r.PayloadLength))
|
||||
} else {
|
||||
buffer.Write(uint64ToBytes(r.PayloadLength, 4))
|
||||
}
|
||||
|
||||
// ID Length byte if we are meant to have it
|
||||
if r.IL {
|
||||
buffer.WriteByte(r.IDLength)
|
||||
}
|
||||
|
||||
// Write the type bytes if we have something
|
||||
if r.TypeLength > 0 {
|
||||
buffer.Write([]byte(r.Type))
|
||||
}
|
||||
|
||||
// Write the ID bytes if we have something
|
||||
if r.IL && r.IDLength > 0 {
|
||||
buffer.Write([]byte(r.ID))
|
||||
}
|
||||
|
||||
buffer.Write(r.Payload)
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// Check verifies that fields in this chunk are not in violation of the spec.
|
||||
func (r *recordChunk) Check() error {
|
||||
// If the TNF value is 0x00, the TYPE_LENGTH, ID_LENGTH,
|
||||
// and PAYLOAD_LENGTH fields MUST be zero and the TYPE, ID,
|
||||
// and PAYLOAD fields MUST be omitted from the record.
|
||||
if r.TNF == Empty && (r.TypeLength > 0 ||
|
||||
r.IDLength > 0 || r.PayloadLength > 0) {
|
||||
return errors.New("Record.check: " +
|
||||
"Empty record TNF but not empty fields")
|
||||
}
|
||||
// If the TNF value is 0x05 or 0x06 (Unknown/Unchanged),
|
||||
// the TYPE_LENGTH field MUST be 0 and the TYPE
|
||||
// field MUST be omitted from the NDEF record.
|
||||
if (r.TNF == Unknown || r.TNF == Unchanged) && r.TypeLength > 0 {
|
||||
return errors.New("Record.check: " +
|
||||
"This TNF does not support a Type field")
|
||||
}
|
||||
|
||||
// The TNF value MUST NOT be 0x07.
|
||||
if r.TNF == Reserved {
|
||||
return errors.New("Record.check: " +
|
||||
"The TNF cannot be Reserved, that value is reserved.")
|
||||
}
|
||||
|
||||
// NFC Record Type Definition 3.4:
|
||||
// The binary encoding of Well Known Types
|
||||
// (including Global and Local Names) and External
|
||||
// Type names MUST be done according to the
|
||||
// ASCII chart in Appendix A.
|
||||
if r.TNF == NFCForumWellKnownType ||
|
||||
r.TNF == NFCForumExternalType {
|
||||
typeString := string(r.Type)
|
||||
for _, rune := range typeString {
|
||||
if rune < 32 || rune > 126 {
|
||||
return errors.New("Record.check(): " +
|
||||
"Record type names SHALL " +
|
||||
"be formed of characters from of the " +
|
||||
"US ASCII [ASCII] character set")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.IL && r.IDLength > 0 {
|
||||
for _, rune := range r.ID {
|
||||
if rune < 32 || rune > 126 {
|
||||
return errors.New("Record.check(): " +
|
||||
"ID must use ASCII characters")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
package ndef
|
||||
|
||||
import (
|
||||
"github.com/hsanjuan/go-ndef/types/absoluteuri"
|
||||
"github.com/hsanjuan/go-ndef/types/ext"
|
||||
"github.com/hsanjuan/go-ndef/types/generic"
|
||||
"github.com/hsanjuan/go-ndef/types/media"
|
||||
"github.com/hsanjuan/go-ndef/types/wkt/text"
|
||||
"github.com/hsanjuan/go-ndef/types/wkt/uri"
|
||||
)
|
||||
|
||||
// The RecordPayload interface should be implemented by supported
|
||||
// NDEF Record types. It ensures that we have a way to interpret payloads
|
||||
// into printable information and to produce NDEF Record payloads for a given
|
||||
// type.
|
||||
type RecordPayload interface {
|
||||
// Returns a string representation of the Payload
|
||||
String() string
|
||||
// Provides serialization for the Payload
|
||||
Marshal() []byte
|
||||
// Provides de-serialization for the Payload
|
||||
Unmarshal(buf []byte)
|
||||
// Returns a string indetifying the type of this payload
|
||||
Type() string
|
||||
// Returns the length of the Payload (serialized)
|
||||
Len() int
|
||||
}
|
||||
|
||||
func makeRecordPayload(tnf byte, rtype string, payload []byte) RecordPayload {
|
||||
var r RecordPayload
|
||||
switch tnf {
|
||||
case NFCForumWellKnownType:
|
||||
switch rtype {
|
||||
case "U":
|
||||
r = new(uri.Payload)
|
||||
case "T":
|
||||
r = new(text.Payload)
|
||||
case "Sp":
|
||||
r = new(SmartPosterPayload)
|
||||
default:
|
||||
r = new(generic.Payload)
|
||||
}
|
||||
case MediaType:
|
||||
r = media.New(rtype, nil)
|
||||
case NFCForumExternalType:
|
||||
r = ext.New(rtype, nil)
|
||||
case AbsoluteURI:
|
||||
r = absoluteuri.New(rtype, nil)
|
||||
default:
|
||||
r = new(generic.Payload)
|
||||
}
|
||||
r.Unmarshal(payload)
|
||||
return r
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
package ndef
|
||||
|
||||
// Unfortunately splitting this to its own package causes a hard to break cycle.
|
||||
|
||||
// SmartPosterPayload represents the Payload of a Smart Poster, which is
|
||||
// an NDEF Message with one or multiple records.
|
||||
type SmartPosterPayload struct {
|
||||
Message *Message
|
||||
}
|
||||
|
||||
// NewSmartPosterPayload returns a new Smart Poster payload.
|
||||
func NewSmartPosterPayload(msg *Message) *SmartPosterPayload {
|
||||
return &SmartPosterPayload{
|
||||
Message: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the contents of the message contained in the Smart Poster.
|
||||
func (sp *SmartPosterPayload) String() string {
|
||||
str := "\n"
|
||||
str += sp.Message.String()
|
||||
return str
|
||||
}
|
||||
|
||||
// Type returns the URN for the Smart Poster type
|
||||
func (sp *SmartPosterPayload) Type() string {
|
||||
return "urn:nfc:wkt:Sp"
|
||||
}
|
||||
|
||||
// Marshal returns the bytes representing the payload of a Smart Poster.
|
||||
// The payload is the contained NDEF Message.
|
||||
func (sp *SmartPosterPayload) Marshal() []byte {
|
||||
bs, _ := sp.Message.Marshal()
|
||||
return bs
|
||||
}
|
||||
|
||||
// Unmarshal parses the SmartPosterPayload from a Smart Poster.
|
||||
func (sp *SmartPosterPayload) Unmarshal(buf []byte) {
|
||||
msg := &Message{}
|
||||
msg.Unmarshal(buf)
|
||||
sp.Message = msg
|
||||
}
|
||||
|
||||
// Len returns the length of this payload in bytes.
|
||||
func (sp *SmartPosterPayload) Len() int {
|
||||
return len(sp.Marshal())
|
||||
}
|
65
vendor/github.com/hsanjuan/go-ndef/types/absoluteuri/absolute_uri.go
generated
vendored
Normal file
65
vendor/github.com/hsanjuan/go-ndef/types/absoluteuri/absolute_uri.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
// Package absoluteuri provides an implementation for NDEF Payloads
|
||||
// of Absolute URI type.
|
||||
package absoluteuri
|
||||
|
||||
// Payload is a wrapper to store a Payload
|
||||
type Payload struct {
|
||||
URIType string
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// New returns a pointer to a Payload type holding the given payload with the
|
||||
// given type.
|
||||
func New(uriType string, payload []byte) *Payload {
|
||||
return &Payload{
|
||||
URIType: uriType,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string explaining that we are not sure how to print
|
||||
// this type.
|
||||
func (absu *Payload) String() string {
|
||||
if absu.Len() > 0 {
|
||||
return "<The message contains a binary payload>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Type returns the URI defining the type for this payload
|
||||
func (absu *Payload) Type() string {
|
||||
return absu.URIType
|
||||
}
|
||||
|
||||
// Marshal returns the bytes representing the payload
|
||||
func (absu *Payload) Marshal() []byte {
|
||||
return absu.Payload
|
||||
}
|
||||
|
||||
// Unmarshal parses a generic payload
|
||||
func (absu *Payload) Unmarshal(buf []byte) {
|
||||
absu.Payload = buf
|
||||
}
|
||||
|
||||
// Len is the length of the byte slice resulting of Marshaling
|
||||
// this Payload.
|
||||
func (absu *Payload) Len() int {
|
||||
return len(absu.Marshal())
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
// Package ext provides an implementation for NDEF Payloads of NFC Forum
|
||||
// External Type.
|
||||
package ext
|
||||
|
||||
// Payload is a wrapper to store a Payload
|
||||
type Payload struct {
|
||||
ExtType string
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// New returns a pointer to a Payload type holding the given payload with the
|
||||
// given type.
|
||||
func New(extType string, payload []byte) *Payload {
|
||||
return &Payload{
|
||||
ExtType: extType,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string explaining that we are not sure how to print
|
||||
// this type.
|
||||
func (extT *Payload) String() string {
|
||||
if extT.Len() > 0 {
|
||||
return "<The message contains a binary payload>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Type returns a readable type name for this payload.
|
||||
func (extT *Payload) Type() string {
|
||||
return "urn:nfc:ext:" + extT.ExtType
|
||||
}
|
||||
|
||||
// Marshal returns the bytes representing the payload
|
||||
func (extT *Payload) Marshal() []byte {
|
||||
return extT.Payload
|
||||
}
|
||||
|
||||
// Unmarshal parses a generic payload
|
||||
func (extT *Payload) Unmarshal(buf []byte) {
|
||||
extT.Payload = buf
|
||||
}
|
||||
|
||||
// Len is the length of the byte slice resulting of Marshaling
|
||||
// this Payload.
|
||||
func (extT *Payload) Len() int {
|
||||
return len(extT.Marshal())
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
// Package generic provides a generic implementation for NDEF Payloads which are
|
||||
// either custom or not supported yet.
|
||||
package generic
|
||||
|
||||
// Payload is a wrapper to store a Payload.
|
||||
type Payload struct {
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// New returns a pointer to a Payload type holding the given payload.
|
||||
func New(payload []byte) *Payload {
|
||||
return &Payload{
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string explaining that we are not sure how to print
|
||||
// this type.
|
||||
func (g *Payload) String() string {
|
||||
if g.Len() > 0 {
|
||||
return "<The message contains a binary payload>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Type returns the name of the payload type. In this case, it's generic.
|
||||
func (g *Payload) Type() string {
|
||||
return "go-ndef-generic"
|
||||
}
|
||||
|
||||
// Marshal returns the bytes representing the payload of a Record of
|
||||
// generic type.
|
||||
func (g *Payload) Marshal() []byte {
|
||||
return g.Payload
|
||||
}
|
||||
|
||||
// Unmarshal parses a generic payload.
|
||||
func (g *Payload) Unmarshal(buf []byte) {
|
||||
g.Payload = buf
|
||||
}
|
||||
|
||||
// Len is the length of the byte slice resulting of Marshaling.
|
||||
func (g *Payload) Len() int {
|
||||
return len(g.Marshal())
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
// Package media provides an implementation for NDEF Payloads for media
|
||||
// types.
|
||||
package media
|
||||
|
||||
// Payload is a wrapper to store a Payload
|
||||
type Payload struct {
|
||||
MimeType string
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// New returns a pointer to a Payload type holding the given payload with the
|
||||
// given type.
|
||||
func New(mimeType string, payload []byte) *Payload {
|
||||
return &Payload{
|
||||
MimeType: mimeType,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string explaining that we are not sure how to print
|
||||
// this type.
|
||||
func (media *Payload) String() string {
|
||||
if media.Len() > 0 {
|
||||
return "<The message contains a payload>"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Type returns the mime type of this payload.
|
||||
func (media *Payload) Type() string {
|
||||
return media.MimeType
|
||||
}
|
||||
|
||||
// Marshal returns the bytes representing the payload
|
||||
func (media *Payload) Marshal() []byte {
|
||||
return media.Payload
|
||||
}
|
||||
|
||||
// Unmarshal parses a generic payload
|
||||
func (media *Payload) Unmarshal(buf []byte) {
|
||||
media.Payload = buf
|
||||
}
|
||||
|
||||
// Len is the length of the byte slice resulting of Marshaling
|
||||
// this Payload.
|
||||
func (media *Payload) Len() int {
|
||||
return len(media.Marshal())
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
// BUG(hector): The implementation ignores the guidelines about displaying the
|
||||
// text and removing the control characters.
|
||||
|
||||
// BUG(hector): UTF-16 with different byte order, with/without BOM is not tested.
|
||||
|
||||
// Package text provides support for NDEF Payloads of Text type.
|
||||
// It follows the NFC Forum Text Record Type Definition specification
|
||||
// (NFCForum-TS-RTD_Text_1.0).
|
||||
//
|
||||
// The Payload type implements the RecordPayload interface from ndef,
|
||||
// so it can be used as ndef.Record.Payload.
|
||||
package text
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// Payload represents a NDEF Record Payload of type "T", which
|
||||
// holds a text field and IANA-formatted language information.
|
||||
type Payload struct {
|
||||
Language string
|
||||
Text string
|
||||
}
|
||||
|
||||
// New returns a pointer to a Payload.
|
||||
//
|
||||
// The language parameter must be compliant to RFC 3066 (i.e. "en_US"),
|
||||
// but no check is performed.
|
||||
func New(text, language string) *Payload {
|
||||
return &Payload{
|
||||
Language: language,
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the actual text. Language information is ommited.
|
||||
func (t *Payload) String() string {
|
||||
return t.Text
|
||||
}
|
||||
|
||||
// Type returns the URN for Text types.
|
||||
func (t *Payload) Type() string {
|
||||
return "urn:nfc:wkt:T"
|
||||
}
|
||||
|
||||
// Marshal returns the bytes representing the payload of a text Record.
|
||||
func (t *Payload) Marshal() []byte {
|
||||
var buf bytes.Buffer
|
||||
ianaLen := byte(len(t.Language))
|
||||
buf.WriteByte(ianaLen)
|
||||
buf.Write([]byte(t.Language))
|
||||
buf.Write([]byte(t.Text))
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// Unmarshal parses the Payload from a text Record.
|
||||
func (t *Payload) Unmarshal(buf []byte) {
|
||||
t.Language = ""
|
||||
t.Text = ""
|
||||
i := byte(0)
|
||||
if len(buf) < 1 {
|
||||
return
|
||||
}
|
||||
firstByte := buf[i]
|
||||
i++
|
||||
isUtf16 := firstByte>>7 == 1
|
||||
// firstByte>>6 must be set to 0
|
||||
ianaLen := 0x3F & firstByte // last 5 bytes
|
||||
if len(buf) < int(i+ianaLen) {
|
||||
return
|
||||
}
|
||||
t.Language = string(buf[i : i+ianaLen])
|
||||
i += ianaLen
|
||||
if len(buf) < int(i) {
|
||||
return
|
||||
}
|
||||
if isUtf16 {
|
||||
//Convert buf to []uint16
|
||||
bytesBuf := bytes.NewBuffer(buf[i:])
|
||||
var finished bool
|
||||
var uint16buf []uint16
|
||||
for !finished {
|
||||
b1, err := bytesBuf.ReadByte()
|
||||
b2, err := bytesBuf.ReadByte()
|
||||
uint16buf = append(uint16buf,
|
||||
uint16(b1<<8)|uint16(b2))
|
||||
finished = err != nil
|
||||
}
|
||||
runes := utf16.Decode(uint16buf)
|
||||
// It appears we get an extra trailing char at the end
|
||||
t.Text = strings.TrimSuffix(string(runes), "\x00")
|
||||
} else {
|
||||
t.Text = string(buf[i:])
|
||||
}
|
||||
}
|
||||
|
||||
// Len is the length of the byte slice resulting of Marshaling..
|
||||
func (t *Payload) Len() int {
|
||||
return len(t.Marshal())
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/***
|
||||
Copyright (c) 2018, Hector Sanjuan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
// Package uri provides support for NDEF Payloads of URI type.
|
||||
// It follows the NFC Forum URI Record Type Definition specification
|
||||
// (NFCForum-TS-RTD_URI_1.0).
|
||||
//
|
||||
// The Payload type implements the RecordPayload interface from ndef,
|
||||
// so it can be used as ndef.Record.Payload.
|
||||
package uri
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// URIProtocols provides a mapping between the first byte if a NDEF Payload of
|
||||
// type "U" (URI) and the string value for the protocol.
|
||||
var URIProtocols = map[byte]string{
|
||||
0: "",
|
||||
1: "http://www.",
|
||||
2: "https://www.",
|
||||
3: "http://",
|
||||
4: "https://",
|
||||
5: "tel:",
|
||||
6: "mailto:",
|
||||
7: "ftp://anonymous:anonymous@",
|
||||
8: "ftp://ftp.",
|
||||
9: "ftps://",
|
||||
10: "sftp://",
|
||||
11: "smb://",
|
||||
12: "nfs://",
|
||||
13: "ftp://",
|
||||
14: "dev://",
|
||||
15: "news:",
|
||||
16: "telnet://",
|
||||
17: "imap:",
|
||||
18: "rtsp://",
|
||||
19: "urn:",
|
||||
20: "pop:",
|
||||
21: "sip:",
|
||||
22: "sips:",
|
||||
23: "tftp:",
|
||||
24: "btspp://",
|
||||
25: "btl2cap://",
|
||||
26: "btgoep://",
|
||||
27: "tcpobex://",
|
||||
28: "irdaobex://",
|
||||
29: "file://",
|
||||
30: "urn:epc:id:",
|
||||
31: "urn:epc:tag:",
|
||||
32: "urn:epc:pat:",
|
||||
33: "urn:epc:raw:",
|
||||
34: "urn:epc:",
|
||||
35: "urn:nfc:",
|
||||
}
|
||||
|
||||
// Payload represents a NDEF Record Payload of Type "U".
|
||||
type Payload struct {
|
||||
IdentCode byte
|
||||
URIField string
|
||||
}
|
||||
|
||||
// New returns a pointer to an Payload object.
|
||||
// The Identifier code is automatically
|
||||
// set based on the provided string.
|
||||
func New(uriStr string) *Payload {
|
||||
u := new(Payload)
|
||||
u.URIField = uriStr
|
||||
for i := byte(1); i < 36; i++ {
|
||||
m, _ := regexp.MatchString("^"+URIProtocols[i], uriStr)
|
||||
if m {
|
||||
u.IdentCode = i
|
||||
u.URIField = strings.Replace(uriStr,
|
||||
URIProtocols[i], "", 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the URI string.
|
||||
func (u *Payload) String() string {
|
||||
return URIProtocols[u.IdentCode] + u.URIField
|
||||
}
|
||||
|
||||
// Type returns the Uniform Resource Name for URIs.
|
||||
func (u *Payload) Type() string {
|
||||
return "urn:nfc:wkt:U"
|
||||
}
|
||||
|
||||
// Marshal returns the bytes representing the payload of a Record of URI type.
|
||||
func (u *Payload) Marshal() []byte {
|
||||
p := []byte{u.IdentCode}
|
||||
return append(p, []byte(u.URIField)...)
|
||||
}
|
||||
|
||||
// Unmarshal parses the payload of a URI type record.
|
||||
func (u *Payload) Unmarshal(buf []byte) {
|
||||
u.IdentCode = 0
|
||||
u.URIField = ""
|
||||
if len(buf) > 0 {
|
||||
u.IdentCode = buf[0]
|
||||
}
|
||||
if len(buf) > 1 {
|
||||
u.URIField = string(buf[1:])
|
||||
}
|
||||
}
|
||||
|
||||
// Len is the length of the byte slice resulting of Marshaling.
|
||||
func (u *Payload) Len() int {
|
||||
return len(u.Marshal())
|
||||
}
|
Loading…
Reference in New Issue