Newer
Older
## src/common/helpers.py
##
## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Alex Mauer <hawke AT hawkesnest.net>
## Copyright (C) 2006-2007 Travis Shirk <travis AT pobox.com>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
## James Newton <redshodan AT gmail.com>
## Julien Pivotto <roidelapluie AT gmail.com>
## Copyright (C) 2007-2008 Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
## Jonathan Schleifer <js-gajim AT webkeks.org>
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import re
import locale

Yann Leboulanger
committed
import subprocess

Yann Leboulanger
committed
import errno
from encodings.punycode import punycode_encode

nkour
committed
import win32api
import win32con
special_groups = (_('Transports'), _('Not in Roster'), _('Observers'), _('Groupchats'))
class InvalidFormat(Exception):
pass
def decompose_jid(jidstring):
user = None
server = None
resource = None
# Search for delimiters
user_sep = jidstring.find('@')
if res_sep == -1:
# host
server = jidstring
else:
# host/resource
server = jidstring[0:res_sep]
resource = jidstring[res_sep + 1:] or None
else:
if res_sep == -1:
# user@host
user = jidstring[0:user_sep] or None
server = jidstring[user_sep + 1:]
else:
if user_sep < res_sep:
# user@host/resource
user = jidstring[0:user_sep] or None
server = jidstring[user_sep + 1:user_sep + (res_sep - user_sep)]
resource = jidstring[res_sep + 1:] or None
else:
# server/resource (with an @ in resource)
server = jidstring[0:res_sep]
resource = jidstring[res_sep + 1:] or None
return user, server, resource
def parse_jid(jidstring):
'''Perform stringprep on all JID fragments from a string
and return the full jid'''
# This function comes from http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
return prep(*decompose_jid(jidstring))
'''convert IDN (Internationalized Domain Names) to ACE
(ASCII-compatible encoding)'''
from encodings import idna
labels = idna.dots.split(host)
converted_labels = []
for label in labels:
converted_labels.append(idna.ToASCII(label))
return ".".join(converted_labels)
def ascii_to_idn(host):
'''convert ACE (ASCII-compatible encoding) to IDN
(Internationalized Domain Names)'''
from encodings import idna
labels = idna.dots.split(host)
converted_labels = []
for label in labels:
converted_labels.append(idna.ToUnicode(label))
return ".".join(converted_labels)
def parse_resource(resource):
'''Perform stringprep on resource and return it'''
if resource:
try:
from xmpp_stringprep import resourceprep
return resourceprep.prepare(unicode(resource))
except UnicodeError:
raise InvalidFormat, 'Invalid character in resource.'
def prep(user, server, resource):
'''Perform stringprep on all JID fragments and return the full jid'''
# This function comes from
#http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
from xmpp_stringprep import nodeprep
user = nodeprep.prepare(unicode(user))
except UnicodeError:
raise InvalidFormat, _('Invalid character in username.')
else:
user = None
if not server:
from xmpp_stringprep import nameprep
server = nameprep.prepare(unicode(server))
except UnicodeError:
raise InvalidFormat, _('Invalid character in hostname.')
if resource:
try:
from xmpp_stringprep import resourceprep
resource = resourceprep.prepare(unicode(resource))
except UnicodeError:
raise InvalidFormat, _('Invalid character in resource.')
else:
resource = None
if user:
if resource:
return '%s@%s/%s' % (user, server, resource)
return '%s@%s' % (user, server)
else:
if resource:
return '%s/%s' % (server, resource)
else:
return server

Yann Leboulanger
committed
def temp_failure_retry(func, *args, **kwargs):
while True:
try:
return func(*args, **kwargs)
except (os.error, IOError, select.error), ex:
if ex.errno == errno.EINTR:
continue
else:
raise

