we now stringprep jid and resource everywhere:

when we recieve a jid from server
when we enter a jid or resource in GUI
This commit is contained in:
Yann Leboulanger 2005-11-03 14:46:56 +00:00
parent 1462b4a27f
commit f130ce29c0
5 changed files with 406 additions and 72 deletions

View File

@ -177,25 +177,14 @@ class Connection:
self.retrycount = 0
# END __init__
def strip_jid(self, jid):
'''look into gajim.contacts if we already have this jid CASE INSENSITIVE
and returns it
the function accetps jid and fjid'''
ji, resource = gajim.get_room_and_nick_from_fjid(jid)
for rjid in gajim.contacts[self.name]:
if ji.lower() == rjid.lower(): # we found the jid
ji = rjid
if resource:
return ji + '/' + resource
return ji
def get_full_jid(self, iq_obj):
'''return the full jid (with resource) from an iq as unicode'''
return unicode(self.strip_jid(str(iq_obj.getFrom())))
return helpers.parse_jid(str(iq_obj.getFrom()))
def get_jid(self, iq_obj):
'''return the jid (without resource) from an iq as unicode'''
return unicode(self.strip_jid(iq_obj.getFrom().getStripped()))
jid = self.get_full_jid(iq_obj)
return gajim.get_jid_without_resource(jid)
def put_event(self, ev):
if gajim.events_for_ui.has_key(self.name):
@ -1096,7 +1085,7 @@ class Connection:
def _rosterSetCB(self, con, iq_obj):
gajim.log.debug('rosterSetCB')
for item in iq_obj.getTag('query').getChildren():
jid = item.getAttr('jid')
jid = helprs.parse_jid(item.getAttr('jid'))
name = item.getAttr('name')
sub = item.getAttr('subscription')
ask = item.getAttr('ask')
@ -1266,9 +1255,11 @@ class Connection:
def _getRosterCB(self, con, iq_obj):
if not self.connection:
return
roster = self.connection.getRoster().getRaw().copy()
if not roster:
roster = {}
r = self.connection.getRoster().getRaw()
roster = {}
for jid in r:
roster[helpers.parse_jid(jid)] = r[jid]
jid = gajim.get_jid_from_account(self.name)

View File

@ -26,6 +26,7 @@ import stat
import gajim
from common import i18n
from common.xmpp_stringprep import nodeprep, resourceprep, nameprep
try:
import winsound # windows-only built-in module for playing wav
@ -35,15 +36,104 @@ except:
_ = i18n._
Q_ = i18n.Q_
class InvalidFormat(Exception):
pass
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
user = None
server = None
resource = None
# Search for delimiters
user_sep = jidstring.find("@")
res_sep = jidstring.find("/")
if user_sep == -1:
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 prep(user, server, resource)
def parse_resource(resource):
'''Perform stringprep on resource and return it'''
if resource:
try:
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
if user:
try:
user = nodeprep.prepare(unicode(user))
except UnicodeError:
raise InvalidFormat, "Invalid character in username"
else:
user = None
if not server:
raise InvalidFormat, "Server address required."
else:
try:
server = nameprep.prepare(unicode(server))
except UnicodeError:
raise InvalidFormat, "Invalid character in hostname"
if resource:
try:
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)
else:
return "%s@%s" % (user, server)
else:
if resource:
return "%s/%s" % (server, resource)
else:
return server
def temp_failure_retry(func, *args, **kwargs):
while True:
try:
return func(*args, **kwargs)
except (os.error, IOError), ex:
if ex.errno == errno.EINTR:
continue
else:
raise
while True:
try:
return func(*args, **kwargs)
except (os.error, IOError), ex:
if ex.errno == errno.EINTR:
continue
else:
raise
def check_paths():
LOGPATH = gajim.LOGPATH
@ -422,13 +512,13 @@ def one_account_connected():
return one_connected
def get_output_of_command(command):
try:
child_stdin, child_stdout = os.popen2(command)
except ValueError:
return None
try:
child_stdin, child_stdout = os.popen2(command)
except ValueError:
return None
output = child_stdout.readlines()
child_stdout.close()
child_stdin.close()
return output
output = child_stdout.readlines()
child_stdout.close()
child_stdin.close()
return output

View File

