]>
jfr.im git - irc/quakenet/qwebirc.git/blob - qwebirc/ircclient.py
1 import twisted
, sys
, codecs
, traceback
2 from twisted
.words
.protocols
import irc
3 from twisted
.internet
import reactor
, protocol
, abstract
4 from twisted
.web
import resource
, server
5 from twisted
.protocols
import basic
6 from twisted
.names
.client
import Resolver
7 import hmac
, time
, config
, random
, qwebirc
.config_options
as config_options
8 from config
import HMACTEMPORAL
10 if config
.get("CONNECTION_RESOLVER"):
11 CONNECTION_RESOLVER
= Resolver(servers
=config
.get("CONNECTION_RESOLVER"))
13 CONNECTION_RESOLVER
= None
15 if hasattr(config
, "WEBIRC_MODE") and config
.WEBIRC_MODE
== "hmac":
16 HMACKEY
= hmac
.HMAC(key
=config
.HMACKEY
)
20 h
.update("%d %s" % (int(time
.time() / HMACTEMPORAL
), " ".join(args
)))
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)
26 codecs
.register_error("mixed-iso-8859-1", utf8_iso8859_1
)
30 return x
.decode("utf-8", "mixed-iso-8859-1")
31 except UnicodeDecodeError:
32 return x
.decode("iso-8859-1", "ignore")
34 class QWebIRCClient(basic
.LineReceiver
):
36 def __init__(self
, *args
, **kwargs
):
37 self
.__nickname
= "(unregistered)"
39 def dataReceived(self
, data
):
40 basic
.LineReceiver
.dataReceived(self
, data
.replace("\r", ""))
42 def lineReceived(self
, line
):
43 line
= irc_decode(irc
.lowDequote(line
))
46 prefix
, command
, params
= irc
.parsemsg(line
)
47 self
.handleCommand(command
, prefix
, params
)
48 except irc
.IRCBadMessage
:
54 self
.__nickname
= params
[0]
56 if self
.__perform
is not None:
57 for x
in self
.__perform
:
60 elif command
== "NICK":
61 nick
= prefix
.split("!", 1)[0]
62 if nick
== self
.__nickname
:
63 self
.__nickname
= params
[0]
65 def handleCommand(self
, command
, prefix
, params
):
66 self("c", command
, prefix
, params
)
68 def __call__(self
, *args
):
69 self
.factory
.publisher
.event(args
)
71 def write(self
, data
):
72 self
.transport
.write("%s\r\n" % irc
.lowQuote(data
.encode("utf-8")))
74 def connectionMade(self
):
75 basic
.LineReceiver
.connectionMade(self
)
78 f
= self
.factory
.ircinit
79 nick
, ident
, ip
, realname
, hostname
, pass_
= f
["nick"], f
["ident"], f
["ip"], f
["realname"], f
["hostname"], f
.get("password")
80 self
.__nickname
= nick
81 self
.__perform
= f
.get("perform")
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":
86 hmac
= hmacfn(ident
, ip
)
87 self
.write("USER %s bleh bleh %s %s :%s" % (ident
, ip
, hmac
, realname
))
88 elif config
.WEBIRC_MODE
== "webirc":
89 self
.write("WEBIRC %s qwebirc %s %s" % (config
.WEBIRC_PASSWORD
, hostname
, ip
))
90 self
.write("USER %s bleh %s :%s" % (ident
, ip
, realname
))
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
))
94 elif config
.WEBIRC_MODE
== config_options
.WEBIRC_REALNAME
or config
.WEBIRC_MODE
is None: # last bit is legacy
98 dispip
= "%s/%s" % (hostname
, ip
)
100 self
.write("USER %s bleh bleh :%s - %s" % (ident
, dispip
, realname
))
102 if pass_
is not None:
103 self
.write("PASS :%s" % pass_
)
104 self
.write("NICK %s" % nick
)
106 self
.factory
.client
= self
110 return "<QWebIRCClient: %s!%s@%s>" % (self
.__nickname
, self
.factory
.ircinit
["ident"], self
.factory
.ircinit
["ip"])
112 def connectionLost(self
, reason
):
114 self
.disconnect("Connection to IRC server lost: %s" % self
.lastError
)
116 self
.disconnect("Connection to IRC server lost.")
117 self
.factory
.client
= None
118 basic
.LineReceiver
.connectionLost(self
, reason
)
120 def error(self
, message
):
121 self
.lastError
= message
122 self
.write("QUIT :qwebirc exception: %s" % message
)
123 self
.transport
.loseConnection()
125 def disconnect(self
, reason
):
126 self("disconnect", reason
)
127 self
.factory
.publisher
.disconnect()
129 class QWebIRCFactory(protocol
.ClientFactory
):
130 protocol
= QWebIRCClient
131 def __init__(self
, publisher
, **kwargs
):
133 self
.publisher
= publisher
134 self
.ircinit
= kwargs
136 def write(self
, data
):
137 self
.client
.write(data
)
139 def error(self
, reason
):
140 self
.client
.error(reason
)
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()
147 def createIRC(*args
, **kwargs
):
148 f
= QWebIRCFactory(*args
, **kwargs
)
151 if hasattr(config
, "OUTGOING_IP"):
152 tcpkwargs
["bindAddress"] = (config
.OUTGOING_IP
, 0)
154 if CONNECTION_RESOLVER
is None:
155 if hasattr(config
, "SSLPORT"):
156 from twisted
.internet
import ssl
157 reactor
.connectSSL(config
.IRCSERVER
, config
.SSLPORT
, f
, ssl
.ClientContextFactory(), **tcpkwargs
)
159 reactor
.connectTCP(config
.IRCSERVER
, config
.IRCPORT
, f
, **tcpkwargs
)
162 def callback(result
):
163 name
, port
= random
.choice(sorted((str(x
.payload
.target
), x
.payload
.port
) for x
in result
[0]))
164 reactor
.connectTCP(name
, port
, f
, **tcpkwargs
)
166 f
.clientConnectionFailed(None, err
) # None?!
168 d
= CONNECTION_RESOLVER
.lookupService(config
.IRCSERVER
, (1, 3, 11))
169 d
.addCallbacks(callback
, errback
)
172 if __name__
== "__main__":
173 e
= createIRC(lambda x
: 2, nick
="slug__moo", ident
="mooslug", ip
="1.2.3.6", realname
="mooooo", hostname
="1.2.3.4")