1 /**
  2  *  ---------
  3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
  4  * |#       #|  
  5  * |#       #|  Copyright (c) 1999-2011 CardContact Software & System Consulting
  6  * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
  7  *  --------- 
  8  *
  9  *  This file is part of OpenSCDP.
 10  *
 11  *  OpenSCDP is free software; you can redistribute it and/or modify
 12  *  it under the terms of the GNU General Public License version 2 as
 13  *  published by the Free Software Foundation.
 14  *
 15  *  OpenSCDP is distributed in the hope that it will be useful,
 16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18  *  GNU General Public License for more details.
 19  *
 20  *  You should have received a copy of the GNU General Public License
 21  *  along with OpenSCDP; if not, write to the Free Software
 22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 23  *
 24  * @fileoverview Create or decode NDEF messages
 25  */
 26 
 27 function Ndef(encoded) {
 28 	if (typeof(encoded) != "undefined") {
 29 		this.decode(encoded);
 30 	} else {
 31 		this.typeLength = 0;
 32 		this.flags = Ndef.messageBegin | Ndef.messageEnd | Ndef.shortRecord | 1;
 33 		this.id = null;
 34 	}
 35 }
 36 
 37 //Flag Byte Constants
 38 Ndef.messageBegin = 0x80;
 39 Ndef.messageEnd = 0x40;
 40 Ndef.chunkFlag = 0x20;
 41 Ndef.shortRecord = 0x10;
 42 Ndef.idLengthFlag = 0x08; //IL
 43 Ndef.tnf = 0x07;
 44 
 45 
 46 //Uri Identifier Abbrevations
 47 Ndef.uriIdentifier = new Array(null, "http://www.", "https://www.", "http://", "https://", "tel:", "mailto:", "ftp://anonymous:anonymous@", "ftp://ftp.", "ftps://", "sftp://", "smb://", "nfs://", "ftp://", "dav://", "news:", "telnet://", "imap:", "rtsp://", "urn:", "pop", "sip:", "sips:", "tftp:", "btspp://", "btl2cap://", "btgoep://", "tcpobex://", "irdaobex://", "file://", "urn:epc:id:", "urn:epc:tag:", "urn:epc:pat:", "urn:epc:raw:", "urn:epc:", "urn:nfc:");
 48  
 49 /**
 50  *	Create Ndef object
 51  *	@param {ByteString} encoded the encoded Ndef ByteString
 52  */
 53 Ndef.prototype.decode = function(encoded) {
 54 	this.flags = encoded.byteAt(0);
 55 	this.typeLength = encoded.byteAt(1);
 56 	var ofs = 2;
 57 	
 58 	if (this.isShortRecord()) {
 59 		this.payloadLength = encoded.byteAt(ofs++);
 60 	} else {
 61 		this.payloadLength = encoded.bytes(ofs, 4).toUnsigned();
 62 		ofs += 4;
 63 	}
 64 	
 65 	if (this.isIdLengthFlag()) {
 66 		this.idLength = encoded.byteAt(ofs++);
 67 	}
 68 
 69 	this.type = encoded.bytes(ofs, this.typeLength);
 70 	ofs += this.typeLength;
 71 	
 72 	if (this.isIdLengthFlag()) {
 73 		this.id = encoded.bytes(ofs, this.idLength);
 74 		ofs += this.idLength;
 75 	}
 76 
 77 	this.payload = encoded.bytes(ofs, this.payloadLength);
 78 	ofs += this.payloadLength;
 79 	
 80 	if (this.isChunked()) {
 81 		var nextRecord = new Ndef(encoded.bytes(ofs));
 82 		this.payload += nextRecord.getPayload();
 83 	}
 84 
 85 	this.setMessageBegin(true);
 86 	this.setMessageEnd(true);
 87 	this.setChunked(false);
 88 	this.payloadLength == this.payload.length;
 89 }
 90 
 91 
 92 /**
 93  *	Return the payload of the NDEF object.
 94  *	@return {ByteString} the payload
 95  */
 96 Ndef.prototype.getPayload = function() {
 97 	return new ByteString(this.payload, HEX);
 98 }
 99 
