~ara/ubuntu/lucid/mago/fix_tests_lucid

« back to all changes in this revision

Viewing changes to mago/application/pidgin/pidgin.py

  • Committer: Bazaar Package Importer
  • Author(s): Ara Pulido
  • Date: 2009-08-04 09:21:40 UTC
  • Revision ID: james.westby@ubuntu.com-20090804092140-7ggrh3nlujflk0v8
Tags: upstream-0.1
ImportĀ upstreamĀ versionĀ 0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from ..main import Application
 
2
from shutil import move, rmtree, copytree
 
3
import ldtp, ooldtp
 
4
import os, traceback
 
5
from time import time, sleep
 
6
from ConfigParser import ConfigParser
 
7
from string import Formatter
 
8
from buddy import new_buddy
 
9
from inspect import getsourcefile
 
10
 
 
11
class AccountInfo(object):
 
12
    PURPLE_PROTOCOLS = {
 
13
        'XMPP' : 'prpl-jabber',
 
14
        'MSN' : 'prpl-msn'
 
15
        }
 
16
    def __init__(self, name, credentials):
 
17
        if not isinstance(credentials, ConfigParser):
 
18
            creds_fn = credentials
 
19
            credentials = ConfigParser()
 
20
            credentials.read(creds_fn)
 
21
        self.details = dict(credentials.items(name))
 
22
        self.name = name
 
23
 
 
24
    def __getattr__(self, name):
 
25
        try:
 
26
            return self.details[name]
 
27
        except KeyError:
 
28
            raise AttributeError
 
29
 
 
30
    @property
 
31
    def prpl_protocol(self):
 
32
        return self.PURPLE_PROTOCOLS[self.protocol]
 
33
 
 
34
    @property
 
35
    def username(self):
 
36
        return self._get_username(False)
 
37
 
 
38
    @property
 
39
    def alias(self):
 
40
        return self.details.get('alias', self._get_username(False))
 
41
 
 
42
    @property
 
43
    def username_and_domain(self):
 
44
        return self._get_username(True)
 
45
 
 
46
    @property
 
47
    def template_args(self):
 
48
        args = {}
 
49
        for arg in ('prpl_protocol', 'username_and_domain', 
 
50
                    'password', 'alias'):
 
51
            args[arg] = getattr(self, arg)
 
52
        return args
 
53
 
 
54
    def _get_username(self, include_resource=False):
 
55
        if self.protocol == 'XMPP':
 
56
            username = '%s@%s' % (self.details['username'],
 
57
                                  self.details['domain'])
 
58
            if include_resource:
 
59
                username += '/%s' % self.details['resource']
 
60
        else:
 
61
            username = self.details['username']
 
62
        return username
 
63
 
 
64
class Pidgin(Application):
 
65
    """
 
66
    Class that manages the Pidgin IM application
 
67
    """
 
68
    # Pidgin constants
 
69
    WINDOW = "frmBuddyList"
 
70
    LAUNCHER = "pidgin"
 
71
 
 
72
    TTBL_BUDDIES = "ttbl0"
 
73
 
 
74
    DLG_ACCOUNTS = "dlgAccounts"
 
75
    DLG_ADD_ACCOUNT = "dlgAddAccount"
 
76
    BTN_ADD = "btnAdd"
 
77
    CBO_PROTOCOL = "cboProtocol"
 
78
    TBL_ACCOUNTS = "tbl0"
 
79
 
 
80
    MNU_CLOSE = "mnuClose"
 
81
    
 
82
    def emptyTest(self):
 
83
        ldtp.generatekeyevent('<alt><F9>')
 
84
        sleep(3)
 
85
 
 
86
    def open(self, clean_profile=True, credentials=''):
 
87
        """
 
88
        It saves the old profile (if needed) and
 
89
        set up a new one. After this initial process,
 
90
        it opens the application
 
91
 
 
92
        @type clean_profile: boolean
 
93
        @param clean_profile: True, to back up the old profile and create a 
 
94
            new one (default)
 
95
        @type credentials: string
 
96
        @param credentials: Path to the config file with accounts information
 
97
        """
 
98
        clean_profile = clean_profile not in ('False', '0', False)
 
99
        self.creds_fn = self._normalize_path(credentials)
 
100
        self.credentials = ConfigParser()
 
101
        self.credentials.read(self.creds_fn)
 
102
        self.buddy = None
 
103
 
 
104
        if clean_profile:
 
