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