1 # Do everything properly, and componentize
2 from twisted.application import internet, service
3 from twisted.internet import protocol, reactor, defer
4 from twisted.words.protocols import irc
5 from twisted.protocols import basic
6 from twisted.python import components
7 from twisted.web import resource, server, static, xmlrpc, microdom
8 from twisted.spread import pb
9 from zope.interface import Interface, implements
10 from OpenSSL import SSL
13 class IFingerService(Interface):
17 Return a deferred returning a string.
22 Return a deferred returning a list of strings.
26 class IFingerSetterService(Interface):
28 def setUser(user, status):
30 Set the user's status to something.
35 return "Internal error in server"
38 class FingerProtocol(basic.LineReceiver):
40 def lineReceived(self, user):
41 d = self.factory.getUser(user)
42 d.addErrback(catchError)
43 def writeValue(value):
44 self.transport.write(value+'\r\n')
45 self.transport.loseConnection()
46 d.addCallback(writeValue)
49 class IFingerFactory(Interface):
53 Return a deferred returning a string.
56 def buildProtocol(addr):
58 Return a protocol returning a string.
62 class FingerFactoryFromService(protocol.ServerFactory):
64 implements(IFingerFactory)
66 protocol = FingerProtocol
68 def __init__(self, service):
69 self.service = service
71 def getUser(self, user):
72 return self.service.getUser(user)
74 components.registerAdapter(FingerFactoryFromService,
79 class FingerSetterProtocol(basic.LineReceiver):
81 def connectionMade(self):
84 def lineReceived(self, line):
85 self.lines.append(line)
87 def connectionLost(self, reason):
88 if len(self.lines) == 2:
89 self.factory.setUser(*self.lines)
92 class IFingerSetterFactory(Interface):
94 def setUser(user, status):
96 Return a deferred returning a string.
99 def buildProtocol(addr):
101 Return a protocol returning a string.
105 class FingerSetterFactoryFromService(protocol.ServerFactory):
107 implements(IFingerSetterFactory)
109 protocol = FingerSetterProtocol
111 def __init__(self, service):
112 self.service = service
114 def setUser(self, user, status):
115 self.service.setUser(user, status)
118 components.registerAdapter(FingerSetterFactoryFromService,
119 IFingerSetterService,
120 IFingerSetterFactory)
123 class IRCReplyBot(irc.IRCClient):
125 def connectionMade(self):
126 self.nickname = self.factory.nickname
127 irc.IRCClient.connectionMade(self)
129 def privmsg(self, user, channel, msg):
130 user = user.split('!')[0]
131 if self.nickname.lower() == channel.lower():
132 d = self.factory.getUser(msg)
133 d.addErrback(catchError)
134 d.addCallback(lambda m: "Status of %s: %s" % (msg, m))
135 d.addCallback(lambda m: self.msg(user, m))
138 class IIRCClientFactory(Interface):
146 Return a deferred returning a string.
149 def buildProtocol(addr):
155 class IRCClientFactoryFromService(protocol.ClientFactory):
157 implements(IIRCClientFactory)
159 protocol = IRCReplyBot
162 def __init__(self, service):
163 self.service = service
165 def getUser(self, user):
166 return self.service.getUser(user)
168 components.registerAdapter(IRCClientFactoryFromService,
173 class UserStatusTree(resource.Resource):
175 def __init__(self, service):
176 resource.Resource.__init__(self)
179 # add a specific child for the path "RPC2"
180 self.putChild("RPC2", UserStatusXR(self.service))
182 # need to do this for resources at the root of the site
183 self.putChild("", self)
185 def _cb_render_GET(self, users, request):
186 userOutput = ''.join(["<li><a href=\"%s\">%s</a></li>" % (user, user)
189 <html><head><title>Users</title></head><body>
193 </ul></body></html>""" % userOutput)
196 def render_GET(self, request):
197 d = self.service.getUsers()
198 d.addCallback(self._cb_render_GET, request)
200 # signal that the rendering is not complete
201 return server.NOT_DONE_YET
203 def getChild(self, path, request):
204 return UserStatus(user=path, service=self.service)
206 components.registerAdapter(UserStatusTree, IFingerService, resource.IResource)
209 class UserStatus(resource.Resource):
211 def __init__(self, user, service):
212 resource.Resource.__init__(self)
214 self.service = service
216 def _cb_render_GET(self, status, request):
217 request.write("""<html><head><title>%s</title></head>
220 </body></html>""" % (self.user, self.user, status))
223 def render_GET(self, request):
224 d = self.service.getUser(self.user)
225 d.addCallback(self._cb_render_GET, request)
227 # signal that the rendering is not complete
228 return server.NOT_DONE_YET
231 class UserStatusXR(xmlrpc.XMLRPC):
233 def __init__(self, service):
234 xmlrpc.XMLRPC.__init__(self)
235 self.service = service
237 def xmlrpc_getUser(self, user):
238 return self.service.getUser(user)
240 def xmlrpc_getUsers(self):
241 return self.service.getUsers()
244 class IPerspectiveFinger(Interface):
246 def remote_getUser(username):
248 Return a user's status.
251 def remote_getUsers():
253 Return a user's status.
256 class PerspectiveFingerFromService(pb.Root):
258 implements(IPerspectiveFinger)
260 def __init__(self, service):
261 self.service = service
263 def remote_getUser(self, username):
264 return self.service.getUser(username)
266 def remote_getUsers(self):
267 return self.service.getUsers()
269 components.registerAdapter(PerspectiveFingerFromService,
274 class FingerService(service.Service):
276 implements(IFingerService)
278 def __init__(self, filename):
279 self.filename = filename
284 for line in file(self.filename):
285 user, status = line.split(':', 1)
287 status = status.strip()
288 self.users[user] = status
289 self.call = reactor.callLater(30, self._read)
291 def getUser(self, user):
292 return defer.succeed(self.users.get(user, "No such user"))
295 return defer.succeed(self.users.keys())
297 def startService(self):
299 service.Service.startService(self)
301 def stopService(self):
302 service.Service.stopService(self)
306 class ServerContextFactory:
308 def getContext(self):
310 Create an SSL context.
312 This is a sample implementation that loads a certificate from a file
315 ctx = SSL.Context(SSL.SSLv23_METHOD)
316 ctx.use_certificate_file('server.pem')
317 ctx.use_privatekey_file('server.pem')
321 application = service.Application('finger', uid=1, gid=1)
322 f = FingerService('/etc/users')
323 serviceCollection = service.IServiceCollection(application)
324 f.setServiceParent(serviceCollection)
325 internet.TCPServer(79, IFingerFactory(f)
326 ).setServiceParent(serviceCollection)
327 site = server.Site(resource.IResource(f))
328 internet.TCPServer(8000, site
329 ).setServiceParent(serviceCollection)
330 internet.SSLServer(443, site, ServerContextFactory()
331 ).setServiceParent(serviceCollection)
332 i = IIRCClientFactory(f)
333 i.nickname = 'fingerbot'
334 internet.TCPClient('irc.freenode.org', 6667, i
335 ).setServiceParent(serviceCollection)
336 internet.TCPServer(8889, pb.PBServerFactory(IPerspectiveFinger(f))
337 ).setServiceParent(serviceCollection)