Example #1
0
        /// <summary>
        /// Create the payload based on the data stored in the properties. Usually called
        /// automatically when changing properties, but you might need to call this manually
        /// if you modify the list of alternative carrier records directly without using
        /// accessor method provided by this class.
        /// </summary>
        /// <exception cref="NdefException">Thrown if unable to assemble the payload.
        /// The exception message contains further details about the issue.</exception>
        public void AssemblePayload()
        {
            if (_handoverVersion == null)
            {
                throw new NdefException(NdefExceptionMessages.ExHandoverInvalidVersion);
            }

            // Convert child records to message
            var childMsg = new NdefMessage();

            if (_handoverAlternativeCarrierRecords != null)
            {
                childMsg.AddRange(_handoverAlternativeCarrierRecords);
            }
            if (_handoverErrorRecord != null)
            {
                childMsg.Add(_handoverErrorRecord);
            }

            var childBytes = childMsg.ToByteArray();

            var newPayload = new byte[childBytes.Length + 1];

            // Frist byte: handover version
            newPayload[0] = _handoverVersion.Version;

            // Rest of the payload: child message containing Alternative Carrier records + Error record
            Array.Copy(childBytes, 0, newPayload, 1, childBytes.Length);

            _payload = newPayload;
        }
Example #2
0
        /// <summary>
        /// Reverse function to parseRecords() - this one takes
        /// the information stored in the individual record instances and assembles
        /// it into the payload of the base class.
        /// </summary>
        /// <remarks>
        /// As the URI is mandatory, the payload will not be assembled
        /// if no URI is defined.
        /// </remarks>
        /// <returns>Whether assembling the payload was successful.</returns>
        private bool AssemblePayload()
        {
            // Uri is mandatory - don't assemble the payload if it's not set
            if (RecordUri == null)
            {
                return(false);
            }

            // URI (mandatory)
            var message = new NdefMessage {
                RecordUri
            };

            // Title(s) (optional)
            if (Titles != null && Titles.Count > 0)
            {
                message.AddRange(Titles);
            }

            // Action (optional)
            if (ActionInUse())
            {
                message.Add(_recordAction);
            }

            // Size (optional)
            if (SizeInUse())
            {
                message.Add(_recordSize);
            }

            // Mime Type (optional)
            if (MimeTypeInUse())
            {
                message.Add(_recordMimeType);
            }

            // Image (optional)
            if (ImageInUse())
            {
                message.Add(_recordImage);
            }

            SetPayloadAndParse(message.ToByteArray(), false);

            return(true);
        }
