diff --git a/core/src/avm2/error.rs b/core/src/avm2/error.rs index e53c2e1d6..e4501f778 100644 --- a/core/src/avm2/error.rs +++ b/core/src/avm2/error.rs @@ -307,6 +307,20 @@ pub fn make_error_2008<'gc>(activation: &mut Activation<'_, 'gc>, param_name: &s } } +#[inline(never)] +#[cold] +pub fn make_error_2126<'gc>(activation: &mut Activation<'_, 'gc>) -> Error<'gc> { + let err = argument_error( + activation, + "Error #2126: NetConnection object must be connected.", + 2126, + ); + match err { + Ok(err) => Error::AvmError(err), + Err(err) => err, + } +} + #[inline(never)] #[cold] pub fn make_error_2025<'gc>(activation: &mut Activation<'_, 'gc>) -> Error<'gc> { diff --git a/core/src/avm2/globals/flash/net/NetConnection.as b/core/src/avm2/globals/flash/net/NetConnection.as index 8a8af6641..68523a381 100644 --- a/core/src/avm2/globals/flash/net/NetConnection.as +++ b/core/src/avm2/globals/flash/net/NetConnection.as @@ -9,6 +9,9 @@ package flash.net { public static var defaultObjectEncoding:uint = 3; public var objectEncoding:uint = NetConnection.defaultObjectEncoding; + public var client:Object = this; + public var maxPeerConnections:uint = 8; + public var proxyType:String = "none"; public native function connect(command:String, ... arguments):void; @@ -22,5 +25,24 @@ package flash.net { } public native function close():void; + + public native function get connected():Boolean; + public native function get connectedProxyType():String; + public native function get farID():String; + public native function get farNonce():String; + public native function get nearID():String; + public native function get nearNonce():String; + public native function get protocol():String; + public native function get uri():String; + public native function get usingTLS():Boolean; + + public function get unconnectedPeerStreams():Array { + if (this.connected) { + // [NA] Arguably this isn't a stub as it can't ever be anything else in our current implementation... + return []; + } else { + throw new ArgumentError("Error #2126: NetConnection object must be connected.", 2126); + } + } } } diff --git a/core/src/avm2/globals/flash/net/net_connection.rs b/core/src/avm2/globals/flash/net/net_connection.rs index 2b2778de2..d79d54611 100644 --- a/core/src/avm2/globals/flash/net/net_connection.rs +++ b/core/src/avm2/globals/flash/net/net_connection.rs @@ -1,7 +1,9 @@ +use crate::avm2::error::make_error_2126; pub use crate::avm2::object::net_connection_allocator; use crate::avm2::object::TObject; use crate::avm2::parameters::ParametersExt; use crate::net_connection::NetConnections; +use crate::string::AvmString; use crate::{ avm2::{Activation, Error, Object, Value}, avm2_stub_method, @@ -58,3 +60,177 @@ pub fn close<'gc>( Ok(Value::Undefined) } + +pub fn get_connected<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(handle) = this.handle() { + return Ok(activation + .context + .net_connections + .is_connected(handle) + .into()); + } + + Ok(false.into()) +} + +pub fn get_connected_proxy_type<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(result) = this.handle().and_then(|handle| { + activation + .context + .net_connections + .get_connected_proxy_type(handle) + }) { + return Ok(result.into()); + } + + Err(make_error_2126(activation)) +} + +pub fn get_far_id<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(result) = this + .handle() + .and_then(|handle| activation.context.net_connections.get_far_id(handle)) + { + return Ok(result.into()); + } + + Err(make_error_2126(activation)) +} + +pub fn get_far_nonce<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(result) = this + .handle() + .and_then(|handle| activation.context.net_connections.get_far_nonce(handle)) + { + return Ok(result.into()); + } + + Err(make_error_2126(activation)) +} + +pub fn get_near_id<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(result) = this + .handle() + .and_then(|handle| activation.context.net_connections.get_near_id(handle)) + { + return Ok(result.into()); + } + + Err(make_error_2126(activation)) +} + +pub fn get_near_nonce<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(result) = this + .handle() + .and_then(|handle| activation.context.net_connections.get_near_nonce(handle)) + { + return Ok(result.into()); + } + + Err(make_error_2126(activation)) +} + +pub fn get_protocol<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(result) = this + .handle() + .and_then(|handle| activation.context.net_connections.get_protocol(handle)) + { + return Ok(result.into()); + } + + Err(make_error_2126(activation)) +} + +pub fn get_uri<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(result) = this + .handle() + .and_then(|handle| activation.context.net_connections.get_uri(handle)) + { + return Ok(AvmString::new_utf8(activation.context.gc_context, result).into()); + } + + Ok(Value::Null) +} + +pub fn get_using_tls<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let this = this + .as_net_connection() + .expect("Must be NetConnection object"); + + if let Some(result) = this + .handle() + .and_then(|handle| activation.context.net_connections.is_using_tls(handle)) + { + return Ok(result.into()); + } + + Err(make_error_2126(activation)) +} diff --git a/core/src/net_connection.rs b/core/src/net_connection.rs index d3e044a87..924ed827e 100644 --- a/core/src/net_connection.rs +++ b/core/src/net_connection.rs @@ -133,6 +133,47 @@ impl<'gc> NetConnections<'gc> { } } } + + pub fn is_connected(&self, handle: NetConnectionHandle) -> bool { + self.connections + .get(handle) + .map(|c| c.is_connected()) + .unwrap_or_default() + } + + pub fn get_connected_proxy_type(&self, handle: NetConnectionHandle) -> Option<&'static str> { + self.connections + .get(handle) + .and_then(|c| c.connected_proxy_type()) + } + + pub fn get_far_id(&self, handle: NetConnectionHandle) -> Option<&'static str> { + self.connections.get(handle).and_then(|c| c.far_id()) + } + + pub fn get_far_nonce(&self, handle: NetConnectionHandle) -> Option<&'static str> { + self.connections.get(handle).and_then(|c| c.far_nonce()) + } + + pub fn get_near_id(&self, handle: NetConnectionHandle) -> Option<&'static str> { + self.connections.get(handle).and_then(|c| c.near_id()) + } + + pub fn get_near_nonce(&self, handle: NetConnectionHandle) -> Option<&'static str> { + self.connections.get(handle).and_then(|c| c.near_nonce()) + } + + pub fn get_protocol(&self, handle: NetConnectionHandle) -> Option<&'static str> { + self.connections.get(handle).and_then(|c| c.protocol()) + } + + pub fn get_uri(&self, handle: NetConnectionHandle) -> Option { + self.connections.get(handle).and_then(|c| c.uri()) + } + + pub fn is_using_tls(&self, handle: NetConnectionHandle) -> Option { + self.connections.get(handle).and_then(|c| c.using_tls()) + } } #[derive(Collect)] @@ -144,6 +185,75 @@ pub struct NetConnection<'gc> { protocol: NetConnectionProtocol, } +impl<'gc> NetConnection<'gc> { + pub fn is_connected(&self) -> bool { + match self.protocol { + NetConnectionProtocol::Local => true, + NetConnectionProtocol::FlashRemoting(_) => false, + } + } + + pub fn connected_proxy_type(&self) -> Option<&'static str> { + match self.protocol { + NetConnectionProtocol::Local => Some("none"), + NetConnectionProtocol::FlashRemoting(_) => None, + } + } + + pub fn far_id(&self) -> Option<&'static str> { + match self.protocol { + NetConnectionProtocol::Local => Some(""), + NetConnectionProtocol::FlashRemoting(_) => None, + } + } + + pub fn far_nonce(&self) -> Option<&'static str> { + match self.protocol { + NetConnectionProtocol::Local => { + Some("0000000000000000000000000000000000000000000000000000000000000000") + } + NetConnectionProtocol::FlashRemoting(_) => None, + } + } + + pub fn near_id(&self) -> Option<&'static str> { + match self.protocol { + NetConnectionProtocol::Local => Some(""), + NetConnectionProtocol::FlashRemoting(_) => None, + } + } + + pub fn near_nonce(&self) -> Option<&'static str> { + match self.protocol { + NetConnectionProtocol::Local => { + Some("0000000000000000000000000000000000000000000000000000000000000000") + } + NetConnectionProtocol::FlashRemoting(_) => None, + } + } + + pub fn protocol(&self) -> Option<&'static str> { + match self.protocol { + NetConnectionProtocol::Local => Some("rtmp"), + NetConnectionProtocol::FlashRemoting(_) => None, + } + } + + pub fn uri(&self) -> Option { + match &self.protocol { + NetConnectionProtocol::Local => Some("null".to_string()), // Yes, it's a string "null", not a real null. + NetConnectionProtocol::FlashRemoting(remoting) => Some(remoting.url.to_string()), + } + } + + pub fn using_tls(&self) -> Option { + match &self.protocol { + NetConnectionProtocol::Local => Some(false), + NetConnectionProtocol::FlashRemoting(_) => None, + } + } +} + #[derive(Debug)] pub enum NetConnectionProtocol { /// A "local" connection, caused by connecting to null diff --git a/tests/tests/swfs/avm2/netconnection_properties/Test.as b/tests/tests/swfs/avm2/netconnection_properties/Test.as new file mode 100644 index 000000000..6edc8b62e --- /dev/null +++ b/tests/tests/swfs/avm2/netconnection_properties/Test.as @@ -0,0 +1,100 @@ +package { + + import flash.display.MovieClip; +import flash.net.NetConnection; +import flash.net.NetConnection; + import flash.events.NetStatusEvent; + + + public class Test extends MovieClip { + + + public function Test() { + var connection:NetConnection = new NetConnection(); + + traceProperties(connection); + trace(""); + + trace("/// connection.connect(null)"); + connection.connect(null); + traceProperties(connection); + trace(""); + + trace("/// connection.connect(\"http://example.org\")"); + connection.connect("http://example.org"); + traceProperties(connection); + trace(""); + + trace("/// connection.close()"); + connection.close(); + traceProperties(connection); + trace(""); + + trace("/// connection.connect(\"https://example.org\")"); + connection.connect("https://example.org"); + traceProperties(connection); + trace(""); + } + + function traceSafe(connection: NetConnection, key: String) { + try { + var value = connection[key]; + if (typeof(value) === "string") { + trace("connection." + key + " = \"" + escapeString(value) + "\""); + } else if (value is Array) { + trace("connection." + key + " = [" + value + "]"); + } else if (value === connection) { + trace("connection." + key + " = connection"); + } else { + trace("connection." + key + " = " + value); + } + } catch (error) { + trace("connection." + key + " = " + error); + } + } + + function traceProperties(connection: NetConnection) { + traceSafe(connection, "client"); + traceSafe(connection, "connected"); + traceSafe(connection, "connectedProxyType"); + traceSafe(connection, "farID"); + traceSafe(connection, "farNonce"); + traceSafe(connection, "maxPeerConnections"); + traceSafe(connection, "nearID"); + traceSafe(connection, "nearNonce"); + traceSafe(connection, "objectEncoding"); + traceSafe(connection, "protocol"); + traceSafe(connection, "proxyType"); + traceSafe(connection, "unconnectedPeerStreams"); + traceSafe(connection, "uri"); + traceSafe(connection, "usingTLS"); + } + + function escapeString(input: String): String { + var output:String = ""; + for (var i:int = 0; i < input.length; i++) { + var char:String = input.charAt(i); + switch (char) { + case "\\": + output += "\\\\"; + break; + case "\"": + output += "\\\""; + break; + case "\n": + output += "\\n"; + break; + case "\r": + output += "\\r"; + break; + case "\t": + output += "\\t"; + break; + default: + output += char; + } + } + return output; + } + } +} diff --git a/tests/tests/swfs/avm2/netconnection_properties/output.txt b/tests/tests/swfs/avm2/netconnection_properties/output.txt new file mode 100644 index 000000000..784c16752 --- /dev/null +++ b/tests/tests/swfs/avm2/netconnection_properties/output.txt @@ -0,0 +1,79 @@ +connection.client = connection +connection.connected = false +connection.connectedProxyType = ArgumentError: Error #2126: NetConnection object must be connected. +connection.farID = ArgumentError: Error #2126: NetConnection object must be connected. +connection.farNonce = ArgumentError: Error #2126: NetConnection object must be connected. +connection.maxPeerConnections = 8 +connection.nearID = ArgumentError: Error #2126: NetConnection object must be connected. +connection.nearNonce = ArgumentError: Error #2126: NetConnection object must be connected. +connection.objectEncoding = 3 +connection.protocol = ArgumentError: Error #2126: NetConnection object must be connected. +connection.proxyType = "none" +connection.unconnectedPeerStreams = ArgumentError: Error #2126: NetConnection object must be connected. +connection.uri = null +connection.usingTLS = ArgumentError: Error #2126: NetConnection object must be connected. + +/// connection.connect(null) +connection.client = connection +connection.connected = true +connection.connectedProxyType = "none" +connection.farID = "" +connection.farNonce = "0000000000000000000000000000000000000000000000000000000000000000" +connection.maxPeerConnections = 8 +connection.nearID = "" +connection.nearNonce = "0000000000000000000000000000000000000000000000000000000000000000" +connection.objectEncoding = 3 +connection.protocol = "rtmp" +connection.proxyType = "none" +connection.unconnectedPeerStreams = [] +connection.uri = "null" +connection.usingTLS = false + +/// connection.connect("http://example.org") +connection.client = connection +connection.connected = false +connection.connectedProxyType = ArgumentError: Error #2126: NetConnection object must be connected. +connection.farID = ArgumentError: Error #2126: NetConnection object must be connected. +connection.farNonce = ArgumentError: Error #2126: NetConnection object must be connected. +connection.maxPeerConnections = 8 +connection.nearID = ArgumentError: Error #2126: NetConnection object must be connected. +connection.nearNonce = ArgumentError: Error #2126: NetConnection object must be connected. +connection.objectEncoding = 3 +connection.protocol = ArgumentError: Error #2126: NetConnection object must be connected. +connection.proxyType = "none" +connection.unconnectedPeerStreams = ArgumentError: Error #2126: NetConnection object must be connected. +connection.uri = "http://example.org" +connection.usingTLS = ArgumentError: Error #2126: NetConnection object must be connected. + +/// connection.close() +connection.client = connection +connection.connected = false +connection.connectedProxyType = ArgumentError: Error #2126: NetConnection object must be connected. +connection.farID = ArgumentError: Error #2126: NetConnection object must be connected. +connection.farNonce = ArgumentError: Error #2126: NetConnection object must be connected. +connection.maxPeerConnections = 8 +connection.nearID = ArgumentError: Error #2126: NetConnection object must be connected. +connection.nearNonce = ArgumentError: Error #2126: NetConnection object must be connected. +connection.objectEncoding = 3 +connection.protocol = ArgumentError: Error #2126: NetConnection object must be connected. +connection.proxyType = "none" +connection.unconnectedPeerStreams = ArgumentError: Error #2126: NetConnection object must be connected. +connection.uri = null +connection.usingTLS = ArgumentError: Error #2126: NetConnection object must be connected. + +/// connection.connect("https://example.org") +connection.client = connection +connection.connected = false +connection.connectedProxyType = ArgumentError: Error #2126: NetConnection object must be connected. +connection.farID = ArgumentError: Error #2126: NetConnection object must be connected. +connection.farNonce = ArgumentError: Error #2126: NetConnection object must be connected. +connection.maxPeerConnections = 8 +connection.nearID = ArgumentError: Error #2126: NetConnection object must be connected. +connection.nearNonce = ArgumentError: Error #2126: NetConnection object must be connected. +connection.objectEncoding = 3 +connection.protocol = ArgumentError: Error #2126: NetConnection object must be connected. +connection.proxyType = "none" +connection.unconnectedPeerStreams = ArgumentError: Error #2126: NetConnection object must be connected. +connection.uri = "https://example.org" +connection.usingTLS = ArgumentError: Error #2126: NetConnection object must be connected. + diff --git a/tests/tests/swfs/avm2/netconnection_properties/test.fla b/tests/tests/swfs/avm2/netconnection_properties/test.fla new file mode 100644 index 000000000..c930cd722 Binary files /dev/null and b/tests/tests/swfs/avm2/netconnection_properties/test.fla differ diff --git a/tests/tests/swfs/avm2/netconnection_properties/test.swf b/tests/tests/swfs/avm2/netconnection_properties/test.swf new file mode 100644 index 000000000..19cb92dd5 Binary files /dev/null and b/tests/tests/swfs/avm2/netconnection_properties/test.swf differ diff --git a/tests/tests/swfs/avm2/netconnection_properties/test.toml b/tests/tests/swfs/avm2/netconnection_properties/test.toml new file mode 100644 index 000000000..dbee897f5 --- /dev/null +++ b/tests/tests/swfs/avm2/netconnection_properties/test.toml @@ -0,0 +1 @@ +num_frames = 1