]>
jfr.im git - irc/quakenet/qwebirc.git/blob - qwebirc/engines/ajaxengine.py
1 from twisted
.web
import resource
, server
, static
2 from twisted
.names
import client
3 from twisted
.internet
import reactor
4 from authgateengine
import login_optional
, getSessionData
5 import simplejson
, md5
, sys
, os
, time
, config
, weakref
, traceback
6 import qwebirc
.ircclient
as ircclient
7 from adminengine
import AdminEngineAction
8 from qwebirc
.util
import HitCounter
13 return md5
.md5(os
.urandom(16)).hexdigest()
15 class BufferOverflowException(Exception):
18 class AJAXException(Exception):
21 class IDGenerationException(Exception):
27 def decorator(*args
, **kwargs
):
29 x
= fn(*args
, **kwargs
)
31 return server
.NOT_DONE_YET
33 except AJAXException
, e
:
36 return simplejson
.dumps(x
)
39 def cleanupSession(id):
46 def __init__(self
, id):
48 self
.subscriptions
= []
53 self
.cleanupschedule
= None
55 def subscribe(self
, channel
):
56 if len(self
.subscriptions
) >= config
.MAXSUBSCRIPTIONS
:
57 self
.subscriptions
.pop(0).close()
59 self
.subscriptions
.append(channel
)
62 def flush(self
, scheduled
=False):
66 if not self
.buffer or not self
.subscriptions
:
73 self
.schedule
= reactor
.callLater(self
.throttle
- t
, self
.flush
, True)
76 # process the rest of the packet
79 self
.schedule
= reactor
.callLater(0, self
.flush
, True)
82 self
.throttle
= t
+ config
.UPDATE_FREQ
84 encdata
= simplejson
.dumps(self
.buffer)
88 for x
in self
.subscriptions
:
92 self
.subscriptions
= newsubs
93 if self
.closed
and not self
.subscriptions
:
94 cleanupSession(self
.id)
96 def event(self
, data
):
97 bufferlen
= sum(map(len, self
.buffer))
98 if bufferlen
+ len(data
) > config
.MAXBUFLEN
:
100 self
.client
.error("Buffer overflow")
103 self
.buffer.append(data
)
106 def push(self
, data
):
108 self
.client
.write(data
)
110 def disconnect(self
):
111 # keep the session hanging around for a few seconds so the
112 # client has a chance to see what the issue was
115 reactor
.callLater(5, cleanupSession
, self
.id)
118 def __init__(self
, request
):
119 self
.request
= request
121 class SingleUseChannel(Channel
):
122 def write(self
, data
):
123 self
.request
.write(data
)
124 self
.request
.finish()
128 self
.request
.finish()
130 class MultipleUseChannel(Channel
):
131 def write(self
, data
):
132 self
.request
.write(data
)
135 class AJAXEngine(resource
.Resource
):
138 def __init__(self
, prefix
):
140 self
.__connect
_hit
= HitCounter()
141 self
.__total
_hit
= HitCounter()
144 def render_POST(self
, request
):
145 path
= request
.path
[len(self
.prefix
):]
147 handler
= self
.COMMANDS
.get(path
[1:])
148 if handler
is not None:
149 return handler(self
, request
)
150 raise AJAXException("404")
152 # def render_GET(self, request):
153 # return self.render_POST(request)
155 def newConnection(self
, request
):
156 ticket
= login_optional(request
)
158 _
, ip
, port
= request
.transport
.getPeer()
160 nick
, ident
, realname
= request
.args
.get("nick"), "webchat", config
.REALNAME
163 raise AJAXException("Nickname not supplied")
168 id = get_session_id()
169 if not Sessions
.get(id):
172 raise IDGenerationException()
174 session
= IRCSession(id)
176 qticket
= getSessionData(request
).get("qticket")
180 perform
= ["PRIVMSG %s :TICKETAUTH %s" % (config
.QBOT
, qticket
)]
183 client
= ircclient
.createIRC(session
, nick
=nick
, ident
=ident
, ip
=ip
, realname
=realname
, perform
=perform
)
184 session
.client
= client
186 Sessions
[id] = session
190 def getSession(self
, request
):
191 sessionid
= request
.args
.get("s")
192 if sessionid
is None:
193 raise AJAXException("Bad session ID")
195 session
= Sessions
.get(sessionid
[0])
197 raise AJAXException("Bad session ID")
200 def subscribe(self
, request
):
201 self
.getSession(request
).subscribe(SingleUseChannel(request
))
204 def push(self
, request
):
205 command
= request
.args
.get("c")
207 raise AJAXException("No command specified")
212 session
= self
.getSession(request
)
215 decoded
= command
.decode("utf-8")
216 except UnicodeDecodeError:
217 decoded
= command
.decode("iso-8859-1", "ignore")
219 if len(decoded
) > config
.MAXLINELEN
:
221 raise AJAXException("Line too long")
224 session
.push(decoded
)
225 except AttributeError: # occurs when we haven't noticed an error
227 raise AJAXException("Connection closed by server.")
228 except Exception, e
: # catch all
230 traceback
.print_exc(file=sys
.stderr
)
231 raise AJAXException("Unknown error.")
235 def closeById(self
, k
):
239 s
.client
.client
.error("Closed by admin interface")
242 def adminEngine(self
):
244 "Sessions": [(str(v
.client
.client
), AdminEngineAction("close", self
.closeById
, k
)) for k
, v
in Sessions
.iteritems() if not v
.closed
],
245 "Connections": [(self
.__connect
_hit
,)],
246 "Total hits": [(self
.__total
_hit
,)],
249 COMMANDS
= dict(p
=push
, n
=newConnection
, s
=subscribe
)