100 
101 /**
102  *	Return the decoded URI in readable form.
103  *	@return {String} the UTF-8 decoded URI
104  */
105 Ndef.prototype.getUri = function() {
106 	//type must be "U" 
107 	var payload = new ByteString(this.payload, HEX);
108 	
109 	if (this.type == 55) {
110 		var i = payload.byteAt(0);
111 		var str = "";
112 		if (i > 0) {
113 			str += Ndef.uriIdentifier[i];
114 		}
115 		str += payload.bytes(1).toString(UTF8);
116 		
117 		return str;
118 	}
119 }
120 
121 
122 /**
123  *	Check if the Message Begin flag is set. The flag indicates the start of an NDEF message. 
124  *	@return {Boolean}
125  */
126 Ndef.prototype.isMessageBegin = function() {
127 	return (this.flags & Ndef.messageBegin) == 0x80;
128 }
129 
130 
131 /**
132  *	Set the Message Begin flag. The flag indicates the start of an NDEF message.
133  *	@param {Boolean} state
134  */
135 Ndef.prototype.setMessageBegin = function(state) {
136 	this.flags = this.flags & ~Ndef.messageBegin | (state ? Ndef.messageBegin : 0);
137 	return this.flags & Ndef.messageBegin;
138 }
139 
140 
141 /**
142  *	Check if the Message End flag is set. The flag indicates the end of an NDEF message.
143  *	@return {Boolean}
144  */
145 Ndef.prototype.isMessageEnd = function(state) {
146 	return (this.flags & Ndef.messageBegin) == 0x40;
147 }
148 
149 
150 /**
151  *	Set the Message End flag. The flag indicates the end of an NDEF message.
152  *	@param {Boolean} state
153  */
154 Ndef.prototype.setMessageEnd = function(state) {
155 	this.flags = this.flags & ~Ndef.messageEnd | (state ? Ndef.messageEnd : 0);
156 	return this.flags & Ndef.messageBegin;
157 }
158 
159 
160 /**
161  *	Check if the Chunk flag is set. 
162  *	The flag indicates that this is either the first or the middle record chunk of a chunked payload.
163  *	@return {Boolean}
164  */
165 Ndef.prototype.isChunked = function() {
166 	return (this.flags & Ndef.chunkFlag) == 0x20;
167 }
168 
169 
170 /**
171  *	Set the Chunk flag.
172  *	The flag indicates that this is either the first or the middle record chunk of a chunked payload.
173  *	@param {Boolean} state
174  */
175 Ndef.prototype.setChunked = function(state) {
176 	this.flags = this.flags & ~Ndef.chunkFlag | (state ? Ndef.chunkFlag : 0);
177 	return this.flags & Ndef.chunkFlag;
178 }
179 
180 
181 /**
182  *	Check if the Short Record flag is set.
183  *	The flag indicates that the payload size will range between 0 to 255 octets.
184  *	@return {Boolean}
185  */
186 Ndef.prototype.isShortRecord = function() {
187 	return (this.flags & Ndef.shortRecord) == 0x10;
188 }
189 
190 
191 /**
192  *	Set the Short Record flag.
193  *	The flag indicates that the payload size will range between 0 to 255 octets.
194  *	@param {Boolean} state
195  */
196 Ndef.prototype.setShortRecord = function(state) {
197 	this.flags = this.flags & ~Ndef.shortRecord | (state ? Ndef.shortRecord : 0);
198 	return this.flags & Ndef.shortRecord;
199 }
200 
201 
202 /**
203  *	Check if the ID Length flag is set.
204  *	The flag indicates that the ID Length field and the ID field are present. Otherwise the both are omitted from the record.
205  *	@return {Boolean}
206  */
207 Ndef.prototype.isIdLengthFlag = function() {
208 	return (this.flags & Ndef.idLengthFlag) == 0x08;
209 }
210 
211 
212 /**
213  *	Set the ID Length flag.
214  *	The flag indicates that the ID Length field and the ID field are present. Otherwise the both are omitted from the record.
215  *	@param {Boolean} state
216  */
217 Ndef.prototype.setIdLengthFlag = function(state) {
218 	this.flags = this.flags & ~Ndef.idLengthFlag | (state ? Ndef.idLengthFlag : 0);
219 	return this.flags & Ndef.idLengthFlag;
220 }
221 
222 
223 /**
224  *	Set the TNF (Type Name Format) flag.
225  *	The flag indicates the structure of the type value.
226  *	
227  *	0x00 = Empty
228  *	0x01 = NFC Forum well-known type
229  *	0x02 = Media-type as defined in RFC 2046
230  *	0x03 = Absolute URI as defined in RFC 3986
231  *	0x04 = NFC Forum external type
232  *	0x05 = Unknown
233  *	0x06 = Unchanged
234  *	0x07 = Reserved
235  *
236  *	@param {Boolean} state
237  */
238 Ndef.prototype.setTNF = function(tnf) {
239 	if (tnf <= 7 && tnf >= 0) {
240 		this.flags = this.flags & ~Ndef.tnf | tnf;
241 	}
242 }
243 
244 
245 /**
246  *	Return the TNF flag
247  *	The flag indicates the structure of the type value.
248  *	
249  *	0x00 = Empty
250  *	0x01 = NFC Forum well-known type
251  *	0x02 = Media-type as defined in RFC 2046
252  *	0x03 = Absolute URI as defined in RFC 3986
253  *	0x04 = NFC Forum external type
254  *	0x05 = Unknown
255  *	0x06 = Unchanged
256  *	0x07 = Reserved
257  *
258  
259  *	@return {ByteString} the TNF
260  */
261 Ndef.prototype.getTNF = function() {
262 	return this.flags & Ndef.tnf;
263 }
264 
265 
266 
267 
268 /**
269  *	Search for possible abbrevation in String and return the corresponding Uri Identifier
270  *	@param {String} str the message coded in UTF-8
271  *	@return {ByteString} the payload consisting of the Uri Identifier and the shortened string
272  */
273 Ndef.shortenString = function(str) {
274 	for(var i = 1; i <= Ndef.uriIdentifier.length; i++) {
275 		
276 		var containsUriID = str.indexOf(Ndef.uriIdentifier[i])
277 		if(containsUriID != -1) {
278 			str = new ByteString(str.substring(Ndef.uriIdentifier[i].length), UTF8);
279 			var payload = ByteString.valueOf(i);
280 			return payload.concat(str);			
281 		}		
282 	}
283 	var payload = new ByteString("0x00", HEX).concat(new ByteString(str, UTF8));
284 	return payload;	
285 }
286 
287 
288 /**
289  *	Create a new Uri from string
290  *	@param {String} str 
291  *	@return {NDEF} the NDEF object
292  */
293 Ndef.newUri = function(str) {
294 	var n = new Ndef();
295 	var payload = Ndef.shortenString(str);
296 	
297 	n.typeLength = ByteString.valueOf(1);
298 	n.payloadLength = new ByteString(payload.length, HEX);
299 	n.type = new ByteString("U", UTF8);
300 	n.payload = payload;
301 	
302 	if (n.payloadLength > 255) {
303 		n.setShortRecord(false);
304 	}
305 	
306 	
307 	return n;
308 }
309 
310 
311 /**
312  *	Create a new NDEF Message
313  *	@param {String} type The type of the NDEF Message coded in US-ASCII. 
314  *	@param {ByteString} payload
315  */
316 Ndef.newMessage = function(type, payload) {
317 	var n = new Ndef();
318 	n.typeLength = new ByteString(type.length, HEX);
319 	n.payloadLength = payload.length;
320 	n.type = type;
321 	n.payload = payload;
322 	
323 	//TODO...
324 	//n.setTNF();
325 	
326 	if (n.payloadLength > 255) {
327 		n.setShortRecord(false);
328 	}
329 	
330 	
331 	return n;
332 }
333 
334 
335 /**
336  *	Return the Ndef in encoded format
337  *	@return {ByteString} encoded Ndef
338  */
339 Ndef.prototype.getEncoded = function() {
340 	var buffer = new ByteBuffer();
341 	var flagByte = new ByteString(ByteString.valueOf(this.flags), HEX);
342 	
343 	this.flags = (this.flags & ~Ndef.idLengthFlag) | (this.id ? Ndef.idLengthFlag : 0);
344 
345 	buffer.append(flagByte);
346 	buffer.append(this.type.length);
347 	buffer.append(this.payload.length);
348 	if (this.id) {
349 		buffer.append(this.id.length);
350 	}
351 	buffer.append(this.type);
352 	if (this.id) {
353 		buffer.append(this.id);
354 	}
355 	buffer.append(this.payload);
356 	
357 	//print(buffer.toByteString());
358 	return buffer.toByteString();
359 }
360 
361 
362 /**
363  *	@return {String} str A String containing the flag byte and the uri payload.
364  */
365 Ndef.prototype.toString = function() {
366 	var str = "flag=" + this.flags.toString(HEX);
367 	str += ",uri=";
368 	var payload = new ByteString(this.payload, HEX);
369 	var i = payload.byteAt(0);
370 	if (i > 0) {
371 		str += Ndef.uriIdentifier[i];
372 	}
373 	str += payload.bytes(1).toString(UTF8);
374 	
375 	return str;
376 }
377