105
            self.backup_config()
 
106
 
 
107
        Application.open(self)
 
108
 
 
109
    def generate_profile(self, profile_template, template_args=None):
 
110
        """
 
111
        It uses the profile_template and the
 
112
        credentials to build a new profile folder
 
113
        """
 
114
        os.mkdir(os.path.expanduser('~/.purple'))
 
115
 
 
116
        formatter = Formatter()
 
117
        for fn in os.listdir(profile_template):
 
118
            if os.path.isdir(os.path.join(profile_template, fn)):
 
119
                copytree(os.path.join(profile_template, fn),
 
120
                         os.path.join(os.path.expanduser('~/.purple'), fn))
 
121
                continue
 
122
            buf = open(os.path.join(profile_template, fn)).read()
 
123
            f = open(os.path.join(os.path.expanduser('~/.purple'), fn), 'w')
 
124
            try:
 
125
                buf = formatter.format(buf, **template_args)
 
126
            except KeyError, e:
 
127
                raise Exception, \
 
128
                    'no section/key in %s: %s' % (self.creds_fn, e)
 
129
            f.write(buf)
 
130
            f.close()
 
131
 
 
132
    def _normalize_path(cls, path):
 
133
        return path
 
134
#        return os.path.normpath(
 
135
#            os.path.join(os.path.dirname(getsourcefile(cls)), path))
 
136
    _normalize_path = classmethod(_normalize_path)
 
137
 
 
138
    def backup_config(self):
 
139
        """
 
140
        It saves the configuration of Pidgin in a path
 
141
        called ~/.purple.bak{.n}
 
142
        """
 
143
        p = os.path.expanduser('~/.purple.bak')
 
144
        backup_path = p
 
145
        i = 2
 
146
        while os.path.exists(backup_path):
 
147
            backup_path = '%s.%d' % (p, i)
 
148
            i += 1
 
149
            
 
150
        try:
 
151
            move(os.path.expanduser('~/.purple'), backup_path)
 
152
        except IOError:
 
153
            pass
 
154
        else:
 
155
            self.backup_path = backup_path
 
156
 
 
157
    def restore_config(self):
 
158
        """
 
159
        It deletes the configuration folder and restore then
 
160
        one backed up (at backup_path)
 
161
        """
 
162
        try:
 
163
            rmtree('~/.purple')
 
164
        except OSError:
 
165
            pass
 
166
 
 
167
        try:
 
168
            move(self.backup_path,
 
169
                 os.path.expanduser('~/.purple'))
 
170
        except IOError:
 
171
            traceback.print_exc()
 
172
 
 
173
    def wait_for_account_connect(self, account_info, timeout=15):
 
174
        """
 
175
        It waits for an account to be connected.
 
176
        A timeout value can be passed, being default 15secs.
 
177
 
 
178
        It raises an exception if the timeout expires.
 
179
 
 
180
        @param account_info: The account information.
 
181
        @type account_info: L{AccountInfo}
 
182
        @param timeout: Number of seconds to wait for the accout to be connected (default:15s)
 
183
        @type timeout: integer
 
184
        """
 
185
        starttime = time()
 
186
        while not self.account_connected(account_info):
 
187
            if time() - starttime >= timeout:
 
188
                raise Exception('IM server connection timed out')
 
189
            exists = ldtp.waittillguiexist('dlgSSLCertificateVerification',
 
190
                                           guiTimeOut=1)
 
191
            if exists:
 
192
                ldtp.click('dlgSSLCertificateVerification', 'btnAccept')
 
193
 
 
194
    def account_connected(self, account_info):
 
195
        '''
 
196
        Checks to see if a specified account is connected,
 
197
        @param account_info: The account information.
 
198
        @type account_info: L{AccountInfo}
 
199
 
 
200
        @return True, if the account if connected, False if not.
 
201
        '''
 
202
        ldtp.remap(self.WINDOW)
 
203
        window = ooldtp.context(self.WINDOW)
 
204
        objs = window.getobjectlist()
 
205
        # It parses the account's submenu, if there is a menu 
 
206
        # item "no actions available", then the account is not yet connected.
 
207
        for obj in objs:
 
208
            if obj.startswith('mnuNoactionsavailable'):
 
209
                parent = ldtp.getobjectproperty(self.WINDOW,obj, 'parent')
 
210
                # TODO, put in resource and protocol for more accuracy.
 
211
                if parent.startswith(
 
212
                    'mnu%s' % account_info.username_and_domain) and \
 