Example #3
0
        /// <summary>
        /// Returns the NDEF message parsed from the contents of <paramref name="message"/>.
        /// </summary>
        /// <remarks>
        /// The <paramref name="message"/> parameter is interpreted as the raw message format
        /// defined in the NFC Forum specifications.
        /// </remarks>
        /// <param name="message">Raw byte array containing the NDEF message, which consists
        /// of 0 or more NDEF records.</param>
        /// <exception cref="NdefException">Thrown if there is an error parsing the NDEF
        /// message out of the byte array.</exception>
        /// <returns>If parsing was successful, the NDEF message containing 0 or more NDEF
        /// records.</returns>
        public static NdefMessage FromByteArray(byte[] message)
        {
            var result = new NdefMessage();

            var seenMessageBegin = false;
            var seenMessageEnd   = false;

            var partialChunk = new MemoryStream();
            var record       = new NdefRecord();

            uint i = 0;

            while (i < message.Length)
            {
                //Debug.WriteLine("Parsing byte[] to NDEF message. New record starts at {0}", i);

                // Parse flags out of NDEF message header

                // The MB flag is a 1-bit field that when set indicates the start of an NDEF message.
                bool messageBegin = (message[i] & 0x80) != 0;
                // The ME flag is a 1-bit field that when set indicates the end of an NDEF message.
                // Note, that in case of a chunked payload, the ME flag is set only in the terminating record chunk of that chunked payload.
                bool messageEnd = (message[i] & 0x40) != 0;
                // The CF flag is a 1-bit field indicating that this is either the first record chunk or a middle record chunk of a chunked payload.
                bool cf = (message[i] & 0x20) != 0;
                // The SR flag is a 1-bit field indicating, if set, that the PAYLOAD_LENGTH field is a single octet.
                bool sr = (message[i] & 0x10) != 0;
                // The IL flag is a 1-bit field indicating, if set, that the ID_LENGTH field is present in the header as a single octet.
                // If the IL flag is zero, the ID_LENGTH field is omitted from the record header and the ID field is also omitted from the record.
                bool il             = (message[i] & 0x08) != 0;
                var  typeNameFormat = (NdefRecord.TypeNameFormatType)(message[i] & 0x07);

                //Debug.WriteLine("ShortRecord: " + (sr ? "yes" : "no"));
                //Debug.WriteLine("Id Length present: " + (il ? "yes" : "no"));

                if (messageBegin && seenMessageBegin)
                {
                    throw new NdefException(NdefExceptionMessages.ExMessageBeginLate);
                }
                else if (!messageBegin && !seenMessageBegin)
                {
                    throw new NdefException(NdefExceptionMessages.ExMessageBeginMissing);
                }
                else if (messageBegin && !seenMessageBegin)
                {
                    seenMessageBegin = true;
                }

                if (messageEnd && seenMessageEnd)
                {
                    throw new NdefException(NdefExceptionMessages.ExMessageEndLate);
                }
                else if (messageEnd && !seenMessageEnd)
                {
                    seenMessageEnd = true;
                }

                if (cf && (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged) && partialChunk.Length > 0)
                {
                    throw new NdefException(NdefExceptionMessages.ExMessagePartialChunk);
                }

                // Header length
                int headerLength = 1;
                headerLength += (sr) ? 1 : 4;
                headerLength += (il) ? 1 : 0;

                if (i + headerLength >= message.Length)
                {
                    throw new NdefException(NdefExceptionMessages.ExMessageUnexpectedEnd);
                }

                // Type length
                byte typeLength = message[++i];

                if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (typeLength != 0))
                {
                    throw new NdefException(NdefExceptionMessages.ExMessageInvalidChunkedType);
                }

                // Payload length (short record?)
                uint payloadLength;
                if (sr)
                {
                    // Short record - payload length is a single octet
                    payloadLength = message[++i];
                }
                else
                {
                    // No short record - payload length is four octets representing a 32 bit unsigned integer (MSB-first)
                    payloadLength  = (uint)((message[++i]) << 24);
                    payloadLength |= (uint)((message[++i]) << 16);
                    payloadLength |= (uint)((message[++i]) << 8);
                    payloadLength |= (uint)((message[++i]) << 0);
                }

                // ID length
                byte idLength;
                idLength = (byte)(il ? message[++i] : 0);

                // Total length of content (= type + payload + ID)
                uint contentLength = typeLength + payloadLength + idLength;
                if (i + contentLength >= message.Length)
                {
                    throw new NdefException(NdefExceptionMessages.ExMessageUnexpectedEnd);
                }


                if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (idLength != 0))
                {
                    throw new NdefException(NdefExceptionMessages.ExMessageInvalidChunkedId);
                }

                if (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged)
                {
                    record.TypeNameFormat = typeNameFormat;
                }

                // Read type
                if (typeLength > 0)
                {
                    record.Type = new byte[typeLength];
                    Array.Copy(message, (int)(++i), record.Type, 0, typeLength);
                    i += (uint)typeLength - 1;
                }

                // Read ID
                if (idLength > 0)
                {
                    record.Id = new byte[idLength];
                    Array.Copy(message, (int)(++i), record.Id, 0, idLength);
                    i += (uint)idLength - 1;
                }

                // Read payload
                if (payloadLength > 0)
                {
                    var payload = new byte[payloadLength];
                    Array.Copy(message, (int)(++i), payload, 0, (int)payloadLength);

                    if (cf)
                    {
                        // chunked payload, except last
                        partialChunk.Write(payload, 0, payload.Length);
                    }
                    else if (typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged)
                    {
                        // last chunk of chunked payload
                        partialChunk.Write(payload, 0, payload.Length);
                        record.Payload = partialChunk.ToArray();
                    }
                    else
                    {
                        // non-chunked payload
                        record.Payload = payload;
                    }

                    i += payloadLength - 1;
                }

                if (!cf)
                {
                    // Add record to the message and create a new record for the next loop iteration
                    result.Add(record);
                    record = new NdefRecord();
                }

                if (!cf && seenMessageEnd)
                {
                    break;
                }

                // move to start of next record
                ++i;
            }


            if (!seenMessageBegin && !seenMessageEnd)
            {
                throw new NdefException(NdefExceptionMessages.ExMessageNoBeginOrEnd);
            }

            return(result);
        }
        /// <summary>
        /// Returns the NDEF message parsed from the contents of <paramref name="message"/>.
        /// </summary>
        /// <remarks>
        /// The <paramref name="message"/> parameter is interpreted as the raw message format
        /// defined in the NFC Forum specifications.
        /// </remarks>
        /// <param name="message">Raw byte array containing the NDEF message, which consists
        /// of 0 or more NDEF records.</param>
        /// <exception cref="NdefException">Thrown if there is an error parsing the NDEF
        /// message out of the byte array.</exception>
        /// <returns>If parsing was successful, the NDEF message containing 0 or more NDEF
        /// records.</returns>
        public static NdefMessage FromByteArray(byte[] message)
        {
            var result = new NdefMessage();

            var seenMessageBegin = false;
            var seenMessageEnd   = false;

            var partialChunk = new MemoryStream();
            var record       = new NdefRecord();

            uint i = 0;

            while (i < message.Length)
            {
                //Debug.WriteLine("Parsing byte[] to NDEF message. New record starts at {0}", i);
                // Parse flags out of NDEF message header
                bool messageBegin   = (message[i] & 0x80) != 0;
                bool messageEnd     = (message[i] & 0x40) != 0;
                bool cf             = (message[i] & 0x20) != 0;
                bool sr             = (message[i] & 0x10) != 0;
                bool il             = (message[i] & 0x08) != 0;
                var  typeNameFormat = (NdefRecord.TypeNameFormatType)(message[i] & 0x07);

                Debug.WriteLine("ShortRecord: " + (sr ? "yes" : "no"));
                Debug.WriteLine("Id Length present: " + (il ? "yes" : "no"));

                if (messageBegin && seenMessageBegin)
                {
                    throw new NdefException("Message Begin Late");
                }
                else if (!messageBegin && !seenMessageBegin)
                {
                    throw new NdefException("Begin Missing");
                }
                else if (messageBegin && !seenMessageBegin)
                {
                    seenMessageBegin = true;
                }

                if (messageEnd && seenMessageEnd)
                {
                    throw new NdefException("End Late");
                }
                else if (messageEnd && !seenMessageEnd)
                {
                    seenMessageEnd = true;
                }

                if (cf && (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged) && partialChunk.Length > 0)
                {
                    throw new NdefException("Partial Chunk");
                }

                int headerLength = 1;
                headerLength += (sr) ? 1 : 4;
                headerLength += (il) ? 1 : 0;

                if (i + headerLength >= message.Length)
                {
                    throw new NdefException("NdefExceptionMessages.ExMessageUnexpectedEnd");
                }

                byte typeLength = message[++i];

                if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (typeLength != 0))
                {
                    throw new NdefException("Invalid Chunk");
                }

                uint payloadLength;
                if (sr)
                {
                    // Short record - payload length is a single octet
                    payloadLength = message[++i];
                }
                else
                {
                    // No short record - payload length is four octets representing a 32 bit unsigned integer (MSB-first)
                    payloadLength  = (uint)((message[++i]) << 24);
                    payloadLength |= (uint)((message[++i]) << 16);
                    payloadLength |= (uint)((message[++i]) << 8);
                    payloadLength |= (uint)((message[++i]) << 0);
                }
                //Debug.WriteLine("Payload length: " + payloadLength);

                byte idLength;
                idLength = (byte)(il ? message[++i] : 0);

                uint contentLength = typeLength + payloadLength + idLength;
                if (i + contentLength >= message.Length)
                {
                    throw new NdefException("Unexpected End");
                }
                //Debug.WriteLine("Content length: {0} (Start at: {1})", contentLength, i);


                if ((typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged) && (idLength != 0))
                {
                    throw new NdefException("Invalid Chunk Id");
                }

                if (typeNameFormat != NdefRecord.TypeNameFormatType.Unchanged)
                {
                    record.TypeNameFormat = typeNameFormat;
                }

                if (typeLength > 0)
                {
                    record.Type = new byte[typeLength];
                    Array.Copy(message, (int)(++i), record.Type, 0, typeLength);
                    i += (uint)typeLength - 1;
                }

                if (idLength > 0)
                {
                    record.Id = new byte[idLength];
                    Array.Copy(message, (int)(++i), record.Id, 0, idLength);
                    i += (uint)idLength - 1;
                }

                if (payloadLength > 0)
                {
                    var payload = new byte[payloadLength];
                    Array.Copy(message, (int)(++i), payload, 0, (int)payloadLength);

                    if (cf)
                    {
                        // chunked payload, except last
                        partialChunk.Write(payload, 0, payload.Length);
                    }
                    else if (typeNameFormat == NdefRecord.TypeNameFormatType.Unchanged)
                    {
                        // last chunk of chunked payload
                        partialChunk.Write(payload, 0, payload.Length);
                        record.Payload = partialChunk.ToArray();
                    }
                    else
                    {
                        // non-chunked payload
                        record.Payload = payload;
                    }

                    i += payloadLength - 1;
                }

                if (!cf)
                {
                    result.Add(record);
                    record = new NdefRecord();
                }

                if (!cf && seenMessageEnd)
                {
                    break;
                }

                // move to start of next record
                ++i;
            }


            if (!seenMessageBegin && !seenMessageEnd)
            {
                throw new NdefException("No Begin Or End");
            }

            return(result);
        }