tor-commits
Threads by month
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
June 2011
- 14 participants
- 716 discussions
12 Jun '11
commit 858801c0e8965aa09f8e68fe14a6ac27679b759c
Author: Nate Hardison <nate(a)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
1
0
[flashproxy/master] Initial version of AS Connector that communicates with Cirrus server and facilitator
by dcf@torproject.org 12 Jun '11
by dcf@torproject.org 12 Jun '11
12 Jun '11
commit 9bcfc6f7a6ea6a94faed9becb1311ea94443727d
Author: Nate Hardison <nate(a)rescomp-09-154551.stanford.edu>
Date: Wed May 4 18:25:39 2011 -0700
Initial version of AS Connector that communicates with Cirrus server and facilitator
---
Connector.as | 137 ++++++++++++++++++++++++++++-
Makefile | 2 +-
RTMFPRelay.as | 235 +++++++-------------------------------------------
RTMFPRelayReactor.as | 13 +++
Utils.as | 24 +++++
5 files changed, 203 insertions(+), 208 deletions(-)
diff --git a/Connector.as b/Connector.as
index f362dd3..fc18692 100644
--- a/Connector.as
+++ b/Connector.as
@@ -16,9 +16,27 @@ package
import flash.utils.setInterval;
import flash.utils.setTimeout;
- public class Connector extends RTMFPRelay {
+ import RTMFPRelay;
+ import RTMFPRelayReactor;
+ import Utils;
+
+ public class Connector extends Sprite implements RTMFPRelayReactor {
+
+ /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
+ crossdomain policy. */
+ private const DEFAULT_TOR_ADDR:Object = {
+ host: "173.255.221.44",
+ port: 9001
+ };
private var output_text:TextField;
+
+ // Socket to Tor relay.
+ private var s_t:Socket;
+ // Socket to facilitator.
+ private var s_f:Socket;
+ // RTMFP data relay
+ private var relay:RTMFPRelay;
private var fac_addr:Object;
private var tor_addr:Object;
@@ -33,10 +51,70 @@ package
addChild(output_text);
puts("Starting.");
+
// Wait until the query string parameters are loaded.
this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
}
+ private function facilitator_is(host:String, port:int):void
+ {
+ if (s_f != null && s_f.connected) {
+ puts("Error: already connected to Facilitator!");
+ return;
+ }
+
+ s_f = new Socket();
+
+ s_f.addEventListener(Event.CONNECT, function (e:Event):void {
+ puts("Facilitator: connected.");
+ onConnectionEvent();
+ });
+ s_f.addEventListener(Event.CLOSE, function (e:Event):void {
+ puts("Facilitator: closed connection.");
+ });
+ s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+ puts("Facilitator: I/O error: " + e.text + ".");
+ });
+ s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+ puts("Facilitator: security error: " + e.text + ".");
+ });
+
+ puts("Facilitator: connecting to " + host + ":" + port + ".");
+ s_f.connect(host, port);
+ }
+
+ private function tor_relay_is(host:String, port:int):void
+ {
+ if (s_t != null && s_t.connected) {
+ puts("Error: already connected to Tor relay!");
+ return;
+ }
+
+ s_t = new Socket();
+
+ s_t.addEventListener(Event.CONNECT, function (e:Event):void {
+ puts("Tor: connected.");
+ onConnectionEvent();
+ });
+ s_t.addEventListener(Event.CLOSE, function (e:Event):void {
+ puts("Tor: closed.");
+ });
+ s_t.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+ puts("Tor: I/O error: " + e.text + ".");
+ });
+ s_t.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+ puts("Tor: security error: " + e.text + ".");
+ });
+
+ puts("Tor: connecting to " + host + ":" + port + ".");
+ s_t.connect(host, port);
+ }
+
+ private function puts(s:String):void
+ {
+ output_text.appendText(s + "\n");
+ output_text.scrollV = output_text.maxScrollV;
+ }
private function loaderinfo_complete(e:Event):void
{
@@ -45,8 +123,61 @@ package
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.parse_addr_spec(fac_spec);
+ if (!fac_addr) {
+ puts("Error: Facilitator spec must be in the form \"host:port\".");
+ return;
+ }
+
+ relay = new RTMFPRelay(this);
+
+ tor_addr = DEFAULT_TOR_ADDR;
+ tor_relay_is(tor_addr.host, tor_addr.port);
+ facilitator_is(fac_addr.host, fac_addr.port);
}
-
+
+ public function onConnectionEvent():void
+ {
+ if (s_f != null && s_f.connected && s_t != null && s_t.connected &&
+ relay != null && relay.connected) {
+ s_f.writeUTFBytes("POST / HTTP/1.1\r\n\r\nclient=%3A"+ relay.cirrus_id + "\r\n");
+ }
+ }
+
+ public function onIOErrorEvent(event:IOErrorEvent):void
+ {
+ puts("Cirrus: I/O error: " + event.text + ".");
+ }
+
+ public function onNetStatusEvent(event:NetStatusEvent):void
+ {
+ switch (event.info.code) {
+ case "NetConnection.Connect.Success" :
+ puts("Cirrus: connected with ID " + relay.cirrus_id + ".");
+ onConnectionEvent();
+ break;
+ case "NetStream.Connect.Success" :
+ puts("Peer: connected.");
+ break;
+ case "NetStream.Publish.BadName" :
+ puts(event.info.code);
+ break;
+ case "NetStream.Connect.Closed" :
+ puts(event.info.code);
+ break;
+ }
+ }
+
+ public function onSecurityErrorEvent(event:SecurityErrorEvent):void
+ {
+ puts("Cirrus: security error: " + event.text + ".");
+ }
+ }
}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index baf0e94..92047fb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
MXMLC ?= mxmlc
-TARGETS = swfcat.swf Proxy.swf
+TARGETS = swfcat.swf Connector.swf
all: $(TARGETS)
diff --git a/RTMFPRelay.as b/RTMFPRelay.as
index 27b3208..a33ebbf 100644
--- a/RTMFPRelay.as
+++ b/RTMFPRelay.as
@@ -15,8 +15,6 @@ package
import flash.utils.clearInterval;
import flash.utils.setInterval;
import flash.utils.setTimeout;
-
- import Utils;
public class RTMFPRelay extends Sprite
{
@@ -24,15 +22,8 @@ package
private static const CIRRUS_DEV_KEY:String = RTMFP::CIRRUS_KEY;
/* The name of the "media" to pass between peers */
- public static const DATA:String = "data";
+ private static const DATA:String = "data";
- protected var output_text:TextField;
-
- // Socket to Tor relay.
- private var s_t:Socket;
- // Socket to facilitator.
- private var s_f:Socket;
-
/* Connection to the Cirrus rendezvous service */
private var cirrus_conn:NetConnection;
@@ -42,231 +33,67 @@ package
/* Data streams to be established with peer */
private var send_stream:NetStream;
private var recv_stream:NetStream;
+
+ private var notifiee:RTMFPRelayReactor;
- private var fac_addr:Object;
- private var tor_addr:Object;
-
- private function puts(s:String):void
- {
- output_text.appendText(s + "\n");
- output_text.scrollV = output_text.maxScrollV;
- }
-
- public function RTMFPRelay()
- {
- 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.");
-
- cirrus_conn = new NetConnection();
- cirrus_conn.addEventListener(NetStatusEvent.NET_STATUS, function (e:NetStatusEvent):void {
- puts("Cirrus: connected.");
- });
- cirrus_conn.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
- puts("Cirrus: I/O error: " + e.text + ".");
- });
-
- cirrus_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityError):void {
- puts("Cirrus: security error: " + e.text + ".");
- });
+ public function RTMFPRelay(notifiee:RTMFPRelayReactor)
+ {
+ this.notifiee = notifiee;
+
+ cirrus_conn = new NetConnection();
+ cirrus_conn.addEventListener(NetStatusEvent.NET_STATUS, notifiee.onNetStatusEvent);
+ cirrus_conn.addEventListener(IOErrorEvent.IO_ERROR, notifiee.onIOErrorEvent);
+ cirrus_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, notifiee.onSecurityErrorEvent);
- cirrus_conn.connect(CIRRUS_ADDRESS + "/" + CIRRUS_DEV_KEY);
-
-
- // Wait until the query string parameters are loaded.
- this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
+ cirrus_conn.connect(CIRRUS_ADDRESS + "/" + CIRRUS_DEV_KEY);
}
- public function data_is(data:ByteArray):void
+ public function get cirrus_id():String
{
+ if (cirrus_conn != null && cirrus_conn.connected) {
+ return cirrus_conn.nearID;
+ }
-
+ return null;
}
- public function facilitator_is(host:String, port:String):void
+ public function get connected():Boolean
+ {
+ return (cirrus_conn != null && cirrus_conn.connected);
+ }
+
+ public function data_is(data:ByteArray):void
{
- if (s_f != null && s_f.connected) {
-
- return;
- }
- s_f = new Socket();
- s_f.addEventListener(Event.CONNECT, function (e:Event):void {
- puts("Facilitator: connected.");
- });
- s_f.addEventListener(Event.CLOSE, function (e:Event):void {
- puts("Facilitator: closed connection.");
- });
- s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
- puts("Facilitator: I/O error: " + e.text + ".");
- });
- s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
- puts("Facilitator: security error: " + e.text + ".");
- });
-
- puts("Facilitator: connecting to " + host + ":" + port + ".");
- s_f.connect(host, port);
}
- public function peer(peer_id:String):String
+ public function get peer():String
{
return this.peer_id;
}
- public function peer_is(peer_id:String):void
+ public function set peer(peer_id:String):void
{
if (peer_id == null) {
- puts("Error: Client ID doesn't exist.");
- return;
+ throw new Error("Peer ID is null.")
} else if (peer_id == cirrus_conn.nearID) {
- puts("Error: Client ID is our ID.");
- return;
+ throw new Error("Peer ID cannot be the same as our ID.");
} else if (this.peer_id == peer_id) {
-
+ throw new Error("Already connected to peer " + peer_id + ".");
} else if (this.recv_stream != null) {
- puts("Error: already set up with a peer!");
- return;
+ throw new Error("Cannot connect to a second peer.");
}
this.peer_id = peer_id;
send_stream = new NetStream(cirrus_conn, NetStream.DIRECT_CONNECTIONS);
- send_stream.addEventListener(NetStatusEvent.NET_STATUS, net_status_event_handler);
+ send_stream.addEventListener(NetStatusEvent.NET_STATUS, notifiee.onNetStatusEvent);
send_stream.publish(DATA);
recv_stream = new NetStream(cirrus_conn, peer_id);
- recv_stream.addEventListener(NetStatusEvent.NET_STATUS, net_status_event_handler);
+ recv_stream.addEventListener(NetStatusEvent.NET_STATUS, notifiee.onNetStatusEvent);
recv_stream.play(DATA);
}
-
- public function tor_relay_is(host:String, port:String):void
- {
- if (s_t != null && s_t.connected) {
- puts("Error: already connected to Tor relay!");
- return;
- }
-
- s_t = new Socket();
-
- s_t.addEventListener(Event.CONNECT, function (e:Event):void {
- puts("Tor: connected.");
- });
- s_t.addEventListener(Event.CLOSE, function (e:Event):void {
- puts("Tor: closed.");
- });
- s_t.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
- puts("Tor: I/O error: " + e.text + ".");
- });
- s_t.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
- puts("Tor: security error: " + e.text + ".");
- });
-
- puts("Tor: connecting to " + host + ":" + port + ".");
- s_t.connect(host, port);
- }
-
- private function loaderinfo_complete(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 = parse_addr_spec(fac_spec);
- if (!fac_addr) {
- puts("Error: Facilitator spec must be in the form \"host:port\".");
- return;
- }
-
- tor_addr = DEFAULT_TOR_ADDR;
-
- go();
- }
-
- private function fac_connected(e:Event):void
- {
-
-
- s_f.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
- var client_spec:String;
- var client_addr:Object;
-
- client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
- puts("Facilitator: got \"" + client_spec + "\"");
-
- /*client_addr = parse_addr_spec(client_spec);
- if (!client_addr) {
- puts("Error: Client spec must be in the form \"host:port\".");
- return;
- }
- if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
- puts("Error: Facilitator has no clients.");
- return;
- }*/
-
- /* Now we have a client, so start up a connection to the Cirrus rendezvous point */
-
- });
-
- s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
- }
-
- private function net_status_event_handler(e:NetStatusEvent):void
- {
- switch (e.info.code) {
- case "NetConnection.Connect.Success" :
- // Cirrus is now connected
- cirrus_connected(e);
- }
-
- }
-
- private function cirrus_connected(e:Event):void
- {
-
-
-
- }
-
- /*private function client_connected(e:Event):void
- {
- puts("Client: connected.");
-
- 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 + ".");
- s_c.writeBytes(bytes);
- });
- s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
- var bytes:ByteArray = new ByteArray();
- s_c.readBytes(bytes, 0, e.bytesLoaded);
- puts("Client: read " + bytes.length + ".");
- s_t.writeBytes(bytes);
- });
- }*/
-
- private function close_connections():void
- {
- if (s_t.connected) s_t.close();
- if (s_f.connected) s_f.close();
- if (cirrus.connected) cirrus.close();
- if (send_stream != null) send_stream.close();
- if (recv_stream != null) recv_stream.close();
- }
-
-
}
}
diff --git a/RTMFPRelayReactor.as b/RTMFPRelayReactor.as
new file mode 100644
index 0000000..5c84779
--- /dev/null
+++ b/RTMFPRelayReactor.as
@@ -0,0 +1,13 @@
+package {
+
+ import flash.events.IOErrorEvent;
+ import flash.events.NetStatusEvent;
+ import flash.events.SecurityErrorEvent;
+
+ public interface RTMFPRelayReactor {
+ function onIOErrorEvent(event:IOErrorEvent):void;
+ function onNetStatusEvent(event:NetStatusEvent):void;
+ function onSecurityErrorEvent(event:SecurityErrorEvent):void
+ }
+
+}
\ No newline at end of file
diff --git a/Utils.as b/Utils.as
new file mode 100644
index 0000000..75ed44c
--- /dev/null
+++ b/Utils.as
@@ -0,0 +1,24 @@
+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 parse_addr_spec(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
1
0
12 Jun '11
commit 8ff0c31bff916fdfe7458abe9b3261b0babc3545
Author: Nate Hardison <nate(a)rescomp-09-154551.stanford.edu>
Date: Wed May 4 17:01:19 2011 -0700
Committing updated Makefile to build Proxy.as
---
Makefile | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/Makefile b/Makefile
index 6f03eb6..baf0e94 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
MXMLC ?= mxmlc
-TARGETS = swfcat.swf
+TARGETS = swfcat.swf Proxy.swf
all: $(TARGETS)
1
0
[flashproxy/master] Added current count using dots beneath numeric counter for total count
by dcf@torproject.org 12 Jun '11
by dcf@torproject.org 12 Jun '11
12 Jun '11
commit 6e08bb51f7dcdc4e39a4ddd9dce6c131e590e22c
Author: ellitron <jdellit(a)stanford.edu>
Date: Thu May 19 09:27:27 2011 -0700
Added current count using dots beneath numeric counter for total count
---
swfcat.as | 68 +++++++++++++++++++++++++++++++++++++++++++------------------
1 files changed, 48 insertions(+), 20 deletions(-)
diff --git a/swfcat.as b/swfcat.as
index 38ea6c0..76de948 100644
--- a/swfcat.as
+++ b/swfcat.as
@@ -48,8 +48,10 @@ package
/* Badge with a client counter */
[Embed(source="badge_con_counter.png")]
private var BadgeImage:Class;
- private var client_count_tf:TextField;
- private var client_count_fmt:TextFormat;
+ private var tot_client_count_tf:TextField;
+ private var tot_client_count_fmt:TextFormat;
+ private var cur_client_count_tf:TextField;
+ private var cur_client_count_fmt:TextFormat;
public function puts(s:String):void
{
@@ -59,10 +61,16 @@ package
public function update_client_count():void
{
+ /* Update total client count. */
if (String(total_proxy_pairs).length == 1)
- client_count_tf.text = "0" + String(total_proxy_pairs);
+ tot_client_count_tf.text = "0" + String(total_proxy_pairs);
else
- client_count_tf.text = String(total_proxy_pairs);
+ tot_client_count_tf.text = String(total_proxy_pairs);
+
+ /* Update current client count. */
+ cur_client_count_tf.text = "";
+ for(var i:Number=0; i<num_proxy_pairs; i++)
+ cur_client_count_tf.appendText(".");;
}
public function swfcat()
@@ -79,19 +87,34 @@ package
output_text.textColor = 0x44cc44;
/* Setup client counter for badge. */
- client_count_fmt = new TextFormat();
- client_count_fmt.color = 0xFFFFFF;
- client_count_fmt.align = "center";
- client_count_fmt.font = "courier-new";
- client_count_fmt.bold = true;
- client_count_fmt.size = 10;
- client_count_tf = new TextField();
- client_count_tf.width = 20;
- client_count_tf.height = 17;
- client_count_tf.background = false;
- client_count_tf.defaultTextFormat = client_count_fmt;
- client_count_tf.x=47;
- client_count_tf.y=3;
+ tot_client_count_fmt = new TextFormat();
+ tot_client_count_fmt.color = 0xFFFFFF;
+ tot_client_count_fmt.align = "center";
+ tot_client_count_fmt.font = "courier-new";
+ tot_client_count_fmt.bold = true;
+ tot_client_count_fmt.size = 10;
+ tot_client_count_tf = new TextField();
+ tot_client_count_tf.width = 20;
+ tot_client_count_tf.height = 17;
+ tot_client_count_tf.background = false;
+ tot_client_count_tf.defaultTextFormat = tot_client_count_fmt;
+ tot_client_count_tf.x=47;
+ tot_client_count_tf.y=0;
+
+ cur_client_count_fmt = new TextFormat();
+ cur_client_count_fmt.color = 0xFFFFFF;
+ cur_client_count_fmt.align = "center";
+ cur_client_count_fmt.font = "courier-new";
+ cur_client_count_fmt.bold = true;
+ cur_client_count_fmt.size = 10;
+ cur_client_count_tf = new TextField();
+ cur_client_count_tf.width = 20;
+ cur_client_count_tf.height = 17;
+ cur_client_count_tf.background = false;
+ cur_client_count_tf.defaultTextFormat = cur_client_count_fmt;
+ cur_client_count_tf.x=47;
+ cur_client_count_tf.y=6;
+
/* Update the client counter on badge. */
update_client_count();
@@ -113,7 +136,8 @@ package
addChild(new BadgeImage());
/* Tried unsuccessfully to add counter to badge. */
/* For now, need two addChilds :( */
- addChild(client_count_tf);
+ addChild(tot_client_count_tf);
+ addChild(cur_client_count_tf);
}
fac_spec = this.loaderInfo.parameters["facilitator"];
@@ -183,15 +207,19 @@ package
num_proxy_pairs++;
total_proxy_pairs++;
+ /* Update the client count on the badge. */
+ update_client_count();
+
proxy_pair = new ProxyPair(this, client_addr, DEFAULT_TOR_ADDR);
proxy_pair.addEventListener(Event.COMPLETE, function(e:Event):void {
proxy_pair.log("Complete.");
+
num_proxy_pairs--;
+ /* Update the client count on the badge. */
+ update_client_count();
});
proxy_pair.connect();
- /* Update the client count on the badge. */
- update_client_count();
}
/* Parse an address in the form "host:port". Returns an Object with
1
0
[flashproxy/master] New RTMFP relay started in Proxy.as, building relay system for Tor client and patsy proxy
by dcf@torproject.org 12 Jun '11
by dcf@torproject.org 12 Jun '11
12 Jun '11
commit 3f5a036ef65d488d575c3ec8e3a8a3f7b7e69f6e
Author: Nate Hardison <nate(a)rescomp-09-154551.stanford.edu>
Date: Wed May 4 16:28:34 2011 -0700
New RTMFP relay started in Proxy.as, building relay system for Tor client and patsy proxy
---
Connector.as | 52 +++++++++++
Makefile | 2 +-
Proxy.as | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
RTMFPRelay.as | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 586 insertions(+), 1 deletions(-)
diff --git a/Connector.as b/Connector.as
new file mode 100644
index 0000000..f362dd3
--- /dev/null
+++ b/Connector.as
@@ -0,0 +1,52 @@
+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.net.NetConnection;
+ import flash.net.NetStream;
+ import flash.utils.ByteArray;
+ import flash.utils.clearInterval;
+ import flash.utils.setInterval;
+ import flash.utils.setTimeout;
+
+ public class Connector extends RTMFPRelay {
+
+ private var output_text:TextField;
+
+ 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.");
+ // Wait until the query string parameters are loaded.
+ this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
+ }
+
+
+ private function loaderinfo_complete(e:Event):void
+ {
+ var fac_spec:String;
+ var tor_spec:String;
+
+ puts("Parameters loaded.");
+
+
+ }
+
+}
+
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 27ba9d8..6f03eb6 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ TARGETS = swfcat.swf
all: $(TARGETS)
%.swf: %.as
- $(MXMLC) -output $@ $^
+ $(MXMLC) -output $@ -define=RTMFP::CIRRUS_KEY,\"$(CIRRUS_KEY)\" $^
clean:
rm -f $(TARGETS)
diff --git a/Proxy.as b/Proxy.as
new file mode 100644
index 0000000..01b2fe3
--- /dev/null
+++ b/Proxy.as
@@ -0,0 +1,261 @@
+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.net.NetConnection;
+ import flash.net.NetStream;
+ import flash.utils.ByteArray;
+ import flash.utils.clearInterval;
+ import flash.utils.setInterval;
+ import flash.utils.setTimeout;
+
+ public class Proxy extends Sprite
+ {
+ /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
+ crossdomain policy. */
+ private const DEFAULT_TOR_ADDR:Object = {
+ host: "173.255.221.44",
+ port: 9001
+ };
+
+ /* Address of the Cirrus rendezvous service with Nate's
+ developer key appended */
+ private static const CIRRUS_ADDRESS:String = "rtmfp://p2p.rtmfp.net/" + RTMFP::CIRRUS_KEY + "/";
+
+ // The name of our data stream
+ public static const DATA:String = "data";
+
+ private var output_text:TextField;
+
+ // Socket to Tor relay.
+ private var s_t:Socket;
+ // Socket to facilitator.
+ private var s_f:Socket;
+
+ /* Connection to the Cirrus rendezvous service that will hook
+ us up with the Tor client */
+ public var cirrus:NetConnection;
+
+ // The Tor client's peerID (used when establishing the client_stream)
+ private var client_id:String;
+
+ // The data streams to be established with the Tor client
+ public var send_stream:NetStream;
+ public var recv_stream:NetStream;
+
+ private var fac_addr:Object;
+ private var tor_addr:Object;
+
+ private function puts(s:String):void
+ {
+ output_text.appendText(s + "\n");
+ output_text.scrollV = output_text.maxScrollV;
+ }
+
+ public function Proxy()
+ {
+ 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.");
+ // Wait until the query string parameters are loaded.
+ this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
+ }
+
+ private function loaderinfo_complete(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 = parse_addr_spec(fac_spec);
+ if (!fac_addr) {
+ puts("Error: Facilitator spec must be in the form \"host:port\".");
+ return;
+ }
+
+ tor_addr = DEFAULT_TOR_ADDR;
+
+ go();
+ }
+
+ /* We connect first to the Tor relay; once that happens we connect to
+ the facilitator to get a client address; once we have the address
+ of a waiting client then we connect to the client and BAM! we're in business. */
+ private function go():void
+ {
+ s_t = new Socket();
+
+ s_t.addEventListener(Event.CONNECT, tor_connected);
+ s_t.addEventListener(Event.CLOSE, function (e:Event):void {
+ puts("Tor: closed.");
+ });
+ s_t.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+ puts("Tor: I/O error: " + e.text + ".");
+ });
+ s_t.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+ puts("Tor: security error: " + e.text + ".");
+
+ });
+
+ puts("Tor: connecting to " + tor_addr.host + ":" + tor_addr.port + ".");
+ s_t.connect(tor_addr.host, tor_addr.port);
+ }
+
+ private function tor_connected(e:Event):void
+ {
+ /* Got a connection to tor, now let's get served a client from the facilitator */
+ s_f = new Socket();
+
+ puts("Tor: connected.");
+ s_f.addEventListener(Event.CONNECT, fac_connected);
+ s_f.addEventListener(Event.CLOSE, function (e:Event):void {
+ puts("Facilitator: closed connection.");
+ });
+ s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+ puts("Facilitator: I/O error: " + e.text + ".");
+ if (s_t.connected)
+ s_t.close();
+ });
+ s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+ puts("Facilitator: security error: " + e.text + ".");
+ if (s_t.connected)
+ s_t.close();
+ });
+
+ puts("Facilitator: connecting to " + fac_addr.host + ":" + fac_addr.port + ".");
+ s_f.connect(fac_addr.host, fac_addr.port);
+ }
+
+ private function fac_connected(e:Event):void
+ {
+ puts("Facilitator: connected.");
+
+ s_f.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+ var client_spec:String;
+ var client_addr:Object;
+
+ client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
+ puts("Facilitator: got \"" + client_spec + "\"");
+
+ /*client_addr = parse_addr_spec(client_spec);
+ if (!client_addr) {
+ puts("Error: Client spec must be in the form \"host:port\".");
+ return;
+ }
+ if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
+ puts("Error: Facilitator has no clients.");
+ return;
+ }*/
+
+ /* Now we have a client, so start up a connection to the Cirrus rendezvous point */
+ cirrus = new NetConnection();
+ cirrus.addEventListener(NetStatusEvent.NET_STATUS, net_status_event_handler);
+ cirrus.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+ if (s_t.connected) s_t.close();
+ if (s_f.connected) s_f.close();
+ });
+
+ cirrus.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityError):void {
+ if (s_t.connected) s_t.close();
+ if (s_f.connected) s_f.close();
+ });
+
+ cirrus.connect(CIRRUS_ADDRESS);
+ });
+
+ s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
+ }
+
+ private function net_status_event_handler(e:NetStatusEvent):void
+ {
+ switch (e.info.code) {
+ case "NetConnection.Connect.Success" :
+ // Cirrus is now connected
+ cirrus_connected(e);
+ }
+
+ }
+
+ private function cirrus_connected(e:Event):void
+ {
+ if (client_id == null) {
+ puts("Error: Client ID doesn't exist.");
+ return;
+ } else if (client_id == cirrus.nearID) {
+ puts("Error: Client ID is our ID.");
+ return;
+ }
+
+ send_stream = new NetStream(cirrus, NetStream.DIRECT_CONNECTIONS);
+ send_stream.addEventListener(NetStatusEvent.NET_STATUS, net_status_event_handler);
+ send_stream.publish(DATA);
+
+ recv_stream = new NetStream(cirrus, client_id);
+ recv_stream.addEventListener(NetStatusEvent.NET_STATUS, net_status_event_handler);
+ recv_stream.play(DATA);
+ }
+
+ /*private function client_connected(e:Event):void
+ {
+ puts("Client: connected.");
+
+ 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 + ".");
+ s_c.writeBytes(bytes);
+ });
+ s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+ var bytes:ByteArray = new ByteArray();
+ s_c.readBytes(bytes, 0, e.bytesLoaded);
+ puts("Client: read " + bytes.length + ".");
+ s_t.writeBytes(bytes);
+ });
+ }*/
+
+ private function close_connections():void
+ {
+ if (s_t.connected) s_t.close();
+ if (s_f.connected) s_f.close();
+ if (cirrus.connected) cirrus.close();
+ if (send_stream != null) send_stream.close();
+ if (recv_stream != null) recv_stream.close();
+ }
+
+ /* Parse an address in the form "host:port". Returns an Object with
+ keys "host" (String) and "port" (int). Returns null on error. */
+ private static function parse_addr_spec(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;
+ }
+ }
+}
diff --git a/RTMFPRelay.as b/RTMFPRelay.as
new file mode 100644
index 0000000..27b3208
--- /dev/null
+++ b/RTMFPRelay.as
@@ -0,0 +1,272 @@
+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.net.NetConnection;
+ import flash.net.NetStream;
+ import flash.utils.ByteArray;
+ import flash.utils.clearInterval;
+ import flash.utils.setInterval;
+ import flash.utils.setTimeout;
+
+ import Utils;
+
+ public class RTMFPRelay extends Sprite
+ {
+ private static const CIRRUS_ADDRESS:String = "rtmfp://p2p.rtmfp.net";
+ private static const CIRRUS_DEV_KEY:String = RTMFP::CIRRUS_KEY;
+
+ /* The name of the "media" to pass between peers */
+ public static const DATA:String = "data";
+
+ protected var output_text:TextField;
+
+ // Socket to Tor relay.
+ private var s_t:Socket;
+ // Socket to facilitator.
+ private var s_f:Socket;
+
+ /* Connection to the Cirrus rendezvous service */
+ private var cirrus_conn:NetConnection;
+
+ /* ID of the peer to connect to */
+ private var peer_id:String;
+
+ /* Data streams to be established with peer */
+ private var send_stream:NetStream;
+ private var recv_stream:NetStream;
+
+ private var fac_addr:Object;
+ private var tor_addr:Object;
+
+ private function puts(s:String):void
+ {
+ output_text.appendText(s + "\n");
+ output_text.scrollV = output_text.maxScrollV;
+ }
+
+ public function RTMFPRelay()
+ {
+ 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.");
+
+ cirrus_conn = new NetConnection();
+ cirrus_conn.addEventListener(NetStatusEvent.NET_STATUS, function (e:NetStatusEvent):void {
+ puts("Cirrus: connected.");
+ });
+ cirrus_conn.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+ puts("Cirrus: I/O error: " + e.text + ".");
+ });
+
+ cirrus_conn.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityError):void {
+ puts("Cirrus: security error: " + e.text + ".");
+ });
+
+ cirrus_conn.connect(CIRRUS_ADDRESS + "/" + CIRRUS_DEV_KEY);
+
+
+ // Wait until the query string parameters are loaded.
+ this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
+ }
+
+ public function data_is(data:ByteArray):void
+ {
+
+
+ }
+
+ public function facilitator_is(host:String, port:String):void
+ {
+ if (s_f != null && s_f.connected) {
+
+ return;
+ }
+
+ s_f = new Socket();
+
+ s_f.addEventListener(Event.CONNECT, function (e:Event):void {
+ puts("Facilitator: connected.");
+ });
+ s_f.addEventListener(Event.CLOSE, function (e:Event):void {
+ puts("Facilitator: closed connection.");
+ });
+ s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+ puts("Facilitator: I/O error: " + e.text + ".");
+ });
+ s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+ puts("Facilitator: security error: " + e.text + ".");
+ });
+
+ puts("Facilitator: connecting to " + host + ":" + port + ".");
+ s_f.connect(host, port);
+ }
+
+ public function peer(peer_id:String):String
+ {
+ return this.peer_id;
+ }
+
+ public function peer_is(peer_id:String):void
+ {
+ if (peer_id == null) {
+ puts("Error: Client ID doesn't exist.");
+ return;
+ } else if (peer_id == cirrus_conn.nearID) {
+ puts("Error: Client ID is our ID.");
+ return;
+ } else if (this.peer_id == peer_id) {
+
+ } else if (this.recv_stream != null) {
+ puts("Error: already set up with a peer!");
+ return;
+ }
+
+ this.peer_id = peer_id;
+
+ send_stream = new NetStream(cirrus_conn, NetStream.DIRECT_CONNECTIONS);
+ send_stream.addEventListener(NetStatusEvent.NET_STATUS, net_status_event_handler);
+ send_stream.publish(DATA);
+
+ recv_stream = new NetStream(cirrus_conn, peer_id);
+ recv_stream.addEventListener(NetStatusEvent.NET_STATUS, net_status_event_handler);
+ recv_stream.play(DATA);
+ }
+
+ public function tor_relay_is(host:String, port:String):void
+ {
+ if (s_t != null && s_t.connected) {
+ puts("Error: already connected to Tor relay!");
+ return;
+ }
+
+ s_t = new Socket();
+
+ s_t.addEventListener(Event.CONNECT, function (e:Event):void {
+ puts("Tor: connected.");
+ });
+ s_t.addEventListener(Event.CLOSE, function (e:Event):void {
+ puts("Tor: closed.");
+ });
+ s_t.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
+ puts("Tor: I/O error: " + e.text + ".");
+ });
+ s_t.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
+ puts("Tor: security error: " + e.text + ".");
+ });
+
+ puts("Tor: connecting to " + host + ":" + port + ".");
+ s_t.connect(host, port);
+ }
+
+ private function loaderinfo_complete(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 = parse_addr_spec(fac_spec);
+ if (!fac_addr) {
+ puts("Error: Facilitator spec must be in the form \"host:port\".");
+ return;
+ }
+
+ tor_addr = DEFAULT_TOR_ADDR;
+
+ go();
+ }
+
+ private function fac_connected(e:Event):void
+ {
+
+
+ s_f.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+ var client_spec:String;
+ var client_addr:Object;
+
+ client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
+ puts("Facilitator: got \"" + client_spec + "\"");
+
+ /*client_addr = parse_addr_spec(client_spec);
+ if (!client_addr) {
+ puts("Error: Client spec must be in the form \"host:port\".");
+ return;
+ }
+ if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
+ puts("Error: Facilitator has no clients.");
+ return;
+ }*/
+
+ /* Now we have a client, so start up a connection to the Cirrus rendezvous point */
+
+ });
+
+ s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
+ }
+
+ private function net_status_event_handler(e:NetStatusEvent):void
+ {
+ switch (e.info.code) {
+ case "NetConnection.Connect.Success" :
+ // Cirrus is now connected
+ cirrus_connected(e);
+ }
+
+ }
+
+ private function cirrus_connected(e:Event):void
+ {
+
+
+
+ }
+
+ /*private function client_connected(e:Event):void
+ {
+ puts("Client: connected.");
+
+ 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 + ".");
+ s_c.writeBytes(bytes);
+ });
+ s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
+ var bytes:ByteArray = new ByteArray();
+ s_c.readBytes(bytes, 0, e.bytesLoaded);
+ puts("Client: read " + bytes.length + ".");
+ s_t.writeBytes(bytes);
+ });
+ }*/
+
+ private function close_connections():void
+ {
+ if (s_t.connected) s_t.close();
+ if (s_f.connected) s_f.close();
+ if (cirrus.connected) cirrus.close();
+ if (send_stream != null) send_stream.close();
+ if (recv_stream != null) recv_stream.close();
+ }
+
+
+ }
+}
1
0
commit 5570e265c210b44c539707aae7398f75630d5388
Author: David Fifield <david(a)bamsoftware.com>
Date: Tue May 24 15:58:28 2011 -0700
Switch back to the public relay.
---
com/rtmfpcat/rtmfpcat.as | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/com/rtmfpcat/rtmfpcat.as b/com/rtmfpcat/rtmfpcat.as
index c3d3fca..328274a 100644
--- a/com/rtmfpcat/rtmfpcat.as
+++ b/com/rtmfpcat/rtmfpcat.as
@@ -29,10 +29,10 @@ package
port: 3333
};
- /* David's bridge (nickname eRYaZuvY02FpExln) that also serves a
+ /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
crossdomain policy. */
private const DEFAULT_TOR_PROXY_ADDR:Object = {
- host: "69.164.193.231",
+ host: "173.255.221.44",
port: 9001
};
1
0
commit c4093a2b4ab730ddc53163009c0f409dcee41dab
Author: ellitron <jdellit(a)stanford.edu>
Date: Tue May 24 19:51:41 2011 -0700
Replaced tabs with spaces
---
com/rtmfpcat/rtmfp/RTMFPSocket.as | 231 +++++++++++++++++++------------------
1 files changed, 116 insertions(+), 115 deletions(-)
diff --git a/com/rtmfpcat/rtmfp/RTMFPSocket.as b/com/rtmfpcat/rtmfp/RTMFPSocket.as
index bb00994..2d634f0 100644
--- a/com/rtmfpcat/rtmfp/RTMFPSocket.as
+++ b/com/rtmfpcat/rtmfp/RTMFPSocket.as
@@ -27,205 +27,206 @@ package rtmfp
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")]
+ [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;
-
+ 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;
+
+ /* 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);
+ 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 close():void
{
- connection.close();
+ connection.close();
}
public function get id():String
{
- if (connection != null && connection.connected) {
- return connection.nearID;
- }
+ if (connection != null && connection.connected) {
+ return connection.nearID;
+ }
- return null;
+ return null;
}
public function get connected():Boolean
{
- return (connection != null && connection.connected);
+ return (connection != null && connection.connected);
}
public function readBytes(bytes:ByteArray):void
{
- recvStream.client.bytes.readBytes(bytes);
+ recvStream.client.bytes.readBytes(bytes);
}
public function writeBytes(bytes:ByteArray):void
{
- sendStream.send("dataAvailable", bytes);
+ sendStream.send("dataAvailable", bytes);
}
public function get peer():String
{
- return this.peerID;
+ 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.");
- }
+ 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;
+ 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);
+ 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);
+ 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));
+ clearInterval(connectionTimeout);
+ dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.CONNECT_FAIL));
}
private function onDataAvailable(event:ProgressEvent):void
{
- dispatchEvent(event);
+ dispatchEvent(event);
}
private function onIOErrorEvent(event:IOErrorEvent):void
{
- fail();
+ 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;
- }
+ 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;
- }
+ // 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;
+ // disallow additional peers connecting to us
+ if (peer.farID != peerID) return false;
- peer.send("setPeerConnectAcknowledged");
- dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_CONNECTED, peer));
+ peer.send("setPeerConnectAcknowledged");
+ dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEER_CONNECTED, peer));
- return true;
+ return true;
}
private function onPeerConnectAcknowledged(event:Event):void
{
- dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_SUCCESS, recvStream));
+ 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));
- }
+ if (!recvStream.client) return;
+ if (!RTMFPSocketClient(recvStream.client).peerConnectAcknowledged) {
+ dispatchEvent(new RTMFPSocketEvent(RTMFPSocketEvent.PEERING_FAIL, recvStream));
+ }
}
private function onSecurityErrorEvent(event:SecurityErrorEvent):void
{
- fail();
+ 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;
- }
- }
+ 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;
+ }
+ }
}
}
1
0
commit f46a9e3f6844ca996d15bb4434c18ac127ac0a71
Author: David Fifield <david(a)bamsoftware.com>
Date: Tue May 24 15:58:16 2011 -0700
Add an RTMFPSocket.close method.
---
com/rtmfpcat/rtmfp/RTMFPSocket.as | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/com/rtmfpcat/rtmfp/RTMFPSocket.as b/com/rtmfpcat/rtmfp/RTMFPSocket.as
index 6c61781..bb00994 100644
--- a/com/rtmfpcat/rtmfp/RTMFPSocket.as
+++ b/com/rtmfpcat/rtmfp/RTMFPSocket.as
@@ -67,6 +67,11 @@ package rtmfp
connectionTimeout = setInterval(fail, DEFAULT_CONNECT_TIMEOUT);
}
+ public function close():void
+ {
+ connection.close();
+ }
+
public function get id():String
{
if (connection != null && connection.connected) {
1
0
12 Jun '11
commit af0580268ef996be857eb8300aecf1848894b724
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 11 18:53:02 2011 -0700
Moving menu generation into a dedicated file
---
src/cli/menu/__init__.py | 2 +-
src/cli/menu/actions.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++
src/cli/menu/menu.py | 69 +------------------------------------------
3 files changed, 76 insertions(+), 68 deletions(-)
diff --git a/src/cli/menu/__init__.py b/src/cli/menu/__init__.py
index e6b5f10..f6d43ec 100644
--- a/src/cli/menu/__init__.py
+++ b/src/cli/menu/__init__.py
@@ -2,5 +2,5 @@
Resources for displaying the menu.
"""
-__all__ = ["item", "menu"]
+__all__ = ["actions", "item", "menu"]
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
new file mode 100644
index 0000000..f6f88ba
--- /dev/null
+++ b/src/cli/menu/actions.py
@@ -0,0 +1,73 @@
+"""
+Generates the menu for arm, binding options with their related actions.
+"""
+
+import cli.menu.item
+
+def makeMenu():
+ """
+ Constructs the base menu and all of its contents.
+ """
+
+ baseMenu = cli.menu.item.Submenu("")
+
+ fileMenu = cli.menu.item.Submenu("File")
+ fileMenu.add(cli.menu.item.MenuItem("Exit", None))
+ baseMenu.add(fileMenu)
+
+ logsMenu = cli.menu.item.Submenu("Logs")
+ logsMenu.add(cli.menu.item.MenuItem("Events", None))
+ logsMenu.add(cli.menu.item.MenuItem("Clear", None))
+ logsMenu.add(cli.menu.item.MenuItem("Save", None))
+ logsMenu.add(cli.menu.item.MenuItem("Filter", None))
+
+ duplicatesSubmenu = cli.menu.item.Submenu("Duplicates")
+ duplicatesSubmenu.add(cli.menu.item.MenuItem("Hidden", None))
+ duplicatesSubmenu.add(cli.menu.item.MenuItem("Visible", None))
+ logsMenu.add(duplicatesSubmenu)
+ baseMenu.add(logsMenu)
+
+ viewMenu = cli.menu.item.Submenu("View")
+ viewMenu.add(cli.menu.item.MenuItem("Graph", None))
+ viewMenu.add(cli.menu.item.MenuItem("Connections", None))
+ viewMenu.add(cli.menu.item.MenuItem("Configuration", None))
+ viewMenu.add(cli.menu.item.MenuItem("Configuration File", None))
+ baseMenu.add(viewMenu)
+
+ graphMenu = cli.menu.item.Submenu("Graph")
+ graphMenu.add(cli.menu.item.MenuItem("Stats", None))
+
+ sizeSubmenu = cli.menu.item.Submenu("Size")
+ sizeSubmenu.add(cli.menu.item.MenuItem("Increase", None))
+ sizeSubmenu.add(cli.menu.item.MenuItem("Decrease", None))
+ graphMenu.add(sizeSubmenu)
+
+ graphMenu.add(cli.menu.item.MenuItem("Update Interval", None))
+
+ boundsSubmenu = cli.menu.item.Submenu("Bounds")
+ boundsSubmenu.add(cli.menu.item.MenuItem("Local Max", None))
+ boundsSubmenu.add(cli.menu.item.MenuItem("Global Max", None))
+ boundsSubmenu.add(cli.menu.item.MenuItem("Tight", None))
+ graphMenu.add(boundsSubmenu)
+ baseMenu.add(graphMenu)
+
+ connectionsMenu = cli.menu.item.Submenu("Connections")
+ connectionsMenu.add(cli.menu.item.MenuItem("Identity", None))
+ connectionsMenu.add(cli.menu.item.MenuItem("Resolver", None))
+ connectionsMenu.add(cli.menu.item.MenuItem("Sort Order", None))
+ baseMenu.add(connectionsMenu)
+
+ configurationMenu = cli.menu.item.Submenu("Configuration")
+
+ commentsSubmenu = cli.menu.item.Submenu("Comments")
+ commentsSubmenu.add(cli.menu.item.MenuItem("Hidden", None))
+ commentsSubmenu.add(cli.menu.item.MenuItem("Visible", None))
+ configurationMenu.add(commentsSubmenu)
+
+ configurationMenu.add(cli.menu.item.MenuItem("Reload", None))
+ configurationMenu.add(cli.menu.item.MenuItem("Reset Tor", None))
+ baseMenu.add(configurationMenu)
+
+ return baseMenu
+
+
diff --git a/src/cli/menu/menu.py b/src/cli/menu/menu.py
index f83b76d..e0728e8 100644
--- a/src/cli/menu/menu.py
+++ b/src/cli/menu/menu.py
@@ -4,75 +4,10 @@ import curses
import cli.popups
import cli.controller
import cli.menu.item
+import cli.menu.actions
from util import uiTools
-def makeMenu():
- """
- Constructs the base menu and all of its contents.
- """
-
- baseMenu = cli.menu.item.Submenu("")
-
- fileMenu = cli.menu.item.Submenu("File")
- fileMenu.add(cli.menu.item.MenuItem("Exit", None))
- baseMenu.add(fileMenu)
-
- logsMenu = cli.menu.item.Submenu("Logs")
- logsMenu.add(cli.menu.item.MenuItem("Events", None))
- logsMenu.add(cli.menu.item.MenuItem("Clear", None))
- logsMenu.add(cli.menu.item.MenuItem("Save", None))
- logsMenu.add(cli.menu.item.MenuItem("Filter", None))
-
- duplicatesSubmenu = cli.menu.item.Submenu("Duplicates")
- duplicatesSubmenu.add(cli.menu.item.MenuItem("Hidden", None))
- duplicatesSubmenu.add(cli.menu.item.MenuItem("Visible", None))
- logsMenu.add(duplicatesSubmenu)
- baseMenu.add(logsMenu)
-
- viewMenu = cli.menu.item.Submenu("View")
- viewMenu.add(cli.menu.item.MenuItem("Graph", None))
- viewMenu.add(cli.menu.item.MenuItem("Connections", None))
- viewMenu.add(cli.menu.item.MenuItem("Configuration", None))
- viewMenu.add(cli.menu.item.MenuItem("Configuration File", None))
- baseMenu.add(viewMenu)
-
- graphMenu = cli.menu.item.Submenu("Graph")
- graphMenu.add(cli.menu.item.MenuItem("Stats", None))
-
- sizeSubmenu = cli.menu.item.Submenu("Size")
- sizeSubmenu.add(cli.menu.item.MenuItem("Increase", None))
- sizeSubmenu.add(cli.menu.item.MenuItem("Decrease", None))
- graphMenu.add(sizeSubmenu)
-
- graphMenu.add(cli.menu.item.MenuItem("Update Interval", None))
-
- boundsSubmenu = cli.menu.item.Submenu("Bounds")
- boundsSubmenu.add(cli.menu.item.MenuItem("Local Max", None))
- boundsSubmenu.add(cli.menu.item.MenuItem("Global Max", None))
- boundsSubmenu.add(cli.menu.item.MenuItem("Tight", None))
- graphMenu.add(boundsSubmenu)
- baseMenu.add(graphMenu)
-
- connectionsMenu = cli.menu.item.Submenu("Connections")
- connectionsMenu.add(cli.menu.item.MenuItem("Identity", None))
- connectionsMenu.add(cli.menu.item.MenuItem("Resolver", None))
- connectionsMenu.add(cli.menu.item.MenuItem("Sort Order", None))
- baseMenu.add(connectionsMenu)
-
- configurationMenu = cli.menu.item.Submenu("Configuration")
-
- commentsSubmenu = cli.menu.item.Submenu("Comments")
- commentsSubmenu.add(cli.menu.item.MenuItem("Hidden", None))
- commentsSubmenu.add(cli.menu.item.MenuItem("Visible", None))
- configurationMenu.add(commentsSubmenu)
-
- configurationMenu.add(cli.menu.item.MenuItem("Reload", None))
- configurationMenu.add(cli.menu.item.MenuItem("Reset Tor", None))
- baseMenu.add(configurationMenu)
-
- return baseMenu
-
class MenuCursor:
"""
Tracks selection and key handling in the menu.
@@ -139,7 +74,7 @@ def showMenu():
try:
# generates the menu and uses the initial selection of the first item in
# the file menu
- menu = makeMenu()
+ menu = cli.menu.actions.makeMenu()
cursor = MenuCursor(menu.getChildren()[0].getChildren()[0])
while not cursor.isDone():
1
0
commit 3cc2ecf82d693a782c11d5a4f6fb41e94e7b5d05
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 11 20:55:34 2011 -0700
Binding handlers for the view submenu
Submenu consisting of page and color selection.
---
src/cli/configPanel.py | 2 +-
src/cli/connections/connPanel.py | 2 +-
src/cli/controller.py | 48 +++++++++++++++++++++++++++----------
src/cli/descriptorPopup.py | 2 +-
src/cli/menu/actions.py | 44 ++++++++++++++++++++++++++++------
src/cli/menu/item.py | 46 ++++++++++++++++++++++++++++++++++++
src/cli/popups.py | 2 +-
src/cli/torrcPanel.py | 2 +-
src/util/uiTools.py | 36 ++++++++++++++++++++++++++--
9 files changed, 155 insertions(+), 29 deletions(-)
diff --git a/src/cli/configPanel.py b/src/cli/configPanel.py
index c44d295..c2e290d 100644
--- a/src/cli/configPanel.py
+++ b/src/cli/configPanel.py
@@ -172,7 +172,7 @@ class ConfigPanel(panel.Panel):
"""
def __init__(self, stdscr, configType, config=None):
- panel.Panel.__init__(self, stdscr, "configState", 0)
+ panel.Panel.__init__(self, stdscr, "configuration", 0)
self.sortOrdering = DEFAULT_SORT_ORDER
self._config = dict(DEFAULT_CONFIG)
diff --git a/src/cli/connections/connPanel.py b/src/cli/connections/connPanel.py
index 45750c3..8b86c65 100644
--- a/src/cli/connections/connPanel.py
+++ b/src/cli/connections/connPanel.py
@@ -31,7 +31,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
"""
def __init__(self, stdscr, config=None):
- panel.Panel.__init__(self, stdscr, "conn", 0)
+ panel.Panel.__init__(self, stdscr, "connections", 0)
threading.Thread.__init__(self)
self.setDaemon(True)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index faf9248..cf17da3 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -160,6 +160,14 @@ class Controller:
return self._screen
+ def getPageCount(self):
+ """
+ Provides the number of pages the interface has. This may be zero if all
+ page panels have been disabled.
+ """
+
+ return len(self._pagePanels)
+
def getPage(self):
"""
Provides the number belonging to this page. Page numbers start at zero.
@@ -167,23 +175,35 @@ class Controller:
return self._page
+ def setPage(self, pageNumber):
+ """
+ Sets the selected page, raising a ValueError if the page number is invalid.
+
+ Arguments:
+ pageNumber - page number to be selected
+ """
+
+ if pageNumber < 0 or pageNumber >= self.getPageCount():
+ raise ValueError("Invalid page number: %i" % pageNumber)
+
+ if pageNumber != self._page:
+ self._page = pageNumber
+ self._forceRedraw = True
+ self.setMsg()
+
def nextPage(self):
"""
Increments the page number.
"""
- self._page = (self._page + 1) % len(self._pagePanels)
- self._forceRedraw = True
- self.setMsg()
+ self.setPage((self._page + 1) % len(self._pagePanels))
def prevPage(self):
"""
Decrements the page number.
"""
- self._page = (self._page - 1) % len(self._pagePanels)
- self._forceRedraw = True
- self.setMsg()
+ self.setPage((self._page - 1) % len(self._pagePanels))
def isPaused(self):
"""
@@ -227,20 +247,22 @@ class Controller:
return list(self._stickyPanels)
- def getDisplayPanels(self, includeSticky = True):
+ def getDisplayPanels(self, pageNumber = None, includeSticky = True):
"""
- Provides all panels belonging to the current page and sticky content above
- it. This is ordered they way they are presented (top to bottom) on the
- page.
+ Provides all panels belonging to a page and sticky content above it. This
+ is ordered they way they are presented (top to bottom) on the page.
Arguments:
+ pageNumber - page number of the panels to be returned, the current
+ page if None
includeSticky - includes sticky panels in the results if true
"""
+ returnPage = self._page if pageNumber == None else pageNumber
+
if includeSticky:
- return self._stickyPanels + self._pagePanels[self._page]
- else:
- return list(self._pagePanels[self._page])
+ return self._stickyPanels + self._pagePanels[returnPage]
+ else: return list(self._pagePanels[returnPage])
def getDaemonPanels(self):
"""
diff --git a/src/cli/descriptorPopup.py b/src/cli/descriptorPopup.py
index f75d7e6..5b9f646 100644
--- a/src/cli/descriptorPopup.py
+++ b/src/cli/descriptorPopup.py
@@ -105,7 +105,7 @@ def showDescriptorPopup(connectionPanel):
# hides the title of the first panel on the page
control = controller.getController()
- topPanel = control.getDisplayPanels(False)[0]
+ topPanel = control.getDisplayPanels(includeSticky = False)[0]
topPanel.setTitleVisible(False)
topPanel.redraw(True)
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index 751751f..f81a162 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -7,7 +7,7 @@ import functools
import cli.controller
import cli.menu.item
-from util import torTools
+from util import torTools, uiTools
def makeMenu():
"""
@@ -16,6 +16,7 @@ def makeMenu():
baseMenu = cli.menu.item.Submenu("")
baseMenu.add(makeActionsMenu())
+ baseMenu.add(makeViewMenu())
logsMenu = cli.menu.item.Submenu("Logs")
logsMenu.add(cli.menu.item.MenuItem("Events", None))
@@ -29,13 +30,6 @@ def makeMenu():
logsMenu.add(duplicatesSubmenu)
baseMenu.add(logsMenu)
- viewMenu = cli.menu.item.Submenu("View")
- viewMenu.add(cli.menu.item.MenuItem("Graph", None))
- viewMenu.add(cli.menu.item.MenuItem("Connections", None))
- viewMenu.add(cli.menu.item.MenuItem("Configuration", None))
- viewMenu.add(cli.menu.item.MenuItem("Configuration File", None))
- baseMenu.add(viewMenu)
-
graphMenu = cli.menu.item.Submenu("Graph")
graphMenu.add(cli.menu.item.MenuItem("Stats", None))
@@ -93,3 +87,37 @@ def makeActionsMenu():
actionsMenu.add(cli.menu.item.MenuItem("Exit", control.quit))
return actionsMenu
+def makeViewMenu():
+ """
+ Submenu consisting of...
+ [X] <Page 1>
+ [ ] <Page 2>
+ [ ] etc...
+ Color (Submenu)
+ """
+
+ viewMenu = cli.menu.item.Submenu("View")
+ control = cli.controller.getController()
+
+ if control.getPageCount() > 0:
+ pageGroup = cli.menu.item.SelectionGroup(control.setPage, control.getPage())
+
+ for i in range(control.getPageCount()):
+ pagePanels = control.getDisplayPanels(pageNumber = i, includeSticky = False)
+ label = " / ".join([uiTools.camelCase(panel.getName()) for panel in pagePanels])
+
+ viewMenu.add(cli.menu.item.SelectionMenuItem(label, pageGroup, i))
+
+ if uiTools.isColorSupported():
+ colorMenu = cli.menu.item.Submenu("Color")
+ colorGroup = cli.menu.item.SelectionGroup(uiTools.setColorOverride, uiTools.getColorOverride())
+
+ colorMenu.add(cli.menu.item.SelectionMenuItem("All", colorGroup, None))
+
+ for color in uiTools.COLOR_LIST:
+ colorMenu.add(cli.menu.item.SelectionMenuItem(uiTools.camelCase(color), colorGroup, color))
+
+ viewMenu.add(colorMenu)
+
+ return viewMenu
+
diff --git a/src/cli/menu/item.py b/src/cli/menu/item.py
index 8c5d314..beaac9c 100644
--- a/src/cli/menu/item.py
+++ b/src/cli/menu/item.py
@@ -147,3 +147,49 @@ class Submenu(MenuItem):
def select(self):
return False
+class SelectionGroup():
+ """
+ Radio button groups that SelectionMenuItems can belong to.
+ """
+
+ def __init__(self, action, selectedArg):
+ self.action = action
+ self.selectedArg = selectedArg
+
+class SelectionMenuItem(MenuItem):
+ """
+ Menu item with an associated group which determines the selection. This is
+ for the common single argument getter/setter pattern.
+ """
+
+ def __init__(self, label, group, arg):
+ MenuItem.__init__(self, label, None)
+ self._group = group
+ self._arg = arg
+
+ def isSelected(self):
+ """
+ True if we're the selected item, false otherwise.
+ """
+
+ return self._arg == self._group.selectedArg
+
+ def getLabel(self):
+ """
+ Provides our label with a "[X]" prefix if selected and "[ ]" if not.
+ """
+
+ myLabel = MenuItem.getLabel(self)[1]
+ myPrefix = "[X] " if self.isSelected() else "[ ] "
+ return (myPrefix, myLabel, "")
+
+ def select(self):
+ """
+ Performs the group's setter action with our argument.
+ """
+
+ if not self.isSelected():
+ self._group.action(self._arg)
+
+ return True
+
diff --git a/src/cli/popups.py b/src/cli/popups.py
index 8061c8f..5f1eac2 100644
--- a/src/cli/popups.py
+++ b/src/cli/popups.py
@@ -280,7 +280,7 @@ def showMenu(title, options, oldSelection):
try:
# hides the title of the first panel on the page
control = cli.controller.getController()
- topPanel = control.getDisplayPanels(False)[0]
+ topPanel = control.getDisplayPanels(includeSticky = False)[0]
topPanel.setTitleVisible(False)
topPanel.redraw(True)
diff --git a/src/cli/torrcPanel.py b/src/cli/torrcPanel.py
index e14a16a..a12cc87 100644
--- a/src/cli/torrcPanel.py
+++ b/src/cli/torrcPanel.py
@@ -24,7 +24,7 @@ class TorrcPanel(panel.Panel):
"""
def __init__(self, stdscr, configType, config=None):
- panel.Panel.__init__(self, stdscr, "configFile", 0)
+ panel.Panel.__init__(self, stdscr, "torrc", 0)
self._config = dict(DEFAULT_CONFIG)
if config:
diff --git a/src/util/uiTools.py b/src/util/uiTools.py
index 5999c64..34d9210 100644
--- a/src/util/uiTools.py
+++ b/src/util/uiTools.py
@@ -18,6 +18,9 @@ COLOR_LIST = {"red": curses.COLOR_RED, "green": curses.COLOR_GREEN,
"cyan": curses.COLOR_CYAN, "magenta": curses.COLOR_MAGENTA,
"black": curses.COLOR_BLACK, "white": curses.COLOR_WHITE}
+# boolean for if we have color support enabled, None not yet determined
+COLOR_IS_SUPPORTED = None
+
# mappings for getColor() - this uses the default terminal color scheme if
# color support is unavailable
COLOR_ATTR_INITIALIZED = False
@@ -138,6 +141,14 @@ def getPrintable(line, keepNewlines = True):
line = "".join([char for char in line if (isprint(char) or (keepNewlines and char == "\n"))])
return line
+def isColorSupported():
+ """
+ True if the display supports showing color, false otherwise.
+ """
+
+ if COLOR_IS_SUPPORTED == None: _initColors()
+ return COLOR_IS_SUPPORTED
+
def getColor(color):
"""
Provides attribute corresponding to a given text color. Supported colors
@@ -261,6 +272,24 @@ def cropStr(msg, size, minWordLen = 4, minCrop = 0, endType = Ending.ELLIPSE, ge
if getRemainder: return (returnMsg, remainder)
else: return returnMsg
+def camelCase(label):
+ """
+ Converts the given string to camel case, ie:
+ >>> camelCase("I_LIKE_PEPPERJACK!")
+ 'I Like Pepperjack!'
+
+ Arguments:
+ label - input string to be converted
+ """
+
+ words = []
+ for entry in label.split("_"):
+ if len(entry) == 0: words.append("")
+ elif len(entry) == 1: words.append(entry.upper())
+ else: words.append(entry[0].upper() + entry[1:].lower())
+
+ return " ".join(words)
+
def drawBox(panel, top, left, width, height, attr=curses.A_NORMAL):
"""
Draws a box in the panel with the given bounds.
@@ -719,16 +748,17 @@ def _initColors():
calling curses.initscr().
"""
- global COLOR_ATTR_INITIALIZED
+ global COLOR_ATTR_INITIALIZED, COLOR_IS_SUPPORTED
if not COLOR_ATTR_INITIALIZED:
COLOR_ATTR_INITIALIZED = True
+ COLOR_IS_SUPPORTED = False
if not CONFIG["features.colorInterface"]: return
- try: hasColorSupport = curses.has_colors()
+ try: COLOR_IS_SUPPORTED = curses.has_colors()
except curses.error: return # initscr hasn't been called yet
# initializes color mappings if color support is available
- if hasColorSupport:
+ if COLOR_IS_SUPPORTED:
colorpair = 0
log.log(CONFIG["log.cursesColorSupport"], "Terminal color support detected and enabled")
1
0