reorder functions in common/helpers.py. Fixes #4764. Thanks Vardo!

This commit is contained in:
Yann Leboulanger 2009-02-08 13:47:28 +00:00
parent baa208d0b6
commit e49c64a452
1 changed files with 345 additions and 341 deletions

View File

@ -42,16 +42,10 @@ import hashlib
from encodings.punycode import punycode_encode
import gajim
from i18n import Q_
from i18n import ngettext
import xmpp
try:
from osx import nsapp
except ImportError:
pass
try:
import winsound # windows-only built-in module for playing wav
import win32api
@ -187,65 +181,6 @@ def temp_failure_retry(func, *args, **kwargs):
else:
raise
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
def get_uf_show(show, use_mnemonic = False):
'''returns a userfriendly string for dnd/xa/chat
and makes all strings translatable
@ -426,6 +361,347 @@ def build_command(executable, parameter):
command = '%s "%s"' % (executable, parameter)
return command
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
def launch_browser_mailer(kind, uri):
#kind = 'url' or 'mail'
if os.name == 'nt':
@ -517,49 +793,6 @@ def play_sound_file(path_to_soundfile):
command = build_command(player, path_to_soundfile)
exec_command(command)
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 get_global_show():
maxi = 0
for account in gajim.connections:
@ -625,91 +858,6 @@ def get_icon_name_to_show(contact, account = None):
return contact.show
return 'not in roster'
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 remove_invalid_xml_chars(string):
if string:
string = re.sub(gajim.interface.invalid_XML_chars_re, '', string)
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 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()))
@ -723,6 +871,10 @@ 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',
@ -832,22 +984,6 @@ def get_os_info():
gajim.os_info = os_info
return os_info
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 allow_showing_notification(account, type_ = 'notify_on_new_message',
advanced_notif_num = None, is_first_message = True):
@ -919,39 +1055,6 @@ def get_chat_control(account, contact):
# unknown contact or offline message
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
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_notification_icon_tooltip_dict():
'''returns a dict of the form {acct: {'show': show, 'message': message,
'event_lines': [list of text lines to show in tooltip]}'''
@ -1086,37 +1189,6 @@ def get_accounts_info():
'show': status, 'message': message})
return accounts
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 get_iconset_path(iconset):
if os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', iconset)):
@ -1179,74 +1251,6 @@ def prepare_and_validate_gpg_keyID(account, jid, keyID):
keyID = 'UNKNOWN'
return keyID
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())
def update_optional_features(account = None):
if account:
accounts = [account]