[tor-commits] [flashproxy/master] New com/jscat structure for Nate's stuff

dcf at torproject.org dcf at torproject.org
Sun Jun 12 08:56:27 UTC 2011


commit 858801c0e8965aa09f8e68fe14a6ac27679b759c
Author: Nate Hardison <nate at rescomp-09-154551.stanford.edu>
Date:   Mon May 9 02:45:24 2011 -0700

    New com/jscat structure for Nate's stuff
---
 com/jscat/Connector.as                     |  188 ++++++++++++++++++++++++
 com/jscat/Utils.as                         |   25 ++++
 com/jscat/facilitator.py                   |  146 +++++++++++++++++++
 com/jscat/rtmfp/RTMFPSocket.as             |  216 ++++++++++++++++++++++++++++
 com/jscat/rtmfp/RTMFPSocketClient.as       |   57 ++++++++
 com/jscat/rtmfp/events/RTMFPSocketEvent.as |   25 ++++
 6 files changed, 657 insertions(+), 0 deletions(-)

diff --git a/com/jscat/Connector.as b/com/jscat/Connector.as
new file mode 100644
index 0000000..dab7d0a
--- /dev/null
+++ b/com/jscat/Connector.as
@@ -0,0 +1,188 @@
+package
+{
+    import flash.display.Sprite;
+    import flash.text.TextField;
+    import flash.net.Socket;
+    import flash.events.Event;
+    import flash.events.EventDispatcher;
+    import flash.events.IOErrorEvent;
+    import flash.events.NetStatusEvent;
+    import flash.events.ProgressEvent;
+    import flash.events.SecurityErrorEvent;
+    import flash.utils.ByteArray;
+    
+    import rtmfp.RTMFPSocket;
+    import rtmfp.events.RTMFPSocketEvent;
+    import Utils;
+    
+    public class Connector extends Sprite {
+      
+      /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
+         crossdomain policy. */
+      private const DEFAULT_TOR_RELAY:Object = {
+          host: "173.255.221.44",
+          port: 9001
+      };
+      
+      private var output_text:TextField;
+      
+      private var s_f:Socket;
+      private var s_r:RTMFPSocket;
+      private var s_t:Socket;
+      
+      private var fac_addr:Object;
+      private var tor_addr:Object;
+      
+      public function Connector()
+      {
+        output_text = new TextField();
+        output_text.width = 400;
+        output_text.height = 300;
+        output_text.background = true;
+        output_text.backgroundColor = 0x001f0f;
+        output_text.textColor = 0x44CC44;
+        addChild(output_text);
+
+        puts("Starting.");
+        
+        this.loaderInfo.addEventListener(Event.COMPLETE, onLoaderInfoComplete);
+      }
+      
+      protected function puts(s:String):void
+      {
+          output_text.appendText(s + "\n");
+          output_text.scrollV = output_text.maxScrollV;
+      }
+      
+      private function onLoaderInfoComplete(e:Event):void
+      {
+        var fac_spec:String;
+        var tor_spec:String;
+
+        puts("Parameters loaded.");
+        
+        fac_spec = this.loaderInfo.parameters["facilitator"];
+        if (!fac_spec) {
+          puts("Error: no \"facilitator\" specification provided.");
+          return;
+        }
+        puts("Facilitator spec: \"" + fac_spec + "\"");
+        fac_addr = Utils.parseAddrSpec(fac_spec);
+        if (!fac_addr) {
+            puts("Error: Facilitator spec must be in the form \"host:port\".");
+            return;
+        }
+        
+        tor_spec = this.loaderInfo.parameters["tor"];
+        if (!tor_spec) {
+          puts("Error: No Tor specification provided.");
+          return;
+        }
+        puts("Tor spec: \"" + tor_spec + "\"")
+        tor_addr = Utils.parseAddrSpec(tor_spec);
+        if (!tor_addr) {
+          puts("Error: Tor spec must be in the form \"host:port\".");
+          return;
+        }
+        
+        s_r = new RTMFPSocket();
+        s_r.addEventListener(RTMFPSocketEvent.CONNECT_SUCCESS, onRTMFPSocketConnect);
+        s_r.addEventListener(RTMFPSocketEvent.CONNECT_FAIL, function (e:Event):void {
+          puts("Error: failed to connect to Cirrus.");
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PUBLISH_START, function(e:RTMFPSocketEvent):void {
+          
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PEER_CONNECTED, function(e:RTMFPSocketEvent):void {
+          
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PEER_DISCONNECTED, function(e:RTMFPSocketEvent):void {
+          
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PEERING_SUCCESS, function(e:RTMFPSocketEvent):void {
+          
+        });
+        s_r.addEventListener(RTMFPSocketEvent.PEERING_FAIL, function(e:RTMFPSocketEvent):void {
+          
+        });
+        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+          var bytes:ByteArray = new ByteArray();
+          s_r.readBytes(bytes);
+          puts("RTMFP: read " + bytes.length + " bytes.");
+          s_t.writeBytes(bytes);
+        });
+        
+        s_r.connect();
+      }
+      
+      private function onRTMFPSocketConnect(event:RTMFPSocketEvent):void
+      {
+        puts("Cirrus: connected with id " + s_r.id + ".");
+        s_t = new Socket();
+        s_t.addEventListener(Event.CONNECT, onTorSocketConnect);
+        s_t.addEventListener(Event.CLOSE, function (e:Event):void {
+          
+        });
+        s_t.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+          puts("Tor: I/O error: " + e.text + ".");
+        });
+        s_t.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+          var bytes:ByteArray = new ByteArray();
+          s_t.readBytes(bytes, 0, e.bytesLoaded);
+          puts("Tor: read " + bytes.length + " bytes.");
+          s_r.writeBytes(bytes);
+        });
+        s_t.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+          puts("Tor: security error: " + e.text + ".");
+        });
+
+        s_t.connect(tor_addr.host, tor_addr.port);
+        onTorSocketConnect(new Event(""));
+      }
+      
+      private function onTorSocketConnect(event:Event):void
+      {
+        puts("Tor: connected to " + tor_addr.host + ":" + tor_addr.port + ".");
+        
+        s_f = new Socket();
+        s_f.addEventListener(Event.CONNECT, onFacilitatorSocketConnect);
+        s_f.addEventListener(Event.CLOSE, function (e:Event):void {
+          
+        });
+        s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+          puts("Facilitator: I/O error: " + e.text + ".");
+        });
+        s_f.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+          var clientID:String = s_f.readMultiByte(e.bytesLoaded, "utf-8");
+          puts("Facilitator: got \"" + clientID + "\"");
+          puts("Connecting to " + clientID + ".");
+          s_r.peer = clientID;
+        });
+        s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+          puts("Facilitator: security error: " + e.text + ".");
+        });
+
+        s_f.connect(fac_addr.host, fac_addr.port);
+      }
+      
+      private function onFacilitatorSocketConnect(event:Event):void
+      {
+        puts("Facilitator: connected to " + fac_addr.host + ":" + fac_addr.port + ".");
+        onConnectionEvent();
+      }
+      
+      private function onConnectionEvent():void
+      {
+        if (s_f != null && s_f.connected && s_t != null && /*s_t.connected && */
+            s_r != null && s_r.connected) {
+              if (this.loaderInfo.parameters["proxy"]) {
+                s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
+              } else {
+                var str:String = "POST / HTTP/1.0\r\n\r\nclient=" + s_r.id + "\r\n"
+                puts(str);
+                s_f.writeUTFBytes(str);
+              }
+        }
+      }
+  }
+}
\ No newline at end of file
diff --git a/com/jscat/Utils.as b/com/jscat/Utils.as
new file mode 100644
index 0000000..48f9a62
--- /dev/null
+++ b/com/jscat/Utils.as
@@ -0,0 +1,25 @@
+package
+{
+  
+  public class Utils {
+    
+    /* Parse an address in the form "host:port". Returns an Object with
+       keys "host" (String) and "port" (int). Returns null on error. */
+    public static function parseAddrSpec(spec:String):Object
+    {
+        var parts:Array;
+        var addr:Object;
+
+        parts = spec.split(":", 2);
+        if (parts.length != 2 || !parseInt(parts[1]))
+            return null;
+        addr = {}
+        addr.host = parts[0];
+        addr.port = parseInt(parts[1]);
+
+        return addr;
+    }
+    
+  }
+  
+}
\ No newline at end of file
diff --git a/com/jscat/facilitator.py b/com/jscat/facilitator.py
new file mode 100755
index 0000000..8c6a44e
--- /dev/null
+++ b/com/jscat/facilitator.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+
+import BaseHTTPServer
+import getopt
+import cgi
+import re
+import sys
+import socket
+from collections import deque
+
+DEFAULT_ADDRESS = "0.0.0.0"
+DEFAULT_PORT = 9002
+
+def usage(f = sys.stdout):
+	print >> f, """\
+Usage: %(progname)s <OPTIONS> [HOST] [PORT]
+Flash bridge facilitator: Register client addresses with HTTP POST requests
+and serve them out again with HTTP GET. Listen on HOST and PORT, by default
+%(addr)s %(port)d.
+  -h, --help		   show this help.\
+""" % {
+	"progname": sys.argv[0],
+	"addr": DEFAULT_ADDRESS,
+	"port": DEFAULT_PORT,
+}
+
+REGS = deque()
+
+class Reg(object):
+	def __init__(self, id):
+		self.id = id
+
+	def __unicode__(self):
+	  return u"%s" % (self.id)
+
+	def __str__(self):
+		return unicode(self).encode("UTF-8")
+
+	def __cmp__(self, other):
+		return cmp((self.id), (other.id))
+
+	@staticmethod
+	def parse(spec, defhost = None, defport = None):
+		host = None
+		port = None
+		m = re.match(r'^\[(.+)\]:(\d*)$', spec)
+		if m:
+			host, port = m.groups()
+			af = socket.AF_INET6
+		else:
+			m = re.match(r'^(.*):(\d*)$', spec)
+			if m:
+				host, port = m.groups()
+				if host:
+					af = socket.AF_INET
+				else:
+					# Has to be guessed from format of defhost.
+					af = 0
+		host = host or defhost
+		port = port or defport
+		if not (host and port):
+			raise ValueError("Bad address specification \"%s\"" % spec)
+
+		try:
+			addrs = socket.getaddrinfo(host, port, af, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_NUMERICHOST)
+		except socket.gaierror, e:
+			raise ValueError("Bad host or port: \"%s\" \"%s\": %s" % (host, port, str(e)))
+		if not addrs:
+			raise ValueError("Bad host or port: \"%s\" \"%s\"" % (host, port))
+
+		af = addrs[0][0]
+		host, port = socket.getnameinfo(addrs[0][4], socket.NI_NUMERICHOST | socket.NI_NUMERICSERV)
+		return Reg(af, host, int(port))
+
+def fetch_reg():
+	"""Get a client registration, or None if none is available."""
+	if not REGS:
+		return None
+	return REGS.popleft()
+
+class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
+	def do_GET(self):
+		print "From " + str(self.client_address) + " received: GET:",
+		reg = fetch_reg()
+		if reg:
+			print "Handing out " + str(reg) + ". Clients: " + str(len(REGS))
+			self.request.send(str(reg))
+		else:
+			print "Registration list is empty"
+			self.request.send("Registration list empty")
+
+	def do_POST(self):
+		print "From " + str(self.client_address) + " received: POST:",
+		data = self.rfile.readline().strip()
+		print data + " :",
+		try:
+			vals = cgi.parse_qs(data, False, True)
+		except ValueError, e:
+			print "Syntax error in POST:", str(e)
+			return
+
+		client_specs = vals.get("client")
+		if client_specs is None or len(client_specs) != 1:
+			print "In POST: need exactly one \"client\" param"
+			return
+		val = client_specs[0]
+
+		try:
+			reg = Reg(val)
+		except ValueError, e:
+			print "Can't parse client \"%s\": %s" % (val, str(e))
+			return
+
+		if reg not in list(REGS):
+			REGS.append(reg)
+			print "Registration " + str(reg) + " added. Registrations: " + str(len(REGS))
+		else:
+			print "Registration " + str(reg) + " already present. Registrations: " + str(len(REGS))
+
+opts, args = getopt.gnu_getopt(sys.argv[1:], "h", ["help"])
+for o, a in opts:
+	if o == "-h" or o == "--help":
+		usage()
+		sys.exit()
+
+if len(args) == 0:
+	address = (DEFAULT_ADDRESS, DEFAULT_PORT)
+elif len(args) == 1:
+	# Either HOST or PORT may be omitted; figure out which one.
+	if args[0].isdigit():
+		address = (DEFAULT_ADDRESS, args[0])
+	else:
+		address = (args[0], DEFAULT_PORT)
+elif len(args) == 2:
+	address = (args[0], args[1])
+else:
+	usage(sys.stderr)
+	sys.exit(1)
+
+# Setup the server
+server = BaseHTTPServer.HTTPServer(address, Handler)
+
+print "Starting Facilitator on " + str(address) + "..."
+
+# Run server... Single threaded serving of requests...
+server.serve_forever()
diff --git a/com/jscat/rtmfp/RTMFPSocket.as b/com/jscat/rtmfp/RTMFPSocket.as
new file mode 100644
index 0000000..4b83784
--- /dev/null
+++ b/com/jscat/rtmfp/RTMFPSocket.as
@@ -0,0 +1,216 @@
+package rtmfp
+{
+    import flash.events.Event;
+    import flash.events.EventDispatcher;
+    import flash.events.IOErrorEvent;
+    import flash.events.NetStatusEvent;
+    import flash.events.ProgressEvent;
+    import flash.events.SecurityErrorEvent;
+    import flash.net.NetConnection;
+    import flash.net.NetStream;
+    import flash.utils.ByteArray;
+    import flash.utils.clearInterval;
+    import flash.utils.setInterval;
+    import flash.utils.setTimeout;
+    
+    import rtmfp.RTMFPSocketClient;
+    import rtmfp.events.RTMFPSocketEvent;
+
+    [Event(name="connectSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="connectFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="publishStart", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="peerConnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="peeringSuccess", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="peeringFail", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+  	[Event(name="peerDisconnected", type="com.jscat.rtmfp.events.RTMFPSocketEvent")]
+    public class RTMFPSocket extends EventDispatcher
+    {
+        /* The name of the "media" to pass between peers */
+        private static const DATA:String = "data";
+		    private static const DEFAULT_CIRRUS_ADDRESS:String = "rtmfp://p2p.rtmfp.net";
+		    private static const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;
+		    private static const DEFAULT_CONNECT_TIMEOUT:uint = 4000;
+		
+        /* Connection to the Cirrus rendezvous service */
+        private var connection:NetConnection;
+		
+		    /* ID of the peer to connect to */
+		    private var peerID:String;
+		
+		    /* Data streams to be established with peer */
+		    private var sendStream:NetStream;
+		    private var recvStream:NetStream;
+		    
+		    /* Timeouts */
+		    private var connectionTimeout:int;
+		    private var peerConnectTimeout:uint;
+
+        public function RTMFPSocket(){}
+        
+        public function connect(addr:String = DEFAULT_CIRRUS_ADDRESS, key:String = DEFAULT_CIRRUS_KEY):void
+        {
+          connection = new NetConnection();
+			    connection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatusEvent);
+			    connection.addEventListener(IOErrorEvent.IO_ERROR, onIOErrorEvent);
+			    connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityErrorEvent);
+          connection.connect(addr + "/" + key);
+			    connectionTimeout = setInterval(fail, DEFAULT_CONNECT_TIMEOUT);
+        }
+
+        public function get id():String
+        {
+          if (connection != null && connection.connected) {
+            return connection.nearID;
+          }
+          
+          return null;
+        }
+        
+        public function get connected():Boolean
+        {
+          return (connection != null && connection.connected);
+        }
+        
+        public function readBytes(bytes:ByteArray):void
+        {
+          recvStream.client.bytes.readBytes(bytes);
+        }
+
+        public function writeBytes(bytes:ByteArray):void
+        {
+          sendStream.send("dataAvailable", bytes);
+        }
+        
+        public function get peer():String
+        {
+          return this.peerID;
+        }
+
+        public function set peer(peerID:String):void
+        {
+          if (peerID == null || peerID.length == 0) {
+            throw new Error("Peer ID is null/empty.")
+    			} else if (peerID == connection.nearID) {
+    				throw new Error("Peer ID cannot be the same as our ID.");
+    			} else if (this.peerID == peerID) {
+    			  throw new Error("Already connected to peer " + peerID + ".");
+    			} else if (this.recvStream != null) {
+            throw new Error("Cannot connect to a second peer.");
+          }
+          
+          this.peerID = peerID;
+
+    			recvStream = new NetStream(connection, peerID);
+    			var client:RTMFPSocketClient = new RTMFPSocketClient();
+    			client.addEventListener(ProgressEvent.SOCKET_DATA, onDataAvailable, false, 0, true);
+    			client.addEventListener(RTMFPSocketClient.PEER_CONNECT_ACKNOWLEDGED, onPeerConnectAcknowledged, false, 0, true);
+    			recvStream.client = client;
+    			recvStream.addEventListener(NetStatusEvent.NET_STATUS, onRecvStreamEvent);
+    			recvStream.play(DATA);
+    			setTimeout(onPeerConnectTimeout, peerConnectTimeout, recvStream);
+        }
+        
+        private function startPublishStream():void
+        {
+          sendStream = new NetStream(connection, NetStream.DIRECT_CONNECTIONS);
+    			sendStream.addEventListener(NetStatusEvent.NET_STATUS, onSendStreamEvent);
+    			var o:Object = new Object();
+    			o.onPeerConnect = onPeerConnect;
+    			sendStream.client = o;
+    			sendStream.publish(DATA);
+        }
+        
+        private function fail():void
+        {
+          clearInterval(connectionTimeout);
+    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_FAIL));
+        }
+        
+        private function onDataAvailable(event:ProgressEvent):void
+        {
+          dispatchEvent(event);
+        }
+        
+        private function onIOErrorEvent(event:IOErrorEvent):void
+        {
+          fail();
+        }
+        
+        private function onNetStatusEvent(event:NetStatusEvent):void
+        {
+          switch (event.info.code) {
+      			case "NetConnection.Connect.Success" :
+      				clearInterval(connectionTimeout);
+      				startPublishStream();
+      				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_SUCCESS));
+      				break;
+      			case "NetStream.Connect.Success" :
+      				break;
+      			case "NetStream.Publish.BadName" :
+      			  fail();
+      				break;
+      			case "NetStream.Connect.Closed" :
+      			  // we've disconnected from the peer
+      			  // can reset to accept another
+      			  // clear the publish stream and re-publish another
+      			  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_DISCONNECTED, recvStream));
+      				break;
+      		} 
+        }
+        
+        private function onPeerConnect(peer:NetStream):Boolean
+        {
+          // establish a bidirectional stream with the peer
+          if (peerID == null) {
+            this.peer = peer.farID;
+          }
+          
+          // disallow additional peers connecting to us
+          if (peer.farID != peerID) return false;
+          
+          peer.send("setPeerConnectAcknowledged");
+          dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_CONNECTED, peer));
+          
+          return true;
+        }
+        
+        private function onPeerConnectAcknowledged(event:Event):void
+        {
+    			dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_SUCCESS, recvStream));
+        }
+        
+        private function onPeerConnectTimeout(peer:NetStream):void
+        {
+          if (!recvStream.client) return;
+    			if (!RTMFPSocketClient(recvStream.client).peerConnectAcknowledged) {
+    				dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_FAIL, recvStream));
+    			}
+        }
+        
+        private function onSecurityErrorEvent(event:SecurityErrorEvent):void
+        {
+          fail();
+        }
+        
+        private function onSendStreamEvent(event:NetStatusEvent):void
+        {
+    			switch (event.info.code) {
+    				case ("NetStream.Publish.Start") :
+    				  dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PUBLISH_START));
+    				  break;
+    				case ("NetStream.Play.Reset") :
+    				case ("NetStream.Play.Start") :
+    					break;
+    			}
+    		}
+    		private function onRecvStreamEvent(event:NetStatusEvent):void
+    		{
+    			switch (event.info.code) {
+    				case ("NetStream.Publish.Start") :
+    				case ("NetStream.Play.Reset") :
+    				case ("NetStream.Play.Start") :
+    					break;
+    			}
+    		}
+    }
+}
diff --git a/com/jscat/rtmfp/RTMFPSocketClient.as b/com/jscat/rtmfp/RTMFPSocketClient.as
new file mode 100644
index 0000000..d9fcffa
--- /dev/null
+++ b/com/jscat/rtmfp/RTMFPSocketClient.as
@@ -0,0 +1,57 @@
+package rtmfp
+{
+	import flash.events.Event;
+	import flash.events.EventDispatcher;
+	import flash.events.ProgressEvent;
+	import flash.utils.ByteArray;
+
+	[Event(name="peerConnectAcknowledged", type="flash.events.Event")]
+	public dynamic class RTMFPSocketClient extends EventDispatcher {
+		public static const PEER_CONNECT_ACKNOWLEDGED:String = "peerConnectAcknowledged";
+		
+		private var _bytes:ByteArray;
+		private var _peerID:String;
+		private var _peerConnectAcknowledged:Boolean;
+		
+		public function RTMFPSocketClient()
+		{
+			super();
+			_bytes = new ByteArray();
+			_peerID = null;
+			_peerConnectAcknowledged = false;
+		}
+		
+		public function get bytes():ByteArray
+		{
+		  return _bytes;
+		}
+		
+		public function dataAvailable(bytes:ByteArray):void
+		{
+		  this._bytes.clear();
+		  bytes.readBytes(this._bytes);
+		  dispatchEvent(new ProgressEvent(ProgressEvent.SOCKET_DATA, false, false, this._bytes.bytesAvailable, this._bytes.length));
+		}
+		
+		public function get peerConnectAcknowledged():Boolean
+		{
+		  return _peerConnectAcknowledged;
+		}
+		
+		public function setPeerConnectAcknowledged():void
+		{
+			_peerConnectAcknowledged = true;
+			dispatchEvent(new Event(PEER_CONNECT_ACKNOWLEDGED));
+		}
+		
+		public function get peerID():String
+		{
+		  return _peerID;
+		}
+		
+		public function set peerID(id:String):void
+		{
+		  _peerID = id;
+		}
+	}
+}
\ No newline at end of file
diff --git a/com/jscat/rtmfp/events/RTMFPSocketEvent.as b/com/jscat/rtmfp/events/RTMFPSocketEvent.as
new file mode 100644
index 0000000..c5b4af1
--- /dev/null
+++ b/com/jscat/rtmfp/events/RTMFPSocketEvent.as
@@ -0,0 +1,25 @@
+package rtmfp.events
+{
+	import flash.events.Event;
+	import flash.net.NetStream;
+
+	public class RTMFPSocketEvent extends Event
+	{
+		public static const CONNECT_SUCCESS:String   = "connectSuccess";
+		public static const CONNECT_FAIL:String      = "connectFail";
+		public static const PUBLISH_START:String     = "publishStart";
+		public static const PEER_CONNECTED:String    = "peerConnected";
+		public static const PEER_DISCONNECTED:String = "peerDisconnected";
+		public static const PEERING_SUCCESS:String   = "peeringSuccess";
+		public static const PEERING_FAIL:String      = "peeringFail";
+		
+		public var stream:NetStream;
+		
+		public function RTMFPSocketEvent(type:String, streamVal:NetStream = null, bubbles:Boolean = false, cancelable:Boolean = false)
+		{
+			super(type, bubbles, cancelable);
+			stream = streamVal;
+		}
+		
+	}
+}
\ No newline at end of file





More information about the tor-commits mailing list