2008-08-15 19:31:51 +02:00
|
|
|
# -*- coding:utf-8 -*-
|
2008-08-15 05:20:23 +02:00
|
|
|
## 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>
|
2005-05-20 17:58:23 +02:00
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## This file is part of Gajim.
|
|
|
|
##
|
|
|
|
## Gajim is free software; you can redistribute it and/or modify
|
2005-05-20 17:58:23 +02:00
|
|
|
## it under the terms of the GNU General Public License as published
|
2007-10-22 13:13:13 +02:00
|
|
|
## by the Free Software Foundation; version 3 only.
|
2005-05-20 17:58:23 +02:00
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## Gajim is distributed in the hope that it will be useful,
|
2005-05-20 17:58:23 +02:00
|
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2008-08-15 05:20:23 +02:00
|
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2005-05-20 17:58:23 +02:00
|
|
|
## GNU General Public License for more details.
|
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## You should have received a copy of the GNU General Public License
|
2008-08-15 05:20:23 +02:00
|
|
|
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
2007-10-22 13:13:13 +02:00
|
|
|
##
|
2005-05-20 17:58:23 +02:00
|
|
|
|
2006-11-19 00:21:59 +01:00
|
|
|
import re
|
2006-11-02 14:42:51 +01:00
|
|
|
import locale
|
2005-08-09 13:33:56 +02:00
|
|
|
import os
|
2006-08-27 00:51:33 +02:00
|
|
|
import subprocess
|
2005-08-14 21:08:56 +02:00
|
|
|
import urllib
|
2005-09-28 17:00:01 +02:00
|
|
|
import errno
|
2006-04-11 20:36:09 +02:00
|
|
|
import select
|
2006-03-19 15:54:00 +01:00
|
|
|
import sha
|
2008-04-21 00:58:47 +02:00
|
|
|
import base64
|
2007-11-14 20:47:52 +01:00
|
|
|
import sys
|
2006-03-26 13:46:04 +02:00
|
|
|
from encodings.punycode import punycode_encode
|
2005-08-09 13:33:56 +02:00
|
|
|
|
2005-08-09 19:27:27 +02:00
|
|
|
import gajim
|
2006-06-15 08:49:04 +02:00
|
|
|
from i18n import Q_
|
2006-10-05 02:06:57 +02:00
|
|
|
from i18n import ngettext
|
2008-04-21 00:58:47 +02:00
|
|
|
import xmpp
|
2006-10-05 02:06:57 +02:00
|
|
|
|
2008-04-27 20:11:34 +02:00
|
|
|
try:
|
|
|
|
# Python 2.5
|
|
|
|
import hashlib
|
|
|
|
hash_md5 = hashlib.md5
|
|
|
|
hash_sha1 = hashlib.sha1
|
2008-05-03 03:30:05 +02:00
|
|
|
except ImportError:
|
2008-04-27 20:11:34 +02:00
|
|
|
# Python 2.4
|
|
|
|
import md5
|
|
|
|
import sha
|
|
|
|
hash_md5 = md5.new
|
|
|
|
hash_sha1 = sha.new
|
|
|
|
|
2008-06-07 22:42:15 +02:00
|
|
|
try:
|
2007-11-14 20:47:52 +01:00
|
|
|
from osx import nsapp
|
2008-06-08 12:19:26 +02:00
|
|
|
except ImportError:
|
2008-06-07 22:42:15 +02:00
|
|
|
pass
|
2007-11-14 20:47:52 +01:00
|
|
|
|
2005-09-13 12:51:02 +02:00
|
|
|
try:
|
2005-09-13 13:05:16 +02:00
|
|
|
import winsound # windows-only built-in module for playing wav
|
2006-03-15 02:40:10 +01:00
|
|
|
import win32api
|
|
|
|
import win32con
|
2008-10-11 11:37:13 +02:00
|
|
|
except Exception:
|
2005-09-13 12:51:02 +02:00
|
|
|
pass
|
|
|
|
|
2007-05-03 23:02:50 +02:00
|
|
|
special_groups = (_('Transports'), _('Not in Roster'), _('Observers'), _('Groupchats'))
|
2006-03-30 23:35:43 +02:00
|
|
|
|
2005-11-03 15:46:56 +01:00
|
|
|
class InvalidFormat(Exception):
|
|
|
|
pass
|
|
|
|
|
2006-11-23 00:21:33 +01:00
|
|
|
def decompose_jid(jidstring):
|
2005-11-03 15:46:56 +01:00
|
|
|
user = None
|
|
|
|
server = None
|
|
|
|
resource = None
|
|
|
|
|
|
|
|
# Search for delimiters
|
2005-11-23 20:12:52 +01:00
|
|
|
user_sep = jidstring.find('@')
|
2006-03-17 20:56:05 +01:00
|
|
|
res_sep = jidstring.find('/')
|
2005-11-03 15:46:56 +01:00
|
|
|
|
2006-02-27 16:29:13 +01:00
|
|
|
if user_sep == -1:
|
2005-11-03 15:46:56 +01:00
|
|
|
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
|
2006-11-23 00:21:33 +01:00
|
|
|
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
|
2005-11-03 15:46:56 +01:00
|
|
|
|
2006-11-23 00:21:33 +01:00
|
|
|
return prep(*decompose_jid(jidstring))
|
2005-11-03 15:46:56 +01:00
|
|
|
|
2007-01-17 12:40:59 +01:00
|
|
|
def idn_to_ascii(host):
|
2008-08-30 20:24:07 +02:00
|
|
|
'''convert IDN (Internationalized Domain Names) to ACE
|
|
|
|
(ASCII-compatible encoding)'''
|
2008-11-05 20:48:58 +01:00
|
|
|
from encodings import idna
|
2007-01-16 23:36:41 +01:00
|
|
|
labels = idna.dots.split(host)
|
|
|
|
converted_labels = []
|
|
|
|
for label in labels:
|
|
|
|
converted_labels.append(idna.ToASCII(label))
|
|
|
|
return ".".join(converted_labels)
|
|
|
|
|
2008-08-30 20:24:07 +02:00
|
|
|
def ascii_to_idn(host):
|
|
|
|
'''convert ACE (ASCII-compatible encoding) to IDN
|
|
|
|
(Internationalized Domain Names)'''
|
2008-11-05 20:48:58 +01:00
|
|
|
from encodings import idna
|
2008-08-30 20:24:07 +02:00
|
|
|
labels = idna.dots.split(host)
|
|
|
|
converted_labels = []
|
|
|
|
for label in labels:
|
|
|
|
converted_labels.append(idna.ToUnicode(label))
|
|
|
|
return ".".join(converted_labels)
|
|
|
|
|
2005-11-03 15:46:56 +01:00
|
|
|
def parse_resource(resource):
|
|
|
|
'''Perform stringprep on resource and return it'''
|
|
|
|
if resource:
|
|
|
|
try:
|
2008-11-05 20:48:58 +01:00
|
|
|
from xmpp_stringprep import resourceprep
|
2005-11-03 15:46:56 +01:00
|
|
|
return resourceprep.prepare(unicode(resource))
|
|
|
|
except UnicodeError:
|
2005-11-05 12:28:19 +01:00
|
|
|
raise InvalidFormat, 'Invalid character in resource.'
|
2005-11-03 15:46:56 +01:00
|
|
|
|
|
|
|
def prep(user, server, resource):
|
|
|
|
'''Perform stringprep on all JID fragments and return the full jid'''
|
2005-11-05 12:28:19 +01:00
|
|
|
# This function comes from
|
|
|
|
#http://svn.twistedmatrix.com/cvs/trunk/twisted/words/protocols/jabber/jid.py
|
2005-11-03 15:46:56 +01:00
|
|
|
|
|
|
|
if user:
|
|
|
|
try:
|
2008-11-05 20:48:58 +01:00
|
|
|
from xmpp_stringprep import nodeprep
|
2005-11-03 15:46:56 +01:00
|
|
|
user = nodeprep.prepare(unicode(user))
|
|
|
|
except UnicodeError:
|
2005-11-22 12:44:33 +01:00
|
|
|
raise InvalidFormat, _('Invalid character in username.')
|
2005-11-03 15:46:56 +01:00
|
|
|
else:
|
|
|
|
user = None
|
|
|
|
|
|
|
|
if not server:
|
2005-11-22 12:44:33 +01:00
|
|
|
raise InvalidFormat, _('Server address required.')
|
2005-11-03 15:46:56 +01:00
|
|
|
else:
|
|
|
|
try:
|
2008-11-05 20:48:58 +01:00
|
|
|
from xmpp_stringprep import nameprep
|
2005-11-03 15:46:56 +01:00
|
|
|
server = nameprep.prepare(unicode(server))
|
|
|
|
except UnicodeError:
|
2005-11-22 12:44:33 +01:00
|
|
|
raise InvalidFormat, _('Invalid character in hostname.')
|
2005-11-03 15:46:56 +01:00
|
|
|
|
|
|
|
if resource:
|
|
|
|
try:
|
2008-11-05 20:48:58 +01:00
|
|
|
from xmpp_stringprep import resourceprep
|
2005-11-03 15:46:56 +01:00
|
|
|
resource = resourceprep.prepare(unicode(resource))
|
|
|
|
except UnicodeError:
|
2005-11-22 12:44:33 +01:00
|
|
|
raise InvalidFormat, _('Invalid character in resource.')
|
2005-11-03 15:46:56 +01:00
|
|
|
else:
|
|
|
|
resource = None
|
|
|
|
|
|
|
|
if user:
|
|
|
|
if resource:
|
2005-11-05 12:28:19 +01:00
|
|
|
return '%s@%s/%s' % (user, server, resource)
|
2005-11-03 15:46:56 +01:00
|
|
|
else:
|
2005-11-05 12:28:19 +01:00
|
|
|
return '%s@%s' % (user, server)
|
2005-11-03 15:46:56 +01:00
|
|
|
else:
|
|
|
|
if resource:
|
2005-11-05 12:28:19 +01:00
|
|
|
return '%s/%s' % (server, resource)
|
2005-11-03 15:46:56 +01:00
|
|
|
else:
|
|
|
|
return server
|
|
|
|
|
2005-09-28 17:00:01 +02:00
|
|
|
def temp_failure_retry(func, *args, **kwargs):
|
2005-11-03 15:46:56 +01:00
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
return func(*args, **kwargs)
|
2005-12-21 18:55:57 +01:00
|
|
|
except (os.error, IOError, select.error), ex:
|
2005-11-03 15:46:56 +01:00
|
|
|
if ex.errno == errno.EINTR:
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
raise
|
2005-09-28 17:00:01 +02:00
|
|
|
|
2005-08-09 19:27:27 +02:00
|
|
|
def convert_bytes(string):
|
|
|
|
suffix = ''
|
|
|
|
# IEC standard says KiB = 1024 bytes KB = 1000 bytes
|
2005-09-08 23:01:15 +02:00
|
|
|
# but do we use the standard?
|
2005-08-09 19:27:27 +02:00
|
|
|
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
|
2006-02-27 16:29:13 +01:00
|
|
|
suffix = _('%s GiB')
|
2005-08-09 19:27:27 +02:00
|
|
|
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:
|
2008-04-04 11:51:49 +02:00
|
|
|
#KiB means kibibyte
|
|
|
|
suffix = _('%s KiB')
|
2005-08-09 19:27:27 +02:00
|
|
|
else:
|
|
|
|
#KB means kilo bytes
|
|
|
|
suffix = _('%s KB')
|
|
|
|
else:
|
2006-02-27 16:29:13 +01:00
|
|
|
#B means bytes
|
2005-08-09 19:27:27 +02:00
|
|
|
suffix = _('%s B')
|
2005-08-26 02:52:44 +02:00
|
|
|
return suffix % unicode(bytes)
|
2005-08-09 19:27:27 +02:00
|
|
|
|
2006-05-09 00:44:47 +02:00
|
|
|
|
|
|
|
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
|
2008-10-07 22:41:59 +02:00
|
|
|
if name in contacts_dict:
|
2006-05-09 00:44:47 +02:00
|
|
|
contact1 = contacts_dict[name]
|
|
|
|
del contacts_dict[name]
|
|
|
|
contacts_dict['%s (%s)' % (name, contact1.jid)] = contact1
|
|
|
|
contacts_dict['%s (%s)' % (name, jid)] = contact
|
|
|
|
else:
|
2007-06-13 23:29:03 +02:00
|
|
|
if contact.name == gajim.get_nick_from_jid(jid):
|
|
|
|
del contacts_dict[jid]
|
2006-05-09 00:44:47 +02:00
|
|
|
contacts_dict[name] = contact
|
|
|
|
return contacts_dict
|
|
|
|
|
2005-10-09 18:08:18 +02:00
|
|
|
def get_uf_show(show, use_mnemonic = False):
|
2005-05-20 17:58:23 +02:00
|
|
|
'''returns a userfriendly string for dnd/xa/chat
|
2005-10-09 18:08:18 +02:00
|
|
|
and makes all strings translatable
|
|
|
|
if use_mnemonic is True, it adds _ so GUI should call with True
|
|
|
|
for accessibility issues'''
|
2005-05-20 18:31:52 +02:00
|
|
|
if show == 'dnd':
|
2005-10-09 18:08:18 +02:00
|
|
|
if use_mnemonic:
|
|
|
|
uf_show = _('_Busy')
|
|
|
|
else:
|
|
|
|
uf_show = _('Busy')
|
2005-05-20 18:31:52 +02:00
|
|
|
elif show == 'xa':
|
2005-10-09 18:08:18 +02:00
|
|
|
if use_mnemonic:
|
|
|
|
uf_show = _('_Not Available')
|
|
|
|
else:
|
|
|
|
uf_show = _('Not Available')
|
2005-05-20 18:31:52 +02:00
|
|
|
elif show == 'chat':
|
2005-10-09 18:08:18 +02:00
|
|
|
if use_mnemonic:
|
|
|
|
uf_show = _('_Free for Chat')
|
|
|
|
else:
|
|
|
|
uf_show = _('Free for Chat')
|
2005-05-21 15:19:42 +02:00
|
|
|
elif show == 'online':
|
2005-10-09 18:08:18 +02:00
|
|
|
if use_mnemonic:
|
|
|
|
uf_show = _('_Available')
|
|
|
|
else:
|
|
|
|
uf_show = _('Available')
|
2005-05-21 15:19:42 +02:00
|
|
|
elif show == 'connecting':
|
2008-04-04 11:51:49 +02:00
|
|
|
uf_show = _('Connecting')
|
2005-05-21 15:19:42 +02:00
|
|
|
elif show == 'away':
|
2005-10-09 18:08:18 +02:00
|
|
|
if use_mnemonic:
|
|
|
|
uf_show = _('A_way')
|
|
|
|
else:
|
|
|
|
uf_show = _('Away')
|
2005-05-21 15:44:50 +02:00
|
|
|
elif show == 'offline':
|
2005-10-09 18:08:18 +02:00
|
|
|
if use_mnemonic:
|
|
|
|
uf_show = _('_Offline')
|
|
|
|
else:
|
|
|
|
uf_show = _('Offline')
|
2005-05-21 15:44:50 +02:00
|
|
|
elif show == 'invisible':
|
2005-10-09 18:08:18 +02:00
|
|
|
if use_mnemonic:
|
|
|
|
uf_show = _('_Invisible')
|
|
|
|
else:
|
|
|
|
uf_show = _('Invisible')
|
2006-02-20 12:02:35 +01:00
|
|
|
elif show == 'not in roster':
|
2006-01-14 23:08:52 +01:00
|
|
|
uf_show = _('Not in Roster')
|
2005-08-04 21:47:01 +02:00
|
|
|
elif show == 'requested':
|
2005-08-13 13:17:49 +02:00
|
|
|
uf_show = Q_('?contact has status:Unknown')
|
2005-05-23 18:57:35 +02:00
|
|
|
else:
|
2005-08-14 15:46:15 +02:00
|
|
|
uf_show = Q_('?contact has status:Has errors')
|
2005-06-01 23:40:33 +02:00
|
|
|
return unicode(uf_show)
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-08-05 00:08:33 +02:00
|
|
|
def get_uf_sub(sub):
|
|
|
|
if sub == 'none':
|
2005-08-13 00:32:35 +02:00
|
|
|
uf_sub = Q_('?Subscription we already have:None')
|
2005-08-05 00:08:33 +02:00
|
|
|
elif sub == 'to':
|
|
|
|
uf_sub = _('To')
|
|
|
|
elif sub == 'from':
|
|
|
|
uf_sub = _('From')
|
|
|
|
elif sub == 'both':
|
|
|
|
uf_sub = _('Both')
|
|
|
|
else:
|
|
|
|
uf_sub = sub
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-08-05 00:09:33 +02:00
|
|
|
return unicode(uf_sub)
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-08-05 00:08:33 +02:00
|
|
|
def get_uf_ask(ask):
|
|
|
|
if ask is None:
|
2005-08-13 00:32:35 +02:00
|
|
|
uf_ask = Q_('?Ask (for Subscription):None')
|
2005-08-05 00:08:33 +02:00
|
|
|
elif ask == 'subscribe':
|
|
|
|
uf_ask = _('Subscribe')
|
|
|
|
else:
|
|
|
|
uf_ask = ask
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-08-05 00:09:33 +02:00
|
|
|
return unicode(uf_ask)
|
2005-06-06 13:53:07 +02:00
|
|
|
|
2005-08-24 16:09:03 +02:00
|
|
|
def get_uf_role(role, plural = False):
|
2005-08-24 16:08:35 +02:00
|
|
|
''' 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
|
2006-09-26 04:41:47 +02:00
|
|
|
|
|
|
|
def get_uf_affiliation(affiliation):
|
|
|
|
'''Get a nice and translated affilition for muc'''
|
|
|
|
if affiliation == 'none':
|
|
|
|
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
|
|
|
|
|
2005-06-06 13:53:07 +02:00
|
|
|
def get_sorted_keys(adict):
|
2008-10-11 11:59:52 +02:00
|
|
|
keys = sorted(adict.keys())
|
2005-06-06 13:53:07 +02:00
|
|
|
return keys
|
2005-07-02 14:36:21 +02:00
|
|
|
|
|
|
|
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
|
2006-11-19 00:48:31 +01:00
|
|
|
expr = re.compile(r'(?<!\\)\\n')
|
|
|
|
msg = expr.sub('\n', msg)
|
2005-07-02 14:36:21 +02:00
|
|
|
msg = msg.replace('\\\\', '\\')
|
|
|
|
# s12 = 'test\\ntest\\\\ntest'
|
|
|
|
# s13 = re.sub('\n', s12)
|
|
|
|
# s14 s13.replace('\\\\', '\\')
|
|
|
|
# s14
|
|
|
|
# 'test\ntest\\ntest'
|
|
|
|
return msg
|
2005-07-22 16:30:35 +02:00
|
|
|
|
|
|
|
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':
|
2007-01-02 13:17:51 +01:00
|
|
|
#paused means he or she was composing but has stopped for a while
|
2005-08-09 13:33:56 +02:00
|
|
|
return _('paused composing a message')
|
2005-07-22 16:30:35 +02:00
|
|
|
elif chatstate == 'gone':
|
2005-07-22 16:35:34 +02:00
|
|
|
return _('has closed the chat window or tab')
|
2005-11-05 21:49:28 +01:00
|
|
|
return ''
|
2005-08-09 13:33:56 +02:00
|
|
|
|
|
|
|
def is_in_path(name_of_command, return_abs_path = False):
|
2006-02-27 16:29:13 +01:00
|
|
|
# if return_abs_path is True absolute path will be returned
|
2005-08-09 13:33:56 +02:00
|
|
|
# for name_of_command
|
|
|
|
# on failures False is returned
|
|
|
|
is_in_dir = False
|
|
|
|
found_in_which_dir = None
|
2008-02-17 17:01:44 +01:00
|
|
|
path = os.getenv('PATH').split(os.pathsep)
|
2005-08-09 13:33:56 +02:00
|
|
|
for path_to_directory in path:
|
|
|
|
try:
|
|
|
|
contents = os.listdir(path_to_directory)
|
|
|
|
except OSError: # user can have something in PATH that is not a dir
|
|
|
|
pass
|
2005-08-10 02:10:43 +02:00
|
|
|
else:
|
|
|
|
is_in_dir = name_of_command in contents
|
2005-08-09 13:33:56 +02:00
|
|
|
if is_in_dir:
|
|
|
|
if return_abs_path:
|
|
|
|
found_in_which_dir = path_to_directory
|
|
|
|
break
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-08-09 13:33:56 +02:00
|
|
|
if found_in_which_dir:
|
|
|
|
abs_path = os.path.join(path_to_directory, name_of_command)
|
|
|
|
return abs_path
|
|
|
|
else:
|
|
|
|
return is_in_dir
|
2005-08-10 00:46:13 +02:00
|
|
|
|
2006-08-27 00:51:33 +02:00
|
|
|
def exec_command(command):
|
2006-09-28 12:44:16 +02:00
|
|
|
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 "
|
2006-09-28 16:47:18 +02:00
|
|
|
command = '%s "%s"' % (executable, parameter)
|
2006-09-28 12:44:16 +02:00
|
|
|
return command
|
2006-08-27 00:51:33 +02:00
|
|
|
|
2005-08-10 00:55:52 +02:00
|
|
|
def launch_browser_mailer(kind, uri):
|
2005-08-10 00:46:13 +02:00
|
|
|
#kind = 'url' or 'mail'
|
|
|
|
if os.name == 'nt':
|
|
|
|
try:
|
|
|
|
os.startfile(uri) # if pywin32 is installed we open
|
2008-10-11 11:37:13 +02:00
|
|
|
except Exception:
|
2005-08-10 00:46:13 +02:00
|
|
|
pass
|
|
|
|
|
|
|
|
else:
|
2008-11-21 12:05:11 +01:00
|
|
|
if kind in ('mail', 'sth_at_sth') and not uri.startswith('mailto:'):
|
2005-08-10 00:46:13 +02:00
|
|
|
uri = 'mailto:' + uri
|
|
|
|
|
|
|
|
if gajim.config.get('openwith') == 'gnome-open':
|
|
|
|
command = 'gnome-open'
|
|
|
|
elif gajim.config.get('openwith') == 'kfmclient exec':
|
|
|
|
command = 'kfmclient exec'
|
2006-10-20 11:25:14 +02:00
|
|
|
elif gajim.config.get('openwith') == 'exo-open':
|
|
|
|
command = 'exo-open'
|
2007-12-27 19:58:07 +01:00
|
|
|
elif ((sys.platform == 'darwin') and\
|
|
|
|
(gajim.config.get('openwith') == 'open')):
|
2007-11-17 16:03:02 +01:00
|
|
|
command = 'open'
|
2005-08-10 00:46:13 +02:00
|
|
|
elif gajim.config.get('openwith') == 'custom':
|
|
|
|
if kind == 'url':
|
|
|
|
command = gajim.config.get('custombrowser')
|
2008-11-21 12:05:11 +01:00
|
|
|
if kind in ('mail', 'sth_at_sth'):
|
2005-08-10 00:46:13 +02:00
|
|
|
command = gajim.config.get('custommailapp')
|
|
|
|
if command == '': # if no app is configured
|
|
|
|
return
|
2006-09-28 12:44:16 +02:00
|
|
|
|
|
|
|
command = build_command(command, uri)
|
2006-08-27 00:51:33 +02:00
|
|
|
try:
|
|
|
|
exec_command(command)
|
2008-10-11 11:37:13 +02:00
|
|
|
except Exception:
|
2005-08-10 00:46:13 +02:00
|
|
|
pass
|
|
|
|
|
2005-08-10 00:55:52 +02:00
|
|
|
def launch_file_manager(path_to_open):
|
2005-08-11 13:44:14 +02:00
|
|
|
if os.name == 'nt':
|
|
|
|
try:
|
|
|
|
os.startfile(path_to_open) # if pywin32 is installed we open
|
2008-10-11 11:37:13 +02:00
|
|
|
except Exception:
|
2005-08-11 13:44:14 +02:00
|
|
|
pass
|
2005-08-11 14:36:32 +02:00
|
|
|
else:
|
|
|
|
if gajim.config.get('openwith') == 'gnome-open':
|
|
|
|
command = 'gnome-open'
|
|
|
|
elif gajim.config.get('openwith') == 'kfmclient exec':
|
|
|
|
command = 'kfmclient exec'
|
2006-10-20 11:25:14 +02:00
|
|
|
elif gajim.config.get('openwith') == 'exo-open':
|
|
|
|
command = 'exo-open'
|
2007-12-27 19:58:07 +01:00
|
|
|
elif ((sys.platform == 'darwin') and\
|
|
|
|
(gajim.config.get('openwith') == 'open')):
|
2007-11-17 16:03:02 +01:00
|
|
|
command = 'open'
|
2005-08-11 14:36:32 +02:00
|
|
|
elif gajim.config.get('openwith') == 'custom':
|
|
|
|
command = gajim.config.get('custom_file_manager')
|
|
|
|
if command == '': # if no app is configured
|
|
|
|
return
|
2006-09-28 12:44:16 +02:00
|
|
|
command = build_command(command, path_to_open)
|
2006-08-27 00:51:33 +02:00
|
|
|
try:
|
|
|
|
exec_command(command)
|
2008-10-11 11:37:13 +02:00
|
|
|
except Exception:
|
2005-08-11 14:36:32 +02:00
|
|
|
pass
|
2005-08-10 00:55:52 +02:00
|
|
|
|
|
|
|
def play_sound(event):
|
2005-08-10 00:46:13 +02:00
|
|
|
if not gajim.config.get('sounds_on'):
|
|
|
|
return
|
|
|
|
path_to_soundfile = gajim.config.get_per('soundevents', event, 'path')
|
2006-07-17 12:04:43 +02:00
|
|
|
play_sound_file(path_to_soundfile)
|
|
|
|
|
|
|
|
def play_sound_file(path_to_soundfile):
|
2006-06-19 17:38:41 +02:00
|
|
|
if path_to_soundfile == 'beep':
|
2007-06-27 12:20:24 +02:00
|
|
|
exec_command('beep')
|
2005-09-11 22:33:10 +02:00
|
|
|
return
|
2006-02-16 16:21:34 +01:00
|
|
|
if path_to_soundfile is None or not os.path.exists(path_to_soundfile):
|
2005-08-10 00:46:13 +02:00
|
|
|
return
|
2007-11-14 20:47:52 +01:00
|
|
|
if sys.platform == 'darwin':
|
2008-06-07 22:42:15 +02:00
|
|
|
try:
|
|
|
|
nsapp.playFile(path_to_soundfile)
|
2008-06-08 12:19:26 +02:00
|
|
|
except NameError:
|
2008-06-07 22:42:15 +02:00
|
|
|
pass
|
2007-11-14 20:47:52 +01:00
|
|
|
elif os.name == 'nt':
|
2005-08-10 00:46:13 +02:00
|
|
|
try:
|
|
|
|
winsound.PlaySound(path_to_soundfile,
|
2006-03-17 20:56:05 +01:00
|
|
|
winsound.SND_FILENAME|winsound.SND_ASYNC)
|
2008-10-11 11:37:13 +02:00
|
|
|
except Exception:
|
2005-08-10 00:46:13 +02:00
|
|
|
pass
|
|
|
|
elif os.name == 'posix':
|
2006-06-19 17:38:41 +02:00
|
|
|
if gajim.config.get('soundplayer') == '':
|
2005-08-10 00:46:13 +02:00
|
|
|
return
|
2006-06-19 17:38:41 +02:00
|
|
|
player = gajim.config.get('soundplayer')
|
2006-09-28 12:44:16 +02:00
|
|
|
command = build_command(player, path_to_soundfile)
|
2006-08-27 00:51:33 +02:00
|
|
|
exec_command(command)
|
2005-08-14 21:08:56 +02:00
|
|
|
|
|
|
|
def get_file_path_from_dnd_dropped_uri(uri):
|
2008-05-11 01:21:28 +02:00
|
|
|
path = urllib.unquote(uri) # escape special chars
|
2005-08-14 21:08:56 +02:00
|
|
|
path = path.strip('\r\n\x00') # remove \r\n and NULL
|
|
|
|
# get the path to file
|
2008-05-11 01:21:28 +02:00
|
|
|
if re.match('^file:///[a-zA-Z]:/', path): # windows
|
2005-08-17 00:38:21 +02:00
|
|
|
path = path[8:] # 8 is len('file:///')
|
|
|
|
elif path.startswith('file://'): # nautilus, rox
|
2007-11-19 16:53:16 +01:00
|
|
|
if sys.platform == 'darwin':
|
|
|
|
# OS/X includes hostname in file:// URI
|
|
|
|
path = re.sub('file://[^/]*', '', path)
|
|
|
|
else:
|
|
|
|
path = path[7:] # 7 is len('file://')
|
2005-08-14 21:08:56 +02:00
|
|
|
elif path.startswith('file:'): # xffm
|
|
|
|
path = path[5:] # 5 is len('file:')
|
|
|
|
return path
|
2005-08-18 21:06:24 +02:00
|
|
|
|
|
|
|
def from_xs_boolean_to_python_boolean(value):
|
2006-02-26 15:12:55 +01:00
|
|
|
# this is xs:boolean so 'true', 'false', '1', '0'
|
2005-08-18 21:06:24 +02:00
|
|
|
# convert those to True/False (python booleans)
|
2005-08-18 21:25:12 +02:00
|
|
|
if value in ('1', 'true'):
|
2005-08-18 21:06:24 +02:00
|
|
|
val = True
|
|
|
|
else: # '0', 'false' or anything else
|
|
|
|
val = False
|
|
|
|
|
|
|
|
return val
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-09-05 15:28:09 +02:00
|
|
|
def get_xmpp_show(show):
|
2005-10-26 19:30:52 +02:00
|
|
|
if show in ('online', 'offline'):
|
2005-09-05 15:28:09 +02:00
|
|
|
return None
|
|
|
|
return show
|
2005-10-09 16:49:14 +02:00
|
|
|
|
2005-10-25 10:39:41 +02:00
|
|
|
def get_output_of_command(command):
|
2005-11-03 15:46:56 +01:00
|
|
|
try:
|
|
|
|
child_stdin, child_stdout = os.popen2(command)
|
|
|
|
except ValueError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
output = child_stdout.readlines()
|
|
|
|
child_stdout.close()
|
|
|
|
child_stdin.close()
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-11-03 15:46:56 +01:00
|
|
|
return output
|
2005-11-09 08:00:46 +01:00
|
|
|
|
|
|
|
def get_global_show():
|
|
|
|
maxi = 0
|
|
|
|
for account in gajim.connections:
|
2005-12-02 20:12:21 +01:00
|
|
|
if not gajim.config.get_per('accounts', account,
|
|
|
|
'sync_with_global_status'):
|
|
|
|
continue
|
2005-11-09 08:00:46 +01:00
|
|
|
connected = gajim.connections[account].connected
|
|
|
|
if connected > maxi:
|
|
|
|
maxi = connected
|
|
|
|
return gajim.SHOW_LIST[maxi]
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-12-30 22:37:36 +01:00
|
|
|
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
|
2005-11-12 21:07:46 +01:00
|
|
|
|
2007-10-14 21:08:29 +02:00
|
|
|
def statuses_unified():
|
|
|
|
'''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
|
2008-04-18 02:02:56 +02:00
|
|
|
if reference is None:
|
2007-10-14 21:08:29 +02:00
|
|
|
reference = gajim.connections[account].connected
|
|
|
|
elif reference != gajim.connections[account].connected:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2005-11-12 22:06:26 +01:00
|
|
|
def get_icon_name_to_show(contact, account = None):
|
2005-11-12 21:07:46 +01:00
|
|
|
'''Get the icon name to show in online, away, requested, ...'''
|
2006-09-02 23:01:11 +02:00
|
|
|
if account and gajim.events.get_nb_roster_events(account, contact.jid):
|
2007-07-13 12:11:49 +02:00
|
|
|
return 'event'
|
2006-09-02 23:01:11 +02:00
|
|
|
if account and gajim.events.get_nb_roster_events(account,
|
2006-03-25 18:05:54 +01:00
|
|
|
contact.get_full_jid()):
|
2007-07-13 12:11:49 +02:00
|
|
|
return 'event'
|
2008-10-07 22:41:59 +02:00
|
|
|
if account and account in gajim.interface.minimized_controls and \
|
2007-05-17 14:55:44 +02:00
|
|
|
contact.jid in gajim.interface.minimized_controls[account] and gajim.interface.\
|
|
|
|
minimized_controls[account][contact.jid].get_nb_unread_pm() > 0:
|
2007-07-13 12:11:49 +02:00
|
|
|
return 'event'
|
2008-10-07 22:41:59 +02:00
|
|
|
if account and contact.jid in gajim.gc_connected[account]:
|
2007-06-03 15:40:14 +02:00
|
|
|
if gajim.gc_connected[account][contact.jid]:
|
|
|
|
return 'muc_active'
|
|
|
|
else:
|
|
|
|
return 'muc_inactive'
|
2005-11-12 22:01:17 +01:00
|
|
|
if contact.jid.find('@') <= 0: # if not '@' or '@' starts the jid ==> agent
|
2005-11-12 21:07:46 +01:00
|
|
|
return contact.show
|
|
|
|
if contact.sub in ('both', 'to'):
|
|
|
|
return contact.show
|
|
|
|
if contact.ask == 'subscribe':
|
|
|
|
return 'requested'
|
2005-11-12 22:01:52 +01:00
|
|
|
transport = gajim.get_transport_name_from_jid(contact.jid)
|
2005-11-12 21:07:46 +01:00
|
|
|
if transport:
|
|
|
|
return contact.show
|
2008-08-12 08:31:32 +02:00
|
|
|
if contact.show in gajim.SHOW_LIST:
|
|
|
|
return contact.show
|
2006-02-20 14:08:45 +01:00
|
|
|
return 'not in roster'
|
2005-11-18 13:17:50 +01:00
|
|
|
|
|
|
|
def decode_string(string):
|
|
|
|
'''try to decode (to make it Unicode instance) given string'''
|
2006-11-02 14:40:57 +01:00
|
|
|
if isinstance(string, unicode):
|
|
|
|
return string
|
2005-11-18 14:26:24 +01:00
|
|
|
# by the time we go to iso15 it better be the one else we show bad characters
|
2006-11-02 14:42:51 +01:00
|
|
|
encodings = (locale.getpreferredencoding(), 'utf-8', 'iso-8859-15')
|
2005-11-18 13:17:50 +01:00
|
|
|
for encoding in encodings:
|
|
|
|
try:
|
|
|
|
string = string.decode(encoding)
|
|
|
|
except UnicodeError:
|
|
|
|
continue
|
|
|
|
break
|
2006-02-27 16:29:13 +01:00
|
|
|
|
2005-11-18 13:17:50 +01:00
|
|
|
return string
|
2006-03-15 02:40:10 +01:00
|
|
|
|
2006-09-06 15:28:38 +02:00
|
|
|
def ensure_utf8_string(string):
|
|
|
|
'''make sure string is in UTF-8'''
|
|
|
|
try:
|
|
|
|
string = decode_string(string).encode('utf-8')
|
2008-10-11 11:37:13 +02:00
|
|
|
except Exception:
|
2006-09-06 15:28:38 +02:00
|
|
|
pass
|
|
|
|
return string
|
|
|
|
|
2008-07-30 14:21:47 +02:00
|
|
|
def remove_invalid_xml_chars(string):
|
|
|
|
if string:
|
|
|
|
string = re.sub(gajim.interface.invalid_XML_chars_re, '', string)
|
|
|
|
return string
|
|
|
|
|
2006-03-15 02:40:10 +01:00
|
|
|
def get_windows_reg_env(varname, default=''):
|
|
|
|
'''asks for paths commonly used but not exposed as ENVs
|
2006-03-15 03:09:57 +01:00
|
|
|
in english Windows 2003 those are:
|
2006-03-15 02:40:10 +01:00
|
|
|
'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:
|
2006-03-17 20:56:05 +01:00
|
|
|
rkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER,
|
2006-03-15 02:40:10 +01:00
|
|
|
r'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders')
|
|
|
|
try:
|
2006-03-17 20:56:05 +01:00
|
|
|
val = str(win32api.RegQueryValueEx(rkey, varname)[0])
|
2006-03-15 03:09:57 +01:00
|
|
|
val = win32api.ExpandEnvironmentStrings(val) # expand using environ
|
2008-10-11 11:37:13 +02:00
|
|
|
except Exception:
|
2006-03-15 02:40:10 +01:00
|
|
|
pass
|
|
|
|
finally:
|
|
|
|
win32api.RegCloseKey(rkey)
|
|
|
|
return val
|
2007-02-04 19:57:25 +01:00
|
|
|
|
2006-03-15 02:40:10 +01:00
|
|
|
def get_my_pictures_path():
|
|
|
|
'''windows-only atm. [Unix lives in the past]'''
|
|
|
|
return get_windows_reg_env('My Pictures')
|
2006-03-15 03:09:57 +01:00
|
|
|
|
|
|
|
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
|
2006-03-19 15:54:00 +01:00
|
|
|
|
|
|
|
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 sha.new("%s%s%s" % (sid, initiator, target)).hexdigest()
|
|
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
}
|
|
|
|
|
2006-03-20 22:24:10 +01:00
|
|
|
def get_random_string_16():
|
|
|
|
''' create random string of length 16'''
|
|
|
|
rng = range(65, 90)
|
|
|
|
rng.extend(range(48, 57))
|
|
|
|
char_sequence = map(lambda e:chr(e), rng)
|
|
|
|
from random import sample
|
2008-10-11 11:49:30 +02:00
|
|
|
return ''.join(sample(char_sequence, 16))
|
2006-03-20 22:24:10 +01:00
|
|
|
|
2006-03-19 15:54:00 +01:00
|
|
|
def get_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',
|
2006-11-29 03:34:07 +01:00
|
|
|
(2, 5, 2): '2003',
|
2007-02-04 19:57:25 +01:00
|
|
|
(2, 6, 0): 'Vista',
|
2006-03-19 15:54:00 +01:00
|
|
|
}
|
2008-10-07 22:41:59 +02:00
|
|
|
if ver_format in win_version:
|
2006-03-19 15:54:00 +01:00
|
|
|
return 'Windows' + ' ' + win_version[ver_format]
|
|
|
|
else:
|
|
|
|
return 'Windows'
|
|
|
|
elif os.name == 'posix':
|
|
|
|
executable = 'lsb_release'
|
2008-11-20 15:37:38 +01:00
|
|
|
params = ' --description --codename --release --short'
|
2006-03-19 15:54:00 +01:00
|
|
|
full_path_to_executable = is_in_path(executable, return_abs_path = True)
|
|
|
|
if full_path_to_executable:
|
|
|
|
command = executable + params
|
2008-03-10 23:11:43 +01:00
|
|
|
p = subprocess.Popen([command], shell=True, stdin=subprocess.PIPE,
|
2008-03-10 14:27:31 +01:00
|
|
|
stdout=subprocess.PIPE, close_fds=True)
|
|
|
|
p.wait()
|
|
|
|
output = temp_failure_retry(p.stdout.readline).strip()
|
2006-03-30 18:08:54 +02:00
|
|
|
# some distros put n/a in places, so remove those
|
|
|
|
output = output.replace('n/a', '').replace('N/A', '')
|
2006-03-19 15:54:00 +01:00
|
|
|
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'):
|
2006-05-29 16:19:18 +02:00
|
|
|
# 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'):
|
2006-03-19 15:54:00 +01:00
|
|
|
text = distro_name + ' ' + text
|
2008-01-30 22:29:55 +01:00
|
|
|
elif path_to_file.endswith('aurox-release') or \
|
|
|
|
path_to_file.endswith('arch-release'):
|
2006-03-19 15:54:00 +01:00
|
|
|
# file doesn't have version
|
|
|
|
text = distro_name
|
|
|
|
elif path_to_file.endswith('lfs-release'): # file just has version
|
|
|
|
text = distro_name + ' ' + text
|
2008-07-21 19:29:49 +02:00
|
|
|
return text.replace('\n', '')
|
2006-03-19 15:54:00 +01:00
|
|
|
|
|
|
|
# our last chance, ask uname and strip it
|
2008-01-30 22:31:19 +01:00
|
|
|
uname_output = get_output_of_command('uname -sr')
|
2006-03-19 15:54:00 +01:00
|
|
|
if uname_output is not None:
|
|
|
|
return uname_output[0] # only first line
|
|
|
|
return 'N/A'
|
2006-03-21 14:55:22 +01:00
|
|
|
|
|
|
|
def sanitize_filename(filename):
|
2008-11-11 16:00:18 +01:00
|
|
|
'''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 = hash_md5(filename)
|
|
|
|
filename = base64.b64encode(hash.digest())
|
|
|
|
|
2006-03-26 21:35:29 +02:00
|
|
|
filename = punycode_encode(filename) # make it latin chars only
|
2006-03-21 14:55:22 +01:00
|
|
|
filename = filename.replace('/', '_')
|
|
|
|
if os.name == 'nt':
|
2006-05-02 08:36:49 +02:00
|
|
|
filename = filename.replace('?', '_').replace(':', '_')\
|
|
|
|
.replace('\\', '_').replace('"', "'").replace('|', '_')\
|
|
|
|
.replace('*', '_').replace('<', '_').replace('>', '_')
|
2006-03-21 14:55:22 +01:00
|
|
|
|
|
|
|
return filename
|
2006-03-28 00:31:14 +02:00
|
|
|
|
2008-10-11 12:22:04 +02:00
|
|
|
def allow_showing_notification(account, type_ = 'notify_on_new_message',
|
2006-09-28 16:47:18 +02:00
|
|
|
advanced_notif_num = None, is_first_message = True):
|
2006-03-28 01:32:39 +02:00
|
|
|
'''is it allowed to show nofication?
|
2006-08-27 00:51:33 +02:00
|
|
|
check OUR status and if we allow notifications for that status
|
2006-09-28 15:35:53 +02:00
|
|
|
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:
|
2006-08-27 00:51:33 +02:00
|
|
|
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
|
|
|
|
'popup')
|
|
|
|
if popup == 'yes':
|
|
|
|
return True
|
|
|
|
if popup == 'no':
|
|
|
|
return False
|
2008-10-11 12:22:04 +02:00
|
|
|
if type_ and (not gajim.config.get(type_) or not is_first_message):
|
2006-08-27 00:51:33 +02:00
|
|
|
return False
|
2006-03-28 01:15:19 +02:00
|
|
|
if gajim.config.get('autopopupaway'): # always show notification
|
|
|
|
return True
|
|
|
|
if gajim.connections[account].connected in (2, 3): # we're online or chat
|
|
|
|
return True
|
2006-03-28 00:31:14 +02:00
|
|
|
return False
|
|
|
|
|
2006-08-27 00:51:33 +02:00
|
|
|
def allow_popup_window(account, advanced_notif_num = None):
|
2006-03-28 00:31:14 +02:00
|
|
|
'''is it allowed to popup windows?'''
|
2008-04-18 02:26:07 +02:00
|
|
|
if advanced_notif_num is not None:
|
2006-08-27 00:51:33 +02:00
|
|
|
popup = gajim.config.get_per('notifications', str(advanced_notif_num),
|
|
|
|
'auto_open')
|
|
|
|
if popup == 'yes':
|
|
|
|
return True
|
|
|
|
if popup == 'no':
|
|
|
|
return False
|
2006-03-28 00:42:42 +02:00
|
|
|
autopopup = gajim.config.get('autopopup')
|
|
|
|
autopopupaway = gajim.config.get('autopopupaway')
|
2006-03-28 01:10:41 +02:00
|
|
|
if autopopup and (autopopupaway or \
|
|
|
|
gajim.connections[account].connected in (2, 3)): # we're online or chat
|
2006-03-28 00:31:14 +02:00
|
|
|
return True
|
|
|
|
return False
|
2006-08-27 00:51:33 +02:00
|
|
|
|
|
|
|
def allow_sound_notification(sound_event, advanced_notif_num = None):
|
2006-09-28 15:35:53 +02:00
|
|
|
if advanced_notif_num is not None:
|
2006-08-27 00:51:33 +02:00
|
|
|
sound = gajim.config.get_per('notifications', str(advanced_notif_num),
|
|
|
|
'sound')
|
|
|
|
if sound == 'yes':
|
|
|
|
return True
|
|
|
|
if sound == 'no':
|
|
|
|
return False
|
|
|
|
if gajim.config.get_per('soundevents', sound_event, 'enabled'):
|
|
|
|
return True
|
|
|
|
return False
|
2006-09-02 23:01:11 +02:00
|
|
|
|
|
|
|
def get_chat_control(account, contact):
|
|
|
|
full_jid_with_resource = contact.jid
|
|
|
|
if contact.resource:
|
|
|
|
full_jid_with_resource += '/' + contact.resource
|
|
|
|
highest_contact = gajim.contacts.get_contact_with_highest_priority(
|
|
|
|
account, contact.jid)
|
2008-06-27 01:36:58 +02:00
|
|
|
|
2006-09-02 23:01:11 +02:00
|
|
|
# Look for a chat control that has the given resource, or default to
|
|
|
|
# one without resource
|
2008-06-27 01:36:58 +02:00
|
|
|
ctrl = gajim.interface.msg_win_mgr.get_control(full_jid_with_resource,
|
2006-09-02 23:01:11 +02:00
|
|
|
account)
|
2008-06-14 10:43:20 +02:00
|
|
|
|
2008-06-27 01:36:58 +02:00
|
|
|
if ctrl:
|
|
|
|
return ctrl
|
|
|
|
elif highest_contact and highest_contact.resource and \
|
|
|
|
contact.resource != highest_contact.resource:
|
2008-06-14 10:43:20 +02:00
|
|
|
return None
|
2008-06-27 01:36:58 +02:00
|
|
|
else:
|
|
|
|
# unknown contact or offline message
|
|
|
|
return gajim.interface.msg_win_mgr.get_control(contact.jid, account)
|
2006-10-05 02:06:57 +02:00
|
|
|
|
2006-10-06 16:52:25 +02:00
|
|
|
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 = map(lambda e: _cut_if_long(e), lines)
|
|
|
|
if lines:
|
2008-10-11 11:49:30 +02:00
|
|
|
reduced_text = '\n'.join(lines)
|
2007-07-30 14:17:04 +02:00
|
|
|
if reduced_text != text:
|
|
|
|
reduced_text += '...'
|
2006-10-06 16:52:25 +02:00
|
|
|
else:
|
|
|
|
reduced_text = ''
|
|
|
|
return reduced_text
|
|
|
|
|
2007-07-03 11:31:43 +02:00
|
|
|
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]}'''
|
|
|
|
# How many events must there be before they're shown summarized, not per-user
|
|
|
|
max_ungrouped_events = 10
|
|
|
|
|
|
|
|
accounts = get_accounts_info()
|
|
|
|
|
|
|
|
# Gather events. (With accounts, when there are more.)
|
|
|
|
for account in accounts:
|
|
|
|
account_name = account['name']
|
|
|
|
account['event_lines'] = []
|
|
|
|
# Gather events per-account
|
|
|
|
pending_events = gajim.events.get_events(account = account_name)
|
|
|
|
messages, non_messages, total_messages, total_non_messages = {}, {}, 0, 0
|
|
|
|
for jid in pending_events:
|
|
|
|
for event in pending_events[jid]:
|
|
|
|
if event.type_.count('file') > 0:
|
|
|
|
# This is a non-messagee event.
|
|
|
|
messages[jid] = non_messages.get(jid, 0) + 1
|
|
|
|
total_non_messages = total_non_messages + 1
|
|
|
|
else:
|
|
|
|
# This is a message.
|
|
|
|
messages[jid] = messages.get(jid, 0) + 1
|
|
|
|
total_messages = total_messages + 1
|
|
|
|
# Display unread messages numbers, if any
|
|
|
|
if total_messages > 0:
|
|
|
|
if total_messages > max_ungrouped_events:
|
|
|
|
text = ngettext(
|
|
|
|
'%d message pending',
|
|
|
|
'%d messages pending',
|
|
|
|
total_messages, total_messages, total_messages)
|
|
|
|
account['event_lines'].append(text)
|
|
|
|
else:
|
|
|
|
for jid in messages.keys():
|
|
|
|
text = ngettext(
|
|
|
|
'%d message pending',
|
|
|
|
'%d messages pending',
|
|
|
|
messages[jid], messages[jid], messages[jid])
|
|
|
|
contact = gajim.contacts.get_first_contact_from_jid(
|
|
|
|
account['name'], jid)
|
|
|
|
if jid in gajim.gc_connected[account['name']]:
|
|
|
|
text += _(' from room %s') % (jid)
|
|
|
|
elif contact:
|
|
|
|
name = contact.get_shown_name()
|
|
|
|
text += _(' from user %s') % (name)
|
|
|
|
else:
|
|
|
|
text += _(' from %s') % (jid)
|
|
|
|
account['event_lines'].append(text)
|
|
|
|
|
|
|
|
# Display unseen events numbers, if any
|
|
|
|
if total_non_messages > 0:
|
|
|
|
if total_non_messages > max_ungrouped_events:
|
|
|
|
text = ngettext(
|
|
|
|
'%d event pending',
|
|
|
|
'%d events pending',
|
|
|
|
total_non_messages, total_non_messages, total_non_messages)
|
|
|
|
accounts[account]['event_lines'].append(text)
|
|
|
|
else:
|
|
|
|
for jid in non_messages.keys():
|
|
|
|
text = ngettext(
|
|
|
|
'%d event pending',
|
|
|
|
'%d events pending',
|
|
|
|
non_messages[jid], non_messages[jid], non_messages[jid])
|
|
|
|
text += _(' from user %s') % (jid)
|
|
|
|
accounts[account]['event_lines'].append(text)
|
|
|
|
|
|
|
|
return accounts
|
|
|
|
|
2006-10-05 02:06:57 +02:00
|
|
|
def get_notification_icon_tooltip_text():
|
|
|
|
text = None
|
2007-07-03 11:31:43 +02:00
|
|
|
# How many events must there be before they're shown summarized, not per-user
|
2008-12-02 16:53:23 +01:00
|
|
|
# max_ungrouped_events = 10
|
2007-07-03 11:31:43 +02:00
|
|
|
# Character which should be used to indent in the tooltip.
|
|
|
|
indent_with = ' '
|
2006-10-05 02:06:57 +02:00
|
|
|
|
2007-07-03 11:31:43 +02:00
|
|
|
accounts = get_notification_icon_tooltip_dict()
|
2006-10-05 02:06:57 +02:00
|
|
|
|
2007-07-03 11:31:43 +02:00
|
|
|
if len(accounts) == 0:
|
|
|
|
# No configured account
|
|
|
|
return _('Gajim')
|
|
|
|
|
|
|
|
# at least one account present
|
|
|
|
|
|
|
|
# Is there more that one account?
|
|
|
|
if len(accounts) == 1:
|
|
|
|
show_more_accounts = False
|
2006-10-05 02:06:57 +02:00
|
|
|
else:
|
2007-07-03 11:31:43 +02:00
|
|
|
show_more_accounts = True
|
|
|
|
|
|
|
|
# If there is only one account, its status is shown on the first line.
|
|
|
|
if show_more_accounts:
|
|
|
|
text = _('Gajim')
|
|
|
|
else:
|
|
|
|
text = _('Gajim - %s') % (get_account_status(accounts[0]))
|
|
|
|
|
|
|
|
# Gather and display events. (With accounts, when there are more.)
|
|
|
|
for account in accounts:
|
|
|
|
account_name = account['name']
|
|
|
|
# Set account status, if not set above
|
|
|
|
if (show_more_accounts):
|
|
|
|
message = '\n' + indent_with + ' %s - %s'
|
|
|
|
text += message % (account_name, get_account_status(account))
|
|
|
|
# Account list shown, messages need to be indented more
|
|
|
|
indent_how = 2
|
|
|
|
else:
|
|
|
|
# If no account list is shown, messages could have default indenting.
|
|
|
|
indent_how = 1
|
|
|
|
for line in account['event_lines']:
|
|
|
|
text += '\n' + indent_with * indent_how + ' '
|
|
|
|
text += line
|
2006-10-05 02:06:57 +02:00
|
|
|
return text
|
|
|
|
|
|
|
|
def get_accounts_info():
|
|
|
|
'''helper for notification icon tooltip'''
|
|
|
|
accounts = []
|
2008-10-11 11:59:52 +02:00
|
|
|
accounts_list = sorted(gajim.contacts.get_accounts())
|
2006-10-05 02:06:57 +02:00
|
|
|
for account in accounts_list:
|
|
|
|
status_idx = gajim.connections[account].connected
|
|
|
|
# uncomment the following to hide offline accounts
|
|
|
|
# if status_idx == 0: continue
|
|
|
|
status = gajim.SHOW_LIST[status_idx]
|
|
|
|
message = gajim.connections[account].status
|
|
|
|
single_line = get_uf_show(status)
|
|
|
|
if message is None:
|
|
|
|
message = ''
|
|
|
|
else:
|
|
|
|
message = message.strip()
|
|
|
|
if message != '':
|
|
|
|
single_line += ': ' + message
|
|
|
|
accounts.append({'name': account, 'status_line': single_line,
|
|
|
|
'show': status, 'message': message})
|
|
|
|
return accounts
|
2007-04-22 20:21:30 +02:00
|
|
|
|
|
|
|
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
|
2007-06-29 23:21:08 +02:00
|
|
|
|
|
|
|
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
|
2008-06-13 17:34:55 +02:00
|
|
|
- Optional Z (that means UTC) appened to the string are removed
|
2007-06-29 23:21:08 +02:00
|
|
|
- XEP-082 datetime strings have all '-' cahrs removed to meet
|
|
|
|
the above format.'''
|
|
|
|
timestamp = timestamp.split('.')[0]
|
|
|
|
timestamp = timestamp.replace('-', '')
|
2008-06-13 17:34:55 +02:00
|
|
|
timestamp = timestamp.replace('z', '')
|
|
|
|
timestamp = timestamp.replace('Z', '')
|
2007-06-29 23:21:08 +02:00
|
|
|
from time import strptime
|
|
|
|
return strptime(timestamp, '%Y%m%dT%H:%M:%S')
|
2007-08-06 00:57:04 +02:00
|
|
|
|
|
|
|
def get_iconset_path(iconset):
|
|
|
|
if os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', iconset)):
|
|
|
|
return os.path.join(gajim.DATA_DIR, 'iconsets', iconset)
|
|
|
|
elif os.path.isdir(os.path.join(gajim.MY_ICONSETS_PATH, iconset)):
|
|
|
|
return os.path.join(gajim.MY_ICONSETS_PATH, iconset)
|
2007-08-23 00:46:31 +02:00
|
|
|
|
2008-07-22 23:08:52 +02:00
|
|
|
def get_mood_iconset_path(iconset):
|
|
|
|
if os.path.isdir(os.path.join(gajim.DATA_DIR, 'moods', iconset)):
|
|
|
|
return os.path.join(gajim.DATA_DIR, 'moods', iconset)
|
|
|
|
elif os.path.isdir(os.path.join(gajim.MY_MOOD_ICONSETS_PATH, iconset)):
|
|
|
|
return os.path.join(gajim.MY_MOOD_ICONSETS_PATH, iconset)
|
|
|
|
|
2008-07-28 22:57:56 +02:00
|
|
|
def get_activity_iconset_path(iconset):
|
|
|
|
if os.path.isdir(os.path.join(gajim.DATA_DIR, 'activities', iconset)):
|
|
|
|
return os.path.join(gajim.DATA_DIR, 'activities', iconset)
|
|
|
|
elif os.path.isdir(os.path.join(gajim.MY_ACTIVITY_ICONSETS_PATH,
|
|
|
|
iconset)):
|
|
|
|
return os.path.join(gajim.MY_ACTIVITY_ICONSETS_PATH, iconset)
|
|
|
|
|
2007-08-23 00:46:31 +02:00
|
|
|
def get_transport_path(transport):
|
|
|
|
if os.path.isdir(os.path.join(gajim.DATA_DIR, 'iconsets', 'transports',
|
|
|
|
transport)):
|
|
|
|
return os.path.join(gajim.DATA_DIR, 'iconsets', 'transports', transport)
|
|
|
|
elif os.path.isdir(os.path.join(gajim.MY_ICONSETS_PATH, 'transports',
|
|
|
|
transport)):
|
|
|
|
return os.path.join(gajim.MY_ICONSETS_PATH, 'transports', transport)
|
2007-12-20 20:43:24 +01:00
|
|
|
# No transport folder found, use default jabber one
|
|
|
|
return get_iconset_path(gajim.config.get('iconset'))
|
2007-12-30 00:28:27 +01:00
|
|
|
|
|
|
|
def prepare_and_validate_gpg_keyID(account, jid, keyID):
|
|
|
|
'''Returns an eight char long keyID that can be used with for GPG encryption with this contact.
|
|
|
|
If the given keyID is None, return UNKNOWN; if the key does not match the assigned key
|
|
|
|
XXXXXXXXMISMATCH is returned. If the key is trusted and not yet assigned, assign it'''
|
|
|
|
if gajim.connections[account].USE_GPG:
|
2007-12-31 02:19:08 +01:00
|
|
|
if keyID and len(keyID) == 16:
|
2007-12-30 00:28:27 +01:00
|
|
|
keyID = keyID[8:]
|
|
|
|
|
|
|
|
attached_keys = gajim.config.get_per('accounts', account,
|
|
|
|
'attached_gpg_keys').split()
|
|
|
|
|
|
|
|
if jid in attached_keys and keyID:
|
|
|
|
attachedkeyID = attached_keys[attached_keys.index(jid) + 1]
|
|
|
|
if attachedkeyID != keyID:
|
|
|
|
# Mismatch! Another gpg key was expected
|
|
|
|
keyID += 'MISMATCH'
|
|
|
|
elif jid in attached_keys:
|
|
|
|
# An unsigned presence, just use the assigned key
|
|
|
|
keyID = attached_keys[attached_keys.index(jid) + 1]
|
|
|
|
elif keyID:
|
|
|
|
public_keys = gajim.connections[account].ask_gpg_keys()
|
|
|
|
# Assign the corresponding key, if we have it in our keyring
|
2008-10-07 22:41:59 +02:00
|
|
|
if keyID in public_keys:
|
2007-12-30 00:28:27 +01:00
|
|
|
for u in gajim.contacts.get_contacts(account, jid):
|
|
|
|
u.keyID = keyID
|
|
|
|
keys_str = gajim.config.get_per('accounts', account, 'attached_gpg_keys')
|
|
|
|
keys_str += jid + ' ' + keyID + ' '
|
|
|
|
gajim.config.set_per('accounts', account, 'attached_gpg_keys', keys_str)
|
|
|
|
elif keyID is None:
|
|
|
|
keyID = 'UNKNOWN'
|
|
|
|
return keyID
|
|
|
|
|
2008-04-21 00:58:47 +02:00
|
|
|
def sort_identities_func(i1, i2):
|
|
|
|
cat1 = i1['category']
|
|
|
|
cat2 = i2['category']
|
|
|
|
if cat1 < cat2:
|
|
|
|
return -1
|
|
|
|
if cat1 > cat2:
|
|
|
|
return 1
|
2008-10-11 11:31:48 +02:00
|
|
|
type1 = i1.get('type', '')
|
|
|
|
type2 = i2.get('type', '')
|
2008-04-21 00:58:47 +02:00
|
|
|
if type1 < type2:
|
|
|
|
return -1
|
|
|
|
if type1 > type2:
|
|
|
|
return 1
|
2008-10-11 11:31:48 +02:00
|
|
|
lang1 = i1.get('xml:lang', '')
|
|
|
|
lang2 = i2.get('xml:lang', '')
|
2008-04-21 00:58:47 +02:00
|
|
|
if lang1 < lang2:
|
|
|
|
return -1
|
|
|
|
if lang1 > lang2:
|
|
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
2008-05-14 20:30:39 +02:00
|
|
|
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'''
|
2008-04-21 00:58:47 +02:00
|
|
|
S = ''
|
|
|
|
identities.sort(cmp=sort_identities_func)
|
|
|
|
for i in identities:
|
|
|
|
c = i['category']
|
2008-10-11 11:31:48 +02:00
|
|
|
type_ = i.get('type', '')
|
|
|
|
lang = i.get('xml:lang', '')
|
|
|
|
name = i.get('name', '')
|
2008-04-21 00:58:47 +02:00
|
|
|
S += '%s/%s/%s/%s<' % (c, type_, lang, name)
|
|
|
|
features.sort()
|
|
|
|
for f in features:
|
|
|
|
S += '%s<' % f
|
2008-05-14 20:30:39 +02:00
|
|
|
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']
|
2008-12-03 18:16:04 +01:00
|
|
|
for var in sorted(fields.keys()):
|
2008-05-14 20:30:39 +02:00
|
|
|
S += '%s<' % var
|
2008-10-11 11:59:52 +02:00
|
|
|
values = sorted(fields[var].getValues())
|
2008-05-14 20:30:39 +02:00
|
|
|
for value in values:
|
|
|
|
S += '%s<' % value
|
|
|
|
|
2008-04-21 00:58:47 +02:00
|
|
|
if hash_method == 'sha-1':
|
2008-12-03 18:16:04 +01:00
|
|
|
hash_ = hash_sha1(S)
|
2008-04-21 00:58:47 +02:00
|
|
|
elif hash_method == 'md5':
|
2008-12-03 18:16:04 +01:00
|
|
|
hash_ = hash_md5(S)
|
2008-04-21 00:58:47 +02:00
|
|
|
else:
|
|
|
|
return ''
|
2008-12-03 18:16:04 +01:00
|
|
|
return base64.b64encode(hash_.digest())
|
2008-04-21 00:58:47 +02:00
|
|
|
|
2008-05-27 12:24:29 +02:00
|
|
|
def update_optional_features(account = None):
|
|
|
|
if account:
|
|
|
|
accounts = [account]
|
|
|
|
else:
|
|
|
|
accounts = [a for a in gajim.connections]
|
|
|
|
for a in accounts:
|
|
|
|
gajim.gajim_optional_features[a] = []
|
|
|
|
if gajim.config.get_per('accounts', a, 'subscribe_mood'):
|
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_MOOD + '+notify')
|
|
|
|
if gajim.config.get_per('accounts', a, 'subscribe_activity'):
|
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_ACTIVITY + '+notify')
|
|
|
|
if gajim.config.get_per('accounts', a, 'publish_tune'):
|
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_TUNE)
|
|
|
|
if gajim.config.get_per('accounts', a, 'subscribe_tune'):
|
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_TUNE + '+notify')
|
|
|
|
if gajim.config.get_per('accounts', a, 'subscribe_nick'):
|
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_NICK + '+notify')
|
|
|
|
if gajim.config.get('outgoing_chat_state_notifactions') != 'disabled':
|
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_CHATSTATES)
|
|
|
|
if not gajim.config.get('ignore_incoming_xhtml'):
|
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_XHTML_IM)
|
2008-07-25 00:13:59 +02:00
|
|
|
if gajim.HAVE_PYCRYPTO \
|
|
|
|
and gajim.config.get_per('accounts', a, 'enable_esessions'):
|
2008-07-20 17:54:05 +02:00
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_ESESSION)
|
2008-06-17 18:03:21 +02:00
|
|
|
if gajim.config.get_per('accounts', a, 'answer_receipts'):
|
|
|
|
gajim.gajim_optional_features[a].append(xmpp.NS_RECEIPTS)
|
2008-05-27 12:24:29 +02:00
|
|
|
gajim.caps_hash[a] = compute_caps_hash([gajim.gajim_identity],
|
|
|
|
gajim.gajim_common_features + gajim.gajim_optional_features[a])
|
|
|
|
# re-send presence with new hash
|
|
|
|
connected = gajim.connections[a].connected
|
2008-04-21 00:58:47 +02:00
|
|
|
if connected > 1 and gajim.SHOW_LIST[connected] != 'invisible':
|
2008-05-27 12:24:29 +02:00
|
|
|
gajim.connections[a].change_status(gajim.SHOW_LIST[connected],
|
|
|
|
gajim.connections[a].status)
|
2008-07-29 21:49:31 +02:00
|
|
|
|
2008-07-30 14:21:47 +02:00
|
|
|
# vim: se ts=3:
|