Yann Leboulanger
committed
def get_uf_show(show, use_mnemonic = False):
'''returns a userfriendly string for dnd/xa/chat
and makes all strings translatable
if use_mnemonic is True, it adds _ so GUI should call with True
for accessibility issues'''
if use_mnemonic:
uf_show = _('_Busy')
else:
uf_show = _('Busy')
if use_mnemonic:
uf_show = _('_Not Available')
else:
uf_show = _('Not Available')
if use_mnemonic:
uf_show = _('_Free for Chat')
else:
uf_show = _('Free for Chat')
if use_mnemonic:
uf_show = _('_Available')
else:
uf_show = _('Available')
if use_mnemonic:
uf_show = _('A_way')
else:
uf_show = _('Away')
if use_mnemonic:
uf_show = _('_Offline')
else:
uf_show = _('Offline')
if use_mnemonic:
uf_show = _('_Invisible')
else:
uf_show = _('Invisible')
elif show == 'not in roster':
return unicode(uf_show)
def get_uf_sub(sub):
if sub == 'none':
uf_sub = Q_('?Subscription we already have:None')
elif sub == 'to':
uf_sub = _('To')
elif sub == 'from':
uf_sub = _('From')
elif sub == 'both':
uf_sub = _('Both')
else:
uf_sub = sub
return unicode(uf_sub)
def get_uf_ask(ask):
if ask is None:
uf_ask = Q_('?Ask (for Subscription):None')
elif ask == 'subscribe':
uf_ask = _('Subscribe')
else:
uf_ask = ask
return unicode(uf_ask)
''' plural determines if you get Moderators or Moderator'''
if role == 'none':
role_name = Q_('?Group Chat Contact Role:None')
elif role == 'moderator':
if plural:
role_name = _('Moderators')
else:
role_name = _('Moderator')
elif role == 'participant':
if plural:
role_name = _('Participants')
else:
role_name = _('Participant')
elif role == 'visitor':
if plural:
role_name = _('Visitors')
else:
role_name = _('Visitor')
return role_name

jimpp
committed
def get_uf_affiliation(affiliation):
'''Get a nice and translated affilition for muc'''

jimpp
committed
affiliation_name = Q_('?Group Chat Contact Affiliation:None')
elif affiliation == 'owner':
affiliation_name = _('Owner')
elif affiliation == 'admin':
affiliation_name = _('Administrator')
elif affiliation == 'member':
affiliation_name = _('Member')
else: # Argl ! An unknown affiliation !
affiliation_name = affiliation.capitalize()
return affiliation_name
keys = sorted(adict.keys())
def to_one_line(msg):
msg = msg.replace('\\', '\\\\')
msg = msg.replace('\n', '\\n')
# s1 = 'test\ntest\\ntest'
# s11 = s1.replace('\\', '\\\\')
# s12 = s11.replace('\n', '\\n')
# s12
# 'test\\ntest\\\\ntest'
return msg
def from_one_line(msg):
# (?<!\\) is a lookbehind assertion which asks anything but '\'
# to match the regexp that follows it
# So here match '\\n' but not if you have a '\' before that
msg = msg.replace('\\\\', '\\')
# s12 = 'test\\ntest\\\\ntest'
# s13 = re.sub('\n', s12)
# s14 s13.replace('\\\\', '\\')
# s14
# 'test\ntest\\ntest'
return msg
def get_uf_chatstate(chatstate):
'''removes chatstate jargon and returns user friendly messages'''
if chatstate == 'active':
return _('is paying attention to the conversation')
elif chatstate == 'inactive':
return _('is doing something else')
elif chatstate == 'composing':
return _('is composing a message...')
elif chatstate == 'paused':
#paused means he or she was composing but has stopped for a while
return _('paused composing a message')
elif chatstate == 'gone':
def is_in_path(command, return_abs_path=False):
'''Returns True if 'command' is found in one of the directories in the
user's path. If 'return_abs_path' is True, returns the absolute path of
the first found command instead. Returns False otherwise and on errors.'''
for directory in os.getenv('PATH').split(os.pathsep):
if command in os.listdir(directory):
if return_abs_path:
return os.path.join(directory, command)
else:
return True
except OSError:
# If the user has non directories in his path