213
                    parent.endswith('(%s)' % account_info.protocol):
 
214
                    return False
 
215
        return True
 
216
 
 
217
    def buddy_available(self, alias):
 
218
        """
 
219
        It searches for a given alias to be in the buddy list
 
220
 
 
221
        @type alias: string
 
222
        @param alias: The name of the buddy to search
 
223
 
 
224
        @return 1, if the buddy is available, 0 otherwise.
 
225
        """
 
226
        return ldtp.doesrowexist(self.WINDOW, self.TTBL_BUDDIES, alias)
 
227
 
 
228
    def wait_for_buddy(self, alias, timeout=15):
 
229
        """
 
230
        It waits for a buddy to be connected.
 
231
        A timeout value can be passed, being default 15secs.
 
232
 
 
233
        It raises an exception if the timeout expires.
 
234
 
 
235
        @type alias: string 
 
236
        @param alias: The name of the buddy to wait to be available
 
237
        @type timeout: integer
 
238
        @param timeout: Number of seconds to wait for the buddy to be connected (default:15s)
 
239
        """
 
240
        starttime = time()
 
241
        while not self.buddy_available(alias):
 
242
            if time() - starttime >= timeout:
 
243
                raise Exception('waiting for buddy timed out')
 
244
            sleep(1)
 
245
 
 
246
    def send_message(self, alias, msg):
 
247
        """
 
248
        It sends a message to a particular buddy.
 
249
 
 
250
        It raises an exception if the particuar buddy is not online.
 
251
 
 
252
        @type alias: string
 
253
        @param alias: The name of the buddy to send the message to
 
254
        @type msg: string
 
255
        @param msg: The message to send to the buddy
 
256
        """
 
257
        if not ldtp.doesrowexist(self.WINDOW, self.TTBL_BUDDIES, alias):
 
258
            raise Exception("user %s is not online" % alias)
 
259
        ldtp.selectrow(self.WINDOW, self.TTBL_BUDDIES, alias)
 
260
        ldtp.generatekeyevent('<return>')
 
261
 
 
262
        ldtp.waittillguiexist('frm' + alias.replace(' ', ''))
 
263
        
 
264
        frame = ooldtp.context('frm' + alias.replace(' ', ''))
 
265
 
 
266
        frame.settextvalue('txt1', msg)
 
267
 
 
268
        ldtp.generatekeyevent('<return>')
 
269
 
 
270
    def get_conversation_log(self, alias):
 
271
        """
 
272
        It gets the text of a on-going conversation (no backlog)
 
273
 
 
274
        @type alias: string
 
275
        @param alias: The buddy to get the conversation text
 
276
 
 
277
        @return The text of the conversion. '' if the conversation does not exist
 
278
        """
 
279
        if not ldtp.guiexist('frm' + alias.replace(' ', '')):
 
280
            return ''
 
281
 
 
282
        return ldtp.gettextvalue('frm' + alias.replace(' ', ''), 'txt0')
 
283
 
 
284
    def close(self):
 
285
        """
 
286
        It restore the previous configuration of Pidgin and exists
 
287
        """
 
288
        if hasattr(self, 'backup_path'):
 
289
            self.restore_config()
 
290
        Application.close(self)
 
291
        
 
292
    def get_all_windows(self):
 
293
        """
 
294
        It gets the list of the pidgin windows
 
295
 
 
296
        @return A list containing the name of the windows
 
297
        """
 
298
        windows = []
 
299
        for w in ldtp.getwindowlist():
 
300
            try:
 
301
                if ldtp.getobjectproperty(w, w, 'parent') == 'pidgin':
 
302
                    windows.append(w)
 
303
            except LdtpExecutionError:
 
304
                continue
 
305
        return windows
 
306
    
 
307
    def close_conversation(self, window_name):
 
308
        """
 
309
        It closes a conversation window
 
310
 
 
311
        @type window_name: string
 
312
        @param window_name: The name of the conversation to close
 
313
        """
 
314
        ldtp.selectmenuitem(window_name, self.MNU_CLOSE)
 
315
 
 
316
    def buddy_login(self, account_info):
 
317
        self.buddy = \
 
318
            new_buddy(account_info.username,
 
319
                      account_info.password,
 
320
                      account_info.protocol)
 
321
 
 
322
        print 'connecting buddy'
 
323
        self.buddy.connect()
 
324
        print 'connected buddy'
 
325