@ -0,0 +1,246 @@
## common/xmpp_stringprep.py
##
## Gajim Team:
## - Yann Le Boulanger <asterix@lagaule.org>
## - Vincent Hanquez <tab@snarc.org>
## - Nikos Kouremenos <kourem@gmail.com>
##
## Copyright (C) 2001-2005 Twisted Matrix Laboratories.
## Copyright (C) 2005 Gajim Team
##
## This program 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 2 only.
##
## This program 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.
##
import sys, warnings
if sys.version_info < (2,3,2):
import re
class IDNA:
dots = re.compile(u"[\u002E\u3002\uFF0E\uFF61]")
def nameprep(self, label):
return label.lower()
idna = IDNA()
crippled = True
warnings.warn("Accented and non-Western Jabber IDs will not be properly "
"case-folded with this version of Python, resulting in "
"incorrect protocol-level behavior. It is strongly "
"recommended you upgrade to Python 2.3.2 or newer if you "
"intend to use Twisted's Jabber support.")
else:
import stringprep
import unicodedata
from encodings import idna
crippled = False
del sys, warnings
class ILookupTable:
""" Interface for character lookup classes. """
def lookup(self, c):
""" Return whether character is in this table. """
class IMappingTable:
""" Interface for character mapping classes. """
def map(self, c):
""" Return mapping for character. """
class LookupTableFromFunction:
__implements__ = ILookupTable
def __init__(self, in_table_function):
self.lookup = in_table_function
class LookupTable:
__implements__ = ILookupTable
def __init__(self, table):
self._table = table
def lookup(self, c):
return c in self._table
class MappingTableFromFunction:
__implements__ = IMappingTable
def __init__(self, map_table_function):
self.map = map_table_function
class EmptyMappingTable:
__implements__ = IMappingTable
def __init__(self, in_table_function):
self._in_table_function = in_table_function
def map(self, c):
if self._in_table_function(c):
return None
else:
return c
class Profile:
def __init__(self, mappings=[], normalize=True, prohibiteds=[],
check_unassigneds=True, check_bidi=True):
self.mappings = mappings
self.normalize = normalize
self.prohibiteds = prohibiteds
self.do_check_unassigneds = check_unassigneds
self.do_check_bidi = check_bidi
def prepare(self, string):
result = self.map(string)
if self.normalize:
result = unicodedata.normalize("NFKC", result)
self.check_prohibiteds(result)
if self.do_check_unassigneds:
self.check_unassigneds(result)
if self.do_check_bidi:
self.check_bidirectionals(result)
return result
def map(self, string):
result = []
for c in string:
result_c = c
for mapping in self.mappings:
result_c = mapping.map(c)
if result_c != c:
break
if result_c is not None:
result.append(result_c)
return u"".join(result)
def check_prohibiteds(self, string):
for c in string:
for table in self.prohibiteds:
if table.lookup(c):
raise UnicodeError, "Invalid character %s" % repr(c)
def check_unassigneds(self, string):
for c in string:
if stringprep.in_table_a1(c):
raise UnicodeError, "Unassigned code point %s" % repr(c)
def check_bidirectionals(self, string):
found_LCat = False
found_RandALCat = False
for c in string:
if stringprep.in_table_d1(c):
found_RandALCat = True
if stringprep.in_table_d2(c):
found_LCat = True
if found_LCat and found_RandALCat:
raise UnicodeError, "Violation of BIDI Requirement 2"
if found_RandALCat and not (stringprep.in_table_d1(string[0]) and
stringprep.in_table_d1(string[-1])):
raise UnicodeError, "Violation of BIDI Requirement 3"
class NamePrep:
""" Implements nameprep on international domain names.
STD3ASCIIRules is assumed true in this implementation.
"""
# Prohibited characters.
prohibiteds = [unichr(n) for n in range(0x00, 0x2c + 1) +
range(0x2e, 0x2f + 1) +
range(0x3a, 0x40 + 1) +
range(0x5b, 0x60 + 1) +
range(0x7b, 0x7f + 1) ]
def prepare(self, string):
result = []
labels = idna.dots.split(string)
if labels and len(labels[-1]) == 0:
trailing_dot = '.'
del labels[-1]
else:
trailing_dot = ''
for label in labels:
result.append(self.nameprep(label))
return ".".join(result)+trailing_dot
def check_prohibiteds(self, string):
for c in string:
if c in self.prohibiteds:
raise UnicodeError, "Invalid character %s" % repr(c)
def nameprep(self, label):
label = idna.nameprep(label)
self.check_prohibiteds(label)
if label[0] == '-':
raise UnicodeError, "Invalid leading hyphen-minus"
if label[-1] == '-':
raise UnicodeError, "Invalid trailing hyphen-minus"
return label
if crippled:
case_map = MappingTableFromFunction(lambda c: c.lower())
nodeprep = Profile(mappings=[case_map],
normalize=False,
prohibiteds=[LookupTable([u' ', u'"', u'&', u"'", u'/',
u':', u'<', u'>', u'@'])],
check_unassigneds=False,
check_bidi=False)
resourceprep = Profile(normalize=False,
check_unassigneds=False,
check_bidi=False)
else:
C_11 = LookupTableFromFunction(stringprep.in_table_c11)
C_12 = LookupTableFromFunction(stringprep.in_table_c12)
C_21 = LookupTableFromFunction(stringprep.in_table_c21)
C_22 = LookupTableFromFunction(stringprep.in_table_c22)
C_3 = LookupTableFromFunction(stringprep.in_table_c3)
C_4 = LookupTableFromFunction(stringprep.in_table_c4)
C_5 = LookupTableFromFunction(stringprep.in_table_c5)
C_6 = LookupTableFromFunction(stringprep.in_table_c6)
C_7 = LookupTableFromFunction(stringprep.in_table_c7)
C_8 = LookupTableFromFunction(stringprep.in_table_c8)
C_9 = LookupTableFromFunction(stringprep.in_table_c9)
B_1 = EmptyMappingTable(stringprep.in_table_b1)
B_2 = MappingTableFromFunction(stringprep.map_table_b2)
nodeprep = Profile(mappings=[B_1, B_2],
prohibiteds=[C_11, C_12, C_21, C_22,
C_3, C_4, C_5, C_6, C_7, C_8, C_9,
LookupTable([u'"', u'&', u"'", u'/',
u':', u'<', u'>', u'@'])])
resourceprep = Profile(mappings=[B_1,],
prohibiteds=[C_12, C_21, C_22,
C_3, C_4, C_5, C_6, C_7, C_8, C_9])
nameprep = NamePrep()