Yann Leboulanger
committed
def exec_command(command):
subprocess.Popen(command, shell = True)
def build_command(executable, parameter):
# we add to the parameter (can hold path with spaces)
# "" so we have good parsing from shell
parameter = parameter.replace('"', '\\"') # but first escape "
return command

Yann Leboulanger
committed
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
def get_file_path_from_dnd_dropped_uri(uri):
path = urllib.unquote(uri) # escape special chars
path = path.strip('\r\n\x00') # remove \r\n and NULL
# get the path to file
if re.match('^file:///[a-zA-Z]:/', path): # windows
path = path[8:] # 8 is len('file:///')
elif path.startswith('file://'): # nautilus, rox
if sys.platform == 'darwin':
# OS/X includes hostname in file:// URI
path = re.sub('file://[^/]*', '', path)
else:
path = path[7:] # 7 is len('file://')
elif path.startswith('file:'): # xffm
path = path[5:] # 5 is len('file:')
return path
def from_xs_boolean_to_python_boolean(value):
# this is xs:boolean so 'true', 'false', '1', '0'
# convert those to True/False (python booleans)
if value in ('1', 'true'):
val = True
else: # '0', 'false' or anything else
val = False
return val
def get_xmpp_show(show):
if show in ('online', 'offline'):
return None
return show
def get_output_of_command(command):
try:
child_stdin, child_stdout = os.popen2(command)
except ValueError:
return None
output = child_stdout.readlines()
child_stdout.close()
child_stdin.close()
return output
def decode_string(string):
'''try to decode (to make it Unicode instance) given string'''
if isinstance(string, unicode):
return string
# by the time we go to iso15 it better be the one else we show bad characters
encodings = (locale.getpreferredencoding(), 'utf-8', 'iso-8859-15')
for encoding in encodings:
try:
string = string.decode(encoding)
except UnicodeError:
continue
break
return string
def ensure_utf8_string(string):
'''make sure string is in UTF-8'''
try:
string = decode_string(string).encode('utf-8')
except Exception:
pass
return string
def get_windows_reg_env(varname, default=''):
'''asks for paths commonly used but not exposed as ENVs
in english Windows 2003 those are:
'AppData' = %USERPROFILE%\Application Data (also an ENV)
'Desktop' = %USERPROFILE%\Desktop
'Favorites' = %USERPROFILE%\Favorites
'NetHood' = %USERPROFILE%\NetHood
'Personal' = D:\My Documents (PATH TO MY DOCUMENTS)
'PrintHood' = %USERPROFILE%\PrintHood
'Programs' = %USERPROFILE%\Start Menu\Programs
'Recent' = %USERPROFILE%\Recent
'SendTo' = %USERPROFILE%\SendTo
'Start Menu' = %USERPROFILE%\Start Menu
'Startup' = %USERPROFILE%\Start Menu\Programs\Startup
'Templates' = %USERPROFILE%\Templates
'My Pictures' = D:\My Documents\My Pictures
'Local Settings' = %USERPROFILE%\Local Settings
'Local AppData' = %USERPROFILE%\Local Settings\Application Data
'Cache' = %USERPROFILE%\Local Settings\Temporary Internet Files
'Cookies' = %USERPROFILE%\Cookies
'History' = %USERPROFILE%\Local Settings\History
'''
if os.name != 'nt':
return ''
val = default
try:
rkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER,
r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders')
try:
val = str(win32api.RegQueryValueEx(rkey, varname)[0])
val = win32api.ExpandEnvironmentStrings(val) # expand using environ
except Exception:
pass
finally:
win32api.RegCloseKey(rkey)
return val
def get_my_pictures_path():
'''windows-only atm. [Unix lives in the past]'''
return get_windows_reg_env('My Pictures')
def get_desktop_path():
if os.name == 'nt':
path = get_windows_reg_env('Desktop')
else:
path = os.path.join(os.path.expanduser('~'), 'Desktop')
return path
def get_documents_path():
if os.name == 'nt':
path = get_windows_reg_env('Personal')
else:
path = os.path.expanduser('~')
return path
def sanitize_filename(filename):
'''makes sure the filename we will write does contain only acceptable and
latin characters, and is not too long (in that case hash it)'''
# 48 is the limit
if len(filename) > 48:
hash = hashlib.md5(filename)
filename = base64.b64encode(hash.digest())
filename = punycode_encode(filename) # make it latin chars only
filename = filename.replace('/', '_')
if os.name == 'nt':
filename = filename.replace('?', '_').replace(':', '_')\
.replace('\\', '_').replace('"', "'").replace('|', '_')\
.replace('*', '_').replace('<', '_').replace('>', '_')
return filename
def reduce_chars_newlines(text, max_chars = 0, max_lines = 0):
'''Cut the chars after 'max_chars' on each line
and show only the first 'max_lines'.
If any of the params is not present (None or 0) the action
on it is not performed'''
def _cut_if_long(string):
if len(string) > max_chars:
string = string[:max_chars - 3] + '...'
return string
if isinstance(text, str):
text = text.decode('utf-8')
if max_lines == 0:
lines = text.split('\n')
else:
lines = text.split('\n', max_lines)[:max_lines]
if max_chars > 0:
if lines:
lines = [_cut_if_long(e) for e in lines]
if lines:
reduced_text = '\n'.join(lines)
if reduced_text != text:
reduced_text += '...'
else:
reduced_text = ''
return reduced_text
def get_account_status(account):
status = reduce_chars_newlines(account['status_line'], 100, 1)
return status
def get_avatar_path(prefix):
'''Returns the filename of the avatar, distinguishes between user- and
contact-provided one. Returns None if no avatar was found at all.
prefix is the path to the requested avatar just before the ".png" or
".jpeg".'''
# First, scan for a local, user-set avatar
for type_ in ('jpeg', 'png'):
file_ = prefix + '_local.' + type_
if os.path.exists(file_):
return file_
# If none available, scan for a contact-provided avatar
for type_ in ('jpeg', 'png'):
file_ = prefix + '.' + type_
if os.path.exists(file_):
return file_
return None
def datetime_tuple(timestamp):
'''Converts timestamp using strptime and the format: %Y%m%dT%H:%M:%S
Because of various datetime formats are used the following exceptions
are handled:
- Optional milliseconds appened to the string are removed
- Optional Z (that means UTC) appened to the string are removed
- XEP-082 datetime strings have all '-' cahrs removed to meet
the above format.'''
timestamp = timestamp.split('.')[0]
timestamp = timestamp.replace('-', '')
timestamp = timestamp.replace('z', '')
timestamp = timestamp.replace('Z', '')
from time import strptime
return strptime(timestamp, '%Y%m%dT%H:%M:%S')
def sort_identities_func(i1, i2):
cat1 = i1['category']
cat2 = i2['category']
if cat1 < cat2:
return -1
if cat1 > cat2:
return 1
type1 = i1.get('type', '')
type2 = i2.get('type', '')
if type1 < type2:
return -1
if type1 > type2:
return 1
lang1 = i1.get('xml:lang', '')
lang2 = i2.get('xml:lang', '')
if lang1 < lang2:
return -1
if lang1 > lang2:
return 1
return 0
def sort_dataforms_func(d1, d2):
f1 = d1.getField('FORM_TYPE')
f2 = d2.getField('FORM_TYPE')
if f1 and f2 and (f1.getValue() < f2.getValue()):
return -1
return 1
def compute_caps_hash(identities, features, dataforms=[], hash_method='sha-1'):
'''Compute caps hash according to XEP-0115, V1.5
dataforms are xmpp.DataForms objects as common.dataforms don't allow several
values without a field type list-multi'''
S = ''
identities.sort(cmp=sort_identities_func)
for i in identities:
c = i['category']
type_ = i.get('type', '')
lang = i.get('xml:lang', '')
name = i.get('name', '')
S += '%s/%s/%s/%s<' % (c, type_, lang, name)
features.sort()
for f in features:
S += '%s<' % f
dataforms.sort(cmp=sort_dataforms_func)
for dataform in dataforms:
# fields indexed by var
fields = {}
for f in dataform.getChildren():
fields[f.getVar()] = f
form_type = fields.get('FORM_TYPE')
if form_type:
S += form_type.getValue() + '<'
del fields['FORM_TYPE']
for var in sorted(fields.keys()):
S += '%s<' % var
values = sorted(fields[var].getValues())
for value in values:
S += '%s<' % value
if hash_method == 'sha-1':
hash_ = hashlib.sha1(S)
elif hash_method == 'md5':
hash_ = hashlib.md5(S)
else:
return ''
return base64.b64encode(hash_.digest())
# import gajim only when needed (after decode_string is defined) see #4764
import gajim
try:
from osx import nsapp
except ImportError:
pass
def convert_bytes(string):
suffix = ''
# IEC standard says KiB = 1024 bytes KB = 1000 bytes
# but do we use the standard?
use_kib_mib = gajim.config.get('use_kib_mib')
align = 1024.
bytes = float(string)
if bytes >= align:
bytes = round(bytes/align, 1)
if bytes >= align:
bytes = round(bytes/align, 1)
if bytes >= align:
bytes = round(bytes/align, 1)
if use_kib_mib:
#GiB means gibibyte
suffix = _('%s GiB')
else:
#GB means gigabyte
suffix = _('%s GB')
else:
if use_kib_mib:
#MiB means mibibyte
suffix = _('%s MiB')
else:
#MB means megabyte
suffix = _('%s MB')
else:
if use_kib_mib:
#KiB means kibibyte
suffix = _('%s KiB')
else:
#KB means kilo bytes
suffix = _('%s KB')
else:
#B means bytes
suffix = _('%s B')
return suffix % unicode(bytes)
def get_contact_dict_for_account(account):
''' create a dict of jid, nick -> contact with all contacts of account.
Can be used for completion lists'''
contacts_dict = {}
for jid in gajim.contacts.get_jid_list(account):
contact = gajim.contacts.get_contact_with_highest_priority(account,
jid)
contacts_dict[jid] = contact
name = contact.name
if name in contacts_dict:
contact1 = contacts_dict[name]
del contacts_dict[name]
contacts_dict['%s (%s)' % (name, contact1.jid)] = contact1
contacts_dict['%s (%s)' % (name, jid)] = contact
else:
if contact.name == gajim.get_nick_from_jid(jid):
del contacts_dict[jid]
contacts_dict[name] = contact
return contacts_dict
#kind = 'url' or 'mail'
if os.name == 'nt':
try:
os.startfile(uri) # if pywin32 is installed we open
pass
else:

