]>
Commit | Line | Data |
---|---|---|
1 | from twisted.web import resource, server, static | |
2 | from twisted.names import client | |
3 | from twisted.internet import reactor | |
4 | import simplejson, md5, sys, os, ircclient, time, config | |
5 | ||
6 | Sessions = {} | |
7 | ||
8 | def get_session_id(): | |
9 | return md5.md5(os.urandom(16)).hexdigest() | |
10 | ||
11 | def jsondump(fn): | |
12 | def decorator(*args, **kwargs): | |
13 | x = fn(*args, **kwargs) | |
14 | if isinstance(x, list): | |
15 | return simplejson.dumps(x) | |
16 | return x | |
17 | return decorator | |
18 | ||
19 | class IRCSession: | |
20 | def __init__(self, id): | |
21 | self.id = id | |
22 | self.subscriptions = [] | |
23 | self.buffer = [] | |
24 | self.throttle = 0 | |
25 | self.schedule = None | |
26 | ||
27 | def subscribe(self, channel): | |
28 | self.subscriptions.append(channel) | |
29 | self.flush() | |
30 | ||
31 | def flush(self, scheduled=False): | |
32 | if scheduled: | |
33 | self.schedule = None | |
34 | ||
35 | if not self.buffer or not self.subscriptions: | |
36 | return | |
37 | ||
38 | t = time.time() | |
39 | ||
40 | if t < self.throttle: | |
41 | if not self.schedule: | |
42 | self.schedule = reactor.callLater(self.throttle - t, self.flush, True) | |
43 | return | |
44 | else: | |
45 | # process the rest of the packet | |
46 | if not scheduled: | |
47 | if not self.schedule: | |
48 | self.schedule = reactor.callLater(0, self.flush, True) | |
49 | return | |
50 | ||
51 | self.throttle = t + config.UPDATE_FREQ | |
52 | ||
53 | encdata = simplejson.dumps(self.buffer) | |
54 | self.buffer = [] | |
55 | ||
56 | newsubs = [] | |
57 | for x in self.subscriptions: | |
58 | if x.write(encdata): | |
59 | newsubs.append(x) | |
60 | ||
61 | self.subscriptions = newsubs | |
62 | ||
63 | def event(self, data): | |
64 | self.buffer.append(data) | |
65 | self.flush() | |
66 | ||
67 | def push(self, data): | |
68 | self.client.write(data) | |
69 | ||
70 | class Channel: | |
71 | def __init__(self, request): | |
72 | self.request = request | |
73 | ||
74 | class SingleUseChannel(Channel): | |
75 | def write(self, data): | |
76 | self.request.write(data) | |
77 | self.request.finish() | |
78 | return False | |
79 | ||
80 | class MultipleUseChannel(Channel): | |
81 | def write(self, data): | |
82 | self.request.write(data) | |
83 | return True | |
84 | ||
85 | class AJAXEngine(resource.Resource): | |
86 | isLeaf = True | |
87 | ||
88 | def __init__(self, prefix): | |
89 | self.prefix = prefix | |
90 | ||
91 | @jsondump | |
92 | def render_POST(self, request): | |
93 | path = request.path[len(self.prefix):] | |
94 | if path == "/n": | |
95 | ip = request.transport.getPeer() | |
96 | ip = ip[1] | |
97 | ||
98 | nick, ident = request.args.get("nick"), "webchat" | |
99 | if not nick: | |
100 | return [False, "Nickname not supplied"] | |
101 | ||
102 | nick = nick[0] | |
103 | ||
104 | id = get_session_id() | |
105 | ||
106 | session = IRCSession(id) | |
107 | ||
108 | client = ircclient.createIRC(session, nick=nick, ident=ident, ip=ip, realname=nick) | |
109 | session.client = client | |
110 | ||
111 | Sessions[id] = session | |
112 | ||
113 | return [True, id] | |
114 | return [False, "404"] | |
115 | ||
116 | @jsondump | |
117 | def render_GET(self, request): | |
118 | path = request.path[len(self.prefix):] | |
119 | if path.startswith("/s/"): | |
120 | sessionid = path[3:] | |
121 | session = Sessions.get(sessionid) | |
122 | ||
123 | if not session: | |
124 | return [False, "Bad session ID"] | |
125 | ||
126 | session.subscribe(SingleUseChannel(request)) | |
127 | return server.NOT_DONE_YET | |
128 | if path.startswith("/p/"): | |
129 | command = request.args.get("c") | |
130 | if not command: | |
131 | return [False, "No command specified"] | |
132 | ||
133 | command = command[0] | |
134 | ||
135 | sessionid = path[3:] | |
136 | session = Sessions.get(sessionid) | |
137 | if not session: | |
138 | return [False, "Bad session ID"] | |
139 | ||
140 | try: | |
141 | decoded = command.decode("utf-8") | |
142 | except UnicodeDecodeError: | |
143 | decoded = command.decode("iso-8859-1", "ignore") | |
144 | session.push(decoded) | |
145 | return [True] | |
146 | ||
147 | return [False, "404"] |