X-Git-Url: https://jfr.im/git/irc/quakenet/qwebirc.git/blobdiff_plain/265f5ce3c0cc94bcb1f1e2658bd78009555ccd65..fa1726fc432d8a95a36662aca71503187d698d20:/qwebirc/engines/ajaxengine.py diff --git a/qwebirc/engines/ajaxengine.py b/qwebirc/engines/ajaxengine.py index 2b67960..a7acb46 100644 --- a/qwebirc/engines/ajaxengine.py +++ b/qwebirc/engines/ajaxengine.py @@ -1,12 +1,12 @@ -from twisted.web import resource, server, static +from twisted.web import resource, server, static, error as http_error from twisted.names import client from twisted.internet import reactor, error from authgateengine import login_optional, getSessionData -import simplejson, md5, sys, os, time, config, weakref, traceback +import simplejson, md5, sys, os, time, config, qwebirc.config_options as config_options, traceback, socket import qwebirc.ircclient as ircclient from adminengine import AdminEngineAction from qwebirc.util import HitCounter - +import qwebirc.dns as qdns Sessions = {} def get_session_id(): @@ -21,6 +21,9 @@ class AJAXException(Exception): class IDGenerationException(Exception): pass +class PassthruException(Exception): + pass + NOT_DONE_YET = None def jsondump(fn): @@ -32,6 +35,8 @@ def jsondump(fn): x = (True, x) except AJAXException, e: x = (False, e[0]) + except PassthruException, e: + return str(e) return simplejson.dumps(x) return decorator @@ -47,6 +52,7 @@ class IRCSession: self.id = id self.subscriptions = [] self.buffer = [] + self.buflen = 0 self.throttle = 0 self.schedule = None self.closed = False @@ -101,7 +107,8 @@ class IRCSession: encdata = simplejson.dumps(self.buffer) self.buffer = [] - + self.buflen = 0 + newsubs = [] for x in self.subscriptions: if x.write(encdata): @@ -112,13 +119,14 @@ class IRCSession: cleanupSession(self.id) def event(self, data): - bufferlen = sum(map(len, self.buffer)) - if bufferlen + len(data) > config.MAXBUFLEN: + newbuflen = self.buflen + len(data) + if newbuflen > config.MAXBUFLEN: self.buffer = [] - self.client.error("Buffer overflow") + self.client.error("Buffer overflow.") return self.buffer.append(data) + self.buflen = newbuflen self.flush() def push(self, data): @@ -132,6 +140,10 @@ class IRCSession: reactor.callLater(5, cleanupSession, self.id) +# DANGER! Breach of encapsulation! +def connect_notice(line): + return "c", "NOTICE", "", ("AUTH", "*** (qwebirc) %s" % line) + class Channel: def __init__(self, request): self.request = request @@ -165,23 +177,23 @@ class AJAXEngine(resource.Resource): handler = self.COMMANDS.get(path[1:]) if handler is not None: return handler(self, request) - raise AJAXException("404") + + raise PassthruException, http_error.NoResource().render(request) -# def render_GET(self, request): -# return self.render_POST(request) - def newConnection(self, request): ticket = login_optional(request) _, ip, port = request.transport.getPeer() - nick, ident, realname = request.args.get("nick"), "webchat", config.REALNAME - + nick = request.args.get("nick") if not nick: - raise AJAXException("Nickname not supplied") - - nick = nick[0] + raise AJAXException, "Nickname not supplied." + nick = ircclient.irc_decode(nick[0]) + password = request.args.get("password") + if password is not None: + password = ircclient.irc_decode(password[0]) + for i in xrange(10): id = get_session_id() if not Sessions.get(id): @@ -195,59 +207,83 @@ class AJAXEngine(resource.Resource): if qticket is None: perform = None else: - perform = ["PRIVMSG %s :TICKETAUTH %s" % (config.QBOT, qticket)] + service_mask = config.AUTH_SERVICE + msg_mask = service_mask.split("!")[0] + "@" + service_mask.split("@", 1)[1] + perform = ["PRIVMSG %s :TICKETAUTH %s" % (msg_mask, qticket)] + + ident, realname = config.IDENT, config.REALNAME + if ident is config_options.IDENT_HEX or ident is None: # latter is legacy + ident = socket.inet_aton(ip).encode("hex") + elif ident is config_options.IDENT_NICKNAME: + ident = nick self.__connect_hit() - client = ircclient.createIRC(session, nick=nick, ident=ident, ip=ip, realname=realname, perform=perform) - session.client = client - + + def proceed(hostname): + kwargs = dict(nick=nick, ident=ident, ip=ip, realname=realname, perform=perform, hostname=hostname) + if password is not None: + kwargs["password"] = password + + client = ircclient.createIRC(session, **kwargs) + session.client = client + + if not hasattr(config, "WEBIRC_MODE") or config.WEBIRC_MODE == "hmac": + proceed(None) + elif config.WEBIRC_MODE != "hmac": + notice = lambda x: session.event(connect_notice(x)) + notice("Looking up your hostname...") + def callback(hostname): + notice("Found your hostname.") + proceed(hostname) + def errback(failure): + notice("Couldn't look up your hostname!") + proceed(ip) + qdns.lookupAndVerifyPTR(ip, timeout=[config.DNS_TIMEOUT]).addCallbacks(callback, errback) + Sessions[id] = session return id def getSession(self, request): + bad_session_message = "Invalid session, this most likely means the server has restarted; close this dialog and then try refreshing the page." + sessionid = request.args.get("s") if sessionid is None: - raise AJAXException("Bad session ID") + raise AJAXException, bad_session_message session = Sessions.get(sessionid[0]) if not session: - raise AJAXException("Bad session ID") + raise AJAXException, bad_session_message return session def subscribe(self, request): - request.channel._savedTimeOut = None # HACK + request.channel.cancelTimeout() self.getSession(request).subscribe(SingleUseChannel(request), request.notifyFinish()) return NOT_DONE_YET def push(self, request): command = request.args.get("c") if command is None: - raise AJAXException("No command specified") + raise AJAXException, "No command specified." self.__total_hit() - command = command[0] + decoded = ircclient.irc_decode(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") + 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.") + raise AJAXException, "Connection closed by server; try reconnecting by reloading the page." except Exception, e: # catch all session.disconnect() traceback.print_exc(file=sys.stderr) - raise AJAXException("Unknown error.") + raise AJAXException, "Unknown error." return True @@ -266,4 +302,4 @@ class AJAXEngine(resource.Resource): } COMMANDS = dict(p=push, n=newConnection, s=subscribe) - \ No newline at end of file +