Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Side by Side Diff: third_party/twisted_8_1/twisted/words/service.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.words.test.test_service -*-
2 # Copyright (c) 2001-2005 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 A module that needs a better name.
7
8 Implements new cred things for words.
9
10 How does this thing work?
11
12 - Network connection on some port expecting to speak some protocol
13
14 - Protocol-specific authentication, resulting in some kind of credentials obje ct
15
16 - twisted.cred.portal login using those credentials for the interface
17 IUser and with something implementing IChatClient as the mind
18
19 - successful login results in an IUser avatar the protocol can call
20 methods on, and state added to the realm such that the mind will have
21 methods called on it as is necessary
22
23 - protocol specific actions lead to calls onto the avatar; remote events
24 lead to calls onto the mind
25
26 - protocol specific hangup, realm is notified, user is removed from active
27 play, the end.
28 """
29
30 from time import time, ctime
31
32 from zope.interface import implements
33
34 from twisted.words import iwords, ewords
35
36 from twisted.python.components import registerAdapter
37 from twisted.cred import portal, credentials, error as ecred
38 from twisted.spread import pb
39 from twisted.words.protocols import irc
40 from twisted.internet import defer, protocol
41 from twisted.python import log, failure, reflect
42 from twisted import copyright
43
44
45 class Group(object):
46 implements(iwords.IGroup)
47
48 def __init__(self, name):
49 self.name = name
50 self.users = {}
51 self.meta = {
52 "topic": "",
53 "topic_author": "",
54 }
55
56
57 def _ebUserCall(self, err, p):
58 return failure.Failure(Exception(p, err))
59
60
61 def _cbUserCall(self, results):
62 for (success, result) in results:
63 if not success:
64 user, err = result.value # XXX
65 self.remove(user, err.getErrorMessage())
66
67
68 def add(self, user):
69 assert iwords.IChatClient.providedBy(user), "%r is not a chat client" % (user,)
70 if user.name not in self.users:
71 additions = []
72 self.users[user.name] = user
73 for p in self.users.itervalues():
74 if p is not user:
75 d = defer.maybeDeferred(p.userJoined, self, user)
76 d.addErrback(self._ebUserCall, p=p)
77 additions.append(d)
78 defer.DeferredList(additions).addCallback(self._cbUserCall)
79 return defer.succeed(None)
80
81
82 def remove(self, user, reason=None):
83 assert reason is None or isinstance(reason, unicode)
84 try:
85 del self.users[user.name]
86 except KeyError:
87 pass
88 else:
89 removals = []
90 for p in self.users.itervalues():
91 if p is not user:
92 d = defer.maybeDeferred(p.userLeft, self, user, reason)
93 d.addErrback(self._ebUserCall, p=p)
94 removals.append(d)
95 defer.DeferredList(removals).addCallback(self._cbUserCall)
96 return defer.succeed(None)
97
98
99 def size(self):
100 return defer.succeed(len(self.users))
101
102
103 def receive(self, sender, recipient, message):
104 assert recipient is self
105 receives = []
106 for p in self.users.itervalues():
107 if p is not sender:
108 d = defer.maybeDeferred(p.receive, sender, self, message)
109 d.addErrback(self._ebUserCall, p=p)
110 receives.append(d)
111 defer.DeferredList(receives).addCallback(self._cbUserCall)
112 return defer.succeed(None)
113
114
115 def setMetadata(self, meta):
116 self.meta = meta
117 sets = []
118 for p in self.users.itervalues():
119 d = defer.maybeDeferred(p.groupMetaUpdate, self, meta)
120 d.addErrback(self._ebUserCall, p=p)
121 sets.append(d)
122 defer.DeferredList(sets).addCallback(self._cbUserCall)
123 return defer.succeed(None)
124
125
126 def iterusers(self):
127 # XXX Deferred?
128 return iter(self.users.values())
129
130
131 class User(object):
132 implements(iwords.IUser)
133
134 realm = None
135 mind = None
136
137 def __init__(self, name):
138 self.name = name
139 self.groups = []
140 self.lastMessage = time()
141
142
143 def loggedIn(self, realm, mind):
144 self.realm = realm
145 self.mind = mind
146 self.signOn = time()
147
148
149 def join(self, group):
150 def cbJoin(result):
151 self.groups.append(group)
152 return result
153 return group.add(self.mind).addCallback(cbJoin)
154
155
156 def leave(self, group, reason=None):
157 def cbLeave(result):
158 self.groups.remove(group)
159 return result
160 return group.remove(self.mind, reason).addCallback(cbLeave)
161
162
163 def send(self, recipient, message):
164 self.lastMessage = time()
165 return recipient.receive(self.mind, recipient, message)
166
167
168 def itergroups(self):
169 return iter(self.groups)
170
171
172 def logout(self):
173 for g in self.groups[:]:
174 self.leave(g)
175
176
177 NICKSERV = 'NickServ!NickServ@services'
178 class IRCUser(irc.IRC):
179 implements(iwords.IChatClient)
180
181 # A list of IGroups in which I am participating
182 groups = None
183
184 # A no-argument callable I should invoke when I go away
185 logout = None
186
187 # An IUser we use to interact with the chat service
188 avatar = None
189
190 # To whence I belong
191 realm = None
192
193 # How to handle unicode (TODO: Make this customizable on a per-user basis)
194 encoding = 'utf-8'
195
196 # Twisted callbacks
197 def connectionMade(self):
198 self.irc_PRIVMSG = self.irc_NICKSERV_PRIVMSG
199
200
201 def connectionLost(self, reason):
202 if self.logout is not None:
203 self.logout()
204 self.avatar = None
205
206
207 # Make sendMessage a bit more useful to us
208 def sendMessage(self, command, *parameter_list, **kw):
209 if not kw.has_key('prefix'):
210 kw['prefix'] = self.hostname
211 if not kw.has_key('to'):
212 kw['to'] = self.name.encode(self.encoding)
213
214 arglist = [self, command, kw['to']] + list(parameter_list)
215 irc.IRC.sendMessage(*arglist, **kw)
216
217
218 # IChatClient implementation
219 def userJoined(self, group, user):
220 self.join(
221 "%s!%s@%s" % (user.name, user.name, self.hostname),
222 '#' + group.name)
223
224
225 def userLeft(self, group, user, reason=None):
226 assert reason is None or isinstance(reason, unicode)
227 self.part(
228 "%s!%s@%s" % (user.name, user.name, self.hostname),
229 '#' + group.name,
230 (reason or u"leaving").encode(self.encoding, 'replace'))
231
232
233 def receive(self, sender, recipient, message):
234 #>> :glyph!glyph@adsl-64-123-27-108.dsl.austtx.swbell.net PRIVMSG glyph_ :hello
235
236 # omg???????????
237 if iwords.IGroup.providedBy(recipient):
238 recipientName = '#' + recipient.name
239 else:
240 recipientName = recipient.name
241
242 text = message.get('text', '<an unrepresentable message>')
243 for L in text.splitlines():
244 self.privmsg(
245 '%s!%s@%s' % (sender.name, sender.name, self.hostname),
246 recipientName,
247 L)
248
249
250 def groupMetaUpdate(self, group, meta):
251 if 'topic' in meta:
252 topic = meta['topic']
253 author = meta.get('topic_author', '')
254 self.topic(
255 self.name,
256 '#' + group.name,
257 topic,
258 '%s!%s@%s' % (author, author, self.hostname)
259 )
260
261 # irc.IRC callbacks - starting with login related stuff.
262 nickname = None
263 password = None
264
265 def irc_PASS(self, prefix, params):
266 """Password message -- Register a password.
267
268 Parameters: <password>
269
270 [REQUIRED]
271
272 Note that IRC requires the client send this *before* NICK
273 and USER.
274 """
275 self.password = params[-1]
276
277
278 def irc_NICK(self, prefix, params):
279 """Nick message -- Set your nickname.
280
281 Parameters: <nickname>
282
283 [REQUIRED]
284 """
285 try:
286 nickname = params[0].decode(self.encoding)
287 except UnicodeDecodeError:
288 self.privmsg(
289 NICKSERV,
290 nickname,
291 'Your nickname is cannot be decoded. Please use ASCII or UTF-8. ')
292 self.transport.loseConnection()
293 return
294
295 if self.password is None:
296 self.nickname = nickname
297 self.privmsg(
298 NICKSERV,
299 nickname,
300 'Password?')
301 else:
302 password = self.password
303 self.password = None
304 self.logInAs(nickname, password)
305
306
307 def irc_USER(self, prefix, params):
308 """User message -- Set your realname.
309
310 Parameters: <user> <mode> <unused> <realname>
311 """
312 # Note: who gives a crap about this? The IUser has the real
313 # information we care about. Save it anyway, I guess, just
314 # for fun.
315 self.realname = params[-1]
316
317
318 def irc_NICKSERV_PRIVMSG(self, prefix, params):
319 """Send a (private) message.
320
321 Parameters: <msgtarget> <text to be sent>
322 """
323 target = params[0]
324 password = params[-1]
325
326 if self.nickname is None:
327 # XXX Send an error response here
328 self.transport.loseConnection()
329 elif target.lower() != "nickserv":
330 self.privmsg(
331 NICKSERV,
332 self.nickname,
333 "Denied. Please send me (NickServ) your password.")
334 else:
335 nickname = self.nickname
336 self.nickname = None
337 self.logInAs(nickname, password)
338
339
340 def logInAs(self, nickname, password):
341 d = self.factory.portal.login(
342 credentials.UsernamePassword(nickname, password),
343 self,
344 iwords.IUser)
345 d.addCallbacks(self._cbLogin, self._ebLogin, errbackArgs=(nickname,))
346
347
348 _welcomeMessages = [
349 (irc.RPL_WELCOME,
350 ":connected to Twisted IRC"),
351 (irc.RPL_YOURHOST,
352 ":Your host is %(serviceName)s, running version %(serviceVersion)s"),
353 (irc.RPL_CREATED,
354 ":This server was created on %(creationDate)s"),
355
356 # "Bummer. This server returned a worthless 004 numeric.
357 # I'll have to guess at all the values"
358 # -- epic
359 (irc.RPL_MYINFO,
360 # w and n are the currently supported channel and user modes
361 # -- specify this better
362 "%(serviceName)s %(serviceVersion)s w n"),
363 ]
364
365
366 def _cbLogin(self, (iface, avatar, logout)):
367 assert iface is iwords.IUser, "Realm is buggy, got %r" % (iface,)
368
369 # Let them send messages to the world
370 del self.irc_PRIVMSG
371
372 self.avatar = avatar
373 self.logout = logout
374 self.realm = avatar.realm
375 self.hostname = self.realm.name
376
377 info = {
378 "serviceName": self.hostname,
379 "serviceVersion": copyright.version,
380 "creationDate": ctime(), # XXX
381 }
382 for code, text in self._welcomeMessages:
383 self.sendMessage(code, text % info)
384
385
386 def _ebLogin(self, err, nickname):
387 if err.check(ewords.AlreadyLoggedIn):
388 self.privmsg(
389 NICKSERV,
390 nickname,
391 "Already logged in. No pod people allowed!")
392 elif err.check(ecred.UnauthorizedLogin):
393 self.privmsg(
394 NICKSERV,
395 nickname,
396 "Login failed. Goodbye.")
397 else:
398 log.msg("Unhandled error during login:")
399 log.err(err)
400 self.privmsg(
401 NICKSERV,
402 nickname,
403 "Server error during login. Sorry.")
404 self.transport.loseConnection()
405
406
407 # Great, now that's out of the way, here's some of the interesting
408 # bits
409 def irc_PING(self, prefix, params):
410 """Ping message
411
412 Parameters: <server1> [ <server2> ]
413 """
414 if self.realm is not None:
415 self.sendMessage('PONG', self.hostname)
416
417
418 def irc_QUIT(self, prefix, params):
419 """Quit
420
421 Parameters: [ <Quit Message> ]
422 """
423 self.transport.loseConnection()
424
425
426 def _channelMode(self, group, modes=None, *args):
427 if modes:
428 self.sendMessage(
429 irc.ERR_UNKNOWNMODE,
430 ":Unknown MODE flag.")
431 else:
432 self.channelMode(self.name, '#' + group.name, '+')
433
434
435 def _userMode(self, user, modes=None):
436 if modes:
437 self.sendMessage(
438 irc.ERR_UNKNOWNMODE,
439 ":Unknown MODE flag.")
440 elif user is self.avatar:
441 self.sendMessage(
442 irc.RPL_UMODEIS,
443 "+")
444 else:
445 self.sendMessage(
446 irc.ERR_USERSDONTMATCH,
447 ":You can't look at someone else's modes.")
448
449
450 def irc_MODE(self, prefix, params):
451 """User mode message
452
453 Parameters: <nickname>
454 *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
455
456 """
457 try:
458 channelOrUser = params[0].decode(self.encoding)
459 except UnicodeDecodeError:
460 self.sendMessage(
461 irc.ERR_NOSUCHNICK, params[0],
462 ":No such nickname (could not decode your unicode!)")
463 return
464
465 if channelOrUser.startswith('#'):
466 def ebGroup(err):
467 err.trap(ewords.NoSuchGroup)
468 self.sendMessage(
469 irc.ERR_NOSUCHCHANNEL, params[0],
470 ":That channel doesn't exist.")
471 d = self.realm.lookupGroup(channelOrUser[1:])
472 d.addCallbacks(
473 self._channelMode,
474 ebGroup,
475 callbackArgs=tuple(params[1:]))
476 else:
477 def ebUser(err):
478 self.sendMessage(
479 irc.ERR_NOSUCHNICK,
480 ":No such nickname.")
481
482 d = self.realm.lookupUser(channelOrUser)
483 d.addCallbacks(
484 self._userMode,
485 ebUser,
486 callbackArgs=tuple(params[1:]))
487
488
489 def irc_USERHOST(self, prefix, params):
490 """Userhost message
491
492 Parameters: <nickname> *( SPACE <nickname> )
493
494 [Optional]
495 """
496 pass
497
498
499 def irc_PRIVMSG(self, prefix, params):
500 """Send a (private) message.
501
502 Parameters: <msgtarget> <text to be sent>
503 """
504 try:
505 targetName = params[0].decode(self.encoding)
506 except UnicodeDecodeError:
507 self.sendMessage(
508 irc.ERR_NOSUCHNICK, targetName,
509 ":No such nick/channel (could not decode your unicode!)")
510 return
511
512 messageText = params[-1]
513 if targetName.startswith('#'):
514 target = self.realm.lookupGroup(targetName[1:])
515 else:
516 target = self.realm.lookupUser(targetName).addCallback(lambda user: user.mind)
517
518 def cbTarget(targ):
519 if targ is not None:
520 return self.avatar.send(targ, {"text": messageText})
521
522 def ebTarget(err):
523 self.sendMessage(
524 irc.ERR_NOSUCHNICK, targetName,
525 ":No such nick/channel.")
526
527 target.addCallbacks(cbTarget, ebTarget)
528
529
530 def irc_JOIN(self, prefix, params):
531 """Join message
532
533 Parameters: ( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] )
534 """
535 try:
536 groupName = params[0].decode(self.encoding)
537 except UnicodeDecodeError:
538 self.sendMessage(
539 irc.IRC_NOSUCHCHANNEL, params[0],
540 ":No such channel (could not decode your unicode!)")
541 return
542
543 if groupName.startswith('#'):
544 groupName = groupName[1:]
545
546 def cbGroup(group):
547 def cbJoin(ign):
548 self.userJoined(group, self)
549 self.names(
550 self.name,
551 '#' + group.name,
552 [user.name for user in group.iterusers()])
553 self._sendTopic(group)
554 return self.avatar.join(group).addCallback(cbJoin)
555
556 def ebGroup(err):
557 self.sendMessage(
558 irc.ERR_NOSUCHCHANNEL, '#' + groupName,
559 ":No such channel.")
560
561 self.realm.getGroup(groupName).addCallbacks(cbGroup, ebGroup)
562
563
564 def irc_PART(self, prefix, params):
565 """Part message
566
567 Parameters: <channel> *( "," <channel> ) [ <Part Message> ]
568 """
569 try:
570 groupName = params[0].decode(self.encoding)
571 except UnicodeDecodeError:
572 self.sendMessage(
573 irc.ERR_NOTONCHANNEL, params[0],
574 ":Could not decode your unicode!")
575 return
576
577 if groupName.startswith('#'):
578 groupName = groupName[1:]
579
580 if len(params) > 1:
581 reason = params[1].decode('utf-8')
582 else:
583 reason = None
584
585 def cbGroup(group):
586 def cbLeave(result):
587 self.userLeft(group, self, reason)
588 return self.avatar.leave(group, reason).addCallback(cbLeave)
589
590 def ebGroup(err):
591 err.trap(ewords.NoSuchGroup)
592 self.sendMessage(
593 irc.ERR_NOTONCHANNEL,
594 '#' + groupName,
595 ":" + err.getErrorMessage())
596
597 self.realm.lookupGroup(groupName).addCallbacks(cbGroup, ebGroup)
598
599
600 def irc_NAMES(self, prefix, params):
601 """Names message
602
603 Parameters: [ <channel> *( "," <channel> ) [ <target> ] ]
604 """
605 #<< NAMES #python
606 #>> :benford.openprojects.net 353 glyph = #python :Orban ... @glyph ... Zymurgy skreech
607 #>> :benford.openprojects.net 366 glyph #python :End of /NAMES list.
608 try:
609 channel = params[-1].decode(self.encoding)
610 except UnicodeDecodeError:
611 self.sendMessage(
612 irc.ERR_NOSUCHCHANNEL, params[-1],
613 ":No such channel (could not decode your unicode!)")
614 return
615
616 if channel.startswith('#'):
617 channel = channel[1:]
618
619 def cbGroup(group):
620 self.names(
621 self.name,
622 '#' + group.name,
623 [user.name for user in group.iterusers()])
624
625 def ebGroup(err):
626 err.trap(ewords.NoSuchGroup)
627 # No group? Fine, no names!
628 self.names(
629 self.name,
630 '#' + channel,
631 [])
632
633 self.realm.lookupGroup(channel).addCallbacks(cbGroup, ebGroup)
634
635
636 def irc_TOPIC(self, prefix, params):
637 """Topic message
638
639 Parameters: <channel> [ <topic> ]
640 """
641 try:
642 channel = params[0].decode(self.encoding)
643 except UnicodeDecodeError:
644 self.sendMessage(
645 irc.ERR_NOSUCHCHANNEL,
646 ":That channel doesn't exist (could not decode your unicode!)")
647 return
648
649 if channel.startswith('#'):
650 channel = channel[1:]
651
652 if len(params) > 1:
653 self._setTopic(channel, params[1])
654 else:
655 self._getTopic(channel)
656
657
658 def _sendTopic(self, group):
659 topic = group.meta.get("topic")
660 author = group.meta.get("topic_author") or "<noone>"
661 date = group.meta.get("topic_date", 0)
662 self.topic(self.name, '#' + group.name, topic)
663 self.topicAuthor(self.name, '#' + group.name, author, date)
664
665
666 def _getTopic(self, channel):
667 #<< TOPIC #python
668 #>> :benford.openprojects.net 332 glyph #python :<churchr> I really did. I sprained all my toes.
669 #>> :benford.openprojects.net 333 glyph #python itamar|nyc 994713482
670 def ebGroup(err):
671 err.trap(ewords.NoSuchGroup)
672 self.sendMessage(
673 irc.ERR_NOSUCHCHANNEL, '=', channel,
674 ":That channel doesn't exist.")
675
676 self.realm.lookupGroup(channel).addCallbacks(self._sendTopic, ebGroup)
677
678
679 def _setTopic(self, channel, topic):
680 #<< TOPIC #divunal :foo
681 #>> :glyph!glyph@adsl-64-123-27-108.dsl.austtx.swbell.net TOPIC #divunal :foo
682
683 def cbGroup(group):
684 newMeta = group.meta.copy()
685 newMeta['topic'] = topic
686 newMeta['topic_author'] = self.name
687 newMeta['topic_date'] = int(time())
688
689 def ebSet(err):
690 self.sendMessage(
691 irc.ERR_CHANOPRIVSNEEDED,
692 "#" + group.name,
693 ":You need to be a channel operator to do that.")
694
695 return group.setMetadata(newMeta).addErrback(ebSet)
696
697 def ebGroup(err):
698 err.trap(ewords.NoSuchGroup)
699 self.sendMessage(
700 irc.ERR_NOSUCHCHANNEL, '=', channel,
701 ":That channel doesn't exist.")
702
703 self.realm.lookupGroup(channel).addCallbacks(cbGroup, ebGroup)
704
705
706 def list(self, channels):
707 """Send a group of LIST response lines
708
709 @type channel: C{list} of C{(str, int, str)}
710 @param channel: Information about the channels being sent:
711 their name, the number of participants, and their topic.
712 """
713 for (name, size, topic) in channels:
714 self.sendMessage(irc.RPL_LIST, name, str(size), ":" + topic)
715 self.sendMessage(irc.RPL_LISTEND, ":End of /LIST")
716
717
718 def irc_LIST(self, prefix, params):
719 """List query
720
721 Return information about the indicated channels, or about all
722 channels if none are specified.
723
724 Parameters: [ <channel> *( "," <channel> ) [ <target> ] ]
725 """
726 #<< list #python
727 #>> :orwell.freenode.net 321 exarkun Channel :Users Name
728 #>> :orwell.freenode.net 322 exarkun #python 358 :The Python programming language
729 #>> :orwell.freenode.net 323 exarkun :End of /LIST
730 if params:
731 # Return information about indicated channels
732 try:
733 channels = params[0].decode(self.encoding).split(',')
734 except UnicodeDecodeError:
735 self.sendMessage(
736 irc.ERR_NOSUCHCHANNEL, params[0],
737 ":No such channel (could not decode your unicode!)")
738 return
739
740 groups = []
741 for ch in channels:
742 if ch.startswith('#'):
743 ch = ch[1:]
744 groups.append(self.realm.lookupGroup(ch))
745
746 groups = defer.DeferredList(groups, consumeErrors=True)
747 groups.addCallback(lambda gs: [r for (s, r) in gs if s])
748 else:
749 # Return information about all channels
750 groups = self.realm.itergroups()
751
752 def cbGroups(groups):
753 def gotSize(size, group):
754 return group.name, size, group.meta.get('topic')
755 d = defer.DeferredList([
756 group.size().addCallback(gotSize, group) for group in groups])
757 d.addCallback(lambda results: self.list([r for (s, r) in results if s]))
758 return d
759 groups.addCallback(cbGroups)
760
761
762 def _channelWho(self, group):
763 self.who(self.name, '#' + group.name,
764 [(m.name, self.hostname, self.realm.name, m.name, "H", 0, m.name) fo r m in group.iterusers()])
765
766
767 def _userWho(self, user):
768 self.sendMessage(irc.RPL_ENDOFWHO,
769 ":User /WHO not implemented")
770
771
772 def irc_WHO(self, prefix, params):
773 """Who query
774
775 Parameters: [ <mask> [ "o" ] ]
776 """
777 #<< who #python
778 #>> :x.opn 352 glyph #python aquarius pc-62-31-193-114-du.blueyonder.co. uk y.opn Aquarius H :3 Aquarius
779 # ...
780 #>> :x.opn 352 glyph #python foobar europa.tranquility.net z.opn skreech H :0 skreech
781 #>> :x.opn 315 glyph #python :End of /WHO list.
782 ### also
783 #<< who glyph
784 #>> :x.opn 352 glyph #python glyph adsl-64-123-27-108.dsl.austtx.swbell. net x.opn glyph H :0 glyph
785 #>> :x.opn 315 glyph glyph :End of /WHO list.
786 if not params:
787 self.sendMessage(irc.RPL_ENDOFWHO, ":/WHO not supported.")
788 return
789
790 try:
791 channelOrUser = params[0].decode(self.encoding)
792 except UnicodeDecodeError:
793 self.sendMessage(
794 irc.RPL_ENDOFWHO, params[0],
795 ":End of /WHO list (could not decode your unicode!)")
796 return
797
798 if channelOrUser.startswith('#'):
799 def ebGroup(err):
800 err.trap(ewords.NoSuchGroup)
801 self.sendMessage(
802 irc.RPL_ENDOFWHO, channelOrUser,
803 ":End of /WHO list.")
804 d = self.realm.lookupGroup(channelOrUser[1:])
805 d.addCallbacks(self._channelWho, ebGroup)
806 else:
807 def ebUser(err):
808 err.trap(ewords.NoSuchUser)
809 self.sendMessage(
810 irc.RPL_ENDOFWHO, channelOrUser,
811 ":End of /WHO list.")
812 d = self.realm.lookupUser(channelOrUser)
813 d.addCallbacks(self._userWho, ebUser)
814
815
816
817 def irc_WHOIS(self, prefix, params):
818 """Whois query
819
820 Parameters: [ <target> ] <mask> *( "," <mask> )
821 """
822 def cbUser(user):
823 self.whois(
824 self.name,
825 user.name, user.name, self.realm.name,
826 user.name, self.realm.name, 'Hi mom!', False,
827 int(time() - user.lastMessage), user.signOn,
828 ['#' + group.name for group in user.itergroups()])
829
830 def ebUser(err):
831 err.trap(ewords.NoSuchUser)
832 self.sendMessage(
833 irc.ERR_NOSUCHNICK,
834 params[0],
835 ":No such nick/channel")
836
837 try:
838 user = params[0].decode(self.encoding)
839 except UnicodeDecodeError:
840 self.sendMessage(
841 irc.ERR_NOSUCHNICK,
842 params[0],
843 ":No such nick/channel")
844 return
845
846 self.realm.lookupUser(user).addCallbacks(cbUser, ebUser)
847
848
849 # Unsupported commands, here for legacy compatibility
850 def irc_OPER(self, prefix, params):
851 """Oper message
852
853 Parameters: <name> <password>
854 """
855 self.sendMessage(irc.ERR_NOOPERHOST, ":O-lines not applicable")
856
857
858 class IRCFactory(protocol.ServerFactory):
859 protocol = IRCUser
860
861 def __init__(self, realm, portal):
862 self.realm = realm
863 self.portal = portal
864
865
866 class PBMind(pb.Referenceable):
867 def __init__(self):
868 pass
869
870 def jellyFor(self, jellier):
871 return reflect.qual(PBMind), jellier.invoker.registerReference(self)
872
873 def remote_userJoined(self, user, group):
874 pass
875
876 def remote_userLeft(self, user, group, reason):
877 pass
878
879 def remote_receive(self, sender, recipient, message):
880 pass
881
882 def remote_groupMetaUpdate(self, group, meta):
883 pass
884
885
886 class PBMindReference(pb.RemoteReference):
887 implements(iwords.IChatClient)
888
889 def receive(self, sender, recipient, message):
890 if iwords.IGroup.providedBy(recipient):
891 rec = PBGroup(self.realm, self.avatar, recipient)
892 else:
893 rec = PBUser(self.realm, self.avatar, recipient)
894 return self.callRemote(
895 'receive',
896 PBUser(self.realm, self.avatar, sender),
897 rec,
898 message)
899
900 def groupMetaUpdate(self, group, meta):
901 return self.callRemote(
902 'groupMetaUpdate',
903 PBGroup(self.realm, self.avatar, group),
904 meta)
905
906 def userJoined(self, group, user):
907 return self.callRemote(
908 'userJoined',
909 PBGroup(self.realm, self.avatar, group),
910 PBUser(self.realm, self.avatar, user))
911
912 def userLeft(self, group, user, reason=None):
913 assert reason is None or isinstance(reason, unicode)
914 return self.callRemote(
915 'userLeft',
916 PBGroup(self.realm, self.avatar, group),
917 PBUser(self.realm, self.avatar, user),
918 reason)
919 pb.setUnjellyableForClass(PBMind, PBMindReference)
920
921
922 class PBGroup(pb.Referenceable):
923 def __init__(self, realm, avatar, group):
924 self.realm = realm
925 self.avatar = avatar
926 self.group = group
927
928
929 def processUniqueID(self):
930 return hash((self.realm.name, self.avatar.name, self.group.name))
931
932
933 def jellyFor(self, jellier):
934 return reflect.qual(self.__class__), self.group.name.encode('utf-8'), je llier.invoker.registerReference(self)
935
936
937 def remote_leave(self, reason=None):
938 return self.avatar.leave(self.group, reason)
939
940
941 def remote_send(self, message):
942 return self.avatar.send(self.group, message)
943
944
945 class PBGroupReference(pb.RemoteReference):
946 implements(iwords.IGroup)
947
948 def unjellyFor(self, unjellier, unjellyList):
949 clsName, name, ref = unjellyList
950 self.name = name.decode('utf-8')
951 return pb.RemoteReference.unjellyFor(self, unjellier, [clsName, ref])
952
953 def leave(self, reason=None):
954 return self.callRemote("leave", reason)
955
956 def send(self, message):
957 return self.callRemote("send", message)
958 pb.setUnjellyableForClass(PBGroup, PBGroupReference)
959
960 class PBUser(pb.Referenceable):
961 def __init__(self, realm, avatar, user):
962 self.realm = realm
963 self.avatar = avatar
964 self.user = user
965
966 def processUniqueID(self):
967 return hash((self.realm.name, self.avatar.name, self.user.name))
968
969
970 class ChatAvatar(pb.Referenceable):
971 implements(iwords.IChatClient)
972
973 def __init__(self, avatar):
974 self.avatar = avatar
975
976
977 def jellyFor(self, jellier):
978 return reflect.qual(self.__class__), jellier.invoker.registerReference(s elf)
979
980
981 def remote_join(self, groupName):
982 assert isinstance(groupName, unicode)
983 def cbGroup(group):
984 def cbJoin(ignored):
985 return PBGroup(self.avatar.realm, self.avatar, group)
986 d = self.avatar.join(group)
987 d.addCallback(cbJoin)
988 return d
989 d = self.avatar.realm.getGroup(groupName)
990 d.addCallback(cbGroup)
991 return d
992 registerAdapter(ChatAvatar, iwords.IUser, pb.IPerspective)
993
994 class AvatarReference(pb.RemoteReference):
995 def join(self, groupName):
996 return self.callRemote('join', groupName)
997
998 def quit(self):
999 d = defer.Deferred()
1000 self.broker.notifyOnDisconnect(lambda: d.callback(None))
1001 self.broker.transport.loseConnection()
1002 return d
1003
1004 pb.setUnjellyableForClass(ChatAvatar, AvatarReference)
1005
1006
1007 class WordsRealm(object):
1008 implements(portal.IRealm, iwords.IChatService)
1009
1010 _encoding = 'utf-8'
1011
1012 def __init__(self, name):
1013 self.name = name
1014
1015
1016 def userFactory(self, name):
1017 return User(name)
1018
1019
1020 def groupFactory(self, name):
1021 return Group(name)
1022
1023
1024 def logoutFactory(self, avatar, facet):
1025 def logout():
1026 # XXX Deferred support here
1027 getattr(facet, 'logout', lambda: None)()
1028 avatar.realm = avatar.mind = None
1029 return logout
1030
1031
1032 def requestAvatar(self, avatarId, mind, *interfaces):
1033 if isinstance(avatarId, str):
1034 avatarId = avatarId.decode(self._encoding)
1035
1036 def gotAvatar(avatar):
1037 if avatar.realm is not None:
1038 raise ewords.AlreadyLoggedIn()
1039 for iface in interfaces:
1040 facet = iface(avatar, None)
1041 if facet is not None:
1042 avatar.loggedIn(self, mind)
1043 mind.name = avatarId
1044 mind.realm = self
1045 mind.avatar = avatar
1046 return iface, facet, self.logoutFactory(avatar, facet)
1047 raise NotImplementedError(self, interfaces)
1048
1049 return self.getUser(avatarId).addCallback(gotAvatar)
1050
1051
1052 # IChatService, mostly.
1053 createGroupOnRequest = False
1054 createUserOnRequest = True
1055
1056 def lookupUser(self, name):
1057 raise NotImplementedError
1058
1059
1060 def lookupGroup(self, group):
1061 raise NotImplementedError
1062
1063
1064 def addUser(self, user):
1065 """Add the given user to this service.
1066
1067 This is an internal method intented to be overridden by
1068 L{WordsRealm} subclasses, not called by external code.
1069
1070 @type user: L{IUser}
1071
1072 @rtype: L{twisted.internet.defer.Deferred}
1073 @return: A Deferred which fires with C{None} when the user is
1074 added, or which fails with
1075 L{twisted.words.ewords.DuplicateUser} if a user with the
1076 same name exists already.
1077 """
1078 raise NotImplementedError
1079
1080
1081 def addGroup(self, group):
1082 """Add the given group to this service.
1083
1084 @type group: L{IGroup}
1085
1086 @rtype: L{twisted.internet.defer.Deferred}
1087 @return: A Deferred which fires with C{None} when the group is
1088 added, or which fails with
1089 L{twisted.words.ewords.DuplicateGroup} if a group with the
1090 same name exists already.
1091 """
1092 raise NotImplementedError
1093
1094
1095 def getGroup(self, name):
1096 assert isinstance(name, unicode)
1097 if self.createGroupOnRequest:
1098 def ebGroup(err):
1099 err.trap(ewords.DuplicateGroup)
1100 return self.lookupGroup(name)
1101 return self.createGroup(name).addErrback(ebGroup)
1102 return self.lookupGroup(name)
1103
1104
1105 def getUser(self, name):
1106 assert isinstance(name, unicode)
1107 if self.createUserOnRequest:
1108 def ebUser(err):
1109 err.trap(ewords.DuplicateUser)
1110 return self.lookupUser(name)
1111 return self.createUser(name).addErrback(ebUser)
1112 return self.lookupUser(name)
1113
1114
1115 def createUser(self, name):
1116 assert isinstance(name, unicode)
1117 def cbLookup(user):
1118 return failure.Failure(ewords.DuplicateUser(name))
1119 def ebLookup(err):
1120 err.trap(ewords.NoSuchUser)
1121 return self.userFactory(name)
1122
1123 name = name.lower()
1124 d = self.lookupUser(name)
1125 d.addCallbacks(cbLookup, ebLookup)
1126 d.addCallback(self.addUser)
1127 return d
1128
1129
1130 def createGroup(self, name):
1131 assert isinstance(name, unicode)
1132 def cbLookup(group):
1133 return failure.Failure(ewords.DuplicateGroup(name))
1134 def ebLookup(err):
1135 err.trap(ewords.NoSuchGroup)
1136 return self.groupFactory(name)
1137
1138 name = name.lower()
1139 d = self.lookupGroup(name)
1140 d.addCallbacks(cbLookup, ebLookup)
1141 d.addCallback(self.addGroup)
1142 return d
1143
1144
1145 class InMemoryWordsRealm(WordsRealm):
1146 def __init__(self, *a, **kw):
1147 super(InMemoryWordsRealm, self).__init__(*a, **kw)
1148 self.users = {}
1149 self.groups = {}
1150
1151
1152 def itergroups(self):
1153 return defer.succeed(self.groups.itervalues())
1154
1155
1156 def addUser(self, user):
1157 if user.name in self.users:
1158 return defer.fail(failure.Failure(ewords.DuplicateUser()))
1159 self.users[user.name] = user
1160 return defer.succeed(user)
1161
1162
1163 def addGroup(self, group):
1164 if group.name in self.groups:
1165 return defer.fail(failure.Failure(ewords.DuplicateGroup()))
1166 self.groups[group.name] = group
1167 return defer.succeed(group)
1168
1169
1170 def lookupUser(self, name):
1171 assert isinstance(name, unicode)
1172 name = name.lower()
1173 try:
1174 user = self.users[name]
1175 except KeyError:
1176 return defer.fail(failure.Failure(ewords.NoSuchUser(name)))
1177 else:
1178 return defer.succeed(user)
1179
1180
1181 def lookupGroup(self, name):
1182 assert isinstance(name, unicode)
1183 name = name.lower()
1184 try:
1185 group = self.groups[name]
1186 except KeyError:
1187 return defer.fail(failure.Failure(ewords.NoSuchGroup(name)))
1188 else:
1189 return defer.succeed(group)
1190
1191 __all__ = [
1192 'Group', 'User',
1193
1194 'WordsRealm', 'InMemoryWordsRealm',
1195 ]
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/words/scripts/im.py ('k') | third_party/twisted_8_1/twisted/words/tap.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698