]> jfr.im git - irc/quakenet/qwebirc.git/commitdiff
Add connection dialog.
authorChris Porter <redacted>
Thu, 10 Jul 2008 22:26:00 +0000 (23:26 +0100)
committerChris Porter <redacted>
Thu, 10 Jul 2008 22:26:00 +0000 (23:26 +0100)
Handle disconnects more gracefully in the JS and Python backend.
Move config example to correct location.
Default run script now hides tracebacks.

config.py.example [moved from qwebirc/config.py.example with 82% similarity]
js/irc/baseirc.js
js/irc/ircclient.js
js/irc/ircconnection.js
js/ui/theme.js
qwebirc/ajaxengine.py
qwebirc/ircclient.py
run.bat
run.sh

similarity index 82%
rename from qwebirc/config.py.example
rename to config.py.example
index 1b934045ff32aaeb1a441324afe3198946815a24..8532cc17eec4c26bb0bba549ddabf56e6834ae42 100644 (file)
@@ -2,3 +2,4 @@ IRCSERVER, IRCPORT = "moo.com", 6667
 HMACKEY = "mrmoo"
 HMACTEMPORAL = 30
 UPDATE_FREQ = 0.5
+MAXBUFSIZE = 10000
index 0912c6561bcb030f13df0b172e69295678c85c73..e53e3577fce2e8d6c18686bec8407be71c00848f 100644 (file)
@@ -29,14 +29,19 @@ var BaseIRCClient = new Class({
   
     this.send = this.connection.send.bind(this.connection);
     this.connect = this.connection.connect.bind(this.connection);
-    this.disconnect = this.connection.disconnect;
+    this.disconnect = this.connection.disconnect.bind(this.connection);
   },
   dispatch: function(data) {
     var message = data[0];
     if(message == "connect") {
       this.connected();
     } else if(message == "disconnect") {
-      this.disconnected();
+      if(data.length == 0) {
+        this.disconnected("No error!");
+      } else {
+        this.disconnected(data[1]);
+      }
+      this.disconnect();
     } else if(message == "c") {
       var command = data[1].toUpperCase();
        
index b63b83283adcf7eceba36b822b514d36a998e4af..328accdffd4e851f035bd8d93829628cef087722 100644 (file)
@@ -341,13 +341,13 @@ var IRCClient = new Class({
       }, this);
     }, this);
   },