View File

@ -1169,16 +1169,28 @@ class AccountModificationWindow:
_('Account name cannot contain spaces.')).get_response()
return
jid = self.xml.get_widget('jid_entry').get_text().decode('utf-8')
if jid == '' or jid.count('@') != 1:
dialogs.ErrorDialog(_('Invalid Jabber ID'),
_('A Jabber ID must be in the form "user@servername".')).get_response()
# check if jid is conform to RFC and stringprep it
try:
jid = helpers.parse_jid(jid)
except helpers.InvalidFormat, s:
pritext = _('Invalid User ID')
dialogs.ErrorDialog(pritext, s).get_response()
return
resource = self.xml.get_widget('resource_entry').get_text().decode('utf-8')
try:
resource = helpers.parse_resource(resource)
except helpers.InvalidFormat, s:
pritext = _('Invalid User ID')
dialogs.ErrorDialog(pritext, s).get_response()
return
config['savepass'] = self.xml.get_widget(
'save_password_checkbutton').get_active()
config['password'] = self.xml.get_widget('password_entry').get_text().\
decode('utf-8')
config['resource'] = self.xml.get_widget('resource_entry').get_text().\
decode('utf-8')
config['resource'] = resource
config['priority'] = self.xml.get_widget('priority_spinbutton').\
get_value_as_int()
config['autoconnect'] = self.xml.get_widget('autoconnect_checkbutton').\
@ -2516,16 +2528,26 @@ class AccountCreationWizardWindow:
server = widgets['server_comboboxentry'].child.get_text()
savepass = widgets['save_password_checkbutton'].get_active()
password = widgets['pass_entry'].get_text()
if self.check_data(username, server):
self.save_account(self.account, username, server, savepass, password,
register_new)
self.finish_label.set_text(finish_text)
self.xml.get_widget('cancel_button').hide()
self.back_button.hide()
self.xml.get_widget('forward_button').hide()
self.finish_button.set_sensitive(True)
self.advanced_button.show()
self.notebook.set_current_page(3)
jid = username + '@' + server
# check if jid is conform to RFC and stringprep it
try:
jid = helpers.parse_jid(jid)
except helpers.InvalidFormat, s:
pritext = _('Invalid User ID')
dialogs.ErrorDialog(pritext, s).get_response()
return
username, server = gajim.get_room_name_and_server_from_room_jid(jid)
self.save_account(self.account, username, server, savepass, password,
register_new)
self.finish_label.set_text(finish_text)
self.xml.get_widget('cancel_button').hide()
self.back_button.hide()
self.xml.get_widget('forward_button').hide()
self.finish_button.set_sensitive(True)
self.advanced_button.show()
self.notebook.set_current_page(3)
def on_advanced_button_clicked(self, widget):
gajim.interface.windows[self.account]['account_modification'] = \
@ -2535,18 +2557,6 @@ class AccountCreationWizardWindow:
def on_finish_button_clicked(self, widget):
self.window.destroy()
def check_data(self, username, server):
if len(username) == 0:
dialogs.ErrorDialog(_('Username is missing'),
_('You need to enter a username to continue.')).get_response()
return False
elif len(server) == 0:
dialogs.ErrorDialog(_('Server address is missing'),
_('You need to enter a valid server address to continue.')).get_response()
return False
else:
return True
def on_nick_entry_changed(self, widget):
self.update_jid(widget)

View File

@ -355,19 +355,16 @@ _('Please fill in the data of the contact you want to add in account %s') %accou
if not jid:
return
if jid.find('@') < 0:
# check if jid is conform to RFC and stringprep it
try:
jid = helpers.parse_jid(jid)
except helpers.InvalidFormat, s:
pritext = _('Invalid User ID')
sectext = _('Jabber ID must be of the form "user@servername".')
ErrorDialog(pritext, sectext).get_response()
ErrorDialog(pritext, s).get_response()
return
# check if contact is already in roster (user@server == UsEr@server)
already_in = False
for rjid in gajim.contacts[self.account]:
if jid.lower() == rjid.lower():
already_in = True
break
if already_in:
# Check if jid is already in roster
if jid in gajim.contacts[self.account]:
ErrorDialog(_('Contact already in roster'),
_('The contact is already listed in your roster.')).get_response()
return