Fixed issues with the credentials dialog
[gc-dialer] / src / session.py
1 import os
2 import time
3 import logging
4
5 from PyQt4 import QtCore
6
7 from util import qore_utils
8 from util import concurrent
9
10 from backends import gv_backend
11
12
13 _moduleLogger = logging.getLogger(__name__)
14
15
16 class Draft(QtCore.QObject):
17
18         sendingMessage = QtCore.pyqtSignal()
19         sentMessage = QtCore.pyqtSignal()
20         calling = QtCore.pyqtSignal()
21         called = QtCore.pyqtSignal()
22         cancelling = QtCore.pyqtSignal()
23         cancelled = QtCore.pyqtSignal()
24         error = QtCore.pyqtSignal(str)
25
26         recipientsChanged = QtCore.pyqtSignal()
27
28         def __init__(self, pool):
29                 QtCore.QObject.__init__(self)
30                 self._contacts = {}
31                 self._pool = pool
32
33         def send(self, text):
34                 assert 0 < len(self._contacts)
35                 self.sendingMessage.emit()
36                 self.error.emit("Not Implemented")
37                 # self.clear()
38
39         def call(self):
40                 assert len(self._contacts) == 1
41                 self.calling.emit()
42                 self.error.emit("Not Implemented")
43                 # self.clear()
44
45         def cancel(self):
46                 self.cancelling.emit()
47                 self.error.emit("Not Implemented")
48
49         def add_contact(self, contact, details):
50                 assert contact not in self._contacts
51                 self._contacts[contact] = details
52                 self.recipientsChanged.emit()
53
54         def remove_contact(self, contact):
55                 assert contact not in self._contacts
56                 del self._contacts[contact]
57                 self.recipientsChanged.emit()
58
59         def get_contacts(self, contact):
60                 return self._contacts
61
62         def clear(self):
63                 self._contacts = {}
64                 self.recipientsChanged.emit()
65
66
67 class Session(QtCore.QObject):
68
69         stateChange = QtCore.pyqtSignal(str)
70         loggedOut = QtCore.pyqtSignal()
71         loggedIn = QtCore.pyqtSignal()
72         callbackNumberChanged = QtCore.pyqtSignal(str)
73
74         contactsUpdated = QtCore.pyqtSignal()
75         messagesUpdated = QtCore.pyqtSignal()
76         historyUpdated = QtCore.pyqtSignal()
77         dndStateChange = QtCore.pyqtSignal(bool)
78
79         error = QtCore.pyqtSignal(str)
80
81         LOGGEDOUT_STATE = "logged out"
82         LOGGINGIN_STATE = "logging in"
83         LOGGEDIN_STATE = "logged in"
84
85         _LOGGEDOUT_TIME = -1
86         _LOGGINGIN_TIME = 0
87
88         def __init__(self, cachePath = None):
89                 QtCore.QObject.__init__(self)
90                 self._pool = qore_utils.AsyncPool()
91                 self._backend = None
92                 self._loggedInTime = self._LOGGEDOUT_TIME
93                 self._loginOps = []
94                 self._cachePath = cachePath
95                 self._username = None
96                 self._draft = Draft(self._pool)
97
98                 self._contacts = []
99                 self._messages = []
100                 self._history = []
101                 self._dnd = False
102
103         @property
104         def state(self):
105                 return {
106                         self._LOGGEDOUT_TIME: self.LOGGEDOUT_STATE,
107                         self._LOGGINGIN_TIME: self.LOGGINGIN_STATE,
108                 }.get(self._loggedInTime, self.LOGGEDIN_STATE)
109
110         @property
111         def draft(self):
112                 return self._draft
113
114         def login(self, username, password):
115                 assert self.state == self.LOGGEDOUT_STATE
116                 if self._cachePath is not None:
117                         cookiePath = os.path.join(self._cachePath, "%s.cookies" % username)
118                 else:
119                         cookiePath = None
120
121                 if self._username != username or self._backend is None:
122                         self._backend = gv_backend.GVDialer(cookiePath)
123
124                 self._pool.start()
125                 le = concurrent.AsyncLinearExecution(self._pool, self._login)
126                 le.start(username, password)
127
128         def logout(self):
129                 assert self.state != self.LOGGEDOUT_STATE
130                 self._pool.stop()
131                 self.error.emit("Not Implemented")
132
133         def clear(self):
134                 assert self.state == self.LOGGEDOUT_STATE
135                 self._backend = None
136                 self._draft.clear()
137                 self._contacts = []
138                 self.contactsUpdated.emit()
139                 self._messages = []
140                 self.messagesUpdated.emit()
141                 self._history = []
142                 self.historyUpdated.emit()
143                 self._dnd = False
144                 self.dndStateChange.emit(self._dnd)
145
146         def update_contacts(self):
147                 le = concurrent.AsyncLinearExecution(self._pool, self._update_contacts)
148                 self._perform_op_while_loggedin(le)
149
150         def get_contacts(self):
151                 return self._contacts
152
153         def update_messages(self):
154                 le = concurrent.AsyncLinearExecution(self._pool, self._update_messages)
155                 self._perform_op_while_loggedin(le)
156
157         def get_messages(self):
158                 return self._messages
159
160         def update_history(self):
161                 le = concurrent.AsyncLinearExecution(self._pool, self._update_history)
162                 self._perform_op_while_loggedin(le)
163
164         def get_history(self):
165                 return self._history
166
167         def update_dnd(self):
168                 le = concurrent.AsyncLinearExecution(self._pool, self._update_dnd)
169                 self._perform_op_while_loggedin(le)
170
171         def set_dnd(self, dnd):
172                 assert self.state == self.LOGGEDIN_STATE
173                 self.error.emit("Not Implemented")
174
175         def get_dnd(self):
176                 return self._dnd
177
178         def get_callback_numbers(self):
179                 return []
180
181         def get_callback_number(self):
182                 return ""
183
184         def set_callback_number(self):
185                 assert self.state == self.LOGGEDIN_STATE
186                 self.error.emit("Not Implemented")
187
188         def _login(self, username, password):
189                 self._loggedInTime = self._LOGGINGIN_TIME
190                 self.stateChange.emit(self.LOGGINGIN_STATE)
191                 finalState = self.LOGGEDOUT_STATE
192                 try:
193                         isLoggedIn = False
194
195                         if not isLoggedIn and self._backend.is_quick_login_possible():
196                                 try:
197                                         isLoggedIn = yield (
198                                                 self._backend.is_authed,
199                                                 (),
200                                                 {},
201                                         )
202                                 except Exception, e:
203                                         self.error.emit(str(e))
204                                         return
205                                 if isLoggedIn:
206                                         _moduleLogger.info("Logged in through cookies")
207
208                         if not isLoggedIn:
209                                 try:
210                                         isLoggedIn = yield (
211                                                 self._backend.login,
212                                                 (username, password),
213                                                 {},
214                                         )
215                                 except Exception, e:
216                                         self.error.emit(str(e))
217                                         return
218                                 if isLoggedIn:
219                                         _moduleLogger.info("Logged in through credentials")
220
221                         if isLoggedIn:
222                                 self._loggedInTime = time.time()
223                                 self._username = username
224                                 finalState = self.LOGGEDIN_STATE
225                                 self.loggedIn.emit()
226                                 # if the username is the same, do nothing
227                                 # else clear the in-memory caches and attempt to load from file-caches
228                                 # If caches went from empty to something, fire signals
229                                 # Fire off queued async ops
230                 except Exception, e:
231                         self.error.emit(str(e))
232                 finally:
233                         self.stateChange.emit(finalState)
234
235         def _update_contacts(self):
236                 self.error.emit("Not Implemented")
237                 try:
238                         isLoggedIn = yield (
239                                 self._backend.is_authed,
240                                 (),
241                                 {},
242                         )
243                 except Exception, e:
244                         self.error.emit(str(e))
245                         return
246
247         def _update_messages(self):
248                 self.error.emit("Not Implemented")
249                 try:
250                         isLoggedIn = yield (
251                                 self._backend.is_authed,
252                                 (),
253                                 {},
254                         )
255                 except Exception, e:
256                         self.error.emit(str(e))
257                         return
258
259         def _update_history(self):
260                 self.error.emit("Not Implemented")
261                 try:
262                         isLoggedIn = yield (
263                                 self._backend.is_authed,
264                                 (),
265                                 {},
266                         )
267                 except Exception, e:
268                         self.error.emit(str(e))
269                         return
270
271         def _update_dnd(self):
272                 self.error.emit("Not Implemented")
273                 try:
274                         isLoggedIn = yield (
275                                 self._backend.is_authed,
276                                 (),
277                                 {},
278                         )
279                 except Exception, e:
280                         self.error.emit(str(e))
281                         return
282
283         def _perform_op_while_loggedin(self, op):
284                 if self.state == self.LOGGEDIN_STATE:
285                         op()
286                 else:
287                         self._push_login_op(op)
288
289         def _push_login_op(self, asyncOp):
290                 assert self.state != self.LOGGEDIN_STATE
291                 if asyncOp in self._loginOps:
292                         _moduleLogger.info("Skipping queueing duplicate op: %r" % asyncOp)
293                         return
294                 self._loginOps.append(asyncOp)