root/branches/gajim_0.9/src/common/helpers.py

Revision 4833, 13.9 kB (checked in by asterix, 3 years ago)

catch correct exception when interrupt system call

  • Property svn:eol-style set to LF
Line 
1##      common/helpers.py
2##
3## Contributors for this file:
4##      - Yann Le Boulanger <asterix@lagaule.org>
5##      - Nikos Kouremenos <kourem@gmail.com>
6##
7## Copyright (C) 2003-2004 Yann Le Boulanger <asterix@lagaule.org>
8##                         Vincent Hanquez <tab@snarc.org>
9## Copyright (C) 2005 Yann Le Boulanger <asterix@lagaule.org>
10##                    Vincent Hanquez <tab@snarc.org>
11##                    Nikos Kouremenos <nkour@jabber.org>
12##                    Dimitur Kirov <dkirov@gmail.com>
13##                    Travis Shirk <travis@pobox.com>
14##                    Norman Rasmussen <norman@rasmussen.co.za>
15##
16## This program is free software; you can redistribute it and/or modify
17## it under the terms of the GNU General Public License as published
18## by the Free Software Foundation; version 2 only.
19##
20## This program is distributed in the hope that it will be useful,
21## but WITHOUT ANY WARRANTY; without even the implied warranty of
22## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23## GNU General Public License for more details.
24##
25
26import sre
27import os
28import urllib
29import errno
30import sys
31import stat
32from pysqlite2 import dbapi2 as sqlite
33
34import gajim
35import logger
36import i18n
37from xmpp_stringprep import nodeprep, resourceprep, nameprep
38
39try:
40        import winsound # windows-only built-in module for playing wav
41except:
42        pass
43
44_ = i18n._
45Q_ = i18n.Q_
46
47class InvalidFormat(Exception):
48        pass
49
50def parse_jid(jidstring):
51        '''Perform stringprep on all JID fragments from a string
52        and return the full jid'''
53        # This function comes from http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
54
55        user = None
56        server = None
57        resource = None
58
59        # Search for delimiters
60        user_sep = jidstring.find('@')
61        res_sep  = jidstring.find('/')
62
63        if user_sep == -1:             
64                if res_sep == -1:
65                        # host
66                        server = jidstring
67                else:
68                        # host/resource
69                        server = jidstring[0:res_sep]
70                        resource = jidstring[res_sep + 1:] or None
71        else:
72                if res_sep == -1:
73                        # user@host
74                        user = jidstring[0:user_sep] or None
75                        server = jidstring[user_sep + 1:]
76                else:
77                        if user_sep < res_sep:
78                                # user@host/resource
79                                user = jidstring[0:user_sep] or None
80                                server = jidstring[user_sep + 1:user_sep + (res_sep - user_sep)]
81                                resource = jidstring[res_sep + 1:] or None
82                        else:
83                                # server/resource (with an @ in resource)
84                                server = jidstring[0:res_sep]
85                                resource = jidstring[res_sep + 1:] or None
86
87        return prep(user, server, resource)
88
89def parse_resource(resource):
90        '''Perform stringprep on resource and return it'''
91        if resource:
92                try:
93                        return resourceprep.prepare(unicode(resource))
94                except UnicodeError:
95                        raise InvalidFormat, 'Invalid character in resource.'
96
97def prep(user, server, resource):
98        '''Perform stringprep on all JID fragments and return the full jid'''
99        # This function comes from
100        #http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
101
102        if user:
103                try:
104                        user = nodeprep.prepare(unicode(user))
105                except UnicodeError:
106                        raise InvalidFormat, _('Invalid character in username.')
107        else:
108                user = None
109
110        if not server:
111                raise InvalidFormat, _('Server address required.')
112        else:
113                try:
114                        server = nameprep.prepare(unicode(server))
115                except UnicodeError:
116                        raise InvalidFormat, _('Invalid character in hostname.')
117
118        if resource:
119                try:
120                        resource = resourceprep.prepare(unicode(resource))
121                except UnicodeError:
122                        raise InvalidFormat, _('Invalid character in resource.')
123        else:
124                resource = None
125
126        if user:
127                if resource:
128                        return '%s@%s/%s' % (user, server, resource)
129                else:
130                        return '%s@%s' % (user, server)
131        else:
132                if resource:
133                        return '%s/%s' % (server, resource)
134                else:
135                        return server
136
137def temp_failure_retry(func, *args, **kwargs):
138        while True:
139                try:
140                        return func(*args, **kwargs)
141                except (os.error, IOError, select.error), ex:
142                        if ex.errno == errno.EINTR:
143                                continue
144                        else:
145                                raise
146
147def convert_bytes(string):
148        suffix = ''
149        # IEC standard says KiB = 1024 bytes KB = 1000 bytes
150        # but do we use the standard?
151        use_kib_mib = gajim.config.get('use_kib_mib')
152        align = 1024.
153        bytes = float(string)
154        if bytes >= align:
155                bytes = round(bytes/align, 1)
156                if bytes >= align:
157                        bytes = round(bytes/align, 1)
158                        if bytes >= align:
159                                bytes = round(bytes/align, 1)
160                                if use_kib_mib:
161                                        #GiB means gibibyte
162                                        suffix = _('%s GiB') 
163                                else:
164                                        #GB means gigabyte
165                                        suffix = _('%s GB')
166                        else:
167                                if use_kib_mib:
168                                        #MiB means mibibyte
169                                        suffix = _('%s MiB')
170                                else:
171                                        #MB means megabyte
172                                        suffix = _('%s MB')
173                else:
174                        if use_kib_mib:
175                                        #KiB means kibibyte
176                                        suffix = _('%s KiB')
177                        else:
178                                #KB means kilo bytes
179                                suffix = _('%s KB')
180        else:
181                #B means bytes
182                suffix = _('%s B')
183        return suffix % unicode(bytes)
184
185def get_uf_show(show, use_mnemonic = False):
186        '''returns a userfriendly string for dnd/xa/chat
187        and makes all strings translatable
188        if use_mnemonic is True, it adds _ so GUI should call with True
189        for accessibility issues'''
190        if show == 'dnd':
191                if use_mnemonic:
192                        uf_show = _('_Busy')
193                else:
194                        uf_show = _('Busy')
195        elif show == 'xa':
196                if use_mnemonic:
197                        uf_show = _('_Not Available')
198                else:
199                        uf_show = _('Not Available')
200        elif show == 'chat':
201                if use_mnemonic:
202                        uf_show = _('_Free for Chat')
203                else:
204                        uf_show = _('Free for Chat')
205        elif show == 'online':
206                if use_mnemonic:
207                        uf_show = _('_Available')
208                else:
209                        uf_show = _('Available')
210        elif show == 'connecting':
211                        uf_show = _('Connecting')
212        elif show == 'away':
213                if use_mnemonic:
214                        uf_show = _('A_way')
215                else:
216                        uf_show = _('Away')
217        elif show == 'offline':
218                if use_mnemonic:
219                        uf_show = _('_Offline')
220                else:
221                        uf_show = _('Offline')
222        elif show == 'invisible':
223                if use_mnemonic:
224                        uf_show = _('_Invisible')
225                else:
226                        uf_show = _('Invisible')
227        elif show == 'not in the roster':
228                uf_show = _('Not in the roster')
229        elif show == 'requested':
230                uf_show = Q_('?contact has status:Unknown')
231        else:
232                uf_show = Q_('?contact has status:Has errors')
233        return unicode(uf_show)
234       
235def get_uf_sub(sub):
236        if sub == 'none':
237                uf_sub = Q_('?Subscription we already have:None')
238        elif sub == 'to':
239                uf_sub = _('To')
240        elif sub == 'from':
241                uf_sub = _('From')
242        elif sub == 'both':
243                uf_sub = _('Both')
244        else:
245                uf_sub = sub
246       
247        return unicode(uf_sub)
248       
249def get_uf_ask(ask):
250        if ask is None:
251                uf_ask = Q_('?Ask (for Subscription):None')
252        elif ask == 'subscribe':
253                uf_ask = _('Subscribe')
254        else:
255                uf_ask = ask
256       
257        return unicode(uf_ask)
258
259def get_uf_role(role, plural = False):
260        ''' plural determines if you get Moderators or Moderator'''
261        if role == 'none':
262                role_name = Q_('?Group Chat Contact Role:None')
263        elif role == 'moderator':
264                if plural:
265                        role_name = _('Moderators')
266                else:
267                        role_name = _('Moderator')
268        elif role == 'participant':
269                if plural:
270                        role_name = _('Participants')
271                else:
272                        role_name = _('Participant')
273        elif role == 'visitor':
274                if plural:
275                        role_name = _('Visitors')
276                else:
277                        role_name = _('Visitor')
278        return role_name
279
280def get_sorted_keys(adict):
281        keys = adict.keys()
282        keys.sort()
283        return keys
284
285def to_one_line(msg):
286        msg = msg.replace('\\', '\\\\')
287        msg = msg.replace('\n', '\\n')
288        # s1 = 'test\ntest\\ntest'
289        # s11 = s1.replace('\\', '\\\\')
290        # s12 = s11.replace('\n', '\\n')
291        # s12
292        # 'test\\ntest\\\\ntest'
293        return msg
294
295def from_one_line(msg):
296        # (?<!\\) is a lookbehind assertion which asks anything but '\'
297        # to match the regexp that follows it
298
299        # So here match '\\n' but not if you have a '\' before that
300        re = sre.compile(r'(?<!\\)\\n')
301        msg = re.sub('\n', msg)
302        msg = msg.replace('\\\\', '\\')
303        # s12 = 'test\\ntest\\\\ntest'
304        # s13 = re.sub('\n', s12)
305        # s14 s13.replace('\\\\', '\\')
306        # s14
307        # 'test\ntest\\ntest'
308        return msg
309
310def get_uf_chatstate(chatstate):
311        '''removes chatstate jargon and returns user friendly messages'''
312        if chatstate == 'active':
313                return _('is paying attention to the conversation')
314        elif chatstate == 'inactive':
315                return _('is doing something else')
316        elif chatstate == 'composing':
317                return _('is composing a message...')
318        elif chatstate == 'paused':
319                #paused means he or she was compoing but has stopped for a while
320                return _('paused composing a message')
321        elif chatstate == 'gone':
322                return _('has closed the chat window or tab')
323        return ''
324
325def is_in_path(name_of_command, return_abs_path = False):
326        # if return_abs_path is True absolute path will be returned
327        # for name_of_command
328        # on failures False is returned
329        is_in_dir = False
330        found_in_which_dir = None
331        path = os.getenv('PATH').split(':')
332        for path_to_directory in path:
333                try:
334                        contents = os.listdir(path_to_directory)
335                except OSError: # user can have something in PATH that is not a dir
336                        pass
337                else:
338                        is_in_dir = name_of_command in contents
339                if is_in_dir:
340                        if return_abs_path:
341                                found_in_which_dir = path_to_directory
342                        break
343       
344        if found_in_which_dir:
345                abs_path = os.path.join(path_to_directory, name_of_command)
346                return abs_path
347        else:
348                return is_in_dir
349
350def launch_browser_mailer(kind, uri):
351        #kind = 'url' or 'mail'
352        if os.name == 'nt':
353                try:
354                        os.startfile(uri) # if pywin32 is installed we open
355                except:
356                        pass
357
358        else:
359                if kind == 'url' and not\
360                        (uri.startswith('http://') or uri.startswith('https://')):
361                        uri = 'http://' + uri
362                elif kind == 'mail' and not uri.startswith('mailto:'):
363                        uri = 'mailto:' + uri
364
365                if gajim.config.get('openwith') == 'gnome-open':
366                        command = 'gnome-open'
367                elif gajim.config.get('openwith') == 'kfmclient exec':
368                        command = 'kfmclient exec'
369                elif gajim.config.get('openwith') == 'custom':
370                        if kind == 'url':
371                                command = gajim.config.get('custombrowser')
372                        if kind == 'mail':
373                                command = gajim.config.get('custommailapp')
374                        if command == '': # if no app is configured
375                                return
376                # we add the uri in "" so we have good parsing from shell
377                uri = uri.replace('"', '\\"') # escape "
378                command = command + ' "' + uri + '" &'
379                try: #FIXME: when we require python2.4+ use subprocess module
380                        os.system(command)
381                except:
382                        pass
383
384def launch_file_manager(path_to_open):
385        if os.name == 'nt':
386                try:
387                        os.startfile(path_to_open) # if pywin32 is installed we open
388                except:
389                        pass
390        else:
391                if gajim.config.get('openwith') == 'gnome-open':
392                        command = 'gnome-open'
393                elif gajim.config.get('openwith') == 'kfmclient exec':
394                        command = 'kfmclient exec'
395                elif gajim.config.get('openwith') == 'custom':
396                        command = gajim.config.get('custom_file_manager')
397                if command == '': # if no app is configured
398                        return
399                # we add the path in "" so we have good parsing from shell
400                path_to_open = path_to_open.replace('"', '\\"') # escape "
401                command = command + ' "' + path_to_open + '" &'
402                try: #FIXME: when we require python2.4+ use subprocess module
403                        os.system(command)
404                except:
405                        pass
406
407def play_sound(event):
408        if not gajim.config.get('sounds_on'):
409                return
410        path_to_soundfile = gajim.config.get_per('soundevents', event, 'path')
411        if path_to_soundfile == 'beep':
412                print '\a' # make a speaker beep
413                return
414        if not os.path.exists(path_to_soundfile):
415                return
416        if os.name == 'nt':
417                try:
418                        winsound.PlaySound(path_to_soundfile,
419                                                                winsound.SND_FILENAME|winsound.SND_ASYNC)
420                except:
421                        pass
422        elif os.name == 'posix':
423                if gajim.config.get('soundplayer') == '':
424                        return
425                player = gajim.config.get('soundplayer')
426                # we add the path in "" so we have good parsing from shell
427                path_to_soundfile = path_to_soundfile.replace('"', '\\"') # escape "
428                command = player + ' "' + path_to_soundfile + '" &'
429                #FIXME: when we require 2.4+ use subprocess module
430                os.system(command)
431
432def get_file_path_from_dnd_dropped_uri(uri):
433        path = urllib.url2pathname(uri) # escape special chars
434        path = path.strip('\r\n\x00') # remove \r\n and NULL
435        # get the path to file
436        if path.startswith('file:\\\\\\'): # windows
437                path = path[8:] # 8 is len('file:///')
438        elif path.startswith('file://'): # nautilus, rox
439                path = path[7:] # 7 is len('file://')
440        elif path.startswith('file:'): # xffm
441                path = path[5:] # 5 is len('file:')
442        return path
443
444def from_xs_boolean_to_python_boolean(value):
445        # this is xs:boolean so 'true','false','1','0'
446        # convert those to True/False (python booleans)
447        if value in ('1', 'true'):
448                val = True
449        else: # '0', 'false' or anything else
450                val = False
451
452        return val
453                       
454def ensure_unicode_string(s):
455        # py23 u'abc'.decode('utf-8') raises
456        # python24 does not. is python23 is ooold we can remove this func
457        # FIXME: remove this when we abandon py23
458        if isinstance(s, str):
459                s = s.decode('utf-8')
460        return s
461
462def get_xmpp_show(show):
463        if show in ('online', 'offline'):
464                return None
465        return show
466
467def one_account_connected():
468        '''returns True if at least one account is connected, else False'''
469        one_connected = False
470        accounts = gajim.connections.keys()
471        for acct in accounts:
472                if gajim.connections[acct].connected > 1:
473                        one_connected = True
474                        break
475        return one_connected
476
477def get_output_of_command(command):
478        try:
479                child_stdin, child_stdout = os.popen2(command)
480        except ValueError:
481                return None
482
483        output = child_stdout.readlines()
484        child_stdout.close()
485        child_stdin.close()
486       
487        return output
488
489def get_global_show():
490        maxi = 0
491        for account in gajim.connections:
492                if not gajim.config.get_per('accounts', account,
493                        'sync_with_global_status'):
494                        continue
495                connected = gajim.connections[account].connected
496                if connected > maxi:
497                        maxi = connected
498        return gajim.SHOW_LIST[maxi]
499
500def get_icon_name_to_show(contact, account = None):
501        '''Get the icon name to show in online, away, requested, ...'''
502        if account and gajim.awaiting_events[account].has_key(contact.jid):
503                #FIXME: change icon for FT
504                return 'message'
505        if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
506                return contact.show
507        if contact.sub in ('both', 'to'):
508                return contact.show
509        if contact.ask == 'subscribe':
510                return 'requested'
511        transport = gajim.get_transport_name_from_jid(contact.jid)
512        if transport:
513                return contact.show
514        return 'not in the roster'
515
516def decode_string(string):
517        '''try to decode (to make it Unicode instance) given string'''
518        # by the time we go to iso15 it better be the one else we show bad characters
519        encodings = (sys.getfilesystemencoding(), 'utf-8', 'iso-8859-15')
520        for encoding in encodings:
521                try:
522                        string = string.decode(encoding)
523                except UnicodeError:
524                        continue
525                break
526       
527        return string
Note: See TracBrowser for help on using the browser.