Yann Leboulanger
committed
if kind in ('mail', 'sth_at_sth') and not uri.startswith('mailto:'):
uri = 'mailto:' + uri
if gajim.config.get('openwith') == 'gnome-open':
command = 'gnome-open'
elif gajim.config.get('openwith') == 'kfmclient exec':
command = 'kfmclient exec'
elif gajim.config.get('openwith') == 'exo-open':
command = 'exo-open'
elif ((sys.platform == 'darwin') and\
(gajim.config.get('openwith') == 'open')):

James Newton
committed
command = 'open'
elif gajim.config.get('openwith') == 'custom':
if kind == 'url':
command = gajim.config.get('custombrowser')

Yann Leboulanger
committed
if uri.startswith('www.'):
uri = 'http://' + uri
elif kind in ('mail', 'sth_at_sth'):
command = gajim.config.get('custommailapp')
if command == '': # if no app is configured
return
command = build_command(command, uri)

Yann Leboulanger
committed
try:
exec_command(command)
if os.name == 'nt':
try:
os.startfile(path_to_open) # if pywin32 is installed we open
else:
if gajim.config.get('openwith') == 'gnome-open':
command = 'gnome-open'
elif gajim.config.get('openwith') == 'kfmclient exec':
command = 'kfmclient exec'
elif gajim.config.get('openwith') == 'exo-open':
command = 'exo-open'
elif ((sys.platform == 'darwin') and\
(gajim.config.get('openwith') == 'open')):