-  disconnected: function() {
+  disconnected: function(message) {
     for(var x in this.parent.channels)
       this.ui.closeWindow(x);
 
     this.tracker = undefined;
     
-    this.newServerLine("DISCONNECT");
+    this.newServerLine("DISCONNECT", {"m": message});
   },
   supported: function(key, value) {
     if(key == "PREFIX") {
index 0d121e89f60996a50ed51af9ad956b3df213d704..fc88942a81b7bb2dbc5983e5ee9f9e7e66c82b9c 100644 (file)
@@ -12,33 +12,44 @@ var IRCConnection = new Class({
     this.disconnected = false;
   },
   send: function(data) {
+    if(this.disconnected)
+      return;
     var r = new Request.JSON({url: "/e/p/" + this.sessionid + "?c=" + encodeURIComponent(data) + "&t=" + this.counter++, onComplete: function(o) {
-      if(o[0] == false)
-        alert("An error occured: " + o[1]);
-    }});
+      if(!o || (o[0] == false)) {
+        if(!this.disconnected) {
+          this.disconnected = true;
+          alert("An error occured: " + o[1]);
+        }
+      }
+    }.bind(this)});
     
     r.get();
   },
-  x: function() {
-    this.fireEvent("recv", [[false, "moo"]]);
-  },
   recv: function() {
-    if(this.disconnected)
-      return;
-      
     var r = new Request.JSON({url: "/e/s/" + this.sessionid + "?t=" + this.counter++, onComplete: function(o) {
       if(o) {
         if(o[0] == false) {
-          alert("An error occured: " + o[1]);
+          if(!this.disconnected) {
+            this.disconnected = true;
+
+            alert("An error occured: " + o[1]);
+          }
           return;
         }
         o.each(function(x) {
           this.fireEvent("recv", [x]);
         }, this);
+      } else {
+        if(!this.disconnected) {
+          this.disconnected = true;
+
+          alert("An unknown error occured.");
+        }
+        return;
       }
       
       this.recv();
-    }.bind(this)});    
+    }.bind(this)});
     r.get();
   },
   connect: function() {
index 9e5ffc2f41b198606b4f5d37cfa3d8c621701d02..806a93e62d599784a78cbc9bad2d7b915c01e1e5 100644 (file)
@@ -11,7 +11,7 @@ var DefaultTheme = {
   "SIGNON": ["Signed on!", true],
   "CONNECT": ["Connected to server.", true],
   "RAW": ["$m", true],
-  "DISCONNECT": ["Disconnected from server.", true],
+  "DISCONNECT": ["Disconnected from server: $m", true],
   "ERROR": ["ERROR: $m", true],
   "SERVERNOTICE": ["$m", true],
   "JOIN": ["$n [$h] has joined $c", true],
index 3dc3da5eac8991ba3ad26d959be9abfbbec314a2..4f91466d5a5dbec599b301b89ff1cf4a13ed229a 100644 (file)
@@ -1,13 +1,17 @@
 from twisted.web import resource, server, static
 from twisted.names import client
 from twisted.internet import reactor
-import simplejson, md5, sys, os, ircclient, time, config
+import traceback
+import simplejson, md5, sys, os, ircclient, time, config, weakref
 
 Sessions = {}
 
 def get_session_id():
-  return md5.md5(os.urandom(16)).hexdigest()
-  
+  return md5.md5(os.urandom(16)).hexdigest()[:10]
+
+class BufferOverflowException(Exception):
+  pass
+
 def jsondump(fn):
   def decorator(*args, **kwargs):
     x = fn(*args, **kwargs)
@@ -16,6 +20,12 @@ def jsondump(fn):
     return x
   return decorator
 
+def cleanupSession(id):
+  try:
+    del Sessions[id]
+  except KeyError:
+    pass
+
 class IRCSession:
   def __init__(self, id):
     self.id = id
@@ -23,7 +33,9 @@ class IRCSession:
     self.buffer = []
     self.throttle = 0
     self.schedule = None
-    
+    self.closed = False
+    self.cleanupschedule = None
+
   def subscribe(self, channel):
     self.subscriptions.append(channel)
     self.flush()
@@ -59,14 +71,30 @@ class IRCSession:
         newsubs.append(x)
 
     self.subscriptions = newsubs
-     
+    if self.closed and not self.subscriptions:
+      cleanupSession(self.id)
+
   def event(self, data):
+    bufferlen = sum(map(len, self.buffer))
+    if bufferlen + len(data) > config.MAXBUFLEN:
+      self.buffer = []
+      self.client.error("Buffer overflow")
+      return
+
     self.buffer.append(data)
     self.flush()
     
   def push(self, data):
-    self.client.write(data)
+    if not self.closed:
+      self.client.write(data)
+
+  def disconnect(self):
+    # keep the session hanging around for a few seconds so the
+    # client has a chance to see what the issue was
+    self.closed = True
+
+    reactor.callLater(5, cleanupSession, self.id)
+
 class Channel:
   def __init__(self, request):
     self.request = request
@@ -141,7 +169,17 @@ class AJAXEngine(resource.Resource):
         decoded = command.decode("utf-8")
       except UnicodeDecodeError:
         decoded = command.decode("iso-8859-1", "ignore")
-      session.push(decoded)
+
+      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."]
+    
       return [True]
 
     return [False, "404"]
index 06589a44380a124d14117659890d60a1895e9db4..e5b88a5c254b32343003590b65b84f584cf0f4ec 100644 (file)
@@ -15,7 +15,7 @@ def hmacfn(*args):
 
 class QWebIRCClient(basic.LineReceiver):
   delimiter = "\n"
-  
+
   def dataReceived(self, data):
     basic.LineReceiver.dataReceived(self, data.replace("\r", ""))
 
@@ -47,6 +47,7 @@ class QWebIRCClient(basic.LineReceiver):
   def connectionMade(self):
     basic.LineReceiver.connectionMade(self)
     
+    self.lastError = None
     f = self.factory.ircinit
     nick, ident, ip, realname = f["nick"], f["ident"], f["ip"], f["realname"]
     
@@ -58,9 +59,21 @@ class QWebIRCClient(basic.LineReceiver):
     self("connect")
 
   def connectionLost(self, reason):
+    if self.lastError:
+      self.disconnect("Connection to IRC server lost: %s" % self.lastError)
+    else:
+      self.disconnect("Connection to IRC server lost.")
     self.factory.client = None
     basic.LineReceiver.connectionLost(self, reason)
-    self("disconnect")
+
+  def error(self, message):
+    self.lastError = message
+    self.write("QUIT :qwebirc exception: %s" % message)
+    self.transport.loseConnection()
+
+  def disconnect(self, reason):
+    self("disconnect", reason)
+    self.factory.publisher.disconnect()
     
 class QWebIRCFactory(protocol.ClientFactory):
   protocol = QWebIRCClient
@@ -71,7 +84,14 @@ class QWebIRCFactory(protocol.ClientFactory):
     
   def write(self, data):
     self.client.write(data)
-    
+
+  def error(self, reason):
+    self.client.error(reason)
+
+  def clientConnectionFailed(self, reason):
+    protocol.ClientFactory.clientConnectionFailed(reason)
+    self.client.disconnect("Connection to IRC server failed.")
+
 def createIRC(*args, **kwargs):
   f = QWebIRCFactory(*args, **kwargs)
   reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f)
@@ -79,4 +99,4 @@ def createIRC(*args, **kwargs):
 
 if __name__ == "__main__":
   e = createIRC(lambda x: 2, nick="slug__moo", ident="mooslug", ip="1.2.3.6", realname="mooooo")
-  reactor.run()
\ No newline at end of file
+  reactor.run()
diff --git a/run.bat b/run.bat
index 2f9809e743e9c7c707b16c71cae43b320073006a..52a51a972a577f491fa333f313702c636f651839 100644 (file)
--- a/run.bat
+++ b/run.bat
@@ -1,3 +1,3 @@
 set PYTHONPATH=.\r
-c:\python25\scripts\twistd.py qwebirc\r
-pause
\ No newline at end of file
+c:\python25\scripts\twistd.py qwebirc -n\r
+pause\r
diff --git a/run.sh b/run.sh
index 78ee92179fe9c13563ab8a833936949851343c0f..75e08a1d63d8f245c0949f6ecd08b8ea88df0578 100755 (executable)
--- a/run.sh
+++ b/run.sh
@@ -1,3 +1,3 @@
 #!/bin/sh
 export PYTHONPATH=.:${PYTHONPATH}
-twistd qwebirc
+twistd qwebirc -n