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