diff --git a/VERSION b/VERSION index 0ea3a94..0c62199 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.0 +0.2.1 diff --git a/vendor/github.com/hsanjuan/go-ndef/LICENSE b/vendor/github.com/hsanjuan/go-ndef/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. diff --git a/vendor/github.com/hsanjuan/go-ndef/byte_utils.go b/vendor/github.com/hsanjuan/go-ndef/byte_utils.go new file mode 100644 index 0000000..2de485e --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/byte_utils.go @@ -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 . +***/ + +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 +} diff --git a/vendor/github.com/hsanjuan/go-ndef/message.go b/vendor/github.com/hsanjuan/go-ndef/message.go new file mode 100644 index 0000000..9c910ce --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/message.go @@ -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 . +***/ + +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" +) diff --git a/vendor/github.com/hsanjuan/go-ndef/ndef.go b/vendor/github.com/hsanjuan/go-ndef/ndef.go new file mode 100644 index 0000000..2b5224c --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/ndef.go @@ -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 . +***/ + +// 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 +) diff --git a/vendor/github.com/hsanjuan/go-ndef/record.go b/vendor/github.com/hsanjuan/go-ndef/record.go new file mode 100644 index 0000000..77b7c2d --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/record.go @@ -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 . +***/ + +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" +) diff --git a/vendor/github.com/hsanjuan/go-ndef/record_chunk.go b/vendor/github.com/hsanjuan/go-ndef/record_chunk.go new file mode 100644 index 0000000..28cd289 --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/record_chunk.go @@ -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 . +***/ + +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 +} diff --git a/vendor/github.com/hsanjuan/go-ndef/record_payload.go b/vendor/github.com/hsanjuan/go-ndef/record_payload.go new file mode 100644 index 0000000..8d63f6d --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/record_payload.go @@ -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 . +***/ + +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 +} diff --git a/vendor/github.com/hsanjuan/go-ndef/sp.go b/vendor/github.com/hsanjuan/go-ndef/sp.go new file mode 100644 index 0000000..6158b4d --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/sp.go @@ -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 . +***/ + +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()) +} diff --git a/vendor/github.com/hsanjuan/go-ndef/types/absoluteuri/absolute_uri.go b/vendor/github.com/hsanjuan/go-ndef/types/absoluteuri/absolute_uri.go new file mode 100644 index 0000000..d8207f5 --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/types/absoluteuri/absolute_uri.go @@ -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 . +***/ + +// 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 "" + } + 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()) +} diff --git a/vendor/github.com/hsanjuan/go-ndef/types/ext/ext.go b/vendor/github.com/hsanjuan/go-ndef/types/ext/ext.go new file mode 100644 index 0000000..bdc5462 --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/types/ext/ext.go @@ -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 . +***/ + +// 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 "" + } + 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()) +} diff --git a/vendor/github.com/hsanjuan/go-ndef/types/generic/generic.go b/vendor/github.com/hsanjuan/go-ndef/types/generic/generic.go new file mode 100644 index 0000000..e1842f2 --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/types/generic/generic.go @@ -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 . +***/ + +// 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 "" + } + 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()) +} diff --git a/vendor/github.com/hsanjuan/go-ndef/types/media/media.go b/vendor/github.com/hsanjuan/go-ndef/types/media/media.go new file mode 100644 index 0000000..4ec2d28 --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/types/media/media.go @@ -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 . +***/ + +// 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 "" + } + 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()) +} diff --git a/vendor/github.com/hsanjuan/go-ndef/types/wkt/text/text.go b/vendor/github.com/hsanjuan/go-ndef/types/wkt/text/text.go new file mode 100644 index 0000000..0916db4 --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/types/wkt/text/text.go @@ -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 . +***/ + +// 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()) +} diff --git a/vendor/github.com/hsanjuan/go-ndef/types/wkt/uri/uri.go b/vendor/github.com/hsanjuan/go-ndef/types/wkt/uri/uri.go new file mode 100644 index 0000000..2b66fd2 --- /dev/null +++ b/vendor/github.com/hsanjuan/go-ndef/types/wkt/uri/uri.go @@ -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 . +***/ + +// 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()) +}