]> jfr.im git - irc/quakenet/qwebirc.git/blame - qwebirc/ircclient.py
Implement our own timeout mechanism as the Twisted stuff is really buggy.
[irc/quakenet/qwebirc.git] / qwebirc / ircclient.py
CommitLineData
c70a7ff6 1import twisted, sys, codecs
9e769c12
CP
2from twisted.words.protocols import irc
3from twisted.internet import reactor, protocol
4from twisted.web import resource, server
5from twisted.protocols import basic
6
7import hmac, time, config
8from config import HMACTEMPORAL
9HMACKEY = hmac.HMAC(key=config.HMACKEY)
10
11def hmacfn(*args):
12 h = HMACKEY.copy()
13 h.update("%d %s" % (int(time.time() / HMACTEMPORAL), " ".join(args)))
14 return h.hexdigest()
15
c70a7ff6
CP
16def utf8_iso8859_1(data, table=dict((x, x.decode("iso-8859-1")) for x in map(chr, range(0, 256)))):
17 return (table.get(data.object[data.start]), data.start+1)
18
19codecs.register_error("mixed-iso-8859-1", utf8_iso8859_1)
20
21def irc_decode(x):
22 try:
23 return x.decode("utf-8", "mixed-iso-8859-1")
24 except UnicodeDecodeError:
25 return x.decode("iso-8859-1", "ignore")
26
9e769c12
CP
27class QWebIRCClient(basic.LineReceiver):
28 delimiter = "\n"
85f01e3f
CP
29 def __init__(self, *args, **kwargs):
30 self.__nickname = "(unregistered)"
31
9e769c12
CP
32 def dataReceived(self, data):
33 basic.LineReceiver.dataReceived(self, data.replace("\r", ""))
34
35 def lineReceived(self, line):
c70a7ff6 36 line = irc_decode(irc.lowDequote(line))
9e769c12
CP
37
38 try:
39 prefix, command, params = irc.parsemsg(line)
40 self.handleCommand(command, prefix, params)
41 except irc.IRCBadMessage:
42 self.badMessage(line, *sys.exc_info())
ace37679 43
85f01e3f
CP
44 if command == "001":
45 self.__nickname = params[0]
ace37679 46
85f01e3f
CP
47 if self.__perform is not None:
48 for x in self.__perform:
49 self.write(x)
50 self.__perform = None
51 elif command == "NICK":
52 nick = prefix.split("!", 1)[0]
53 if nick == self.__nickname:
54 self.__nickname = params[0]
55
9e769c12
CP
56 def badMessage(self, args):
57 self("badmessage", args)
58
59 def handleCommand(self, command, prefix, params):
60 self("c", command, prefix, params)
61
62 def __call__(self, *args):
63 self.factory.publisher.event(args)
64
65 def write(self, data):
66 self.transport.write("%s\r\n" % irc.lowQuote(data.encode("utf-8")))
67
68 def connectionMade(self):
69 basic.LineReceiver.connectionMade(self)
70
8dc46dfa 71 self.lastError = None
9e769c12
CP
72 f = self.factory.ircinit
73 nick, ident, ip, realname = f["nick"], f["ident"], f["ip"], f["realname"]
85f01e3f 74 self.__nickname = nick
ace37679 75 self.__perform = f.get("perform")
9e769c12
CP
76
77 hmac = hmacfn(ident, ip)
78 self.write("USER %s bleh bleh %s %s :%s" % (ident, ip, hmac, realname))
79 self.write("NICK %s" % nick)
80
81 self.factory.client = self
82 self("connect")
83
85f01e3f
CP
84 def __str__(self):
85 return "<QWebIRCClient: %s!%s@%s>" % (self.__nickname, self.factory.ircinit["ident"], self.factory.ircinit["ip"])
86
9e769c12 87 def connectionLost(self, reason):
8dc46dfa
CP
88 if self.lastError:
89 self.disconnect("Connection to IRC server lost: %s" % self.lastError)
90 else:
91 self.disconnect("Connection to IRC server lost.")
9e769c12
CP
92 self.factory.client = None
93 basic.LineReceiver.connectionLost(self, reason)
8dc46dfa
CP
94
95 def error(self, message):
96 self.lastError = message
97 self.write("QUIT :qwebirc exception: %s" % message)
98 self.transport.loseConnection()
99
100 def disconnect(self, reason):
101 self("disconnect", reason)
102 self.factory.publisher.disconnect()
9e769c12
CP
103
104class QWebIRCFactory(protocol.ClientFactory):
105 protocol = QWebIRCClient
106 def __init__(self, publisher, **kwargs):
107 self.client = None
108 self.publisher = publisher
109 self.ircinit = kwargs
110
111 def write(self, data):
112 self.client.write(data)
8dc46dfa
CP
113
114 def error(self, reason):
115 self.client.error(reason)
116
623b5428
CP
117 def clientConnectionFailed(self, connector, reason):
118 protocol.ClientFactory.clientConnectionFailed(self, connector, reason)
119 self.publisher.event(["disconnect", "Connection to IRC server failed."])
120 self.publisher.disconnect()
8dc46dfa 121
9e769c12
CP
122def createIRC(*args, **kwargs):
123 f = QWebIRCFactory(*args, **kwargs)
124 reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f)
125 return f
126
127if __name__ == "__main__":
128 e = createIRC(lambda x: 2, nick="slug__moo", ident="mooslug", ip="1.2.3.6", realname="mooooo")
8dc46dfa 129 reactor.run()