from twisted.web import resource, server, static
from twisted.names import client
from twisted.internet import reactor
-import traceback
-import simplejson, md5, sys, os, ircclient, time, config, weakref
+from authgateengine import login_optional
+import simplejson, md5, sys, os, ircclient, time, config, weakref, traceback
Sessions = {}
def get_session_id():
- return md5.md5(os.urandom(16)).hexdigest()[:10]
+ return md5.md5(os.urandom(16)).hexdigest()
class BufferOverflowException(Exception):
pass
+class AJAXException(Exception):
+ pass
+
+class IDGenerationException(Exception):
+ pass
+
+NOT_DONE_YET = None
+
def jsondump(fn):
def decorator(*args, **kwargs):
- x = fn(*args, **kwargs)
- if isinstance(x, list):
- return simplejson.dumps(x)
- return x
+ try:
+ x = fn(*args, **kwargs)
+ if x is None:
+ return server.NOT_DONE_YET
+ x = (True, x)
+ except AJAXException, e:
+ x = (False, e[0])
+
+ return simplejson.dumps(x)
return decorator
def cleanupSession(id):
def subscribe(self, channel):
if len(self.subscriptions) >= config.MAXSUBSCRIPTIONS:
- self.subscriptions.pop(0)
+ self.subscriptions.pop(0).close()
self.subscriptions.append(channel)
self.flush()
self.request.finish()
return False
+ def close(self):
+ self.request.finish()
+
class MultipleUseChannel(Channel):
def write(self, data):
self.request.write(data)
@jsondump
def render_POST(self, request):
path = request.path[len(self.prefix):]
- if path == "/n":
- ip = request.transport.getPeer()
- ip = ip[1]
-
- nick, ident = request.args.get("nick"), "webchat"
- if not nick:
- return [False, "Nickname not supplied"]
-
- nick = nick[0]
-
- id = get_session_id()
-
- session = IRCSession(id)
+ if path[0] == "/":
+ handler = self.COMMANDS.get(path[1:])
+ if handler is not None:
+ return handler(self, request)
+ raise AJAXException("404")
+
+# def render_GET(self, request):
+# return self.render_POST(request)
+
+ def newConnection(self, request):
+ ticket = login_optional(request)
+
+ _, ip, port = request.transport.getPeer()
- client = ircclient.createIRC(session, nick=nick, ident=ident, ip=ip, realname=nick)
- session.client = client
+ nick, ident, realname = request.args.get("nick"), "webchat", config.REALNAME
+
+ if not ticket is None:
+ realname = "%s (%s:%d:%s)" % (realname, ticket.username, ticket.id, ticket.authflags)
- Sessions[id] = session
+ if not nick:
+ raise AJAXException("Nickname not supplied")
- return [True, id]
- return [False, "404"]
+ nick = nick[0]
- @jsondump
- def render_GET(self, request):
- path = request.path[len(self.prefix):]
- if path.startswith("/s/"):
- sessionid = path[3:]
- session = Sessions.get(sessionid)
-
- if not session:
- return [False, "Bad session ID"]
+ for i in xrange(10):
+ id = get_session_id()
+ if not Sessions.get(id):
+ break
+ else:
+ raise IDGenerationException()
- session.subscribe(SingleUseChannel(request))
- return server.NOT_DONE_YET
- if path.startswith("/p/"):
- command = request.args.get("c")
- if not command:
- return [False, "No command specified"]
+ session = IRCSession(id)
- command = command[0]
+ client = ircclient.createIRC(session, nick=nick, ident=ident, ip=ip, realname=realname)
+ session.client = client
+
+ Sessions[id] = session
+
+ return id
+
+ def getSession(self, request):
+ sessionid = request.args.get("s")
+ if sessionid is None:
+ raise AJAXException("Bad session ID")
- sessionid = path[3:]
- session = Sessions.get(sessionid)
- if not session:
- return [False, "Bad session ID"]
-
- try:
- decoded = command.decode("utf-8")
- except UnicodeDecodeError:
- decoded = command.decode("iso-8859-1", "ignore")
-
- try:
- session.push(decoded)
- except AttributeError: # occurs when we haven't noticed an error
- session.disconnect()
- return [False, "Connection closed by server."]
- except Exception, e: # catch all
- session.disconnect()
- traceback.print_exc(file=sys.stderr)
- return [False, "Unknown error."]
+ session = Sessions.get(sessionid[0])
+ if not session:
+ raise AJAXException("Bad session ID")
+ return session
- return [True]
+ def subscribe(self, request):
+ self.getSession(request).subscribe(SingleUseChannel(request))
+ return NOT_DONE_YET
- return [False, "404"]
+ def push(self, request):
+ command = request.args.get("c")
+ if command is None:
+ raise AJAXException("No command specified")
+
+ command = command[0]
+
+ session = self.getSession(request)
+
+ try:
+ decoded = command.decode("utf-8")
+ except UnicodeDecodeError:
+ decoded = command.decode("iso-8859-1", "ignore")
+
+ if len(decoded) > config.MAXLINELEN:
+ session.disconnect()
+ raise AJAXException("Line too long")
+
+ try:
+ session.push(decoded)
+ except AttributeError: # occurs when we haven't noticed an error
+ session.disconnect()
+ raise AJAXException("Connection closed by server.")
+ except Exception, e: # catch all
+ session.disconnect()
+ traceback.print_exc(file=sys.stderr)
+ raise AJAXException("Unknown error.")
+
+ return True
+
+ COMMANDS = dict(p=push, n=newConnection, s=subscribe)
+
\ No newline at end of file