1 /**
  2  *  ---------
  3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
  4  * |#       #|
  5  * |#       #|  Copyright (c) 1999-2006 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 Remote Application Management over HTTP (RAMoverHTTP) client
 25  */
 26 
 27 /**
 28  * Create a client instance for attaching a card to a server via the RAMOverHTTP procotol
 29  *
 30  * @Class Class implementing a RAMOverHTTP client
 31  * @constructor
 32  * @param {Card} card the card instance to attach
 33  * @param {String} url the URL to connect to
 34  * @param {String} session the session cookie
 35  */
 36 function RAMClient(card, url, session) {
 37 	this.card = card;
 38 	this.url = url;
 39 	this.session = session;
 40 	this.httpCode = -1;
 41 	this.apduCount = 0;
 42 	this.aPDULimit = 100000;
 43 }
 44 
 45 
 46 
 47 exports.RAMClient = RAMClient;
 48 
 49 /**
 50  * @private
 51  */
 52 RAMClient.prototype.encodeInitiation = function(atr) {
 53 	return (new ASN1(0xE8, new ASN1(0xC0, atr))).getBytes();
 54 }
 55 
 56 
 57 
 58 /**
 59  * Set a notification listener.
 60  *
 61  * The listener object must implement a notificationReceived() method that
 62  * receives the three parameter id, msg and ttc.
 63  *
 64  * @param {Object} the listener object implementing notificationReceived()
 65  */
 66 RAMClient.prototype.setNotificationListener = function(notificationListener) {
 67 	this.notificationListener = notificationListener;
 68 }
 69 
 70 
 71 
 72 /**
 73  * Set a limiting number of APDU
 74  *
 75  * This is helpful for testing to intercept processing after a certain number
 76  * of APDUs (Default 100000).
 77  *
 78  * @param {Number} aPDULimit the maximum number of APDU processing in update()
 79  */
 80 RAMClient.prototype.setAPDULimit = function(aPDULimit) {
 81 	this.aPDULimit = aPDULimit;
 82 }
 83 
 84 
 85 
 86 /**
 87  * @private
 88  */
 89 RAMClient.prototype.getURLConnection = function() {
 90 	var c = new URLConnection(this.url);
 91 	if (typeof(this.session) != "undefined") {
 92 //		print(typeof(this.session));
 93 		c.addHeaderField("Cookie", this.session);
 94 	}
 95 	c.addHeaderField("Content-Type", "application/org.openscdp-content-mgt-response;version=1.0");
 96 	return c;
 97 }
 98 
 99 
100 
101 /**
102  * @private
103  */
104 RAMClient.prototype.initialConnect = function() {
105 	var c = this.getURLConnection();
106 	var atr = this.card.getATR();
107 	var b = this.encodeInitiation(atr.toByteString());
108 	var cst = c.postBinary(b);
109 
110 	var cookie = c.getHeaderField("Set-Cookie");
111 	if (cookie) {
112 //		print(cookie);
113 		this.session = cookie[0];
114 	}
115 
116 	this.httpCode = c.responseCode;
117 	return cst;
118 }
119 
120 
121 
122 /**
123  * @private
124  */
125 RAMClient.prototype.next = function(rst) {
126 	var c = this.getURLConnection();
127 	var cst = c.postBinary(rst);
128 
129 	this.httpCode = c.responseCode;
130 	return cst;
131 }
132 
133 
134 
135 /**
136  * @private
137  */
138 RAMClient.prototype.processAPDU = function(capdu) {
139 	var rapdu = this.card.plainApdu(capdu);
140 	rapdu = rapdu.concat(ByteString.valueOf(this.card.SW, 2));
141 	this.apduCount++;
142 	return new ASN1(0x23, rapdu);
143 }
144 
145 
146 
147 /**
148  * @private
149  */
150 RAMClient.prototype.processReset = function() {
151 	var atr = this.card.reset(Card.RESET_COLD);
152 	if (atr instanceof Atr) {
153 		atr = atr.toByteString();
154 	}
155 	return new ASN1(0xC0, atr);
156 }
157 
158 
159 
160 /**
161  * @private
162  */
163 RAMClient.prototype.processNotify = function(e) {
164 	if (this.notificationListener) {
165 		var id = e.get(0).value.toSigned();
166 		var str = e.get(1).value.toString(UTF8);
167 		var ttc = undefined;
168 		if (e.elements > 2) {
169 			ttc = e.get(0).value.toUnsigned();
170 		}
171 		this.notificationListener.notificationReceived(id, str, ttc);
172 	} else {
173 		print("Notify:");
174 		print(e);
175 	}
176 }
177 
178 
179 
180 /**
181  * @private
182  */
183 RAMClient.prototype.process = function(cst) {
184 //	print(cst);
185 	var a = new ASN1(cst);
186 //	print(a);
187 
188 	var rst = new ASN1(0xAB);
189 
190 	for (var i = 0; i < a.elements; i++) {
191 		var e = a.get(i);
192 		switch(e.tag) {
193 			case 0x22:
194 				rst.add(this.processAPDU(e.value));
195 				break;
196 			case 0xC0:
197 				rst.add(this.processReset());
198 				break;
199 			case 0xE0:
200 				this.processNotify(e);
201 				break;
202 		}
203 	}
204 
205 	rst.add(new ASN1(0x80, ByteString.valueOf(1)));
206 //	print(rst);
207 //	print(rst.getBytes());
208 	return rst.getBytes();
209 }
210 
211 
212 
213 /**
214  * Close connection, e.g. after exception on the client side
215  *
216  * @param {Number} id the message id
217  * @param {String} msg the closing message
218  */
219 RAMClient.prototype.close = function(id, msg) {
220 	var c = this.getURLConnection();
221 
222 	var encid;
223 	if (id < 0) {
224 		var len = 1;
225 		if (id < -0x80) len++;
226 		if (id < -0x8000) len++;
227 		if (id < -0x800000) len++;
228 		encid = ByteString.valueOf(id, len);
229 	} else {
230 		encid = ByteString.valueOf(id);
231 	}
232 
233 	var rst = new ASN1(0xAB,
234 			new ASN1(0xE1,
235 				 new ASN1(ASN1.INTEGER, encid),
236 				 new ASN1(ASN1.UTF8String, new ByteString(msg, UTF8))
237 			    )
238 		      ).getBytes();
239 
240 	var cst = this.next(rst);
241 	while (this.httpCode == 200) {
242 		var rst = this.process(cst);
243 		cst = this.next(rst);
244 	}
245 }
246 
247 
248 
249 /**
250  * Perform update operation
251  */
252 RAMClient.prototype.update = function() {
253 	this.httpCode = -1;
254 	this.apduCount = 0;
255 	var cst = this.initialConnect();
256 	while ((this.httpCode == 200) && (this.apduCount < this.aPDULimit)) {
257 		var rst = this.process(cst);
258 		cst = this.next(rst);
259 	}
260 }
261