1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2007 Adobe Systems Incorporated 5// All Rights Reserved. 6// 7// NOTICE: Adobe permits you to use, modify, and distribute this file 8// in accordance with the terms of the license agreement accompanying it. 9// 10//////////////////////////////////////////////////////////////////////////////// 11 12package mx.core 13{ 14 15import flash.display.Loader; 16import flash.events.Event; 17import flash.events.ErrorEvent; 18import flash.events.ProgressEvent; 19import flash.events.IOErrorEvent; 20import flash.events.SecurityErrorEvent; 21import flash.net.URLRequest; 22import flash.net.URLLoader; 23import flash.net.URLLoaderDataFormat; 24import flash.system.LoaderContext; 25import flash.system.ApplicationDomain; 26import flash.system.LoaderContext; 27import flash.system.Security; 28import flash.system.SecurityDomain; 29import flash.utils.ByteArray; 30//import flash.utils.getTimer; // PERFORMANCE_INFO 31 32import mx.events.RSLEvent; 33import mx.utils.SHA256; 34import mx.utils.LoaderUtil; 35 36[ExcludeClass] 37 38/** 39 * @private 40 * Cross-domain RSL Item Class. 41 * 42 * The rsls are typically located on a different host than the loader. 43 * There are signed and unsigned Rsls, both have a digest to confirm the 44 * correct rsl is loaded. 45 * Signed Rsls are loaded by setting the digest of the URLRequest. 46 * Unsigned Rsls are check using actionScript to calculate a sha-256 hash of 47 * the loaded bytes and compare them to the expected digest. 48 * 49 */ 50public class CrossDomainRSLItem extends RSLItem 51{ 52 include "../core/Version.as"; 53 54 //-------------------------------------------------------------------------- 55 // 56 // Variables 57 // 58 //-------------------------------------------------------------------------- 59 60 private var rslUrls:Array; // first url is the primary url in the url parameter, others are failovers 61 private var policyFileUrls:Array; // optional policy files, parallel array to rslUrls 62 private var digests:Array; // option rsl digest, parallel array to rslUrls 63 private var isSigned:Array; // each entry is a boolean value. "true" if the rsl in the parallel array is signed 64 private var hashTypes:Array; // type of hash used to create the digest 65 private var urlIndex:int = 0; // index into url being loaded in rslsUrls and other parallel arrays 66 67 // this reference to the loader keeps the loader from being garbage 68 // collected before the complete event can be sent. 69 private var loadBytesLoader:Loader; 70 71// private var startTime:int; // PERFORMANCE_INFO 72 73 //-------------------------------------------------------------------------- 74 // 75 // Constructor 76 // 77 //-------------------------------------------------------------------------- 78 79 /** 80 * Create a cross-domain RSL item to load. 81 * 82 * @param rslUrls Array of Strings, may not be null. Each String is the url of an RSL to load. 83 * @param policyFileUrls Array of Strings, may not be null. Each String contains the url of an 84 * policy file which may be required to allow the RSL to be read from another 85 * domain. An empty string means there is no policy file specified. 86 * @param digests Array of Strings, may not be null. A String contains the value of the digest 87 * computed by the hash in the corresponding entry in the hashTypes Array. An empty 88 * string may be provided for unsigned RSLs to loaded them without verifying the digest. 89 * This is provided as a development cycle convenience and should not be used in a 90 * production application. 91 * @param hashTypes Array of Strings, may not be null. Each String identifies the type of hash 92 * used to compute the digest. Currently the only valid value is SHA256.TYPE_ID. 93 * @param isSigned Array of boolean, may not be null. Each boolean value specifies if the RSL to be 94 * loaded is a signed or unsigned RSL. If the value is true the RSL is signed. 95 * If the value is false the RSL is unsigned. 96 * @param rootURL provides the url used to locate relative RSL urls. 97 */ 98 public function CrossDomainRSLItem(rslUrls:Array, 99 policyFileUrls:Array, 100 digests:Array, 101 hashTypes:Array, 102 isSigned:Array, 103 rootURL:String = null) 104 { 105 super(rslUrls[0], rootURL); 106 107 this.rslUrls = rslUrls; 108 this.policyFileUrls = policyFileUrls; 109 this.digests = digests; 110 this.hashTypes = hashTypes; 111 this.isSigned = isSigned; 112 113 // startTime = getTimer(); // PERFORMANCE_INFO 114 } 115 116 117 //-------------------------------------------------------------------------- 118 // 119 // Overridden Methods 120 // 121 //-------------------------------------------------------------------------- 122 123 124 /** 125 * 126 * Load an RSL. 127 * 128 * @param progressHandler receives ProgressEvent.PROGRESS events, may be null. 129 * @param completeHandler receives Event.COMPLETE events, may be null. 130 * @param ioErrorHandler receives IOErrorEvent.IO_ERROR events, may be null. 131 * @param securityErrorHandler receives SecurityErrorEvent.SECURITY_ERROR events, may be null. 132 * @param rslErrorHandler receives RSLEvent.RSL_ERROR events, may be null. 133 * 134 */ 135 override public function load(progressHandler:Function, 136 completeHandler:Function, 137 ioErrorHandler:Function, 138 securityErrorHandler:Function, 139 rslErrorHandler:Function):void 140 { 141 chainedProgressHandler = progressHandler; 142 chainedCompleteHandler = completeHandler; 143 chainedIOErrorHandler = ioErrorHandler; 144 chainedSecurityErrorHandler = securityErrorHandler; 145 chainedRSLErrorHandler = rslErrorHandler; 146 147 148/* 149 // Debug loading of swf files 150 151 trace("begin load of " + url); 152 if (Security.sandboxType == Security.REMOTE) 153 { 154 trace(" in REMOTE sandbox"); 155 } 156 else if (Security.sandboxType == Security.LOCAL_TRUSTED) 157 { 158 trace(" in LOCAL_TRUSTED sandbox"); 159 } 160 else if (Security.sandboxType == Security.LOCAL_WITH_FILE) 161 { 162 trace(" in LOCAL_WITH_FILE sandbox"); 163 } 164 else if (Security.sandboxType == Security.LOCAL_WITH_NETWORK) 165 { 166 trace(" in LOCAL_WITH_NETWORK sandbox"); 167 } 168*/ 169 170 urlRequest = new URLRequest(LoaderUtil.createAbsoluteURL(rootURL, rslUrls[urlIndex])); 171 var loader:URLLoader = new URLLoader(); 172 loader.dataFormat = URLLoaderDataFormat.BINARY; 173 174 // We needs to listen to certain events. 175 176 loader.addEventListener( 177 ProgressEvent.PROGRESS, itemProgressHandler); 178 179 loader.addEventListener( 180 Event.COMPLETE, itemCompleteHandler); 181 182 loader.addEventListener( 183 IOErrorEvent.IO_ERROR, itemErrorHandler); 184 185 loader.addEventListener( 186 SecurityErrorEvent.SECURITY_ERROR, itemErrorHandler); 187 188 if (policyFileUrls.length > urlIndex && 189 policyFileUrls[urlIndex] != "") 190 { 191 Security.loadPolicyFile(policyFileUrls[urlIndex]); 192 } 193 194 if (isSigned[urlIndex]) 195 { 196 if (urlRequest.hasOwnProperty("digest")) 197 { 198 // load a signed rsl by specifying the digest 199 urlRequest.digest = digests[urlIndex]; 200 } 201 else if (hasFailover()) 202 { 203 loadFailover(); 204 return; 205 } 206 else 207 { 208 // B Feature: externalize error message 209 var rslError:ErrorEvent = new ErrorEvent(RSLEvent.RSL_ERROR); 210 rslError.text = "Flex Error #1002: Flash Player 9.0.115 and above is required to support signed RSLs. Problem occurred when trying to load the RSL " + 211 urlRequest.url + 212 ". Upgrade your Flash Player and try again."; 213 super.itemErrorHandler(rslError); 214 return; 215 } 216 } 217 218// trace("start load of " + urlRequest.url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO 219 220 loader.load(urlRequest); 221 } 222 223 224 225 //-------------------------------------------------------------------------- 226 // 227 // Methods 228 // 229 //-------------------------------------------------------------------------- 230 231 /** 232 * @private 233 * Complete the load of the cross-domain rsl by loading it into the current 234 * application domain. The load was started by loadCdRSL. 235 * 236 * @param - urlLoader from the complete event. 237 * 238 * @return - true if the load was completed successfully or unsuccessfully, 239 * false if the load of a failover rsl was started 240 */ 241 private function completeCdRslLoad(urlLoader:URLLoader):Boolean 242 { 243 // handle player bug #204244, complete event without data after an error 244 if (urlLoader == null || urlLoader.data == null || ByteArray(urlLoader.data).bytesAvailable == 0) 245 { 246 return true; 247 } 248 249 // load the bytes into the current application domain. 250 loadBytesLoader = new Loader(); 251 var context:LoaderContext = new LoaderContext(); 252 context.applicationDomain = ApplicationDomain.currentDomain; 253 context.securityDomain = null; 254 255 // If the AIR flag is available then set it to true so we can 256 // load the RSL without a security error. 257 if ("allowLoadBytesCodeExecution" in context) 258 { 259 context["allowLoadBytesCodeExecution"] = true; 260 } 261 262 // verify the digest, if any, is correct 263 if (digests[urlIndex] != null && String(digests[urlIndex]).length > 0) 264 { 265 var verifiedDigest:Boolean = false; 266 if (!isSigned[urlIndex]) 267 { 268 // verify an unsigned rsl 269 if (hashTypes[urlIndex] == SHA256.TYPE_ID) 270 { 271 // get the bytes from the rsl and calculate the hash 272 var rslDigest:String = null; 273 if (urlLoader.data != null) 274 { 275 rslDigest = SHA256.computeDigest(urlLoader.data); 276 } 277 278 if (rslDigest == digests[urlIndex]) 279 { 280 verifiedDigest = true; 281 } 282 } 283 } 284 else 285 { 286 // signed rsls are verified by the player 287 verifiedDigest = true; 288 } 289 290 if (!verifiedDigest) 291 { 292 // failover to the next rsl, if one exists 293 // no failover to load, all the rsls have failed to load 294 // report an error. 295 // B Feature: externalize error message 296 var hasFailover:Boolean = hasFailover(); 297 var rslError:ErrorEvent = new ErrorEvent(RSLEvent.RSL_ERROR); 298 rslError.text = "Flex Error #1001: Digest mismatch with RSL " + 299 urlRequest.url + 300 ". Redeploy the matching RSL or relink your application with the matching library."; 301 itemErrorHandler(rslError); 302 303 return !hasFailover; 304 } 305 } 306 307 // load the rsl into memory 308 loadBytesLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadBytesCompleteHandler); 309 loadBytesLoader.loadBytes(urlLoader.data, context); 310 return true; 311 } 312 313 314 /** 315 * Does the current url being processed have a failover? 316 * 317 * @return true if a failover url exists, false otherwise. 318 */ 319 public function hasFailover():Boolean 320 { 321 return (rslUrls.length > (urlIndex + 1)); 322 } 323 324 325 /** 326 * Load the next url from the list of failover urls. 327 */ 328 public function loadFailover():void 329 { 330 // try to load the failover from the same node again 331 if (urlIndex < rslUrls.length) 332 { 333 trace("Failed to load RSL " + rslUrls[urlIndex]); 334 trace("Failing over to RSL " + rslUrls[urlIndex+1]); 335 urlIndex++; // move to failover url 336 url = rslUrls[urlIndex]; 337 load(chainedProgressHandler, 338 chainedCompleteHandler, 339 chainedIOErrorHandler, 340 chainedSecurityErrorHandler, 341 chainedRSLErrorHandler); 342 } 343 } 344 345 346 347 //-------------------------------------------------------------------------- 348 // 349 // Overridden Event Handlers 350 // 351 //-------------------------------------------------------------------------- 352 353 /** 354 * @private 355 */ 356 override public function itemCompleteHandler(event:Event):void 357 { 358// trace("complete load of " + url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO 359 360 // complete loading the cross-domain rsl by calling loadBytes. 361 completeCdRslLoad(event.target as URLLoader); 362 } 363 364 /** 365 * @private 366 */ 367 override public function itemErrorHandler(event:ErrorEvent):void 368 { 369// trace("error loading " + url + " at " + (getTimer() - startTime)); // PERFORMANCE_INFO 370 371 // if a failover exists, try to load it. Otherwise call super() 372 // for default error handling. 373 if (hasFailover()) 374 { 375 trace(decodeURI(event.text)); 376 loadFailover(); 377 } 378 else 379 { 380 super.itemErrorHandler(event); 381 } 382 } 383 384 385 /** 386 * loader.loadBytes() has a complete event. 387 * Done loading this rsl into memory. Call the completeHandler 388 * to start loading the next rsl. 389 * 390 * @private 391 */ 392 private function loadBytesCompleteHandler(event:Event):void 393 { 394 loadBytesLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadBytesCompleteHandler); 395 loadBytesLoader = null; 396 super.itemCompleteHandler(event); 397 } 398 399} 400}