]> jfr.im git - irc/quakenet/qwebirc.git/blame - qwebirc/ircclient.py
Split up about.html lines.
[irc/quakenet/qwebirc.git] / qwebirc / ircclient.py
CommitLineData
0faa6c16 1import twisted, sys, codecs, traceback
9e769c12 2from twisted.words.protocols import irc
0f8ffda8 3from twisted.internet import reactor, protocol, abstract
9e769c12
CP
4from twisted.web import resource, server
5from twisted.protocols import basic
f6c5443a
CP
6from twisted.names.client import Resolver
7import hmac, time, config, random, qwebirc.config_options as config_options
9e769c12 8from config import HMACTEMPORAL
b5c84380 9
f6c5443a
CP
10if config.get("CONNECTION_RESOLVER"):
11 CONNECTION_RESOLVER = Resolver(servers=config.get("CONNECTION_RESOLVER"))
12else:
13 CONNECTION_RESOLVER = None
14
930be88a 15if hasattr(config, "WEBIRC_MODE") and config.WEBIRC_MODE == "hmac":
b5c84380 16 HMACKEY = hmac.HMAC(key=config.HMACKEY)
9e769c12
CP
17
18def hmacfn(*args):
19 h = HMACKEY.copy()
20 h.update("%d %s" % (int(time.time() / HMACTEMPORAL), " ".join(args)))
21 return h.hexdigest()
22
c70a7ff6
CP
23def 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
26codecs.register_error("mixed-iso-8859-1", utf8_iso8859_1)
27
28def 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
34class 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
129class 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
147def createIRC(*args, **kwargs):
148 f = QWebIRCFactory(*args, **kwargs)
dc8d270f
CP
149
150 tcpkwargs = {}
151 if hasattr(config, "OUTGOING_IP"):
152 tcpkwargs["bindAddress"] = (config.OUTGOING_IP, 0)
153
f6c5443a 154 if CONNECTION_RESOLVER is None:
bf568ed8 155 if hasattr(config, "SSLPORT"):
0f8ffda8 156 from twisted.internet import ssl
bf568ed8
CP
157 reactor.connectSSL(config.IRCSERVER, config.SSLPORT, f, ssl.ClientContextFactory(), **tcpkwargs)
158 else:
159 reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f, **tcpkwargs)
b60ea11d
CP
160 return f
161
f6c5443a
CP
162 def callback(result):
163 name, port = random.choice(sorted((str(x.payload.target), x.payload.port) for x in result[0]))
dc8d270f 164 reactor.connectTCP(name, port, f, **tcpkwargs)
b60ea11d
CP
165 def errback(err):
166 f.clientConnectionFailed(None, err) # None?!
167
f6c5443a 168 d = CONNECTION_RESOLVER.lookupService(config.IRCSERVER, (1, 3, 11))
b60ea11d 169 d.addCallbacks(callback, errback)
9e769c12
CP
170 return f
171
172if __name__ == "__main__":
b5c84380 173 e = createIRC(lambda x: 2, nick="slug__moo", ident="mooslug", ip="1.2.3.6", realname="mooooo", hostname="1.2.3.4")
8dc46dfa 174 reactor.run()