James Newton
committed
command = 'open'
elif gajim.config.get('openwith') == 'custom':
command = gajim.config.get('custom_file_manager')
if command == '': # if no app is configured
return
command = build_command(command, path_to_open)

Yann Leboulanger
committed
try:
exec_command(command)
if not gajim.config.get('sounds_on'):
return
path_to_soundfile = gajim.config.get_per('soundevents', event, 'path')
play_sound_file(path_to_soundfile)
def play_sound_file(path_to_soundfile):
if path_to_soundfile == 'beep':
exec_command('beep')
if path_to_soundfile is None or not os.path.exists(path_to_soundfile):
return
if sys.platform == 'darwin':
try:
nsapp.playFile(path_to_soundfile)
except NameError:
pass
elif os.name == 'nt':
try:
winsound.PlaySound(path_to_soundfile,
winsound.SND_FILENAME|winsound.SND_ASYNC)
except Exception:
pass
elif os.name == 'posix':
if gajim.config.get('soundplayer') == '':
return
player = gajim.config.get('soundplayer')
command = build_command(player, path_to_soundfile)
exec_command(command)
def get_global_show():
maxi = 0
for account in gajim.connections:

Yann Leboulanger
committed
if not gajim.config.get_per('accounts', account,
'sync_with_global_status'):
continue
connected = gajim.connections[account].connected
if connected > maxi:
maxi = connected
return gajim.SHOW_LIST[maxi]
def get_global_status():
maxi = 0
for account in gajim.connections:
if not gajim.config.get_per('accounts', account,
'sync_with_global_status'):
continue
connected = gajim.connections[account].connected
if connected > maxi:
maxi = connected
status = gajim.connections[account].status
return status

