]>
Commit | Line | Data |
---|---|---|
0faa6c16 | 1 | import twisted, sys, codecs, traceback |
9e769c12 | 2 | from twisted.words.protocols import irc |
f6c5443a | 3 | from twisted.internet import reactor, protocol, abstract |
9e769c12 CP |
4 | from twisted.web import resource, server |
5 | from twisted.protocols import basic | |
f6c5443a CP |
6 | from twisted.names.client import Resolver |
7 | import hmac, time, config, random, qwebirc.config_options as config_options | |
9e769c12 | 8 | from config import HMACTEMPORAL |
b5c84380 | 9 | |
f6c5443a CP |
10 | if config.get("CONNECTION_RESOLVER"): |
11 | CONNECTION_RESOLVER = Resolver(servers=config.get("CONNECTION_RESOLVER")) | |
12 | else: | |
13 | CONNECTION_RESOLVER = None | |
14 | ||
930be88a | 15 | if hasattr(config, "WEBIRC_MODE") and config.WEBIRC_MODE == "hmac": |
b5c84380 | 16 | HMACKEY = hmac.HMAC(key=config.HMACKEY) |
9e769c12 CP |
17 | |
18 | def hmacfn(*args): | |
19 | h = HMACKEY.copy() | |
20 | h.update("%d %s" % (int(time.time() / HMACTEMPORAL), " ".join(args))) | |
21 | return h.hexdigest() | |
22 | ||
c70a7ff6 CP |
23 | def utf8_iso8859_1(data, table=dict((x, x.decode("iso-8859-1")) for x in map(chr, range(0, 256)))): |
24 | return (table.get(data.object[data.start]), data.start+1) | |
25 | ||
26 | codecs.register_error("mixed-iso-8859-1", utf8_iso8859_1) | |
27 | ||
28 | def irc_decode(x): | |
29 | try: | |
30 | return x.decode("utf-8", "mixed-iso-8859-1") | |
31 | except UnicodeDecodeError: | |
32 | return x.decode("iso-8859-1", "ignore") | |
33 | ||
9e769c12 CP |
34 | class QWebIRCClient(basic.LineReceiver): |
35 | delimiter = "\n" | |
85f01e3f CP |
36 | def __init__(self, *args, **kwargs): |
37 | self.__nickname = "(unregistered)" | |
38 | ||
9e769c12 CP |
39 | def dataReceived(self, data): |
40 | basic.LineReceiver.dataReceived(self, data.replace("\r", "")) | |
41 | ||
42 | def lineReceived(self, line): | |
c70a7ff6 | 43 | line = irc_decode(irc.lowDequote(line)) |
9e769c12 CP |
44 | |
45 | try: | |
46 | prefix, command, params = irc.parsemsg(line) | |
47 | self.handleCommand(command, prefix, params) | |
48 | except irc.IRCBadMessage: | |
0faa6c16 CP |
49 | # emit and ignore |
50 | traceback.print_exc() | |
51 | return | |
52 | ||
85f01e3f CP |
53 | if command == "001": |
54 | self.__nickname = params[0] | |
ace37679 | 55 | |
85f01e3f CP |
56 | if self.__perform is not None: |
57 | for x in self.__perform: | |
58 | self.write(x) | |
59 | self.__perform = None | |
60 | elif command == "NICK": | |
61 | nick = prefix.split("!", 1)[0] | |
62 | if nick == self.__nickname: | |
63 | self.__nickname = params[0] | |
64 | ||
9e769c12 CP |
65 | def handleCommand(self, command, prefix, params): |
66 | self("c", command, prefix, params) | |
67 | ||
68 | def __call__(self, *args): | |
69 | self.factory.publisher.event(args) | |
70 | ||
71 | def write(self, data): | |
72 | self.transport.write("%s\r\n" % irc.lowQuote(data.encode("utf-8"))) | |
73 | ||
74 | def connectionMade(self): | |
75 | basic.LineReceiver.connectionMade(self) | |
76 | ||
8dc46dfa | 77 | self.lastError = None |
9e769c12 | 78 | f = self.factory.ircinit |
6ce70043 | 79 | nick, ident, ip, realname, hostname, pass_ = f["nick"], f["ident"], f["ip"], f["realname"], f["hostname"], f.get("password") |
85f01e3f | 80 | self.__nickname = nick |
ace37679 | 81 | self.__perform = f.get("perform") |
b5c84380 | 82 | |
930be88a CP |
83 | if not hasattr(config, "WEBIRC_MODE"): |
84 | self.write("USER %s bleh bleh %s :%s" % (ident, ip, realname)) | |
85 | elif config.WEBIRC_MODE == "hmac": | |
b5c84380 CP |
86 | hmac = hmacfn(ident, ip) |
87 | self.write("USER %s bleh bleh %s %s :%s" % (ident, ip, hmac, realname)) | |
28c4ad01 | 88 | elif config.WEBIRC_MODE == "webirc": |
195fe862 | 89 | self.write("WEBIRC %s qwebirc %s %s" % (config.WEBIRC_PASSWORD, hostname, ip)) |
b5c84380 | 90 | self.write("USER %s bleh %s :%s" % (ident, ip, realname)) |
711593cd CP |
91 | elif config.WEBIRC_MODE == "cgiirc": |
92 | self.write("PASS %s_%s_%s" % (config.CGIIRC_STRING, ip, hostname)) | |
93 | self.write("USER %s bleh %s :%s" % (ident, ip, realname)) | |
930be88a | 94 | elif config.WEBIRC_MODE == config_options.WEBIRC_REALNAME or config.WEBIRC_MODE is None: # last bit is legacy |
b5c84380 CP |
95 | if ip == hostname: |
96 | dispip = ip | |
97 | else: | |
98 | dispip = "%s/%s" % (hostname, ip) | |
99 | ||
28c4ad01 | 100 | self.write("USER %s bleh bleh :%s - %s" % (ident, dispip, realname)) |
b5c84380 | 101 | |
2f74dea9 CP |
102 | if pass_ is not None: |
103 | self.write("PASS :%s" % pass_) | |
9e769c12 CP |
104 | self.write("NICK %s" % nick) |
105 | ||
106 | self.factory.client = self | |
107 | self("connect") | |
108 | ||
85f01e3f CP |
109 | def __str__(self): |
110 | return "<QWebIRCClient: %s!%s@%s>" % (self.__nickname, self.factory.ircinit["ident"], self.factory.ircinit["ip"]) | |
111 | ||
9e769c12 | 112 | def connectionLost(self, reason): |
8dc46dfa CP |
113 | if self.lastError: |
114 | self.disconnect("Connection to IRC server lost: %s" % self.lastError) | |
115 | else: | |
116 | self.disconnect("Connection to IRC server lost.") | |
9e769c12 CP |
117 | self.factory.client = None |
118 | basic.LineReceiver.connectionLost(self, reason) | |
8dc46dfa CP |
119 | |
120 | def error(self, message): | |
121 | self.lastError = message | |
122 | self.write("QUIT :qwebirc exception: %s" % message) | |
123 | self.transport.loseConnection() | |
124 | ||
125 | def disconnect(self, reason): | |
126 | self("disconnect", reason) | |
127 | self.factory.publisher.disconnect() | |
9e769c12 CP |
128 | |
129 | class QWebIRCFactory(protocol.ClientFactory): | |
130 | protocol = QWebIRCClient | |
131 | def __init__(self, publisher, **kwargs): | |
132 | self.client = None | |
133 | self.publisher = publisher | |
134 | self.ircinit = kwargs | |
135 | ||
136 | def write(self, data): | |
137 | self.client.write(data) | |
8dc46dfa CP |
138 | |
139 | def error(self, reason): | |
140 | self.client.error(reason) | |
141 | ||
623b5428 CP |
142 | def clientConnectionFailed(self, connector, reason): |
143 | protocol.ClientFactory.clientConnectionFailed(self, connector, reason) | |
144 | self.publisher.event(["disconnect", "Connection to IRC server failed."]) | |
145 | self.publisher.disconnect() | |
8dc46dfa | 146 | |
9e769c12 CP |
147 | def createIRC(*args, **kwargs): |
148 | f = QWebIRCFactory(*args, **kwargs) | |
f6c5443a | 149 | if CONNECTION_RESOLVER is None: |
b60ea11d CP |
150 | reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f) |
151 | return f | |
152 | ||
f6c5443a CP |
153 | def callback(result): |
154 | name, port = random.choice(sorted((str(x.payload.target), x.payload.port) for x in result[0])) | |
155 | reactor.connectTCP(name, port, f) | |
b60ea11d CP |
156 | def errback(err): |
157 | f.clientConnectionFailed(None, err) # None?! | |
158 | ||
f6c5443a | 159 | d = CONNECTION_RESOLVER.lookupService(config.IRCSERVER, (1, 3, 11)) |
b60ea11d | 160 | d.addCallbacks(callback, errback) |
9e769c12 CP |
161 | return f |
162 | ||
163 | if __name__ == "__main__": | |
b5c84380 | 164 | e = createIRC(lambda x: 2, nick="slug__moo", ident="mooslug", ip="1.2.3.6", realname="mooooo", hostname="1.2.3.4") |
8dc46dfa | 165 | reactor.run() |