Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / words / im / basesupport.py
1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 #
5
6 """Instance Messenger base classes for protocol support.
7
8 You will find these useful if you're adding a new protocol to IM.
9 """
10
11 # Abstract representation of chat "model" classes
12
13 from twisted.words.im.locals import ONLINE, OFFLINE, OfflineError
14 from twisted.words.im import interfaces
15
16 from twisted.internet.protocol import Protocol
17
18 from twisted.python.reflect import prefixedMethods
19 from twisted.persisted import styles
20
21 from twisted.internet import error
22
23 class AbstractGroup:
24     def __init__(self, name, account):
25         self.name = name
26         self.account = account
27
28     def getGroupCommands(self):
29         """finds group commands
30
31         these commands are methods on me that start with imgroup_; they are
32         called with no arguments
33         """
34         return prefixedMethods(self, "imgroup_")
35
36     def getTargetCommands(self, target):
37         """finds group commands
38
39         these commands are methods on me that start with imgroup_; they are
40         called with a user present within this room as an argument
41
42         you may want to override this in your group in order to filter for
43         appropriate commands on the given user
44         """
45         return prefixedMethods(self, "imtarget_")
46
47     def join(self):
48         if not self.account.client:
49             raise OfflineError
50         self.account.client.joinGroup(self.name)
51
52     def leave(self):
53         if not self.account.client:
54             raise OfflineError
55         self.account.client.leaveGroup(self.name)
56
57     def __repr__(self):
58         return '<%s %r>' % (self.__class__, self.name)
59
60     def __str__(self):
61         return '%s@%s' % (self.name, self.account.accountName)
62
63 class AbstractPerson:
64     def __init__(self, name, baseAccount):
65         self.name = name
66         self.account = baseAccount
67         self.status = OFFLINE
68
69     def getPersonCommands(self):
70         """finds person commands
71
72         these commands are methods on me that start with imperson_; they are
73         called with no arguments
74         """
75         return prefixedMethods(self, "imperson_")
76
77     def getIdleTime(self):
78         """
79         Returns a string.
80         """
81         return '--'
82
83     def __repr__(self):
84         return '<%s %r/%s>' % (self.__class__, self.name, self.status)
85
86     def __str__(self):
87         return '%s@%s' % (self.name, self.account.accountName)
88
89 class AbstractClientMixin:
90     """Designed to be mixed in to a Protocol implementing class.
91
92     Inherit from me first.
93
94     @ivar _logonDeferred: Fired when I am done logging in.
95     """
96     def __init__(self, account, chatui, logonDeferred):
97         for base in self.__class__.__bases__:
98             if issubclass(base, Protocol):
99                 self.__class__._protoBase = base
100                 break
101         else:
102             pass
103         self.account = account
104         self.chat = chatui
105         self._logonDeferred = logonDeferred
106
107     def connectionMade(self):
108         self._protoBase.connectionMade(self)
109
110     def connectionLost(self, reason):
111         self.account._clientLost(self, reason)
112         self.unregisterAsAccountClient()
113         return self._protoBase.connectionLost(self, reason)
114
115     def unregisterAsAccountClient(self):
116         """Tell the chat UI that I have `signed off'.
117         """
118         self.chat.unregisterAccountClient(self)
119
120
121 class AbstractAccount(styles.Versioned):
122     """Base class for Accounts.
123
124     I am the start of an implementation of L{IAccount<interfaces.IAccount>}, I
125     implement L{isOnline} and most of L{logOn}, though you'll need to implement
126     L{_startLogOn} in a subclass.
127
128     @cvar _groupFactory: A Callable that will return a L{IGroup} appropriate
129         for this account type.
130     @cvar _personFactory: A Callable that will return a L{IPerson} appropriate
131         for this account type.
132
133     @type _isConnecting: boolean
134     @ivar _isConnecting: Whether I am in the process of establishing a
135     connection to the server.
136     @type _isOnline: boolean
137     @ivar _isOnline: Whether I am currently on-line with the server.
138
139     @ivar accountName:
140     @ivar autoLogin:
141     @ivar username:
142     @ivar password:
143     @ivar host:
144     @ivar port:
145     """
146
147     _isOnline = 0
148     _isConnecting = 0
149     client = None
150
151     _groupFactory = AbstractGroup
152     _personFactory = AbstractPerson
153
154     persistanceVersion = 2
155
156     def __init__(self, accountName, autoLogin, username, password, host, port):
157         self.accountName = accountName
158         self.autoLogin = autoLogin
159         self.username = username
160         self.password = password
161         self.host = host
162         self.port = port
163
164         self._groups = {}
165         self._persons = {}
166
167     def upgrateToVersion2(self):
168         # Added in CVS revision 1.16.
169         for k in ('_groups', '_persons'):
170             if not hasattr(self, k):
171                 setattr(self, k, {})
172
173     def __getstate__(self):
174         state = styles.Versioned.__getstate__(self)
175         for k in ('client', '_isOnline', '_isConnecting'):
176             try:
177                 del state[k]
178             except KeyError:
179                 pass
180         return state
181
182     def isOnline(self):
183         return self._isOnline
184
185     def logOn(self, chatui):
186         """Log on to this account.
187
188         Takes care to not start a connection if a connection is
189         already in progress.  You will need to implement
190         L{_startLogOn} for this to work, and it would be a good idea
191         to override L{_loginFailed} too.
192
193         @returntype: Deferred L{interfaces.IClient}
194         """
195         if (not self._isConnecting) and (not self._isOnline):
196             self._isConnecting = 1
197             d = self._startLogOn(chatui)
198             d.addCallback(self._cb_logOn)
199             # if chatui is not None:
200             # (I don't particularly like having to pass chatUI to this function,
201             # but we haven't factored it out yet.)
202             d.addCallback(chatui.registerAccountClient)
203             d.addErrback(self._loginFailed)
204             return d
205         else:
206             raise error.ConnectError("Connection in progress")
207
208     def getGroup(self, name):
209         """Group factory.
210
211         @param name: Name of the group on this account.
212         @type name: string
213         """
214         group = self._groups.get(name)
215         if group is None:
216             group = self._groupFactory(name, self)
217             self._groups[name] = group
218         return group
219
220     def getPerson(self, name):
221         """Person factory.
222
223         @param name: Name of the person on this account.
224         @type name: string
225         """
226         person = self._persons.get(name)
227         if person is None:
228             person = self._personFactory(name, self)
229             self._persons[name] = person
230         return person
231
232     def _startLogOn(self, chatui):
233         """Start the sign on process.
234
235         Factored out of L{logOn}.
236
237         @returntype: Deferred L{interfaces.IClient}
238         """
239         raise NotImplementedError()
240
241     def _cb_logOn(self, client):
242         self._isConnecting = 0
243         self._isOnline = 1
244         self.client = client
245         return client
246
247     def _loginFailed(self, reason):
248         """Errorback for L{logOn}.
249
250         @type reason: Failure
251
252         @returns: I{reason}, for further processing in the callback chain.
253         @returntype: Failure
254         """
255         self._isConnecting = 0
256         self._isOnline = 0 # just in case
257         return reason
258
259     def _clientLost(self, client, reason):
260         self.client = None
261         self._isConnecting = 0
262         self._isOnline = 0
263         return reason
264
265     def __repr__(self):
266         return "<%s: %s (%s@%s:%s)>" % (self.__class__,
267                                         self.accountName,
268                                         self.username,
269                                         self.host,
270                                         self.port)