Yann Leboulanger
committed
'''testing if all statuses are the same.'''
reference = None
for account in gajim.connections:
if not gajim.config.get_per('accounts', account,
'sync_with_global_status'):
continue
if reference is None:

Yann Leboulanger
committed
reference = gajim.connections[account].connected
elif reference != gajim.connections[account].connected:
return False
return True
'''Get the icon name to show in online, away, requested, ...'''

Yann Leboulanger
committed
if account and gajim.events.get_nb_roster_events(account, contact.jid):
return 'event'

Yann Leboulanger
committed
if account and gajim.events.get_nb_roster_events(account,
contact.get_full_jid()):
return 'event'
if account and account in gajim.interface.minimized_controls and \

roidelapluie
committed
contact.jid in gajim.interface.minimized_controls[account] and gajim.interface.\
minimized_controls[account][contact.jid].get_nb_unread_pm() > 0:
return 'event'
if account and contact.jid in gajim.gc_connected[account]:

Yann Leboulanger
committed
if gajim.gc_connected[account][contact.jid]:
return 'muc_active'
else:
return 'muc_inactive'
if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
return contact.show
if contact.sub in ('both', 'to'):
return contact.show
if contact.ask == 'subscribe':
return 'requested'
if transport:
return contact.show
if contact.show in gajim.SHOW_LIST:
return contact.show
def get_full_jid_from_iq(iq_obj):
'''return the full jid (with resource) from an iq as unicode'''
return parse_jid(str(iq_obj.getFrom()))
def get_jid_from_iq(iq_obj):
'''return the jid (without resource) from an iq as unicode'''
jid = get_full_jid_from_iq(iq_obj)
return gajim.get_jid_without_resource(jid)
def get_auth_sha(sid, initiator, target):
''' return sha of sid + initiator + target used for proxy auth'''
return hashlib.sha1("%s%s%s" % (sid, initiator, target)).hexdigest()
def remove_invalid_xml_chars(string):
if string:
string = re.sub(gajim.interface.invalid_XML_chars_re, '', string)
return string
distro_info = {
'Arch Linux': '/etc/arch-release',
'Aurox Linux': '/etc/aurox-release',
'Conectiva Linux': '/etc/conectiva-release',
'CRUX': '/usr/bin/crux',
'Debian GNU/Linux': '/etc/debian_release',
'Debian GNU/Linux': '/etc/debian_version',
'Fedora Linux': '/etc/fedora-release',
'Gentoo Linux': '/etc/gentoo-release',
'Linux from Scratch': '/etc/lfs-release',
'Mandrake Linux': '/etc/mandrake-release',
'Slackware Linux': '/etc/slackware-release',
'Slackware Linux': '/etc/slackware-version',
'Solaris/Sparc': '/etc/release',
'Source Mage': '/etc/sourcemage_version',
'SUSE Linux': '/etc/SuSE-release',
'Sun JDS': '/etc/sun-release',
'PLD Linux': '/etc/pld-release',
'Yellow Dog Linux': '/etc/yellowdog-release',
# many distros use the /etc/redhat-release for compatibility
# so Redhat is the last
'Redhat Linux': '/etc/redhat-release'
}
def get_random_string_16():
''' create random string of length 16'''
rng = range(65, 90)
rng.extend(range(48, 57))
char_sequence = [chr(e) for e in rng]
return ''.join(sample(char_sequence, 16))
if gajim.os_info:
return gajim.os_info
if os.name == 'nt':
ver = os.sys.getwindowsversion()
ver_format = ver[3], ver[0], ver[1]
win_version = {
(1, 4, 0): '95',
(1, 4, 10): '98',
(1, 4, 90): 'ME',
(2, 4, 0): 'NT',
(2, 5, 0): '2000',
(2, 5, 1): 'XP',
(2, 5, 2): '2003',
if ver_format in win_version:
os_info = 'Windows' + ' ' + win_version[ver_format]
os_info = 'Windows'
gajim.os_info = os_info
return os_info
elif os.name == 'posix':
executable = 'lsb_release'
params = ' --description --codename --release --short'
full_path_to_executable = is_in_path(executable, return_abs_path = True)
if full_path_to_executable:
command = executable + params
p = subprocess.Popen([command], shell=True, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, close_fds=True)
p.wait()
output = temp_failure_retry(p.stdout.readline).strip()
# some distros put n/a in places, so remove those
output = output.replace('n/a', '').replace('N/A', '')
gajim.os_info = output
return output
# lsb_release executable not available, so parse files
for distro_name in distro_info:
path_to_file = distro_info[distro_name]
if os.path.exists(path_to_file):
if os.access(path_to_file, os.X_OK):
# the file is executable (f.e. CRUX)
# yes, then run it and get the first line of output.
text = get_output_of_command(path_to_file)[0]
else:
fd = open(path_to_file)
text = fd.readline().strip() # get only first line
fd.close()
if path_to_file.endswith('version'):
# sourcemage_version and slackware-version files
# have all the info we need (name and version of distro)
if not os.path.basename(path_to_file).startswith(
'sourcemage') or not\
os.path.basename(path_to_file).startswith('slackware'):
elif path_to_file.endswith('aurox-release') or \
path_to_file.endswith('arch-release'):
# file doesn't have version
text = distro_name
elif path_to_file.endswith('lfs-release'): # file just has version
text = distro_name + ' ' + text
os_info = text.replace('\n', '')
gajim.os_info = os_info
return os_info
# our last chance, ask uname and strip it
uname_output = get_output_of_command('uname -sr')
os_info = uname_output[0] # only first line
gajim.os_info = os_info
return os_info
os_info = 'N/A'
gajim.os_info = os_info
return os_info

nkour
committed

Yann Leboulanger
committed
def allow_showing_notification(account, type_ = 'notify_on_new_message',
'''is it allowed to show nofication?

Yann Leboulanger
committed
check OUR status and if we allow notifications for that status
type is the option that need to be True e.g.: notify_on_signing
is_first_message: set it to false when it's not the first message'''
if advanced_notif_num is not None:

Yann Leboulanger
committed
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
'popup')
if popup == 'yes':
return True