Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorYann Leboulanger <asterix@lagaule.org>2010-07-19 21:53:29 +0400
committerYann Leboulanger <asterix@lagaule.org>2010-07-19 21:53:29 +0400
commit796213c9cb1e4b1ac9a138f2b9ec8cfb71c0a63d (patch)
treec862a47f0f88435054171641d3f4adde003b3c40 /src
parent9b5ee1e13b3a3e9f31aeec48e62fa69271c35e1e (diff)
parent337b09d389950edd253457432ab4c3bde2fdf5a4 (diff)
merge with trunk
Diffstat (limited to 'src')
-rw-r--r--src/adhoc_commands.py100
-rw-r--r--src/advanced_configuration_window.py4
-rw-r--r--src/atom_window.py46
-rw-r--r--src/cell_renderer_image.py2
-rw-r--r--src/chat_control.py224
-rw-r--r--src/command_system/__init__.py8
-rw-r--r--src/command_system/dispatching.py9
-rw-r--r--src/command_system/errors.py13
-rw-r--r--src/command_system/framework.py210
-rw-r--r--src/command_system/implementation/__init__.py7
-rw-r--r--src/command_system/implementation/custom.py40
-rw-r--r--src/command_system/implementation/hosts.py18
-rw-r--r--src/command_system/implementation/middleware.py56
-rw-r--r--src/command_system/implementation/standard.py99
-rw-r--r--src/command_system/mapping.py210
-rw-r--r--src/common/GnuPG.py3
-rw-r--r--src/common/GnuPGInterface.py2
-rw-r--r--src/common/atom.py2
-rw-r--r--src/common/caps_cache.py2
-rw-r--r--src/common/check_paths.py14
-rw-r--r--src/common/commands.py41
-rw-r--r--src/common/config.py10
-rw-r--r--src/common/configpaths.py4
-rw-r--r--src/common/connection.py293
-rw-r--r--src/common/connection_handlers.py287
-rw-r--r--src/common/contacts.py9
-rw-r--r--src/common/dataforms.py87
-rw-r--r--src/common/dbus_support.py9
-rw-r--r--src/common/defs.py4
-rw-r--r--src/common/dh.py2
-rw-r--r--src/common/events.py2
-rw-r--r--src/common/exceptions.py2
-rwxr-xr-xsrc/common/fuzzyclock.py2
-rw-r--r--src/common/gajim.py12
-rw-r--r--src/common/helpers.py19
-rw-r--r--src/common/i18n.py36
-rw-r--r--src/common/jingle.py54
-rw-r--r--src/common/jingle_content.py5
-rw-r--r--src/common/jingle_rtp.py105
-rw-r--r--src/common/jingle_session.py109
-rw-r--r--src/common/latex.py2
-rw-r--r--src/common/location_listener.py71
-rw-r--r--src/common/logger.py2
-rw-r--r--src/common/multimedia_helpers.py10
-rw-r--r--src/common/optparser.py19
-rw-r--r--src/common/passwords.py2
-rw-r--r--src/common/pep.py4
-rw-r--r--src/common/protocol/bytestream.py381
-rw-r--r--src/common/proxy65_manager.py7
-rw-r--r--src/common/pubsub.py28
-rw-r--r--src/common/rst_xhtml_generator.py2
-rw-r--r--src/common/sleepy.py2
-rw-r--r--src/common/socks5.py2
-rw-r--r--src/common/stanza_session.py4
-rw-r--r--src/common/xmpp/auth_nb.py153
-rw-r--r--src/common/xmpp/client_nb.py7
-rw-r--r--src/common/xmpp/dispatcher_nb.py4
-rw-r--r--src/common/xmpp/idlequeue.py2
-rw-r--r--src/common/xmpp/protocol.py7
-rw-r--r--src/common/xmpp/roster_nb.py8
-rw-r--r--src/common/xmpp/stringprepare.py2
-rw-r--r--src/common/xmpp/tls_nb.py40
-rw-r--r--src/common/xmpp/transports_nb.py11
-rw-r--r--src/common/zeroconf/client_zeroconf.py15
-rw-r--r--src/common/zeroconf/connection_handlers_zeroconf.py10
-rw-r--r--src/common/zeroconf/connection_zeroconf.py13
-rw-r--r--src/common/zeroconf/zeroconf_bonjour.py6
-rw-r--r--src/config.py289
-rw-r--r--src/conversation_textview.py26
-rw-r--r--src/dataforms_widget.py30
-rw-r--r--src/dialogs.py208
-rw-r--r--src/disco.py14
-rw-r--r--src/features_window.py5
-rw-r--r--src/filetransfers_window.py4
-rw-r--r--src/gajim-remote.py4
-rw-r--r--src/gajim.py134
-rw-r--r--src/gajim_themes_window.py2
-rw-r--r--src/groupchat_control.py607
-rw-r--r--src/groups.py4
-rw-r--r--src/gtkexcepthook.py2
-rw-r--r--src/gtkgui_helpers.py92
-rw-r--r--src/gui_interface.py1107
-rw-r--r--src/gui_menu_builder.py15
-rw-r--r--src/history_manager.py2
-rw-r--r--src/history_window.py9
-rw-r--r--src/htmltextview.py2
-rw-r--r--src/ipython_view.py2
-rw-r--r--src/message_control.py6
-rw-r--r--src/message_textview.py2
-rw-r--r--src/message_window.py10
-rw-r--r--src/music_track_listener.py2
-rw-r--r--src/negotiation.py2
-rw-r--r--src/network_manager_listener.py2
-rw-r--r--src/notify.py215
-rw-r--r--src/profile_window.py20
-rw-r--r--src/remote_control.py2
-rw-r--r--src/roster_window.py1143
-rw-r--r--src/search_window.py2
-rw-r--r--src/session.py31
-rw-r--r--src/statusicon.py26
-rw-r--r--src/tooltips.py112
-rw-r--r--src/vcard.py8
102 files changed, 4245 insertions, 2871 deletions
diff --git a/src/adhoc_commands.py b/src/adhoc_commands.py
index 29a9ed5c9..85ba9cfd1 100644
--- a/src/adhoc_commands.py
+++ b/src/adhoc_commands.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
## Stephan Erb <steve-e AT h3c.de>
##
@@ -55,21 +55,15 @@ class CommandWindow:
# an account object
self.account = gajim.connections[account]
self.jid = jid
-
- self.pulse_id=None # to satisfy self.setup_pulsing()
- self.commandlist=None # a list of (commandname, commanddescription)
-
- # command's data
self.commandnode = commandnode
- self.sessionid = None
- self.dataform = None
- self.allow_stage3_close = False
+ self.data_form_widget = None
# retrieving widgets from xml
self.xml = gtkgui_helpers.get_gtk_builder('adhoc_commands_window.ui')
self.window = self.xml.get_object('adhoc_commands_window')
- self.window.connect('delete-event', self.on_adhoc_commands_window_delete_event)
- for name in ('back_button', 'forward_button',
+ self.window.connect('delete-event',
+ self.on_adhoc_commands_window_delete_event)
+ for name in ('restart_button', 'back_button', 'forward_button',
'execute_button', 'close_button', 'stages_notebook',
'retrieving_commands_stage_vbox',
'command_list_stage_vbox', 'command_list_vbox',
@@ -78,7 +72,22 @@ class CommandWindow:
'error_description_label'):
self.__dict__[name] = self.xml.get_object(name)
+ self.initiate()
+
+ def initiate(self):
+
+ self.pulse_id = None # to satisfy self.setup_pulsing()
+ self.commandlist = None # a list of (commandname, commanddescription)
+
+ # command's data
+ self.sessionid = None
+ self.dataform = None
+ self.allow_stage3_close = False
+
# creating data forms widget
+ if self.data_form_widget:
+ self.sending_form_stage_vbox.remove(self.data_form_widget)
+ self.data_form_widget.destroy()
self.data_form_widget = dataforms_widget.DataFormWidget()
self.data_form_widget.show()
self.sending_form_stage_vbox.pack_start(self.data_form_widget)
@@ -94,6 +103,8 @@ class CommandWindow:
self.xml.connect_signals(self)
self.window.show_all()
+ self.restart_button.set_sensitive(False)
+
# These functions are set up by appropriate stageX methods.
def stage_finish(self, *anything):
pass
@@ -130,7 +141,7 @@ class CommandWindow:
return self.stage_close_button_clicked(*anything)
def on_adhoc_commands_window_destroy(self, *anything):
- # TODO: do all actions that are needed to remove this object from memory...
+ # TODO: do all actions that are needed to remove this object from memory
self.remove_pulsing()
def on_adhoc_commands_window_delete_event(self, *anything):
@@ -167,7 +178,8 @@ class CommandWindow:
# setup the callbacks
self.stage_finish = self.stage1_finish
self.stage_close_button_clicked = self.stage1_close_button_clicked
- self.stage_adhoc_commands_window_delete_event = self.stage1_adhoc_commands_window_delete_event
+ self.stage_adhoc_commands_window_delete_event = \
+ self.stage1_adhoc_commands_window_delete_event
def stage1_finish(self):
self.remove_pulsing()
@@ -196,8 +208,7 @@ class CommandWindow:
assert len(self.commandlist)>0
self.stages_notebook.set_current_page(
- self.stages_notebook.page_num(
- self.command_list_stage_vbox))
+ self.stages_notebook.page_num(self.command_list_stage_vbox))
self.close_button.set_sensitive(True)
self.back_button.set_sensitive(False)
@@ -208,7 +219,8 @@ class CommandWindow:
first_radio = None
for (commandnode, commandname) in self.commandlist:
radio = gtk.RadioButton(first_radio, label=commandname)
- radio.connect("toggled", self.on_command_radiobutton_toggled, commandnode)
+ radio.connect("toggled", self.on_command_radiobutton_toggled,
+ commandnode)
if not first_radio:
first_radio = radio
self.commandnode = commandnode
@@ -266,7 +278,8 @@ class CommandWindow:
self.stage_forward_button_clicked = self.stage3_forward_button_clicked
self.stage_execute_button_clicked = self.stage3_execute_button_clicked
self.stage_close_button_clicked = self.stage3_close_button_clicked
- self.stage_adhoc_commands_window_delete_event = self.stage3_close_button_clicked
+ self.stage_adhoc_commands_window_delete_event = \
+ self.stage3_close_button_clicked
def stage3_finish(self):
pass
@@ -276,10 +289,10 @@ class CommandWindow:
We are in the middle of executing command. Ask user if he really want to
cancel the process, then cancel it
"""
- # this works also as a handler for window_delete_event, so we have to return appropriate
- # values
+ # this works also as a handler for window_delete_event, so we have to
+ # return appropriate values
if self.form_status == 'completed':
- if widget!=self.window:
+ if widget != self.window:
self.window.destroy()
return False
@@ -291,10 +304,10 @@ class CommandWindow:
self.allow_stage3_close = True
self.window.destroy()
- dialog = dialogs.HigDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT | \
- gtk.DIALOG_MODAL, gtk.BUTTONS_YES_NO, _('Cancel confirmation'),
- _('You are in process of executing command. Do you really want to '
- 'cancel it?'), on_response_yes=on_yes)
+ dialog = dialogs.HigDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT \
+ | gtk.DIALOG_MODAL, gtk.BUTTONS_YES_NO, _('Cancel confirmation'),
+ _('You are in process of executing command. Do you really want to '
+ 'cancel it?'), on_response_yes=on_yes)
dialog.popup()
return True # Block event, don't close window
@@ -310,7 +323,7 @@ class CommandWindow:
def stage3_submit_form(self, action='execute'):
self.data_form_widget.set_sensitive(False)
if self.data_form_widget.get_data_form():
- self.data_form_widget.data_form.type='submit'
+ self.data_form_widget.data_form.type = 'submit'
else:
self.data_form_widget.hide()
@@ -346,9 +359,10 @@ class CommandWindow:
self.data_form_widget.set_sensitive(True)
try:
- self.data_form_widget.data_form=self.dataform
+ self.data_form_widget.data_form = self.dataform
except dataforms.Error:
- self.stage5(error=_('Service sent malformed data'), senderror=True)
+ self.stage5(error=_('Service sent malformed data'),
+ senderror=True)
return
self.data_form_widget.show()
if self.data_form_widget.title:
@@ -362,7 +376,8 @@ class CommandWindow:
# actions, actions, actions...
self.close_button.set_sensitive(True)
self.back_button.set_sensitive(actions.getTag('prev') is not None)
- self.forward_button.set_sensitive(actions.getTag('next') is not None)
+ self.forward_button.set_sensitive(
+ actions.getTag('next') is not None)
self.execute_button.set_sensitive(True)
else:
self.close_button.set_sensitive(True)
@@ -372,11 +387,13 @@ class CommandWindow:
if self.form_status == 'completed':
self.close_button.set_sensitive(True)
+ self.restart_button.set_sensitive(True)
self.back_button.hide()
self.forward_button.hide()
self.execute_button.hide()
self.close_button.show()
- self.stage_adhoc_commands_window_delete_event = self.stage3_close_button_clicked
+ self.stage_adhoc_commands_window_delete_event = \
+ self.stage3_close_button_clicked
note = command.getTag('note')
if note:
@@ -387,6 +404,10 @@ class CommandWindow:
self.notes_label.set_no_show_all(True)
self.notes_label.hide()
+ def on_restart_button_clicked(self, widget):
+ self.commandnode = None
+ self.initiate()
+
# stage 4: no commands are exposed
def stage4(self):
"""
@@ -396,8 +417,7 @@ class CommandWindow:
self.stage_finish()
self.stages_notebook.set_current_page(
- self.stages_notebook.page_num(
- self.no_commands_stage_vbox))
+ self.stages_notebook.page_num(self.no_commands_stage_vbox))
self.close_button.set_sensitive(True)
self.back_button.set_sensitive(False)
@@ -442,8 +462,7 @@ class CommandWindow:
assert False
self.stages_notebook.set_current_page(
- self.stages_notebook.page_num(
- self.error_stage_vbox))
+ self.stages_notebook.page_num(self.error_stage_vbox))
self.close_button.set_sensitive(True)
self.back_button.hide()
@@ -481,14 +500,15 @@ class CommandWindow:
"""
if self.pulse_id:
gobject.source_remove(self.pulse_id)
- self.pulse_id=None
+ self.pulse_id = None
# handling xml stanzas
def request_command_list(self):
"""
Request the command list. Change stage on delivery
"""
- query = xmpp.Iq(typ='get', to=xmpp.JID(self.jid), queryNS=xmpp.NS_DISCO_ITEMS)
+ query = xmpp.Iq(typ='get', to=xmpp.JID(self.jid),
+ queryNS=xmpp.NS_DISCO_ITEMS)
query.setQuerynode(xmpp.NS_COMMANDS)
def callback(response):
@@ -512,7 +532,8 @@ class CommandWindow:
self.commandlist = []
self.stage4()
else:
- self.commandlist = [(t.getAttr('node'), t.getAttr('name')) for t in items]
+ self.commandlist = [(t.getAttr('node'), t.getAttr('name')) \
+ for t in items]
self.stage2()
self.account.connection.SendAndCallForResponse(query, callback)
@@ -533,9 +554,6 @@ class CommandWindow:
cmdnode.setAttr('sessionid', self.sessionid)
if self.data_form_widget.data_form:
-# cmdnode.addChild(node=dataforms.DataForm(tofill=self.data_form_widget.data_form))
- # FIXME: simplified form to send
-
cmdnode.addChild(node=self.data_form_widget.data_form)
def callback(response):
@@ -564,6 +582,6 @@ class CommandWindow:
self.account.connection.send(stanza)
else:
- # we did not received any reply from service; FIXME: we should wait and
- # then send cancel; for now we do nothing
+ # we did not received any reply from service;
+ # FIXME: we should wait and then send cancel; for now we do nothing
pass
diff --git a/src/advanced_configuration_window.py b/src/advanced_configuration_window.py
index 33831f827..d59ed09a1 100644
--- a/src/advanced_configuration_window.py
+++ b/src/advanced_configuration_window.py
@@ -3,8 +3,8 @@
##
## Copyright (C) 2005 Travis Shirk <travis AT pobox.com>
## Vincent Hanquez <tab AT snarc.org>
-## Copyright (C) 2005-2007 Yann Leboulanger <asterix AT lagaule.org>
-## Nikos Kouremenos <kourem AT gmail.com>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
##
diff --git a/src/atom_window.py b/src/atom_window.py
index 9052ed1ae..70d47e2f2 100644
--- a/src/atom_window.py
+++ b/src/atom_window.py
@@ -2,7 +2,7 @@
## src/atom_window.py
##
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
@@ -53,15 +53,16 @@ class AtomWindow:
"""
Create new window... only if we have anything to show
"""
- assert len(self.__class__.entries)>0
+ assert len(self.__class__.entries)
- self.entry = None # the entry actually displayed
+ self.entry = None # the entry actually displayed
self.xml = gtkgui_helpers.get_gtk_builder('atom_entry_window.ui')
self.window = self.xml.get_object('atom_entry_window')
- for name in ('new_entry_label', 'feed_title_label', 'feed_title_eventbox',
- 'feed_tagline_label', 'entry_title_label', 'entry_title_eventbox',
- 'last_modified_label', 'close_button', 'next_button'):
+ for name in ('new_entry_label', 'feed_title_label',
+ 'feed_title_eventbox', 'feed_tagline_label', 'entry_title_label',
+ 'entry_title_eventbox', 'last_modified_label', 'close_button',
+ 'next_button'):
self.__dict__[name] = self.xml.get_object(name)
self.displayNextEntry()
@@ -83,23 +84,26 @@ class AtomWindow:
# fill the fields
if newentry.feed_link is not None:
self.feed_title_label.set_markup(
- u'<span foreground="blue" underline="single">%s</span>' % \
- gobject.markup_escape_text(newentry.feed_title))
+ u'<span foreground="blue" underline="single">%s</span>' % \
+ gobject.markup_escape_text(newentry.feed_title))
else:
self.feed_title_label.set_markup(
- gobject.markup_escape_text(newentry.feed_title))
+ gobject.markup_escape_text(newentry.feed_title))
self.feed_tagline_label.set_markup(
- u'<small>%s</small>' % \
- gobject.markup_escape_text(newentry.feed_tagline))
+ u'<small>%s</small>' % \
+ gobject.markup_escape_text(newentry.feed_tagline))
- if newentry.uri is not None:
- self.entry_title_label.set_markup(
+ if newentry.title:
+ if newentry.uri is not None:
+ self.entry_title_label.set_markup(
u'<span foreground="blue" underline="single">%s</span>' % \
gobject.markup_escape_text(newentry.title))
- else:
- self.entry_title_label.set_markup(
+ else:
+ self.entry_title_label.set_markup(
gobject.markup_escape_text(newentry.title))
+ else:
+ self.entry_title_label.set_markup('')
self.last_modified_label.set_text(newentry.updated)
@@ -114,11 +118,11 @@ class AtomWindow:
changed
"""
count = len(self.__class__.entries)
- if count>0:
+ if count:
self.new_entry_label.set_text(i18n.ngettext(
- 'You have received new entries (and %d not displayed):',
- 'You have received new entries (and %d not displayed):', count,
- count, count))
+ 'You have received new entries (and %d not displayed):',
+ 'You have received new entries (and %d not displayed):', count,
+ count, count))
self.next_button.set_sensitive(True)
else:
self.new_entry_label.set_text(_('You have received new entry:'))
@@ -131,7 +135,7 @@ class AtomWindow:
def on_next_button_clicked(self, widget):
self.displayNextEntry()
- def on_entry_title_button_press_event(self, widget, event):
+ def on_entry_title_eventbox_button_press_event(self, widget, event):
#FIXME: make it using special gtk2.10 widget
if event.button == 1: # left click
uri = self.entry.uri
@@ -139,7 +143,7 @@ class AtomWindow:
helpers.launch_browser_mailer('url', uri)
return True
- def on_feed_title_button_press_event(self, widget, event):
+ def on_feed_title_eventbox_button_press_event(self, widget, event):
#FIXME: make it using special gtk2.10 widget
if event.button == 1: # left click
uri = self.entry.feed_uri
diff --git a/src/cell_renderer_image.py b/src/cell_renderer_image.py
index 0b85e8c44..1e5b4bb8e 100644
--- a/src/cell_renderer_image.py
+++ b/src/cell_renderer_image.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/cell_renderer_image.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
diff --git a/src/chat_control.py b/src/chat_control.py
index 26b2241ce..8221b898d 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -2,8 +2,8 @@
## src/chat_control.py
##
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
-## Jean-Marie Traissard <jim AT lapin.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Nikos Kouremenos <kourem AT gmail.com>
## Travis Shirk <travis AT pobox.com>
## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
@@ -94,6 +94,15 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
A base class containing a banner, ConversationTextview, MessageTextView
"""
+ keymap = gtk.gdk.keymap_get_default()
+ try:
+ keycode_c = keymap.get_entries_for_keyval(gtk.keysyms.c)[0][0]
+ except TypeError:
+ keycode_c = 54
+ try:
+ keycode_ins = keymap.get_entries_for_keyval(gtk.keysyms.Insert)[0][0]
+ except TypeError:
+ keycode_ins = 118
def make_href(self, match):
url_color = gajim.config.get('urlmsgcolor')
url = match.group()
@@ -147,7 +156,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
self.draw_banner_text()
self._update_banner_state_image()
gajim.plugin_manager.gui_extension_point('chat_control_base_draw_banner',
- self)
+ self)
def draw_banner_text(self):
"""
@@ -231,6 +240,29 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
def status_url_clicked(self, widget, url):
helpers.launch_browser_mailer('url', url)
+ def setup_seclabel(self, combo):
+ self.seclabel_combo = combo
+ self.seclabel_combo.hide()
+ self.seclabel_combo.set_no_show_all(True)
+ lb = gtk.ListStore(str)
+ self.seclabel_combo.set_model(lb)
+ cell = gtk.CellRendererText()
+ cell.set_property('xpad', 5) # padding for status text
+ self.seclabel_combo.pack_start(cell, True)
+ # text to show is in in first column of liststore
+ self.seclabel_combo.add_attribute(cell, 'text', 0)
+ if gajim.connections[self.account].seclabel_supported:
+ gajim.connections[self.account].seclabel_catalogue(self.contact.jid, self.on_seclabels_ready)
+
+ def on_seclabels_ready(self):
+ lb = self.seclabel_combo.get_model()
+ lb.clear()
+ for label in gajim.connections[self.account].seclabel_catalogues[self.contact.jid][2]:
+ lb.append([label])
+ self.seclabel_combo.set_active(0)
+ self.seclabel_combo.set_no_show_all(False)
+ self.seclabel_combo.show_all()
+
def __init__(self, type_id, parent_win, widget_name, contact, acct,
resource=None):
# Undo needs this variable to know if space has been pressed.
@@ -383,7 +415,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
# instance object (also subclasses, eg. ChatControl or GroupchatControl)
gajim.plugin_manager.gui_extension_point('chat_control_base', self)
-
def set_speller(self):
# now set the one the user selected
per_type = 'contacts'
@@ -419,7 +450,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
i += 1
menu.show_all()
-
def shutdown(self):
# PluginSystem: removing GUI extension points connected with ChatControlBase
# instance object
@@ -568,8 +598,11 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
self.connect_style_event(widget, opts[0], opts[1])
def _conv_textview_key_press_event(self, widget, event):
- if (event.state & gtk.gdk.CONTROL_MASK and event.keyval in (gtk.keysyms.c,
- gtk.keysyms.Insert)) or (event.state & gtk.gdk.SHIFT_MASK and \
+ # translate any layout to latin_layout
+ keymap = gtk.gdk.keymap_get_default()
+ keycode = keymap.get_entries_for_keyval(event.keyval)[0][0]
+ if (event.state & gtk.gdk.CONTROL_MASK and keycode in (self.keycode_c,
+ self.keycode_ins)) or (event.state & gtk.gdk.SHIFT_MASK and \
event.keyval in (gtk.keysyms.Page_Down, gtk.keysyms.Page_Up)):
return False
self.parent_win.notebook.emit('key_press_event', event)
@@ -729,6 +762,16 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
self.drag_entered_conv = True
self.conv_textview.tv.set_editable(True)
+ def get_seclabel(self):
+ label = None
+ if self.seclabel_combo is not None:
+ idx = self.seclabel_combo.get_active()
+ if idx != -1:
+ cat = gajim.connections[self.account].seclabel_catalogues[self.contact.jid]
+ lname = cat[2][idx]
+ label = cat[1][lname]
+ return label
+
def send_message(self, message, keyID='', type_='chat', chatstate=None,
msg_id=None, composing_xep=None, resource=None, xhtml=None,
callback=None, callback_args=[], process_commands=True):
@@ -741,9 +784,11 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
if process_commands and self.process_as_command(message):
return
+ label = self.get_seclabel()
MessageControl.send_message(self, message, keyID, type_=type_,
chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
resource=resource, user_nick=self.user_nick, xhtml=xhtml,
+ label=label,
callback=callback, callback_args=callback_args)
# Record message history
@@ -777,7 +822,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
other_tags_for_name=[], other_tags_for_time=[],
other_tags_for_text=[], count_as_new=True, subject=None,
old_kind=None, xhtml=None, simple=False, xep0184_id=None,
- graphics=True):
+ graphics=True, displaymarking=None):
"""
Print 'chat' type messages
"""
@@ -789,7 +834,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
end = True
textview.print_conversation_line(text, jid, kind, name, tim,
other_tags_for_name, other_tags_for_time, other_tags_for_text,
- subject, old_kind, xhtml, simple=simple, graphics=graphics)
+ subject, old_kind, xhtml, simple=simple, graphics=graphics,
+ displaymarking=displaymarking)
if xep0184_id is not None:
textview.show_xep0184_warning(xep0184_id)
@@ -1267,13 +1313,12 @@ class ChatControl(ChatControlBase):
A control for standard 1-1 chat
"""
(
- JINGLE_STATE_NOT_AVAILABLE,
- JINGLE_STATE_AVAILABLE,
+ JINGLE_STATE_NULL,
JINGLE_STATE_CONNECTING,
JINGLE_STATE_CONNECTION_RECEIVED,
JINGLE_STATE_CONNECTED,
JINGLE_STATE_ERROR
- ) = range(6)
+ ) = range(5)
TYPE_ID = message_control.TYPE_CHAT
old_msg_kind = None # last kind of the printed message
@@ -1360,9 +1405,11 @@ class ChatControl(ChatControlBase):
self._audio_banner_image = self.xml.get_object('audio_banner_image')
self._video_banner_image = self.xml.get_object('video_banner_image')
self.audio_sid = None
- self.audio_state = self.JINGLE_STATE_NOT_AVAILABLE
+ self.audio_state = self.JINGLE_STATE_NULL
+ self.audio_available = False
self.video_sid = None
- self.video_state = self.JINGLE_STATE_NOT_AVAILABLE
+ self.video_state = self.JINGLE_STATE_NULL
+ self.video_available = False
self.update_toolbar()
@@ -1420,6 +1467,15 @@ class ChatControl(ChatControlBase):
id_ = widget.connect('released', self.on_num_button_released)
self.handlers[id_] = widget
+ self.dtmf_window = self.xml.get_object('dtmf_window')
+ id_ = self.dtmf_window.connect('focus-out-event',
+ self.on_dtmf_window_focus_out_event)
+ self.handlers[id_] = self.dtmf_window
+
+ widget = self.xml.get_object('dtmf_button')
+ id_ = widget.connect('clicked', self.on_dtmf_button_clicked)
+ self.handlers[id_] = widget
+
widget = self.xml.get_object('mic_hscale')
id_ = widget.connect('value_changed', self.on_mic_hscale_value_changed)
self.handlers[id_] = widget
@@ -1436,6 +1492,7 @@ class ChatControl(ChatControlBase):
session = gajim.connections[self.account].find_controlless_session(
self.contact.jid, resource)
+ self.setup_seclabel(self.xml.get_object('label_selector'))
if session:
session.control = self
self.session = session
@@ -1488,38 +1545,24 @@ class ChatControl(ChatControlBase):
# Jingle detection
if self.contact.supports(NS_JINGLE_ICE_UDP) and \
gajim.HAVE_FARSIGHT and self.contact.resource:
- if self.contact.supports(NS_JINGLE_RTP_AUDIO):
- if self.audio_state == self.JINGLE_STATE_NOT_AVAILABLE:
- self.set_audio_state('available')
- else:
- self.set_audio_state('not_available')
-
- if self.contact.supports(NS_JINGLE_RTP_VIDEO):
- if self.video_state == self.JINGLE_STATE_NOT_AVAILABLE:
- self.set_video_state('available')
- else:
- self.set_video_state('not_available')
+ self.audio_available = self.contact.supports(NS_JINGLE_RTP_AUDIO)
+ self.video_available = self.contact.supports(NS_JINGLE_RTP_VIDEO)
else:
- if self.audio_state != self.JINGLE_STATE_NOT_AVAILABLE:
- self.set_audio_state('not_available')
- if self.video_state != self.JINGLE_STATE_NOT_AVAILABLE:
- self.set_video_state('not_available')
+ if self.video_available or self.audio_available:
+ self.stop_jingle()
+ self.video_available = False
+ self.audio_available = False
# Audio buttons
- if self.audio_state == self.JINGLE_STATE_NOT_AVAILABLE:
- self._audio_button.set_sensitive(False)
- else:
- self._audio_button.set_sensitive(True)
+ self._audio_button.set_sensitive(self.audio_available)
# Video buttons
- if self.video_state == self.JINGLE_STATE_NOT_AVAILABLE:
- self._video_button.set_sensitive(False)
- else:
- self._video_button.set_sensitive(True)
+ self._video_button.set_sensitive(self.video_available)
# Send file
if self.contact.supports(NS_FILE) and self.contact.resource:
self._send_file_button.set_sensitive(True)
+ self._send_file_button.set_tooltip_text('')
else:
self._send_file_button.set_sensitive(False)
if not self.contact.supports(NS_FILE):
@@ -1554,18 +1597,16 @@ class ChatControl(ChatControlBase):
else:
img.hide()
- # PluginSystem: adding GUI extension point for this ChatControl
+ # PluginSystem: adding GUI extension point for this ChatControl
# instance object
gajim.plugin_manager.gui_extension_point('chat_control', self)
-
def _update_jingle(self, jingle_type):
if jingle_type not in ('audio', 'video'):
return
banner_image = getattr(self, '_' + jingle_type + '_banner_image')
state = getattr(self, jingle_type + '_state')
- if state in (self.JINGLE_STATE_NOT_AVAILABLE,
- self.JINGLE_STATE_AVAILABLE):
+ if state == self.JINGLE_STATE_NULL:
banner_image.hide()
else:
banner_image.show()
@@ -1585,7 +1626,7 @@ class ChatControl(ChatControlBase):
def update_audio(self):
self._update_jingle('audio')
- vbox = self.xml.get_object('audio_vbox')
+ hbox = self.xml.get_object('audio_buttons_hbox')
if self.audio_state == self.JINGLE_STATE_CONNECTED:
# Set volume from config
input_vol = gajim.config.get('audio_input_volume')
@@ -1595,11 +1636,11 @@ class ChatControl(ChatControlBase):
self.xml.get_object('mic_hscale').set_value(input_vol)
self.xml.get_object('sound_hscale').set_value(output_vol)
# Show vbox
- vbox.set_no_show_all(False)
- vbox.show_all()
+ hbox.set_no_show_all(False)
+ hbox.show_all()
elif not self.audio_sid:
- vbox.set_no_show_all(True)
- vbox.hide()
+ hbox.set_no_show_all(True)
+ hbox.hide()
def update_video(self):
self._update_jingle('video')
@@ -1617,43 +1658,42 @@ class ChatControl(ChatControlBase):
# update MessageWindow._controls
self.parent_win.change_jid(self.account, old_full_jid, new_full_jid)
+ def stop_jingle(self, sid=None, reason=None):
+ if self.audio_sid and sid in (self.audio_sid, None):
+ self.close_jingle_content('audio')
+ if self.video_sid and sid in (self.video_sid, None):
+ self.close_jingle_content('video')
+
+
def _set_jingle_state(self, jingle_type, state, sid=None, reason=None):
if jingle_type not in ('audio', 'video'):
return
- if state in ('connecting', 'connected', 'stop') and reason:
+ if state in ('connecting', 'connected', 'stop', 'error') and reason:
str = _('%(type)s state : %(state)s, reason: %(reason)s') % {
'type': jingle_type.capitalize(), 'state': state, 'reason': reason}
self.print_conversation(str, 'info')
- states = {'not_available': self.JINGLE_STATE_NOT_AVAILABLE,
- 'available': self.JINGLE_STATE_AVAILABLE,
- 'connecting': self.JINGLE_STATE_CONNECTING,
+ states = {'connecting': self.JINGLE_STATE_CONNECTING,
'connection_received': self.JINGLE_STATE_CONNECTION_RECEIVED,
'connected': self.JINGLE_STATE_CONNECTED,
- 'stop': self.JINGLE_STATE_AVAILABLE,
+ 'stop': self.JINGLE_STATE_NULL,
'error': self.JINGLE_STATE_ERROR}
- if state in states:
- jingle_state = states[state]
- if getattr(self, jingle_type + '_state') == jingle_state:
- return
- setattr(self, jingle_type + '_state', jingle_state)
+ jingle_state = states[state]
+ if getattr(self, jingle_type + '_state') == jingle_state or state == 'error':
+ return
+
+ if state == 'stop' and getattr(self, jingle_type + '_sid') not in (None, sid):
+ return
- # Destroy existing session with the user when he signs off
- # We need to do that before modifying the sid
- if state == 'not_available':
- gajim.connections[self.account].delete_jingle_session(
- self.contact.get_full_jid(), getattr(self, jingle_type + '_sid'))
+ setattr(self, jingle_type + '_state', jingle_state)
- if state in ('not_available', 'available', 'stop'):
+ if jingle_state == self.JINGLE_STATE_NULL:
setattr(self, jingle_type + '_sid', None)
if state in ('connection_received', 'connecting'):
setattr(self, jingle_type + '_sid', sid)
- if state in ('connecting', 'connected', 'connection_received'):
- getattr(self, '_' + jingle_type + '_button').set_active(True)
- elif state in ('not_available', 'stop'):
- getattr(self, '_' + jingle_type + '_button').set_active(False)
+ getattr(self, '_' + jingle_type + '_button').set_active(jingle_state != self.JINGLE_STATE_NULL)
getattr(self, 'update_' + jingle_type)()
@@ -1674,19 +1714,21 @@ class ChatControl(ChatControlBase):
def on_num_button_released(self, released):
self._get_audio_content()._stop_dtmf()
- def on_mic_hscale_value_changed(self, widget):
- value = widget.get_value()
+ def on_dtmf_button_clicked(self, widget):
+ self.dtmf_window.show_all()
+
+ def on_dtmf_window_focus_out_event(self, widget, event):
+ self.dtmf_window.hide()
+
+ def on_mic_hscale_value_changed(self, widget, value):
self._get_audio_content().set_mic_volume(value / 100)
# Save volume to config
- # FIXME: Putting it here is maybe not the right thing to do?
gajim.config.set('audio_input_volume', value)
- def on_sound_hscale_value_changed(self, widget):
- value = widget.get_value()
+ def on_sound_hscale_value_changed(self, widget, value):
self._get_audio_content().set_out_volume(value / 100)
# Save volume to config
- # FIXME: Putting it here is maybe not the right thing to do?
gajim.config.set('audio_output_volume', value)
def on_avatar_eventbox_enter_notify_event(self, widget, event):
@@ -1708,6 +1750,8 @@ class ChatControl(ChatControlBase):
# do we have something bigger to show?
if avatar_w > scaled_buf_w or avatar_h > scaled_buf_h:
# wait for 0.5 sec in case we leave earlier
+ if self.show_bigger_avatar_timeout_id is not None:
+ gobject.source_remove(self.show_bigger_avatar_timeout_id)
self.show_bigger_avatar_timeout_id = gobject.timeout_add(500,
self.show_bigger_avatar, widget)
@@ -1718,6 +1762,7 @@ class ChatControl(ChatControlBase):
# did we add a timeout? if yes remove it
if self.show_bigger_avatar_timeout_id is not None:
gobject.source_remove(self.show_bigger_avatar_timeout_id)
+ self.show_bigger_avatar_timeout_id = None
def on_avatar_eventbox_button_press_event(self, widget, event):
"""
@@ -1727,8 +1772,8 @@ class ChatControl(ChatControlBase):
menu = gtk.Menu()
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
id_ = menuitem.connect('activate',
- gtkgui_helpers.on_avatar_save_as_menuitem_activate,
- self.contact.jid, self.account, self.contact.get_shown_name())
+ gtkgui_helpers.on_avatar_save_as_menuitem_activate,
+ self.contact.jid, self.contact.get_shown_name())
self.handlers[id_] = menuitem
menu.append(menuitem)
menu.show_all()
@@ -1899,12 +1944,16 @@ class ChatControl(ChatControlBase):
sid = getattr(self, jingle_type + '_sid')
if not sid:
return
+ setattr(self, jingle_type + '_sid', None)
+ setattr(self, jingle_type + '_state', self.JINGLE_STATE_NULL)
session = gajim.connections[self.account].get_jingle_session(
self.contact.get_full_jid(), sid)
if session:
content = session.get_content(jingle_type)
if content:
session.remove_content(content.creator, content.name)
+ getattr(self, '_' + jingle_type + '_button').set_active(False)
+ getattr(self, 'update_' + jingle_type)()
def on_jingle_button_toggled(self, widget, jingle_type):
img_name = 'gajim-%s_%s' % ({'audio': 'mic', 'video': 'cam'}[jingle_type],
@@ -1913,7 +1962,7 @@ class ChatControl(ChatControlBase):
if widget.get_active():
if getattr(self, jingle_type + '_state') == \
- self.JINGLE_STATE_AVAILABLE:
+ self.JINGLE_STATE_NULL:
sid = getattr(gajim.connections[self.account],
'start_' + jingle_type)(self.contact.get_full_jid())
getattr(self, 'set_' + jingle_type + '_state')('connecting', sid)
@@ -2069,20 +2118,23 @@ class ChatControl(ChatControlBase):
gobject.source_remove(self.possible_inactive_timeout_id)
self._schedule_activity_timers()
- def _on_sent(id_, contact, message, encrypted, xhtml):
+ def _on_sent(id_, contact, message, encrypted, xhtml, label):
if contact.supports(NS_RECEIPTS) and gajim.config.get_per('accounts',
self.account, 'request_receipt'):
xep0184_id = id_
else:
xep0184_id = None
-
+ if label:
+ displaymarking = label.getTag('displaymarking')
+ else:
+ displaymarking = None
self.print_conversation(message, self.contact.jid, encrypted=encrypted,
- xep0184_id=xep0184_id, xhtml=xhtml)
+ xep0184_id=xep0184_id, xhtml=xhtml, displaymarking=displaymarking)
ChatControlBase.send_message(self, message, keyID, type_='chat',
chatstate=chatstate_to_send, composing_xep=composing_xep,
xhtml=xhtml, callback=_on_sent,
- callback_args=[contact, message, encrypted, xhtml],
+ callback_args=[contact, message, encrypted, xhtml, self.get_seclabel()],
process_commands=process_commands)
def check_for_possible_paused_chatstate(self, arg):
@@ -2171,7 +2223,8 @@ class ChatControl(ChatControlBase):
self.session.is_loggable(), self.session and self.session.verified_identity)
def print_conversation(self, text, frm='', tim=None, encrypted=False,
- subject=None, xhtml=None, simple=False, xep0184_id=None):
+ subject=None, xhtml=None, simple=False, xep0184_id=None,
+ displaymarking=None):
"""
Print a line in the conversation
@@ -2234,7 +2287,7 @@ class ChatControl(ChatControlBase):
xhtml = '<body xmlns="%s">%s</body>' % (NS_XHTML, xhtml)
ChatControlBase.print_conversation_line(self, text, kind, name, tim,
subject=subject, old_kind=self.old_msg_kind, xhtml=xhtml,
- simple=simple, xep0184_id=xep0184_id)
+ simple=simple, xep0184_id=xep0184_id, displaymarking=displaymarking)
if text.startswith('/me ') or text.startswith('/me\n'):
self.old_msg_kind = None
else:
@@ -2423,9 +2476,8 @@ class ChatControl(ChatControlBase):
super(ChatControl, self).shutdown()
# PluginSystem: removing GUI extension points connected with ChatControl
# instance object
- gajim.plugin_manager.remove_gui_extension_point('chat_control', self)
+ gajim.plugin_manager.remove_gui_extension_point('chat_control', self) # Send 'gone' chatstate
- # Send 'gone' chatstate
self.send_chatstate('gone', self.contact)
self.contact.chatstate = None
self.contact.our_chatstate = None
@@ -2688,8 +2740,12 @@ class ChatControl(ChatControlBase):
kind = 'info'
else:
kind = 'print_queue'
+ dm = None
+ if len(data) > 10:
+ dm = data[10]
self.print_conversation(data[0], kind, tim = data[3],
- encrypted = data[4], subject = data[1], xhtml = data[7])
+ encrypted = data[4], subject = data[1], xhtml = data[7],
+ displaymarking=dm)
if len(data) > 6 and isinstance(data[6], int):
message_ids.append(data[6])
diff --git a/src/command_system/__init__.py b/src/command_system/__init__.py
index ff61c186e..2fb336264 100644
--- a/src/command_system/__init__.py
+++ b/src/command_system/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -14,7 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-The command system providing scalable, clean and convenient architecture in
-combination with declarative way of defining commands and a fair amount of
-automatization for routine processes.
+The command system providing scalable, clean and convenient architecture
+in combination with declarative way of defining commands and a fair
+amount of automatization for routine processes.
"""
diff --git a/src/command_system/dispatching.py b/src/command_system/dispatching.py
index 7f365a915..0c613ec15 100644
--- a/src/command_system/dispatching.py
+++ b/src/command_system/dispatching.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -14,9 +14,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-The backbone of the command system. Provides automatic dispatching which does
-not require explicit registering commands or containers and remains active even
-after everything is done, so new commands can be added during the runtime.
+The backbone of the command system. Provides automatic dispatching which
+does not require explicit registering commands or containers and remains
+active even after everything is done, so new commands can be added
+during the runtime.
"""
from types import NoneType
diff --git a/src/command_system/errors.py b/src/command_system/errors.py
index 4281e9fd7..d3b29e85e 100644
--- a/src/command_system/errors.py
+++ b/src/command_system/errors.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -15,8 +15,9 @@
class BaseError(Exception):
"""
- Common base for errors which relate to a specific command. Encapsulates
- everything needed to identify a command, by either its object or name.
+ Common base for errors which relate to a specific command.
+ Encapsulates everything needed to identify a command, by either its
+ object or name.
"""
def __init__(self, message, command=None, name=None):
@@ -44,3 +45,9 @@ class CommandError(BaseError):
Used to indicate errors occured during command execution.
"""
pass
+
+class NoCommandError(BaseError):
+ """
+ Used to indicate an inability to find the specified command.
+ """
+ pass
diff --git a/src/command_system/framework.py b/src/command_system/framework.py
index 30f5cd53c..c8c822494 100644
--- a/src/command_system/framework.py
+++ b/src/command_system/framework.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -14,8 +14,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-Provides a tiny framework with simple, yet powerful and extensible architecture
-to implement commands in a streight and flexible, declarative way.
+Provides a tiny framework with simple, yet powerful and extensible
+architecture to implement commands in a streight and flexible,
+declarative way.
"""
import re
@@ -24,46 +25,47 @@ from inspect import getargspec
from dispatching import Dispatcher, HostDispatcher, ContainerDispatcher
from mapping import parse_arguments, adapt_arguments
-from errors import DefinitionError, CommandError
+from errors import DefinitionError, CommandError, NoCommandError
class CommandHost(object):
"""
- Command host is a hub between numerous command processors and command
- containers. Aimed to participate in a dispatching process in order to
- provide clean and transparent architecture.
+ Command host is a hub between numerous command processors and
+ command containers. Aimed to participate in a dispatching process in
+ order to provide clean and transparent architecture.
"""
__metaclass__ = HostDispatcher
class CommandContainer(object):
"""
- Command container is an entity which holds defined commands, allowing them
- to be dispatched and proccessed correctly. Each command container may be
- bound to a one or more command hosts.
+ Command container is an entity which holds defined commands,
+ allowing them to be dispatched and proccessed correctly. Each
+ command container may be bound to a one or more command hosts.
- Bounding is controlled by the HOSTS variable, which must be defined in the
- body of the command container. This variable should contain a list of hosts
- to bound to, as a tuple or list.
+ Bounding is controlled by the HOSTS variable, which must be defined
+ in the body of the command container. This variable should contain a
+ list of hosts to bound to, as a tuple or list.
"""
__metaclass__ = ContainerDispatcher
class CommandProcessor(object):
"""
- Command processor is an immediate command emitter. It does not participate
- in the dispatching process directly, but must define a host to bound to.
+ Command processor is an immediate command emitter. It does not
+ participate in the dispatching process directly, but must define a
+ host to bound to.
- Bounding is controlled by the COMMAND_HOST variable, which must be defined
- in the body of the command processor. This variable should be set to a
- specific command host.
+ Bounding is controlled by the COMMAND_HOST variable, which must be
+ defined in the body of the command processor. This variable should
+ be set to a specific command host.
"""
- # This defines a command prefix (or an initializer), which should preceede a
- # a text in order it to be processed as a command.
+ # This defines a command prefix (or an initializer), which should
+ # preceede a a text in order it to be processed as a command.
COMMAND_PREFIX = '/'
def process_as_command(self, text):
"""
- Try to process text as a command. Returns True if it has been processed
- as a command and False otherwise.
+ Try to process text as a command. Returns True if it has been
+ processed as a command and False otherwise.
"""
prefix = text.startswith(self.COMMAND_PREFIX)
length = len(text) > len(self.COMMAND_PREFIX)
@@ -97,28 +99,28 @@ class CommandProcessor(object):
def command_preprocessor(self, command, name, arguments, args, kwargs):
"""
- Redefine this method in the subclass to execute custom code before
- command gets executed.
+ Redefine this method in the subclass to execute custom code
+ before command gets executed.
- If returns True then command execution will be interrupted and command
- will not be executed.
+ If returns True then command execution will be interrupted and
+ command will not be executed.
"""
pass
def command_postprocessor(self, command, name, arguments, args, kwargs, value):
"""
- Redefine this method in the subclass to execute custom code after
- command gets executed.
+ Redefine this method in the subclass to execute custom code
+ after command gets executed.
"""
pass
def looks_like_command(self, text, body, name, arguments):
"""
- This hook is being called before any processing, but after it was
- determined that text looks like a command.
+ This hook is being called before any processing, but after it
+ was determined that text looks like a command.
- If returns value other then None - then further processing will be
- interrupted and that value will be used to return from
+ If returns value other then None - then further processing will
+ be interrupted and that value will be used to return from
process_as_command.
"""
pass
@@ -126,7 +128,7 @@ class CommandProcessor(object):
def get_command(self, name):
command = Dispatcher.get_command(self.COMMAND_HOST, name)
if not command:
- raise CommandError("Command does not exist", name=name)
+ raise NoCommandError("Command does not exist", name=name)
return command
def list_commands(self):
@@ -136,8 +138,9 @@ class CommandProcessor(object):
class Command(object):
- # These two regular expression patterns control how command documentation
- # will be formatted to be transformed to a normal, readable state.
+ # These two regular expression patterns control how command
+ # documentation will be formatted to be transformed to a normal,
+ # readable state.
DOC_STRIP_PATTERN = re.compile(r'(?:^[ \t]+|\A\n)', re.MULTILINE)
DOC_FORMAT_PATTERN = re.compile(r'(?<!\n)\n(?!\n)', re.MULTILINE)
@@ -145,8 +148,8 @@ class Command(object):
self.handler = handler
self.names = names
- # Automatically set all the properties passed to a constructor by the
- # command decorator.
+ # Automatically set all the properties passed to a constructor
+ # by the command decorator.
for key, value in properties.iteritems():
setattr(self, key, value)
@@ -154,18 +157,20 @@ class Command(object):
try:
return self.handler(*args, **kwargs)
- # This allows to use a shortcuted way of raising an exception inside a
- # handler. That is to raise a CommandError without command or name
- # attributes set. They will be set to a corresponding values right here
- # in case if they was not set by the one who raised an exception.
+ # This allows to use a shortcuted way of raising an exception
+ # inside a handler. That is to raise a CommandError without
+ # command or name attributes set. They will be set to a
+ # corresponding values right here in case if they was not set by
+ # the one who raised an exception.
except CommandError, error:
if not error.command and not error.name:
raise CommandError(error.message, self)
raise
# This one is a little bit too wide, but as Python does not have
- # anything more constrained - there is no other choice. Take a look here
- # if command complains about invalid arguments while they are ok.
+ # anything more constrained - there is no other choice. Take a
+ # look here if command complains about invalid arguments while
+ # they are ok.
except TypeError:
raise CommandError("Command received invalid arguments", self)
@@ -185,8 +190,8 @@ class Command(object):
def extract_documentation(self):
"""
- Extract handler's documentation which is a doc-string and transform it
- to a usable format.
+ Extract handler's documentation which is a doc-string and
+ transform it to a usable format.
Transformation is done based on the DOC_STRIP_PATTERN and
DOC_FORMAT_PATTERN regular expression patterns.
@@ -211,19 +216,19 @@ class Command(object):
def extract_specification(self):
"""
- Extract handler's arguments specification, as it was defined preserving
- their order.
+ Extract handler's arguments specification, as it was defined
+ preserving their order.
"""
names, var_args, var_kwargs, defaults = getargspec(self.handler)
- # Behavior of this code need to be checked. Might yield incorrect
- # results on some rare occasions.
+ # Behavior of this code need to be checked. Might yield
+ # incorrect results on some rare occasions.
spec_args = names[:-len(defaults) if defaults else len(names)]
spec_kwargs = list(zip(names[-len(defaults):], defaults)) if defaults else {}
- # Removing self from arguments specification. Command handler should
- # receive the processors as a first argument, which should be self by
- # the canonical means.
+ # Removing self from arguments specification. Command handler
+ # should receive the processors as a first argument, which
+ # should be self by the canonical means.
if spec_args.pop(0) != 'self':
raise DefinitionError("First argument must be self", self)
@@ -231,44 +236,47 @@ class Command(object):
def command(*names, **properties):
"""
- A decorator for defining commands in a declarative way. Provides facilities
- for setting command's names and properties.
-
- Names should contain a set of names (aliases) by which the command can be
- reached. If no names are given - the the native name (the one extracted from
- the command handler) will be used.
-
- If include_native=True is given (default) and names is non-empty - then the
- native name of the command will be prepended in addition to the given names.
-
- If usage=True is given (default) - then command help will be appended with
- autogenerated usage info, based of the command handler arguments
- introspection.
-
- If source=True is given - then the first argument of the command will
- receive the source arguments, as a raw, unprocessed string. The further
- mapping of arguments and options will not be affected.
-
- If raw=True is given - then command considered to be raw and should define
- positional arguments only. If it defines only one positional argument - this
- argument will receive all the raw and unprocessed arguments. If the command
- defines more then one positional argument - then all the arguments except
- the last one will be processed normally; the last argument will get what is
- left after the processing as raw and unprocessed string.
-
- If empty=True is given - this will allow to call a raw command without
- arguments.
-
- If extra=True is given - then all the extra arguments passed to a command
- will be collected into a sequence and given to the last positional argument.
-
- If overlap=True is given - then all the extra arguments will be mapped as if
- they were values for the keyword arguments.
-
- If expand_short=True is given (default) - then short, one-letter options
- will be expanded to a verbose ones, based of the comparison of the first
- letter. If more then one option with the same first letter is given - then
- only first one will be used in the expansion.
+ A decorator for defining commands in a declarative way. Provides
+ facilities for setting command's names and properties.
+
+ Names should contain a set of names (aliases) by which the command
+ can be reached. If no names are given - the the native name (the one
+ extracted from the command handler) will be used.
+
+ If include_native=True is given (default) and names is non-empty -
+ then the native name of the command will be prepended in addition to
+ the given names.
+
+ If usage=True is given (default) - then command help will be
+ appended with autogenerated usage info, based of the command handler
+ arguments introspection.
+
+ If source=True is given - then the first argument of the command
+ will receive the source arguments, as a raw, unprocessed string. The
+ further mapping of arguments and options will not be affected.
+
+ If raw=True is given - then command considered to be raw and should
+ define positional arguments only. If it defines only one positional
+ argument - this argument will receive all the raw and unprocessed
+ arguments. If the command defines more then one positional argument
+ - then all the arguments except the last one will be processed
+ normally; the last argument will get what is left after the
+ processing as raw and unprocessed string.
+
+ If empty=True is given - this will allow to call a raw command
+ without arguments.
+
+ If extra=True is given - then all the extra arguments passed to a
+ command will be collected into a sequence and given to the last
+ positional argument.
+
+ If overlap=True is given - then all the extra arguments will be
+ mapped as if they were values for the keyword arguments.
+
+ If expand_short=True is given (default) - then short, one-letter
+ options will be expanded to a verbose ones, based of the comparison
+ of the first letter. If more then one option with the same first
+ letter is given - then only first one will be used in the expansion.
"""
names = list(names)
@@ -300,34 +308,32 @@ def command(*names, **properties):
def decorator(handler):
"""
- Decorator which receives handler as a first argument and then wraps it
- in the command which then returns back.
+ Decorator which receives handler as a first argument and then
+ wraps it in the command which then returns back.
"""
command = Command(handler, *names, **properties)
# Extract and inject a native name if either no other names are
- # specified or include_native property is enabled, while making sure it
- # is going to be the first one in the list.
+ # specified or include_native property is enabled, while making
+ # sure it is going to be the first one in the list.
if not names or include_native:
names.insert(0, command.native_name)
command.names = tuple(names)
return command
- # Workaround if we are getting called without parameters. Keep in mind that
- # in that case - first item in the names will be the handler.
+ # Workaround if we are getting called without parameters. Keep in
+ # mind that in that case - first item in the names will be the
+ # handler.
if names and isinstance(names[0], FunctionType):
return decorator(names.pop(0))
return decorator
-def documentation(text):
+def doc(text):
"""
- This decorator is used to bind a documentation (a help) to a command.
-
- Though this can be done easily by using doc-strings in a declarative and
- Pythonic way - some of Gajim's developers are against it because of the
- scaffolding needed to support the tranlation of such documentation.
+ This decorator is used to bind a documentation (a help) to a
+ command.
"""
def decorator(target):
if isinstance(target, Command):
diff --git a/src/command_system/implementation/__init__.py b/src/command_system/implementation/__init__.py
index c77c23e3f..4e179f5ea 100644
--- a/src/command_system/implementation/__init__.py
+++ b/src/command_system/implementation/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -14,6 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-The implementation and auxilary systems which implement the standard Gajim
-commands and also provide an infrastructure for adding custom commands.
+The implementation and auxilary systems which implement the standard
+Gajim commands and also provide an infrastructure for adding custom
+commands.
"""
diff --git a/src/command_system/implementation/custom.py b/src/command_system/implementation/custom.py
index e4aa32dbf..64b872e54 100644
--- a/src/command_system/implementation/custom.py
+++ b/src/command_system/implementation/custom.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -14,21 +14,22 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-The module contains examples of how to create your own commands, by creating a
-new command container and definding a set of commands.
+The module contains examples of how to create your own commands, by
+creating a new command container and definding a set of commands.
-Keep in mind that this module is not being loaded, so the code will not be
-executed and commands defined here will not be detected.
+Keep in mind that this module is not being loaded, so the code will not
+be executed and commands defined here will not be detected.
"""
-from ..framework import CommandContainer, command, documentation
+from ..framework import CommandContainer, command, doc
from hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
class CustomCommonCommands(CommandContainer):
"""
This command container bounds to all three available in the default
- implementation command hosts. This means that commands defined in this
- container will be available to all - chat, private chat and a group chat.
+ implementation command hosts. This means that commands defined in
+ this container will be available to all - chat, private chat and a
+ group chat.
"""
HOSTS = (ChatCommands, PrivateChatCommands, GroupChatCommands)
@@ -39,12 +40,13 @@ class CustomCommonCommands(CommandContainer):
First line of the doc string is called a description and will be
programmatically extracted and formatted.
- After that you can give more help, like explanation of the options. This
- one will be programatically extracted and formatted too.
+ After that you can give more help, like explanation of the
+ options. This one will be programatically extracted and
+ formatted too.
- After all the documentation - there will be autogenerated (based on the
- method signature) usage information appended. You can turn it off
- though, if you want.
+ After all the documentation - there will be autogenerated (based
+ on the method signature) usage information appended. You can
+ turn it off though, if you want.
"""
return "I can't dance, you stupid fuck, I'm just a command system! A cool one, though..."
@@ -56,15 +58,16 @@ class CustomChatCommands(CommandContainer):
HOSTS = (ChatCommands,)
- @documentation(_("The same as using a doc-string, except it supports translation"))
+ @doc(_("The same as using a doc-string, except it supports translation"))
@command
def sing(self):
return "Are you phreaking kidding me? Buy yourself a damn stereo..."
class CustomPrivateChatCommands(CommandContainer):
"""
- This command container bounds only to the PrivateChatCommands command host.
- Therefore command defined here will be available only to a private chat.
+ This command container bounds only to the PrivateChatCommands
+ command host. Therefore command defined here will be available only
+ to a private chat.
"""
HOSTS = (PrivateChatCommands,)
@@ -75,8 +78,9 @@ class CustomPrivateChatCommands(CommandContainer):
class CustomGroupChatCommands(CommandContainer):
"""
- This command container bounds only to the GroupChatCommands command host.
- Therefore command defined here will be available only to a group chat.
+ This command container bounds only to the GroupChatCommands command
+ host. Therefore command defined here will be available only to a
+ group chat.
"""
HOSTS = (GroupChatCommands,)
diff --git a/src/command_system/implementation/hosts.py b/src/command_system/implementation/hosts.py
index a90dab464..3624da2dc 100644
--- a/src/command_system/implementation/hosts.py
+++ b/src/command_system/implementation/hosts.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -14,29 +14,29 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-The module defines a set of command hosts, which are bound to a different
-command processors, which are the source of commands.
+The module defines a set of command hosts, which are bound to a
+different command processors, which are the source of commands.
"""
from ..framework import CommandHost
class ChatCommands(CommandHost):
"""
- This command host is bound to the command processor which processes commands
- from a chat.
+ This command host is bound to the command processor which processes
+ commands from a chat.
"""
pass
class PrivateChatCommands(CommandHost):
"""
- This command host is bound to the command processor which processes commands
- from a private chat.
+ This command host is bound to the command processor which processes
+ commands from a private chat.
"""
pass
class GroupChatCommands(CommandHost):
"""
- This command host is bound to the command processor which processes commands
- from a group chat.
+ This command host is bound to the command processor which processes
+ commands from a group chat.
"""
pass
diff --git a/src/command_system/implementation/middleware.py b/src/command_system/implementation/middleware.py
index 2f262f8ed..ed158d5b4 100644
--- a/src/command_system/implementation/middleware.py
+++ b/src/command_system/implementation/middleware.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -14,10 +14,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-Provides a glue to tie command system framework and the actual code where it
-would be dropped in. Defines a little bit of scaffolding to support interaction
-between the two and a few utility methods so you don't need to dig up the code
-itself code to write basic commands.
+Provides a glue to tie command system framework and the actual code
+where it would be dropped in. Defines a little bit of scaffolding to
+support interaction between the two and a few utility methods so you
+don't need to dig up the code itself code to write basic commands.
"""
from types import StringTypes
@@ -26,17 +26,18 @@ from traceback import print_exc
from common import gajim
from ..framework import CommandProcessor
-from ..errors import CommandError
+from ..errors import CommandError, NoCommandError
class ChatCommandProcessor(CommandProcessor):
"""
- A basic scaffolding to provide convenient interaction between the command
- system and chat controls.
+ A basic scaffolding to provide convenient interaction between the
+ command system and chat controls.
"""
def process_as_command(self, text):
+ self.command_succeeded = False
flag = super(ChatCommandProcessor, self).process_as_command(text)
- if flag:
+ if flag and self.command_succeeded:
self.add_history(text)
self.clear_input()
return flag
@@ -44,40 +45,49 @@ class ChatCommandProcessor(CommandProcessor):
def execute_command(self, name, arguments):
try:
super(ChatCommandProcessor, self).execute_command(name, arguments)
+ except NoCommandError, error:
+ details = dict(name=error.name, message=error.message)
+ message = "%(name)s: %(message)s\n" % details
+ message += "Try using the //%(name)s or /say /%(name)s " % details
+ message += "construct if you intended to send it as a text."
+ self.echo(message, 'error')
except CommandError, error:
- self.echo("%s: %s" %(error.name, error.message), 'error')
+ self.echo("%s: %s" % (error.name, error.message), 'error')
except Exception:
self.echo("An error occured while trying to execute the command", 'error')
print_exc()
+ else:
+ self.command_succeeded = True
def looks_like_command(self, text, body, name, arguments):
- # Command escape stuff ggoes here. If text was prepended by the command
- # prefix twice, like //not_a_command (if prefix is set to /) then it
- # will be escaped, that is sent just as a regular message with one (only
- # one) prefix removed, so message will be /not_a_command.
+ # Command escape stuff ggoes here. If text was prepended by the
+ # command prefix twice, like //not_a_command (if prefix is set
+ # to /) then it will be escaped, that is sent just as a regular
+ # message with one (only one) prefix removed, so message will be
+ # /not_a_command.
if body.startswith(self.COMMAND_PREFIX):
self.send(body)
return True
def command_preprocessor(self, command, name, arguments, args, kwargs):
- # If command argument contain h or help option - forward it to the /help
- # command. Dont forget to pass self, as all commands are unbound. And
- # also don't forget to print output.
+ # If command argument contain h or help option - forward it to
+ # the /help command. Dont forget to pass self, as all commands
+ # are unbound. And also don't forget to print output.
if 'h' in kwargs or 'help' in kwargs:
help = self.get_command('help')
self.echo(help(self, name))
return True
def command_postprocessor(self, command, name, arguments, args, kwargs, value):
- # If command returns a string - print it to a user. A convenient and
- # sufficient in most simple cases shortcut to a using echo.
+ # If command returns a string - print it to a user. A convenient
+ # and sufficient in most simple cases shortcut to a using echo.
if value and isinstance(value, StringTypes):
self.echo(value)
class CommandTools:
"""
- Contains a set of basic tools and shortcuts you can use in your commands to
- performe some simple operations.
+ Contains a set of basic tools and shortcuts you can use in your
+ commands to performe some simple operations.
"""
def echo(self, text, kind='info'):
@@ -107,8 +117,8 @@ class CommandTools:
def add_history(self, text):
"""
- Add given text to the input history, so user can scroll through it using
- ctrl + up/down arrow keys.
+ Add given text to the input history, so user can scroll through
+ it using ctrl + up/down arrow keys.
"""
self.save_sent_message(text)
diff --git a/src/command_system/implementation/standard.py b/src/command_system/implementation/standard.py
index 91bfbc8e9..af4ca8ded 100644
--- a/src/command_system/implementation/standard.py
+++ b/src/command_system/implementation/standard.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -27,7 +27,7 @@ from common.exceptions import GajimGeneralException
from common.logger import Constants
from ..errors import CommandError
-from ..framework import CommandContainer, command, documentation
+from ..framework import CommandContainer, command, doc
from ..mapping import generate_usage
from hosts import ChatCommands, PrivateChatCommands, GroupChatCommands
@@ -38,25 +38,25 @@ lc = Constants()
class StandardCommonCommands(CommandContainer):
"""
- This command container contains standard commands which are common to all -
- chat, private chat, group chat.
+ This command container contains standard commands which are common
+ to all - chat, private chat, group chat.
"""
HOSTS = (ChatCommands, PrivateChatCommands, GroupChatCommands)
@command
- @documentation(_("Clear the text window"))
+ @doc(_("Clear the text window"))
def clear(self):
self.conv_textview.clear()
@command
- @documentation(_("Hide the chat buttons"))
+ @doc(_("Hide the chat buttons"))
def compact(self):
new_status = not self.hide_chat_buttons
self.chat_buttons_set_visible(new_status)
@command(overlap=True)
- @documentation(_("Show help on a given command or a list of available commands if -(-a)ll is given"))
+ @doc(_("Show help on a given command or a list of available commands if -(-a)ll is given"))
def help(self, command=None, all=False):
if command:
command = self.get_command(command)
@@ -83,17 +83,17 @@ class StandardCommonCommands(CommandContainer):
self.echo(help(self, 'help'))
@command(raw=True)
- @documentation(_("Send a message to the contact"))
+ @doc(_("Send a message to the contact"))
def say(self, message):
self.send(message)
@command(raw=True)
- @documentation(_("Send action (in the third person) to the current chat"))
+ @doc(_("Send action (in the third person) to the current chat"))
def me(self, action):
self.send("/me %s" % action)
@command('lastlog', overlap=True)
- @documentation(_("Show logged messages which mention given text"))
+ @doc(_("Show logged messages which mention given text"))
def grep(self, text, limit=None):
results = gajim.logger.get_search_results_for_query(self.contact.jid,
text, self.account)
@@ -129,7 +129,7 @@ class StandardCommonCommands(CommandContainer):
self.echo(formatted)
@command(raw=True, empty=True)
- @documentation(_("""
+ @doc(_("""
Set current the status
Status can be given as one of the following values: online, away,
@@ -142,7 +142,7 @@ class StandardCommonCommands(CommandContainer):
connection.change_status(status, message)
@command(raw=True, empty=True)
- @documentation(_("Set the current status to away"))
+ @doc(_("Set the current status to away"))
def away(self, message):
if not message:
message = _("Away")
@@ -150,35 +150,40 @@ class StandardCommonCommands(CommandContainer):
connection.change_status('away', message)
@command('back', raw=True, empty=True)
- @documentation(_("Set the current status to online"))
+ @doc(_("Set the current status to online"))
def online(self, message):
if not message:
message = _("Available")
for connection in gajim.connections.itervalues():
connection.change_status('online', message)
-class StandardChatCommands(CommandContainer):
+class StandardCommonChatCommands(CommandContainer):
"""
- This command container contains standard command which are unique to a chat.
+ This command container contans standard commands, which are common
+ to a chat and a private chat only.
"""
- HOSTS = (ChatCommands,)
+ HOSTS = (ChatCommands, PrivateChatCommands)
+
+ @command
+ @doc(_("Toggle the GPG encryption"))
+ def gpg(self):
+ self._toggle_gpg()
@command
- @documentation(_("Send a ping to the contact"))
+ @doc(_("Send a ping to the contact"))
def ping(self):
if self.account == gajim.ZEROCONF_ACC_NAME:
raise CommandError(_('Command is not supported for zeroconf accounts'))
gajim.connections[self.account].sendPing(self.contact)
@command
- @documentation(_("Sends DTMF events through an open audio session"))
+ @doc(_("Send DTMF events through an open audio session"))
def dtmf(self, events):
if not self.audio_sid:
raise CommandError(_("There is no open audio session with this contact"))
- # Valid values for DTMF tones are *, # or a number
- events = [event for event in events
- if event in ('*', '#') or event.isdigit()]
+ # Valid values for DTMF tones are *, # or a number.
+ events = [e for e in events if e in ('*', '#') or e.isdigit()]
if events:
session = gajim.connections[self.account].get_jingle_session(
self.contact.get_full_jid(), self.audio_sid)
@@ -188,10 +193,10 @@ class StandardChatCommands(CommandContainer):
raise CommandError(_("No valid DTMF event specified"))
@command
- @documentation(_("Toggle audio session"))
+ @doc(_("Toggle audio session"))
def audio(self):
- if self.audio_state == self.JINGLE_STATE_NOT_AVAILABLE:
- raise CommandError(_("Video sessions are not available"))
+ if not self.audio_available:
+ raise CommandError(_("Audio sessions are not available"))
else:
# A state of an audio session is toggled by inverting a state of the
# appropriate button.
@@ -199,9 +204,9 @@ class StandardChatCommands(CommandContainer):
self._audio_button.set_active(not state)
@command
- @documentation(_("Toggle video session"))
+ @doc(_("Toggle video session"))
def video(self):
- if self.video_state == self.JINGLE_STATE_NOT_AVAILABLE:
+ if not self.video_available:
raise CommandError(_("Video sessions are not available"))
else:
# A state of a video session is toggled by inverting a state of the
@@ -209,24 +214,32 @@ class StandardChatCommands(CommandContainer):
state = self._video_button.get_active()
self._video_button.set_active(not state)
+class StandardChatCommands(CommandContainer):
+ """
+ This command container contains standard commands which are unique
+ to a chat.
+ """
+
+ HOSTS = (ChatCommands,)
+
class StandardPrivateChatCommands(CommandContainer):
"""
- This command container contains standard command which are unique to a
- private chat.
+ This command container contains standard commands which are unique
+ to a private chat.
"""
HOSTS = (PrivateChatCommands,)
-class StandardGroupchatCommands(CommandContainer):
+class StandardGroupChatCommands(CommandContainer):
"""
- This command container contains standard command which are unique to a group
- chat.
+ This command container contains standard commands which are unique
+ to a group chat.
"""
HOSTS = (GroupChatCommands,)
@command(raw=True)
- @documentation(_("Change your nickname in a group chat"))
+ @doc(_("Change your nickname in a group chat"))
def nick(self, new_nick):
try:
new_nick = helpers.parse_resource(new_nick)
@@ -236,7 +249,7 @@ class StandardGroupchatCommands(CommandContainer):
self.new_nick = new_nick
@command('query', raw=True)
- @documentation(_("Open a private chat window with a specified occupant"))
+ @doc(_("Open a private chat window with a specified occupant"))
def chat(self, nick):
nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
if nick in nicks:
@@ -245,7 +258,7 @@ class StandardGroupchatCommands(CommandContainer):
raise CommandError(_("Nickname not found"))
@command('msg', raw=True)
- @documentation(_("Open a private chat window with a specified occupant and send him a message"))
+ @doc(_("Open a private chat window with a specified occupant and send him a message"))
def message(self, nick, a_message):
nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
if nick in nicks:
@@ -254,7 +267,7 @@ class StandardGroupchatCommands(CommandContainer):
raise CommandError(_("Nickname not found"))
@command(raw=True, empty=True)
- @documentation(_("Display or change a group chat topic"))
+ @doc(_("Display or change a group chat topic"))
def topic(self, new_topic):
if new_topic:
self.connection.send_gc_subject(self.room_jid, new_topic)
@@ -262,13 +275,13 @@ class StandardGroupchatCommands(CommandContainer):
return self.subject
@command(raw=True, empty=True)
- @documentation(_("Invite a user to a room for a reason"))
+ @doc(_("Invite a user to a room for a reason"))
def invite(self, jid, reason):
self.connection.send_invite(self.room_jid, jid, reason)
return _("Invited %s to %s") % (jid, self.room_jid)
@command(raw=True, empty=True)
- @documentation(_("Join a group chat given by a jid, optionally using given nickname"))
+ @doc(_("Join a group chat given by a jid, optionally using given nickname"))
def join(self, jid, nick):
if not nick:
nick = self.nick
@@ -285,12 +298,12 @@ class StandardGroupchatCommands(CommandContainer):
pass
@command('part', 'close', raw=True, empty=True)
- @documentation(_("Leave the groupchat, optionally giving a reason, and close tab or window"))
+ @doc(_("Leave the groupchat, optionally giving a reason, and close tab or window"))
def leave(self, reason):
self.parent_win.remove_tab(self, self.parent_win.CLOSE_COMMAND, reason)
@command(raw=True, empty=True)
- @documentation(_("""
+ @doc(_("""
Ban user by a nick or a jid from a groupchat
If given nickname is not found it will be treated as a jid.
@@ -302,14 +315,14 @@ class StandardGroupchatCommands(CommandContainer):
self.connection.gc_set_affiliation(self.room_jid, who, 'outcast', reason or str())
@command(raw=True, empty=True)
- @documentation(_("Kick user by a nick from a groupchat"))
+ @doc(_("Kick user by a nick from a groupchat"))
def kick(self, who, reason):
if not who in gajim.contacts.get_nick_list(self.account, self.room_jid):
raise CommandError(_("Nickname not found"))
self.connection.gc_set_role(self.room_jid, who, 'none', reason or str())
@command
- @documentation(_("Display names of all group chat occupants"))
+ @doc(_("Display names of all group chat occupants"))
def names(self, verbose=False):
get_contact = lambda nick: gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
nicks = gajim.contacts.get_nick_list(self.account, self.room_jid)
@@ -330,11 +343,11 @@ class StandardGroupchatCommands(CommandContainer):
return ', '.join(nicks)
@command('ignore', raw=True)
- @documentation(_("Forbid an occupant to send you public or private messages"))
+ @doc(_("Forbid an occupant to send you public or private messages"))
def block(self, who):
self.on_block(None, who)
@command('unignore', raw=True)
- @documentation(_("Allow an occupant to send you public or private messages"))
+ @doc(_("Allow an occupant to send you public or private messages"))
def unblock(self, who):
self.on_unblock(None, who)
diff --git a/src/command_system/mapping.py b/src/command_system/mapping.py
index ecf8f0783..8f7a31470 100644
--- a/src/command_system/mapping.py
+++ b/src/command_system/mapping.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009 Alexander Cherniuk <ts33kr@gmail.com>
+# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
#
# 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
@@ -14,17 +14,16 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-The module contains routines to parse command arguments and map them to the
-command handler's positonal and keyword arguments.
+The module contains routines to parse command arguments and map them to
+the command handler's positonal and keyword arguments.
-Mapping is done in two stages: 1) parse arguments into positional arguments and
-options; 2) adapt them to the specific command handler according to the command
-properties.
+Mapping is done in two stages: 1) parse arguments into positional
+arguments and options; 2) adapt them to the specific command handler
+according to the command properties.
"""
import re
from types import BooleanType, UnicodeType
-from types import TupleType, ListType
from operator import itemgetter
from errors import DefinitionError, CommandError
@@ -34,28 +33,32 @@ from errors import DefinitionError, CommandError
ARG_PATTERN = re.compile(r'(\'|")?(?P<body>(?(1).+?|\S+))(?(1)\1)')
OPT_PATTERN = re.compile(r'(?<!\w)--?(?P<key>[\w-]+)(?:(?:=|\s)(\'|")?(?P<value>(?(2)[^-]+?|[^-\s]+))(?(2)\2))?')
-# Option keys needs to be encoded to a specific encoding as Python does not
-# allow to expand dictionary with raw unicode strings as keys from a **kwargs.
+# Option keys needs to be encoded to a specific encoding as Python does
+# not allow to expand dictionary with raw unicode strings as keys from a
+# **kwargs.
KEY_ENCODING = 'UTF-8'
-# Defines how complete representation of command usage (generated based on
-# command handler argument specification) will be rendered.
+# Defines how complete representation of command usage (generated based
+# on command handler argument specification) will be rendered.
USAGE_PATTERN = 'Usage: %s %s'
def parse_arguments(arguments):
"""
- Simple yet effective and sufficient in most cases parser which parses
- command arguments and returns them as two lists.
+ Simple yet effective and sufficient in most cases parser which
+ parses command arguments and returns them as two lists.
- First list represents positional arguments as (argument, position), and
- second representing options as (key, value, position) tuples, where position
- is a (start, end) span tuple of where it was found in the string.
+ First list represents positional arguments as (argument, position),
+ and second representing options as (key, value, position) tuples,
+ where position is a (start, end) span tuple of where it was found in
+ the string.
- Options may be given in --long or -short format. As --option=value or
- --option value or -option value. Keys without values will get None as value.
+ Options may be given in --long or -short format. As --option=value
+ or --option value or -option value. Keys without values will get
+ None as value.
- Arguments and option values that contain spaces may be given as 'one two
- three' or "one two three"; that is between single or double quotes.
+ Arguments and option values that contain spaces may be given as 'one
+ two three' or "one two three"; that is between single or double
+ quotes.
"""
args, opts = [], []
@@ -90,14 +93,16 @@ def parse_arguments(arguments):
position = match.span()
args.append((body, position))
- # Primitive but sufficiently effective way of disposing of conflicted
- # sectors. Remove any arguments that intersect with options.
+ # Primitive but sufficiently effective way of disposing of
+ # conflicted sectors. Remove any arguments that intersect with
+ # options.
for arg, position in args[:]:
if intersects_opts(position):
args.remove((arg, position))
- # Primitive but sufficiently effective way of disposing of conflicted
- # sectors. Remove any options that intersect with arguments.
+ # Primitive but sufficiently effective way of disposing of
+ # conflicted sectors. Remove any options that intersect with
+ # arguments.
for key, value, position in opts[:]:
if intersects_args(position):
opts.remove((key, value, position))
@@ -106,41 +111,40 @@ def parse_arguments(arguments):
def adapt_arguments(command, arguments, args, opts):
"""
- Adapt args and opts got from the parser to a specific handler by means of
- arguments specified on command definition. That is transform them to *args
- and **kwargs suitable for passing to a command handler.
-
- Dashes (-) in the option names will be converted to underscores. So you can
- map --one-more-option to a one_more_option=None.
-
- If the initial value of a keyword argument is a boolean (False in most
- cases) - then this option will be treated as a switch, that is an option
- which does not take an argument. If a switch is followed by an argument -
- then this argument will be treated just like a normal positional argument.
-
- If the initial value of a keyword argument is a sequence, that is a tuple or
- list - then a value of this option will be considered correct only if it is
- present in the sequence.
+ Adapt args and opts got from the parser to a specific handler by
+ means of arguments specified on command definition. That is
+ transform them to *args and **kwargs suitable for passing to a
+ command handler.
+
+ Dashes (-) in the option names will be converted to underscores. So
+ you can map --one-more-option to a one_more_option=None.
+
+ If the initial value of a keyword argument is a boolean (False in
+ most cases) - then this option will be treated as a switch, that is
+ an option which does not take an argument. If a switch is followed
+ by an argument - then this argument will be treated just like a
+ normal positional argument.
"""
spec_args, spec_kwargs, var_args, var_kwargs = command.extract_specification()
norm_kwargs = dict(spec_kwargs)
- # Quite complex piece of neck-breaking logic to extract raw arguments if
- # there is more, then one positional argument specified by the command. In
- # case if it's just one argument which is the collector - this is fairly
- # easy. But when it's more then one argument - the neck-breaking logic of
- # how to retrieve residual arguments as a raw, all in one piece string,
- # kicks in.
+ # Quite complex piece of neck-breaking logic to extract raw
+ # arguments if there is more, then one positional argument specified
+ # by the command. In case if it's just one argument which is the
+ # collector - this is fairly easy. But when it's more then one
+ # argument - the neck-breaking logic of how to retrieve residual
+ # arguments as a raw, all in one piece string, kicks in.
if command.raw:
if arguments:
spec_fix = 1 if command.source else 0
spec_len = len(spec_args) - spec_fix
arguments_end = len(arguments) - 1
- # If there are any optional arguments given they should be either an
- # unquoted postional argument or part of the raw argument. So we
- # find all optional arguments that can possibly be unquoted argument
- # and append them as is to the args.
+ # If there are any optional arguments given they should be
+ # either an unquoted postional argument or part of the raw
+ # argument. So we find all optional arguments that can
+ # possibly be unquoted argument and append them as is to the
+ # args.
for key, value, (start, end) in opts[:spec_len]:
if value:
end -= len(value) + 1
@@ -149,9 +153,9 @@ def adapt_arguments(command, arguments, args, opts):
else:
args.append((arguments[start:end], (start, end)))
- # We need in-place sort here because after manipulations with
- # options order of arguments might be wrong and we just can't have
- # more complex logic to not let that happen.
+ # We need in-place sort here because after manipulations
+ # with options order of arguments might be wrong and we just
+ # can't have more complex logic to not let that happen.
args.sort(key=itemgetter(1))
if spec_len > 1:
@@ -160,27 +164,28 @@ def adapt_arguments(command, arguments, args, opts):
except IndexError:
raise CommandError("Missing arguments", command)
- # The essential point of the whole play. After boundaries are
- # being determined (supposingly correct) we separate raw part
- # from the rest of arguments, which should be normally
- # processed.
+ # The essential point of the whole play. After
+ # boundaries are being determined (supposingly correct)
+ # we separate raw part from the rest of arguments, which
+ # should be normally processed.
raw = arguments[end:]
raw = raw.strip() or None
if not raw and not command.empty:
raise CommandError("Missing arguments", command)
- # Discard residual arguments and all of the options as raw
- # command does not support options and if an option is given it
- # is rather a part of a raw argument.
+ # Discard residual arguments and all of the options as
+ # raw command does not support options and if an option
+ # is given it is rather a part of a raw argument.
args = args[:spec_len - 1]
opts = []
args.append((raw, (end, arguments_end)))
else:
- # Substitue all of the arguments with only one, which contain
- # raw and unprocessed arguments as a string. And discard all the
- # options, as raw command does not support them.
+ # Substitue all of the arguments with only one, which
+ # contain raw and unprocessed arguments as a string. And
+ # discard all the options, as raw command does not
+ # support them.
args = [(arguments, (0, arguments_end))]
opts = []
else:
@@ -189,16 +194,17 @@ def adapt_arguments(command, arguments, args, opts):
else:
raise CommandError("Missing arguments", command)
- # The first stage of transforming options we have got to a format that can
- # be used to associate them with declared keyword arguments. Substituting
- # dashes (-) in their names with underscores (_).
+ # The first stage of transforming options we have got to a format
+ # that can be used to associate them with declared keyword
+ # arguments. Substituting dashes (-) in their names with
+ # underscores (_).
for index, (key, value, position) in enumerate(opts):
if '-' in key:
opts[index] = (key.replace('-', '_'), value, position)
# The second stage of transforming options to an associatable state.
- # Expanding short, one-letter options to a verbose ones, if corresponding
- # optin has been given.
+ # Expanding short, one-letter options to a verbose ones, if
+ # corresponding optin has been given.
if command.expand_short:
expanded = []
for spec_key, spec_value in norm_kwargs.iteritems():
@@ -210,26 +216,27 @@ def adapt_arguments(command, arguments, args, opts):
opts[index] = (spec_key, value, position)
break
- # Detect switches and set their values accordingly. If any of them carries a
- # value - append it to args.
+ # Detect switches and set their values accordingly. If any of them
+ # carries a value - append it to args.
for index, (key, value, position) in enumerate(opts):
if isinstance(norm_kwargs.get(key), BooleanType):
opts[index] = (key, True, position)
if value:
args.append((value, position))
- # Sorting arguments and options (just to be sure) in regarding to their
- # positions in the string.
+ # Sorting arguments and options (just to be sure) in regarding to
+ # their positions in the string.
args.sort(key=itemgetter(1))
opts.sort(key=itemgetter(2))
- # Stripping down position information supplied with arguments and options as
- # it won't be needed again.
+ # Stripping down position information supplied with arguments and
+ # options as it won't be needed again.
args = map(lambda (arg, position): arg, args)
opts = map(lambda (key, value, position): (key, value), opts)
- # If command has extra option enabled - collect all extra arguments and pass
- # them to a last positional argument command defines as a list.
+ # If command has extra option enabled - collect all extra arguments
+ # and pass them to a last positional argument command defines as a
+ # list.
if command.extra:
if not var_args:
spec_fix = 1 if not command.source else 2
@@ -240,9 +247,9 @@ def adapt_arguments(command, arguments, args, opts):
else:
raise DefinitionError("Can not have both, extra and *args")
- # Detect if positional arguments overlap keyword arguments. If so and this
- # is allowed by command options - then map them directly to their options,
- # so they can get propert further processings.
+ # Detect if positional arguments overlap keyword arguments. If so
+ # and this is allowed by command options - then map them directly to
+ # their options, so they can get propert further processings.
spec_fix = 1 if command.source else 0
spec_len = len(spec_args) - spec_fix
if len(args) > spec_len:
@@ -262,49 +269,32 @@ def adapt_arguments(command, arguments, args, opts):
if not isinstance(value, BooleanType):
raise CommandError("%s: Switch can not take an argument" % key, command)
- # Detect every sequence constraint and ensure that if corresponding options
- # are given - they contain proper values, within the constraint range.
- for key, value in opts:
- initial = norm_kwargs.get(key)
- if isinstance(initial, (TupleType, ListType)):
- if value not in initial:
- raise CommandError("%s: Invalid argument" % key, command)
-
- # If argument to an option constrained by a sequence was not given - then
- # it's value should be set to None.
- for spec_key, spec_value in spec_kwargs:
- if isinstance(spec_value, (TupleType, ListType)):
- for key, value in opts:
- if spec_key == key:
- break
- else:
- opts.append((spec_key, None))
-
- # We need to encode every keyword argument to a simple string, not the
- # unicode one, because ** expansion does not support it.
+ # We need to encode every keyword argument to a simple string, not
+ # the unicode one, because ** expansion does not support it.
for index, (key, value) in enumerate(opts):
if isinstance(key, UnicodeType):
opts[index] = (key.encode(KEY_ENCODING), value)
- # Inject the source arguments as a string as a first argument, if command
- # has enabled the corresponding option.
+ # Inject the source arguments as a string as a first argument, if
+ # command has enabled the corresponding option.
if command.source:
args.insert(0, arguments)
- # Return *args and **kwargs in the form suitable for passing to a command
- # handler and being expanded.
+ # Return *args and **kwargs in the form suitable for passing to a
+ # command handler and being expanded.
return tuple(args), dict(opts)
def generate_usage(command, complete=True):
"""
- Extract handler's arguments specification and wrap them in a human-readable
- format usage information. If complete is given - then USAGE_PATTERN will be
- used to render the specification completly.
+ Extract handler's arguments specification and wrap them in a
+ human-readable format usage information. If complete is given - then
+ USAGE_PATTERN will be used to render the specification completly.
"""
spec_args, spec_kwargs, var_args, var_kwargs = command.extract_specification()
- # Remove some special positional arguments from the specifiaction, but store
- # their names so they can be used for usage info generation.
+ # Remove some special positional arguments from the specifiaction,
+ # but store their names so they can be used for usage info
+ # generation.
sp_source = spec_args.pop(0) if command.source else None
sp_extra = spec_args.pop() if command.extra else None
@@ -317,8 +307,6 @@ def generate_usage(command, complete=True):
if isinstance(value, BooleanType):
value = str()
- elif isinstance(value, (TupleType, ListType)):
- value = '={%s}' % ', '.join(value)
else:
value = '=%s' % value
@@ -350,8 +338,8 @@ def generate_usage(command, complete=True):
if var_kwargs:
usage += (' ' if args else str()) + '[[%s]]' % var_kwargs
- # Native name will be the first one if it is included. Otherwise, names will
- # be in the order they were specified.
+ # Native name will be the first one if it is included. Otherwise,
+ # names will be in the order they were specified.
if len(command.names) > 1:
names = '%s (%s)' % (command.first_name, ', '.join(command.names[1:]))
else:
diff --git a/src/common/GnuPG.py b/src/common/GnuPG.py
index a5c0c0559..32c1d4452 100644
--- a/src/common/GnuPG.py
+++ b/src/common/GnuPG.py
@@ -1,9 +1,8 @@
## src/common/GnuPG.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2008 Jean-Marie Traissard <jim AT lapin.org>
## Jonathan Schleifer <js-gajim AT webkeks.org>
diff --git a/src/common/GnuPGInterface.py b/src/common/GnuPGInterface.py
index f7f5cb522..4b41a65da 100644
--- a/src/common/GnuPGInterface.py
+++ b/src/common/GnuPGInterface.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2001 Frank J. Tobin <ftobin AT neverending.org>
## Copyright (C) 2005 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Jean-Marie Traissard <jim AT lapin.org>
##
## This file is part of Gajim.
diff --git a/src/common/atom.py b/src/common/atom.py
index 12101b886..e8531274b 100644
--- a/src/common/atom.py
+++ b/src/common/atom.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
## Tomasz Melcer <liori AT exroot.org>
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
##
## This file is part of Gajim.
##
diff --git a/src/common/caps_cache.py b/src/common/caps_cache.py
index 23c54c279..af69c8aeb 100644
--- a/src/common/caps_cache.py
+++ b/src/common/caps_cache.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2007 Tomasz Melcer <liori AT exroot.org>
## Travis Shirk <travis AT pobox.com>
-## Copyright (C) 2007-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
## Jonathan Schleifer <js-gajim AT webkeks.org>
## Copyright (C) 2008-2009 Stephan Erb <steve-e AT h3c.de>
diff --git a/src/common/check_paths.py b/src/common/check_paths.py
index 5b18ac480..00a50e6fe 100644
--- a/src/common/check_paths.py
+++ b/src/common/check_paths.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2005-2006 Travis Shirk <travis AT pobox.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2007 Tomasz Melcer <liori AT exroot.org>
## Copyright (C) 2008 Jean-Marie Traissard <jim AT lapin.org>
@@ -129,7 +129,9 @@ def split_db():
print 'spliting database'
if os.name == 'nt':
try:
- OLD_LOG_DB_FOLDER = os.path.join(fse(os.environ[u'appdata']), u'Gajim')
+ import configpaths
+ OLD_LOG_DB_FOLDER = os.path.join(configpaths.fse(
+ os.environ[u'appdata']), u'Gajim')
except KeyError:
OLD_LOG_DB_FOLDER = u'.'
else:
@@ -184,7 +186,8 @@ def check_and_possibly_move_config():
if os.name == 'nt':
try:
- OLD_LOG_DB_FOLDER = os.path.join(fse(os.environ[u'appdata']), u'Gajim')
+ OLD_LOG_DB_FOLDER = os.path.join(configpaths.fse(
+ os.environ[u'appdata']), u'Gajim')
except KeyError:
OLD_LOG_DB_FOLDER = u'.'
else:
@@ -340,5 +343,10 @@ def check_and_possibly_create_paths():
sys.exit()
def create_path(directory):
+ head, tail = os.path.split(directory)
+ if not os.path.exists(head):
+ create_path(head)
+ if os.path.exists(directory):
+ return
print _('creating %s directory') % directory
os.mkdir(directory, 0700)
diff --git a/src/common/commands.py b/src/common/commands.py
index 88f9f3332..d303de922 100644
--- a/src/common/commands.py
+++ b/src/common/commands.py
@@ -1,8 +1,8 @@
# -*- coding:utf-8 -*-
## src/common/commands.py
##
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
-## Tomasz Melcer <liori AT exroot.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
## Stephan Erb <steve-e AT h3c.de>
@@ -290,6 +290,41 @@ class ForwardMessagesCommand(AdHocCommand):
return False # finish the session
+class FwdMsgThenDisconnectCommand(AdHocCommand):
+ commandnode = 'fwd-msd-disconnect'
+ commandname = _('Forward unread message then disconnect')
+
+ @staticmethod
+ def isVisibleFor(samejid):
+ """
+ Change status is visible only if the entity has the same bare jid
+ """
+ return samejid
+
+ def execute(self, request):
+ account = self.connection.name
+ # Forward messages
+ events = gajim.events.get_events(account, types=['chat', 'normal'])
+ j, resource = gajim.get_room_and_nick_from_fjid(self.jid)
+ for jid in events:
+ for event in events[jid]:
+ self.connection.send_message(j, event.parameters[0], '',
+ type_=event.type_, subject=event.parameters[1],
+ resource=resource, forward_from=jid, delayed=event.time_,
+ now=True)
+
+ response, cmd = self.buildResponse(request, status = 'completed')
+ cmd.addChild('note', {}, _('The status has been changed.'))
+
+ # if going offline, we need to push response so it won't go into
+ # queue and disappear
+ self.connection.connection.send(response, now = True)
+
+ # send new status
+ gajim.interface.roster.send_status(self.connection.name, 'offline', '')
+ # finish the session
+ return False
+
class ConnectionCommands:
"""
This class depends on that it is a part of Connection() class
@@ -299,7 +334,7 @@ class ConnectionCommands:
# a list of all commands exposed: node -> command class
self.__commands = {}
for cmdobj in (ChangeStatusCommand, ForwardMessagesCommand,
- LeaveGroupchatsCommand):
+ LeaveGroupchatsCommand, FwdMsgThenDisconnectCommand):
self.__commands[cmdobj.commandnode] = cmdobj
# a list of sessions; keys are tuples (jid, sessionid, node)
diff --git a/src/common/config.py b/src/common/config.py
index 383a289a5..adcd9572a 100644
--- a/src/common/config.py
+++ b/src/common/config.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/common/config.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2005 Stéphan Kochen <stephan AT kochen.nl>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
@@ -277,6 +277,8 @@ class Config:
'audio_output_device': [opt_str, 'autoaudiosink'],
'video_input_device': [opt_str, 'autovideosrc ! videoscale ! ffmpegcolorspace'],
'video_output_device': [opt_str, 'autovideosink'],
+ 'video_framerate': [opt_str, '', _('Optionally fix jingle output video framerate. Example: 10/1 or 25/2')],
+ 'video_size': [opt_str, '', _('Optionally resize jingle output video. Example: 320x240')],
'audio_input_volume': [opt_int, 50],
'audio_output_volume': [opt_int, 50],
'use_stun_server': [opt_bool, True, _('If True, Gajim will try to use a STUN server when using jingle. The one in "stun_server" option, or the one given by the jabber server.')],
@@ -289,6 +291,7 @@ class Config:
'name': [ opt_str, '', '', True ],
'hostname': [ opt_str, '', '', True ],
'anonymous_auth': [ opt_bool, False ],
+ 'client_cert': [ opt_str, '', '', True ],
'savepass': [ opt_bool, False ],
'password': [ opt_str, '' ],
'resource': [ opt_str, 'gajim', '', True ],
@@ -315,6 +318,7 @@ class Config:
'connection_types': [ opt_str, 'tls ssl plain', _('Ordered list (space separated) of connection type to try. Can contain tls, ssl or plain')],
'warn_when_plaintext_connection': [ opt_bool, True, _('Show a warning dialog before sending password on an plaintext connection.') ],
'warn_when_insecure_ssl_connection': [ opt_bool, True, _('Show a warning dialog before using standard SSL library.') ],
+ 'warn_when_insecure_password': [ opt_bool, True, _('Show a warning dialog before sending PLAIN password over a plain conenction.') ],
'ssl_fingerprint_sha1': [ opt_str, '', '', True ],
'ignore_ssl_errors': [ opt_str, '', _('Space separated list of ssl errors to ignore.') ],
'use_srv': [ opt_bool, True, '', True ],
@@ -451,8 +455,8 @@ class Config:
'urgency_hint': [opt_bool, False],
}, {}),
'plugins': ({
- 'active': [opt_bool, False, _('State whether plugins should be activated on exit (this is saved on Gajim exit). This option SHOULD NOT be used to (de)activate plug-ins. Use GUI instead.')],
- }, {}),
+ 'active': [opt_bool, False, _('State whether plugins should be activated on exit (this is saved on Gajim exit). This option SHOULD NOT be used to (de)activate plug-ins. Use GUI instead.')],
+ },{}),
}
statusmsg_default = {
diff --git a/src/common/configpaths.py b/src/common/configpaths.py
index 5b542e5a2..74027994d 100644
--- a/src/common/configpaths.py
+++ b/src/common/configpaths.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
## Junglecow J <junglecow AT gmail.com>
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
@@ -157,7 +157,7 @@ class ConfigPaths:
self.add('ICONS', None, os.path.join(basedir, windowsify(u'icons')))
self.add('HOME', None, fse(os.path.expanduser('~')))
self.add('PLUGINS_BASE', None, os.path.join(basedir,
- windowsify(u'plugins')))
+ windowsify(u'plugins')))
try:
self.add('TMP', None, fse(tempfile.gettempdir()))
except IOError, e:
diff --git a/src/common/connection.py b/src/common/connection.py
index 3ba5a2311..0e281d5a7 100644
--- a/src/common/connection.py
+++ b/src/common/connection.py
@@ -2,7 +2,7 @@
## src/common/connection.py
##
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
## Stéphan Kochen <stephan AT kochen.nl>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
@@ -135,6 +135,9 @@ class CommonConnection:
self.blocked_groups = []
self.blocked_all = False
+ self.seclabel_supported = False
+ self.seclabel_catalogues = {}
+
self.pep_supported = False
self.pep = {}
# Do we continue connection when we get roster (send presence,get vcard..)
@@ -154,6 +157,8 @@ class CommonConnection:
self.muc_jid = {} # jid of muc server for each transport type
self._stun_servers = [] # STUN servers of our jabber server
+ self.awaiting_cids = {} # Used for XEP-0231
+
self.get_config_values_or_default()
def _compute_resource(self):
@@ -239,7 +244,7 @@ class CommonConnection:
def _prepare_message(self, jid, msg, keyID, type_='chat', subject='',
chatstate=None, msg_id=None, composing_xep=None, resource=None,
user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None,
- original_message=None, delayed=None, callback=None):
+ label=None, original_message=None, delayed=None, callback=None):
if not self.connection or self.connected < 2:
return 1
try:
@@ -285,33 +290,37 @@ class CommonConnection:
gajim.thread_interface(encrypt_thread, [msg, keyID,
True], _on_encrypted, [])
else:
- self._message_encrypted_cb(output, type_, msg, msgtxt,
- original_message, fjid, resource, jid, xhtml,
- subject, chatstate, composing_xep, forward_from,
- delayed, session, form_node, user_nick, keyID,
- callback)
+ self._message_encrypted_cb(output, type_, msg,
+ msgtxt, original_message, fjid, resource,
+ jid, xhtml, subject, chatstate, msg_id,
+ composing_xep, label, forward_from, delayed,
+ session, form_node, user_nick, keyID,
+ callback)
self.dispatch('GPG_ALWAYS_TRUST', _on_always_trust)
else:
self._message_encrypted_cb(output, type_, msg, msgtxt,
- original_message, fjid, resource, jid, xhtml, subject,
- chatstate, composing_xep, forward_from, delayed, session,
- form_node, user_nick, keyID, callback)
+ original_message, fjid, resource, jid, xhtml,
+ subject, chatstate, msg_id, composing_xep, label,
+ forward_from, delayed, session, form_node,
+ user_nick, keyID, callback)
gajim.thread_interface(encrypt_thread, [msg, keyID, False],
_on_encrypted, [])
return
self._message_encrypted_cb(('', error), type_, msg, msgtxt,
- original_message, fjid, resource, jid, xhtml, subject, chatstate,
- composing_xep, forward_from, delayed, session, form_node, user_nick,
- keyID, callback)
+ original_message, fjid, resource, jid, xhtml, subject,
+ chatstate, msg_id, composing_xep, label, forward_from, delayed,
+ session, form_node, user_nick, keyID, callback)
self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
- resource, jid, xhtml, subject, msgenc, keyID, chatstate, composing_xep,
- forward_from, delayed, session, form_node, user_nick, callback)
-
- def _message_encrypted_cb(self, output, type_, msg, msgtxt, original_message,
- fjid, resource, jid, xhtml, subject, chatstate, composing_xep, forward_from,
- delayed, session, form_node, user_nick, keyID, callback):
+ resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
+ composing_xep, label, forward_from, delayed, session, form_node,
+ user_nick, callback)
+
+ def _message_encrypted_cb(self, output, type_, msg, msgtxt,
+ original_message, fjid, resource, jid, xhtml, subject, chatstate, msg_id,
+ composing_xep, label, forward_from, delayed, session, form_node, user_nick,
+ keyID, callback):
msgenc, error = output
if msgenc and not error:
@@ -321,18 +330,19 @@ class CommonConnection:
# one in locale and one en
msgtxt = _('[This message is *encrypted* (See :XEP:`27`]') + \
' (' + msgtxt + ')'
- self._on_continue_message(type_, msg, msgtxt, original_message, fjid,
- resource, jid, xhtml, subject, msgenc, keyID, chatstate,
- composing_xep, forward_from, delayed, session, form_node, user_nick,
- callback)
+ self._on_continue_message(type_, msg, msgtxt, original_message,
+ fjid, resource, jid, xhtml, subject, msgenc, keyID,
+ chatstate, msg_id, composing_xep, label, forward_from, delayed,
+ session, form_node, user_nick, callback)
return
# Encryption failed, do not send message
tim = localtime()
self.dispatch('MSGNOTSENT', (jid, error, msgtxt, tim, session))
def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid,
- resource, jid, xhtml, subject, msgenc, keyID, chatstate, composing_xep,
- forward_from, delayed, session, form_node, user_nick, callback):
+ resource, jid, xhtml, subject, msgenc, keyID, chatstate, msg_id,
+ composing_xep, label, forward_from, delayed, session, form_node, user_nick,
+ callback):
if type_ == 'chat':
msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_,
xhtml=xhtml)
@@ -343,11 +353,17 @@ class CommonConnection:
else:
msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ='normal',
xhtml=xhtml)
+
+ if msg_id:
+ msg_iq.setID(msg_id)
+
if msgenc:
msg_iq.setTag(common.xmpp.NS_ENCRYPTED + ' x').setData(msgenc)
if form_node:
msg_iq.addChild(node=form_node)
+ if label:
+ msg_iq.addChild(node=label)
# XEP-0172: user_nickname
if user_nick:
@@ -430,13 +446,15 @@ class CommonConnection:
try:
gajim.logger.write(kind, jid, log_msg)
except exceptions.PysqliteOperationalError, e:
- self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
+ self.dispatch('DB_ERROR', (_('Disk Write Error'),
+ str(e)))
except exceptions.DatabaseMalformed:
pritext = _('Database Error')
sectext = _('The database file (%s) cannot be read. Try to '
'repair it (see http://trac.gajim.org/wiki/DatabaseBackup)'
' or remove it (all history will be lost).') % \
common.logger.LOG_DB_PATH
+ self.dispatch('DB_ERROR', (pritext, sectext))
def ack_subscribed(self, jid):
"""
@@ -507,11 +525,24 @@ class CommonConnection:
def account_changed(self, new_name):
self.name = new_name
- def request_last_status_time(self, jid, resource):
+ def request_last_status_time(self, jid, resource, groupchat_jid=None):
"""
- To be implemented by derivated classes
+ groupchat_jid is used when we want to send a request to a real jid and
+ act as if the answer comes from the groupchat_jid
"""
- raise NotImplementedError
+ if not gajim.account_is_connected(self.name):
+ return
+ to_whom_jid = jid
+ if resource:
+ to_whom_jid += '/' + resource
+ iq = common.xmpp.Iq(to=to_whom_jid, typ='get', queryNS=\
+ common.xmpp.NS_LAST)
+ id_ = self.connection.getAnID()
+ iq.setID(id_)
+ if groupchat_jid:
+ self.groupchat_jids[id_] = groupchat_jid
+ self.last_ids.append(id_)
+ self.connection.send(iq)
def request_os_info(self, jid, resource):
"""
@@ -686,6 +717,8 @@ class Connection(CommonConnection, ConnectionHandlers):
'ping_alive_every_foo_secs')
else:
self.pingalives = 0
+ self.client_cert = gajim.config.get_per('accounts', self.name,
+ 'client_cert')
def check_jid(self, jid):
return helpers.parse_jid(jid)
@@ -975,9 +1008,10 @@ class Connection(CommonConnection, ConnectionHandlers):
proxy['port'] = 3128
if len(login) == 2:
- proxy['password'] = login[1]
+ proxy['pass'] = login[1]
+ proxy['useauth'] = True
else:
- proxy['password'] = u''
+ proxy['pass'] = u''
except Exception:
proxy = None
@@ -1284,8 +1318,6 @@ class Connection(CommonConnection, ConnectionHandlers):
self.on_connect_auth(con)
self.on_connect_auth = None
else:
- # Forget password, it's wrong
- self.password = None
gajim.log.debug("Couldn't authenticate to %s" % self._hostname)
self.disconnect(on_purpose = True)
self.dispatch('STATUS', 'offline')
@@ -1302,7 +1334,7 @@ class Connection(CommonConnection, ConnectionHandlers):
stanza.setAttr('xml:lang', self.lang)
def get_privacy_lists(self):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
common.xmpp.features_nb.getPrivacyLists(self.connection)
@@ -1321,7 +1353,7 @@ class Connection(CommonConnection, ConnectionHandlers):
Send XMPP Ping (XEP-0199) request. If pingTo is not set, ping is sent to
server to detect connection failure at application level
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
id_ = self.connection.getAnID()
if pingTo:
@@ -1349,12 +1381,12 @@ class Connection(CommonConnection, ConnectionHandlers):
'accounts', self.name, 'time_for_ping_alive_answer'))
def get_active_default_lists(self):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
common.xmpp.features_nb.getActiveAndDefaultPrivacyLists(self.connection)
def del_privacy_list(self, privacy_list):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
def _on_del_privacy_list_result(result):
if result:
@@ -1368,22 +1400,22 @@ class Connection(CommonConnection, ConnectionHandlers):
_on_del_privacy_list_result)
def get_privacy_list(self, title):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
common.xmpp.features_nb.getPrivacyList(self.connection, title)
def set_privacy_list(self, listname, tags):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
common.xmpp.features_nb.setPrivacyList(self.connection, listname, tags)
def set_active_list(self, listname):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
common.xmpp.features_nb.setActivePrivacyList(self.connection, listname, 'active')
def set_default_list(self, listname):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname)
@@ -1426,14 +1458,14 @@ class Connection(CommonConnection, ConnectionHandlers):
"""
Activate a privacy rule
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq('set', common.xmpp.NS_PRIVACY, xmlns = '')
iq.getTag('query').setTag('active', {'name': name})
self.connection.send(iq)
def send_invisible_presence(self, msg, signed, initial = False):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
if not self.privacy_rules_supported:
self.dispatch('STATUS', gajim.SHOW_LIST[self.connected])
@@ -1504,7 +1536,7 @@ class Connection(CommonConnection, ConnectionHandlers):
def _discover_server_at_connection(self, con):
self.connection = con
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
self.connection.set_send_timeout(self.keepalives, self.send_keepalive)
self.connection.set_send_timeout2(self.pingalives, self.sendPing)
@@ -1530,7 +1562,7 @@ class Connection(CommonConnection, ConnectionHandlers):
def send_custom_status(self, show, msg, jid):
if not show in gajim.SHOW_LIST:
return -1
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
sshow = helpers.get_xmpp_show(show)
if not msg:
@@ -1578,21 +1610,22 @@ class Connection(CommonConnection, ConnectionHandlers):
self.dispatch('STATUS', show)
def send_motd(self, jid, subject = '', msg = '', xhtml = None):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
msg_iq = common.xmpp.Message(to = jid, body = msg, subject = subject,
xhtml = xhtml)
self.connection.send(msg_iq)
- def send_message(self, jid, msg, keyID, type_='chat', subject='',
+ def send_message(self, jid, msg, keyID=None, type_='chat', subject='',
chatstate=None, msg_id=None, composing_xep=None, resource=None,
- user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None,
- original_message=None, delayed=None, callback=None, callback_args=[]):
+ user_nick=None, xhtml=None, label=None, session=None, forward_from=None,
+ form_node=None, original_message=None, delayed=None, callback=None,
+ callback_args=[], now=False):
- def cb(jid, msg, keyID, forward_from, session, original_message, subject,
- type_, msg_iq):
- msg_id = self.connection.send(msg_iq)
+ def cb(jid, msg, keyID, forward_from, session, original_message,
+ subject, type_, msg_iq):
+ msg_id = self.connection.send(msg_iq, now=now)
jid = helpers.parse_jid(jid)
self.dispatch('MSGSENT', (jid, msg, keyID))
if callback:
@@ -1602,16 +1635,16 @@ class Connection(CommonConnection, ConnectionHandlers):
subject, type_)
self._prepare_message(jid, msg, keyID, type_=type_, subject=subject,
- chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
- resource=resource, user_nick=user_nick, xhtml=xhtml, session=session,
- forward_from=forward_from, form_node=form_node,
- original_message=original_message, delayed=delayed, callback=cb)
+ chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep,
+ resource=resource, user_nick=user_nick, xhtml=xhtml, label=label,
+ session=session, forward_from=forward_from, form_node=form_node,
+ original_message=original_message, delayed=delayed, callback=cb)
def send_contacts(self, contacts, jid):
"""
Send contacts with RosterX (Xep-0144)
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
if len(contacts) == 1:
msg = _('Sent contact: "%s" (%s)') % (contacts[0].get_full_jid(),
@@ -1637,14 +1670,14 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(stanza)
def ack_subscribed(self, jid):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
log.debug('ack\'ing subscription complete for %s' % jid)
p = common.xmpp.Presence(jid, 'subscribe')
self.connection.send(p)
def ack_unsubscribed(self, jid):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
log.debug('ack\'ing unsubscription complete for %s' % jid)
p = common.xmpp.Presence(jid, 'unsubscribe')
@@ -1652,7 +1685,7 @@ class Connection(CommonConnection, ConnectionHandlers):
def request_subscription(self, jid, msg='', name='', groups=[],
auto_auth=False, user_nick=''):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
log.debug('subscription request for %s' % jid)
if auto_auth:
@@ -1677,21 +1710,21 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(p)
def send_authorization(self, jid):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
p = common.xmpp.Presence(jid, 'subscribed')
p = self.add_sha(p)
self.connection.send(p)
def refuse_authorization(self, jid):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
p = common.xmpp.Presence(jid, 'unsubscribed')
p = self.add_sha(p)
self.connection.send(p)
def unsubscribe(self, jid, remove_auth = True):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
if remove_auth:
self.connection.getRoster().delItem(jid)
@@ -1704,7 +1737,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.update_contact(jid, '', [])
def unsubscribe_agent(self, agent):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq('set', common.xmpp.NS_REGISTER, to = agent)
iq.getTag('query').setTag('remove')
@@ -1754,31 +1787,12 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection = con
common.xmpp.features_nb.getRegInfo(con, self._hostname)
- def request_last_status_time(self, jid, resource, groupchat_jid=None):
- """
- groupchat_jid is used when we want to send a request to a real jid and
- act as if the answer comes from the groupchat_jid
- """
- if not self.connection:
- return
- to_whom_jid = jid
- if resource:
- to_whom_jid += '/' + resource
- iq = common.xmpp.Iq(to = to_whom_jid, typ = 'get', queryNS =\
- common.xmpp.NS_LAST)
- id_ = self.connection.getAnID()
- iq.setID(id_)
- if groupchat_jid:
- self.groupchat_jids[id_] = groupchat_jid
- self.last_ids.append(id_)
- self.connection.send(iq)
-
def request_os_info(self, jid, resource, groupchat_jid=None):
"""
groupchat_jid is used when we want to send a request to a real jid and
act as if the answer comes from the groupchat_jid
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
# If we are invisible, do not request
if self.connected == gajim.SHOW_LIST.index('invisible'):
@@ -1801,7 +1815,7 @@ class Connection(CommonConnection, ConnectionHandlers):
groupchat_jid is used when we want to send a request to a real jid and
act as if the answer comes from the groupchat_jid
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
# If we are invisible, do not request
if self.connected == gajim.SHOW_LIST.index('invisible'):
@@ -1823,15 +1837,24 @@ class Connection(CommonConnection, ConnectionHandlers):
"""
Get Gajim settings as described in XEP 0049
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
iq2.addChild(name='gajim', namespace='gajim:prefs')
self.connection.send(iq)
+ def seclabel_catalogue(self, to, callback):
+ if not gajim.account_is_connected(self.name):
+ return
+ self.seclabel_catalogue_request(to, callback)
+ iq = common.xmpp.Iq(typ='get')
+ iq2 = iq.addChild(name='catalog', namespace=common.xmpp.NS_SECLABEL_CATALOG)
+ iq2.setAttr('to', to)
+ self.connection.send(iq)
+
def _request_bookmarks_xml(self):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
@@ -1849,7 +1872,7 @@ class Connection(CommonConnection, ConnectionHandlers):
storage_type can be set to xml to force request to xml storage
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
if self.pubsub_supported and storage_type != 'xml':
self.send_pb_retrieve('', 'storage:bookmarks')
@@ -1866,7 +1889,7 @@ class Connection(CommonConnection, ConnectionHandlers):
storage_type can be set to 'pubsub' or 'xml' so store in only one method
else it will be stored on both
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Node(tag='storage', attrs={'xmlns': 'storage:bookmarks'})
for bm in self.bookmarks:
@@ -1885,21 +1908,19 @@ class Connection(CommonConnection, ConnectionHandlers):
if bm.get('print_status', None):
iq2.setTagData('print_status', bm['print_status'])
- if self.pubsub_supported and storage_type != 'xml':
- if self.pubsub_publish_options_supported:
- options = common.xmpp.Node(common.xmpp.NS_DATA + ' x',
- attrs={'type': 'submit'})
- f = options.addChild('field', attrs={'var': 'FORM_TYPE',
- 'type': 'hidden'})
- f.setTagData('value', common.xmpp.NS_PUBSUB_PUBLISH_OPTIONS)
- f = options.addChild('field', attrs={'var': 'pubsub#persist_items'})
- f.setTagData('value', 'true')
- f = options.addChild('field', attrs={'var': 'pubsub#access_model'})
- f.setTagData('value', 'whitelist')
- else:
- options = None
+ if self.pubsub_supported and self.pubsub_publish_options_supported and \
+ storage_type != 'xml':
+ options = common.xmpp.Node(common.xmpp.NS_DATA + ' x',
+ attrs={'type': 'submit'})
+ f = options.addChild('field', attrs={'var': 'FORM_TYPE',
+ 'type': 'hidden'})
+ f.setTagData('value', common.xmpp.NS_PUBSUB_PUBLISH_OPTIONS)
+ f = options.addChild('field', attrs={'var': 'pubsub#persist_items'})
+ f.setTagData('value', 'true')
+ f = options.addChild('field', attrs={'var': 'pubsub#access_model'})
+ f.setTagData('value', 'whitelist')
self.send_pb_publish('', 'storage:bookmarks', iq, 'current',
- options=options)
+ options=options)
if storage_type != 'pubsub':
iqA = common.xmpp.Iq(typ='set')
iqB = iqA.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
@@ -1911,7 +1932,7 @@ class Connection(CommonConnection, ConnectionHandlers):
Get Annonations from storage as described in XEP 0048, and XEP 0145
"""
self.annotations = {}
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
@@ -1922,7 +1943,7 @@ class Connection(CommonConnection, ConnectionHandlers):
"""
Set Annonations in private storage as described in XEP 0048, and XEP 0145
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
@@ -1939,7 +1960,7 @@ class Connection(CommonConnection, ConnectionHandlers):
"""
Get metacontacts list from storage as described in XEP 0049
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
@@ -1953,7 +1974,7 @@ class Connection(CommonConnection, ConnectionHandlers):
"""
Send meta contacts to the storage namespace
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ='set')
iq2 = iq.addChild(name='query', namespace=common.xmpp.NS_PRIVATE)
@@ -1968,15 +1989,23 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq)
def send_agent_status(self, agent, ptype):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
show = helpers.get_xmpp_show(gajim.SHOW_LIST[self.connected])
p = common.xmpp.Presence(to = agent, typ = ptype, show = show)
p = self.add_sha(p, ptype != 'unavailable')
self.connection.send(p)
+ def send_captcha(self, jid, form_node):
+ if not gajim.account_is_connected(self.name):
+ return
+ iq = common.xmpp.Iq(typ='set', to=jid)
+ captcha = iq.addChild(name='captcha', namespace=common.xmpp.NS_CAPTCHA)
+ captcha.addChild(node=form_node)
+ self.connection.send(iq)
+
def check_unique_room_id_support(self, server, instance):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ = 'get', to = server)
iq.setAttr('id', 'unique1')
@@ -1991,7 +2020,7 @@ class Connection(CommonConnection, ConnectionHandlers):
def join_gc(self, nick, room_jid, password, change_nick=False):
# FIXME: This room JID needs to be normalized; see #1364
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
show = helpers.get_xmpp_show(gajim.SHOW_LIST[self.connected])
if show == 'invisible':
@@ -2041,24 +2070,26 @@ class Connection(CommonConnection, ConnectionHandlers):
t.setTagData('password', password)
self.connection.send(p)
- def send_gc_message(self, jid, msg, xhtml = None):
- if not self.connection:
+ def send_gc_message(self, jid, msg, xhtml = None, label = None):
+ if not gajim.account_is_connected(self.name):
return
if not xhtml and gajim.config.get('rst_formatting_outgoing_messages'):
from common.rst_xhtml_generator import create_xhtml
xhtml = create_xhtml(msg)
msg_iq = common.xmpp.Message(jid, msg, typ = 'groupchat', xhtml = xhtml)
+ if label is not None:
+ msg_iq.addChild(node = label)
self.connection.send(msg_iq)
self.dispatch('MSGSENT', (jid, msg))
def send_gc_subject(self, jid, subject):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
msg_iq = common.xmpp.Message(jid, typ = 'groupchat', subject = subject)
self.connection.send(msg_iq)
def request_gc_config(self, room_jid):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ = 'get', queryNS = common.xmpp.NS_MUC_OWNER,
to = room_jid)
@@ -2066,7 +2097,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq)
def destroy_gc_room(self, room_jid, reason = '', jid = ''):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ = 'set', queryNS = common.xmpp.NS_MUC_OWNER,
to = room_jid)
@@ -2113,7 +2144,7 @@ class Connection(CommonConnection, ConnectionHandlers):
"""
Role is for all the life of the room so it's based on nick
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
common.xmpp.NS_MUC_ADMIN)
@@ -2128,7 +2159,7 @@ class Connection(CommonConnection, ConnectionHandlers):
"""
Affiliation is for all the life of the room so it's based on jid
"""
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
common.xmpp.NS_MUC_ADMIN)
@@ -2140,7 +2171,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq)
def send_gc_affiliation_list(self, room_jid, users_dict):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS = \
common.xmpp.NS_MUC_ADMIN)
@@ -2153,7 +2184,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq)
def get_affiliation_list(self, room_jid, affiliation):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ = 'get', to = room_jid, queryNS = \
common.xmpp.NS_MUC_ADMIN)
@@ -2162,7 +2193,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq)
def send_gc_config(self, room_jid, form):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
iq = common.xmpp.Iq(typ = 'set', to = room_jid, queryNS =\
common.xmpp.NS_MUC_OWNER)
@@ -2172,7 +2203,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(iq)
def change_password(self, password):
- if not self.connection:
+ if not gajim.account_is_connected(self.name):
return
hostname = gajim.config.get_per('accounts', self.name, 'hostname')
username = gajim.config.get_per('accounts', self.name, 'name')
@@ -2182,17 +2213,29 @@ class Connection(CommonConnection, ConnectionHandlers):
q.setTagData('password', password)
self.connection.send(iq)
- def get_password(self, callback):
+ def get_password(self, callback, type_):
+ self.pasword_callback = (callback, type_)
if self.password:
- callback(self.password)
+ self.set_password(self.password)
return
- self.pasword_callback = callback
self.dispatch('PASSWORD_REQUIRED', None)
def set_password(self, password):
self.password = password
if self.pasword_callback:
- self.pasword_callback(password)
+ callback, type_ = self.pasword_callback
+ if self._current_type == 'plain' and type_ == 'PLAIN' and \
+ gajim.config.get_per('accounts', self.name,
+ 'warn_when_insecure_password'):
+ self.dispatch('INSECURE_PASSWORD', None)
+ return
+ callback(password)
+ self.pasword_callback = None
+
+ def accept_insecure_password(self):
+ if self.pasword_callback:
+ callback, type_ = self.pasword_callback
+ callback(self.password)
self.pasword_callback = None
def unregister_account(self, on_remove_success):
diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py
index b0ba56568..6ae4b70b1 100644
--- a/src/common/connection_handlers.py
+++ b/src/common/connection_handlers.py
@@ -6,7 +6,7 @@
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
## Travis Shirk <travis AT pobox.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
## Jean-Marie Traissard <jim AT lapin.org>
@@ -49,7 +49,7 @@ from common.commands import ConnectionCommands
from common.pubsub import ConnectionPubSub
from common.pep import ConnectionPEP
from common.protocol.caps import ConnectionCaps
-from common.protocol.bytestream import ConnectionBytestream
+from common.protocol.bytestream import ConnectionSocks5Bytestream
import common.caps_cache as capscache
from common.nec import NetworkEvent
if gajim.HAVE_FARSIGHT:
@@ -342,6 +342,8 @@ class ConnectionDisco:
if features.__contains__(common.xmpp.NS_GMAILNOTIFY):
gajim.gmail_domains.append(jid)
self.request_gmail_notifications()
+ if features.__contains__(common.xmpp.NS_SECLABEL):
+ self.seclabel_supported = True
for identity in identities:
if identity['category'] == 'pubsub' and identity.get('type') == \
'pep':
@@ -353,6 +355,11 @@ class ConnectionDisco:
self.pubsub_supported = True
if features.__contains__(common.xmpp.NS_PUBSUB_PUBLISH_OPTIONS):
self.pubsub_publish_options_supported = True
+ else:
+ # Remove stored bookmarks accessible to everyone.
+ our_jid = gajim.get_jid_from_account(self.name)
+ self.send_pb_purge(our_jid, 'storage:bookmarks')
+ self.send_pb_delete(our_jid, 'storage:bookmarks')
if features.__contains__(common.xmpp.NS_BYTESTREAM):
our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name) +\
'/' + self.server_resource)
@@ -547,8 +554,7 @@ class ConnectionVcard:
id_ = iq_obj.getID()
gajim.nec.push_incoming_event(NetworkEvent('raw-iq-received',
- conn = con,
- xmpp_iq = iq_obj))
+ conn=con, xmpp_iq=iq_obj))
# Check if we were waiting a timeout for this id
found_tim = None
@@ -641,9 +647,19 @@ class ConnectionVcard:
else:
if iq_obj.getErrorCode() not in ('403', '406', '404'):
self.private_storage_supported = False
+
# We can now continue connection by requesting the roster
- version = gajim.config.get_per('accounts', self.name,
+ version = None
+ if con.Stream.features and con.Stream.features.getTag('ver',
+ namespace=common.xmpp.NS_ROSTER_VER):
+ version = gajim.config.get_per('accounts', self.name,
'roster_version')
+ if version and not gajim.contacts.get_contacts_jid_list(
+ self.name):
+ gajim.config.set_per('accounts', self.name,
+ 'roster_version', '')
+ version = None
+
iq_id = self.connection.initRoster(version=version)
self.awaiting_answers[iq_id] = (ROSTER_ARRIVED, )
elif self.awaiting_answers[id_][0] == ROSTER_ARRIVED:
@@ -669,7 +685,13 @@ class ConnectionVcard:
# Ask metacontacts before roster
self.get_metacontacts()
elif self.awaiting_answers[id_][0] == PEP_CONFIG:
+ if iq_obj.getType() == 'error':
+ return
+ if not iq_obj.getTag('pubsub'):
+ return
conf = iq_obj.getTag('pubsub').getTag('configure')
+ if not conf:
+ return
node = conf.getAttr('node')
form_tag = conf.getTag('x', namespace=common.xmpp.NS_DATA)
if form_tag:
@@ -782,10 +804,42 @@ class ConnectionHandlersBase:
# keep the jids we auto added (transports contacts) to not send the
# SUBSCRIBED event to gui
self.automatically_added = []
+ # IDs of jabber:iq:last requests
+ self.last_ids = []
# keep track of sessions this connection has with other JIDs
self.sessions = {}
+ def _ErrorCB(self, con, iq_obj):
+ log.debug('ErrorCB')
+ jid_from = helpers.get_full_jid_from_iq(iq_obj)
+ jid_stripped, resource = gajim.get_room_and_nick_from_fjid(jid_from)
+ id_ = unicode(iq_obj.getID())
+ if id_ in self.last_ids:
+ self.dispatch('LAST_STATUS_TIME', (jid_stripped, resource, -1, ''))
+ self.last_ids.remove(id_)
+ return
+
+ def _LastResultCB(self, con, iq_obj):
+ log.debug('LastResultCB')
+ qp = iq_obj.getTag('query')
+ seconds = qp.getAttr('seconds')
+ status = qp.getData()
+ try:
+ seconds = int(seconds)
+ except Exception:
+ return
+ id_ = iq_obj.getID()
+ if id_ in self.groupchat_jids:
+ who = self.groupchat_jids[id_]
+ del self.groupchat_jids[id_]
+ else:
+ who = helpers.get_full_jid_from_iq(iq_obj)
+ if id_ in self.last_ids:
+ self.last_ids.remove(id_)
+ jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
+ self.dispatch('LAST_STATUS_TIME', (jid_stripped, resource, seconds, status))
+
def get_sessions(self, jid):
"""
Get all sessions for the given full jid
@@ -915,13 +969,13 @@ class ConnectionHandlersBase:
return sess
-class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
- ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionPEP,
- ConnectionCaps, ConnectionHandlersBase, ConnectionJingle):
+class ConnectionHandlers(ConnectionVcard, ConnectionSocks5Bytestream,
+ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionPEP,
+ConnectionCaps, ConnectionHandlersBase, ConnectionJingle):
def __init__(self):
global HAS_IDLE
ConnectionVcard.__init__(self)
- ConnectionBytestream.__init__(self)
+ ConnectionSocks5Bytestream.__init__(self)
ConnectionCommands.__init__(self)
ConnectionPubSub.__init__(self)
ConnectionPEP.__init__(self, account=self.name, dispatcher=self,
@@ -936,8 +990,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
# keep the latest subscribed event for each jid to prevent loop when we
# acknowledge presences
self.subscribed_events = {}
- # IDs of jabber:iq:last requests
- self.last_ids = []
# IDs of jabber:iq:version requests
self.version_ids = []
# IDs of urn:xmpp:time requests
@@ -960,10 +1012,14 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
if not self.connection or self.connected < 2:
return
if answer == 'yes':
- self.connection.send(iq_obj.buildReply('result'))
+ confirm = iq_obj.getTag('confirm')
+ reply = iq_obj.buildReply('result')
+ if iq_obj.getName() == 'message':
+ reply.addChild(node=confirm)
+ self.connection.send(reply)
elif answer == 'no':
err = common.xmpp.Error(iq_obj,
- common.xmpp.protocol.ERR_NOT_AUTHORIZED)
+ common.xmpp.protocol.ERR_NOT_AUTHORIZED)
self.connection.send(err)
def _HttpAuthCB(self, con, iq_obj):
@@ -981,6 +1037,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
def _ErrorCB(self, con, iq_obj):
log.debug('ErrorCB')
+ ConnectionHandlersBase._ErrorCB(self, con, iq_obj)
jid_from = helpers.get_full_jid_from_iq(iq_obj)
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(jid_from)
id_ = unicode(iq_obj.getID())
@@ -988,10 +1045,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
self.dispatch('OS_INFO', (jid_stripped, resource, '', ''))
self.version_ids.remove(id_)
return
- if id_ in self.last_ids:
- self.dispatch('LAST_STATUS_TIME', (jid_stripped, resource, -1, ''))
- self.last_ids.remove(id_)
- return
if id_ in self.entity_time_ids:
self.dispatch('ENTITY_TIME', (jid_stripped, resource, ''))
self.entity_time_ids.remove(id_)
@@ -1029,6 +1082,33 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
annotation = note.getData()
self.annotations[jid] = annotation
+ def _SecLabelCB(self, con, iq_obj):
+ """
+ Security Label callback, used for catalogues.
+ """
+ log.debug('SecLabelCB')
+ query = iq_obj.getTag('catalog')
+ to = query.getAttr('to')
+ items = query.getTags('securitylabel')
+ labels = {}
+ ll = []
+ for item in items:
+ label = item.getTag('displaymarking').getData()
+ labels[label] = item
+ ll.append(label)
+ if to not in self.seclabel_catalogues:
+ self.seclabel_catalogues[to] = [[], None, None]
+ self.seclabel_catalogues[to][1] = labels
+ self.seclabel_catalogues[to][2] = ll
+ for callback in self.seclabel_catalogues[to][0]:
+ callback()
+ self.seclabel_catalogues[to][0] = []
+
+ def seclabel_catalogue_request(self, to, callback):
+ if to not in self.seclabel_catalogues:
+ self.seclabel_catalogues[to] = [[], None, None]
+ self.seclabel_catalogues[to][0].append(callback)
+
def _parse_bookmarks(self, storage, storage_type):
"""
storage_type can be 'pubsub' or 'xml' to tell from where we got bookmarks
@@ -1132,26 +1212,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
self.connection.send(iq_obj)
raise common.xmpp.NodeProcessed
- def _LastResultCB(self, con, iq_obj):
- log.debug('LastResultCB')
- qp = iq_obj.getTag('query')
- seconds = qp.getAttr('seconds')
- status = qp.getData()
- try:
- seconds = int(seconds)
- except Exception:
- return
- id_ = iq_obj.getID()
- if id_ in self.groupchat_jids:
- who = self.groupchat_jids[id_]
- del self.groupchat_jids[id_]
- else:
- who = helpers.get_full_jid_from_iq(iq_obj)
- if id_ in self.last_ids:
- self.last_ids.remove(id_)
- jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
- self.dispatch('LAST_STATUS_TIME', (jid_stripped, resource, seconds, status))
-
def _VersionResultCB(self, con, iq_obj):
log.debug('VersionResultCB')
client_info = ''
@@ -1373,13 +1433,10 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
log.debug('MessageCB')
gajim.nec.push_incoming_event(NetworkEvent('raw-message-received',
- conn = con,
- xmpp_msg = msg,
- account = self.name))
+ conn=con, xmpp_msg=msg, account=self.name))
mtype = msg.getType()
-
# check if the message is a roster item exchange (XEP-0144)
if msg.getTag('x', namespace=common.xmpp.NS_ROSTERX):
self._rosterItemExchangeCB(con, msg)
@@ -1440,7 +1497,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
thread_id = msg.getThread()
- if not mtype:
+ if not mtype or mtype not in ('chat', 'groupchat', 'error'):
mtype = 'normal'
msgtxt = msg.getBody()
@@ -1449,6 +1506,14 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
xep_200_encrypted = msg.getTag('c', namespace=common.xmpp.NS_STANZA_CRYPTO)
session = None
+ gc_control = gajim.interface.msg_win_mgr.get_gc_control(jid, self.name)
+ if not gc_control and \
+ jid in gajim.interface.minimized_controls[self.name]:
+ gc_control = gajim.interface.minimized_controls[self.name][jid]
+
+ if gc_control and jid == frm: # message from a gc without a resource
+ mtype = 'groupchat'
+
if mtype != 'groupchat':
session = self.get_or_create_session(frm, thread_id)
@@ -1573,16 +1638,65 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
gajim.logger.write('error', frm, error_msg, tim=tim,
subject=subject)
except exceptions.PysqliteOperationalError, e:
- self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
+ self.dispatch('DB_ERROR', (_('Disk Write Error'), str(e)))
except exceptions.DatabaseMalformed:
pritext = _('Database Error')
sectext = _('The database file (%s) cannot be read. Try to repair '
'it (see http://trac.gajim.org/wiki/DatabaseBackup) or remove '
'it (all history will be lost).') % common.logger.LOG_DB_PATH
- self.dispatch('ERROR', (pritext, sectext))
+ self.dispatch('DB_ERROR', (pritext, sectext))
self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt,
tim, session))
+ def _on_bob_received(self, conn, result, cid):
+ """
+ Called when we receive BoB data
+ """
+ if cid not in self.awaiting_cids:
+ return
+
+ if result.getType() == 'result':
+ data = msg.getTags('data', namespace=common.xmpp.NS_BOB)
+ if data.getAttr('cid') == cid:
+ for func in self.awaiting_cids[cid]:
+ cb = func[0]
+ args = func[1]
+ pos = func[2]
+ bob_data = data.getData()
+ def recurs(node, cid, data):
+ if node.getData() == 'cid:' + cid:
+ node.setData(data)
+ else:
+ for child in node.getChildren():
+ recurs(child, cid, data)
+ recurs(args[pos], cid, bob_data)
+ cb(*args)
+ del self.awaiting_cids[cid]
+ return
+
+ # An error occured, call callback without modifying data.
+ for func in self.awaiting_cids[cid]:
+ cb = func[0]
+ args = func[1]
+ cb(*args)
+ del self.awaiting_cids[cid]
+
+ def get_bob_data(self, cid, to, callback, args, position):
+ """
+ Request for BoB (XEP-0231) and when data will arrive, call callback
+ with given args, after having replaced cid by it's data in
+ args[position]
+ """
+ if cid in self.awaiting_cids:
+ self.awaiting_cids[cid].appends((callback, args, position))
+ else:
+ self.awaiting_cids[cid] = [(callback, args, position)]
+ iq = common.xmpp.Iq(to=to, typ='get')
+ data = iq.addChild(name='data', attrs={'cid': cid},
+ namespace=common.xmpp.NS_BOB)
+ self.connection.SendAndCallForResponse(iq, self._on_bob_received,
+ {'cid': cid})
+
# process and dispatch a groupchat message
def dispatch_gc_message(self, msg, frm, msgtxt, jid, tim):
has_timestamp = bool(msg.timestamp)
@@ -1603,12 +1717,35 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode))
return
- # Ignore message from room in which we are not
+ displaymarking = None
+ seclabel = msg.getTag('securitylabel')
+ if seclabel and seclabel.getNamespace() == common.xmpp.NS_SECLABEL:
+ displaymarking = seclabel.getTag('displaymarking') # Ignore message from room in which we are not
if jid not in self.last_history_time:
return
- self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msg.getXHTML(),
- statusCode))
+ captcha = msg.getTag('captcha', namespace=common.xmpp.NS_CAPTCHA)
+ if captcha:
+ captcha = captcha.getTag('x', namespace=common.xmpp.NS_DATA)
+ for field in captcha.getTags('field'):
+ for media in field.getTags('media'):
+ for uri in media.getTags('uri'):
+ uri_data = uri.getData()
+ if uri_data.startswith('cid:'):
+ uri_data = uri_data[4:]
+ found = False
+ for data in msg.getTags('data',
+ namespace=common.xmpp.NS_BOB):
+ if data.getAttr('cid') == uri_data:
+ uri.setData(data.getData())
+ found = True
+ if not found:
+ self.get_bob_data(uri_data, frm,
+ self.dispatch_gc_message, [msg, frm, msgtxt,
+ jid, tim], 0)
+ return
+ self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp,
+ msg.getXHTML(), statusCode, displaymarking, captcha))
tim_int = int(float(mktime(tim)))
if gajim.config.should_log(self.name, jid) and not \
@@ -1624,13 +1761,13 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
self.last_history_time[jid] = mktime(tim)
except exceptions.PysqliteOperationalError, e:
- self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
+ self.dispatch('DB_ERROR', (_('Disk Write Error'), str(e)))
except exceptions.DatabaseMalformed:
pritext = _('Database Error')
sectext = _('The database file (%s) cannot be read. Try to repair '
'it (see http://trac.gajim.org/wiki/DatabaseBackup) or remove '
'it (all history will be lost).') % common.logger.LOG_DB_PATH
- self.dispatch('ERROR', (pritext, sectext))
+ self.dispatch('DB_ERROR', (pritext, sectext))
def dispatch_invite_message(self, invite, frm):
item = invite.getTag('invite')
@@ -1654,7 +1791,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
Called when we receive a presence
"""
gajim.nec.push_incoming_event(NetworkEvent('raw-pres-received',
- conn=con, xmpp_pres=prs))
+ conn=con, xmpp_pres=prs))
ptype = prs.getType()
if ptype == 'available':
ptype = None
@@ -1769,34 +1906,41 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource,
prio, keyID, timestamp, None))
elif (errcode == '503'):
- # maximum user number reached
- self.dispatch('ERROR', (_('Unable to join group chat'),
- _('Maximum number of users for %s has been reached') % \
- room_jid))
+ if gc_control is None or gc_control.autorejoin is None:
+ # maximum user number reached
+ self.dispatch('GC_ERROR', (gc_control,
+ _('Unable to join group chat'),
+ _('Maximum number of users for %s has been '
+ 'reached') % room_jid))
elif (errcode == '401') or (errcon == 'not-authorized'):
# password required to join
self.dispatch('GC_PASSWORD_REQUIRED', (room_jid, nick))
elif (errcode == '403') or (errcon == 'forbidden'):
# we are banned
- self.dispatch('ERROR', (_('Unable to join group chat'),
- _('You are banned from group chat %s.') % room_jid))
+ self.dispatch('GC_ERROR', (gc_control,
+ _('Unable to join group chat'),
+ _('You are banned from group chat %s.') % room_jid))
elif (errcode == '404') or (errcon in ('item-not-found',
'remote-server-not-found')):
if gc_control is None or gc_control.autorejoin is None:
# group chat does not exist
- self.dispatch('ERROR', (_('Unable to join group chat'),
- _('Group chat %s does not exist.') % room_jid))
+ self.dispatch('GC_ERROR', (gc_control,
+ _('Unable to join group chat'),
+ _('Group chat %s does not exist.') % room_jid))
elif (errcode == '405') or (errcon == 'not-allowed'):
- self.dispatch('ERROR', (_('Unable to join group chat'),
- _('Group chat creation is restricted.')))
+ self.dispatch('GC_ERROR', (gc_control,
+ _('Unable to join group chat'),
+ _('Group chat creation is restricted.')))
elif (errcode == '406') or (errcon == 'not-acceptable'):
- self.dispatch('ERROR', (_('Unable to join group chat'),
- _('Your registered nickname must be used in group chat %s.') \
- % room_jid))
+ self.dispatch('GC_ERROR', (gc_control,
+ _('Unable to join group chat'),
+ _('Your registered nickname must be used in group chat '
+ '%s.') % room_jid))
elif (errcode == '407') or (errcon == 'registration-required'):
- self.dispatch('ERROR', (_('Unable to join group chat'),
- _('You are not in the members list in groupchat %s.') % \
- room_jid))
+ self.dispatch('GC_ERROR', (gc_control,
+ _('Unable to join group chat'),
+ _('You are not in the members list in groupchat %s.') %\
+ room_jid))
elif (errcode == '409') or (errcon == 'conflict'):
# nick conflict
room_jid = gajim.get_room_from_fjid(who)
@@ -1820,14 +1964,15 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
try:
gajim.logger.write('gcstatus', who, st, show)
except exceptions.PysqliteOperationalError, e:
- self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
+ self.dispatch('DB_ERROR', (_('Disk Write Error'),
+ str(e)))
except exceptions.DatabaseMalformed:
pritext = _('Database Error')
sectext = _('The database file (%s) cannot be read. Try to '
'repair it (see http://trac.gajim.org/wiki/DatabaseBackup)'
' or remove it (all history will be lost).') % \
common.logger.LOG_DB_PATH
- self.dispatch('ERROR', (pritext, sectext))
+ self.dispatch('DB_ERROR', (pritext, sectext))
if avatar_sha or avatar_sha == '':
if avatar_sha == '':
# contact has no avatar
@@ -1970,14 +2115,14 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
try:
gajim.logger.write('status', jid_stripped, status, show)
except exceptions.PysqliteOperationalError, e:
- self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
+ self.dispatch('DB_ERROR', (_('Disk Write Error'), str(e)))
except exceptions.DatabaseMalformed:
pritext = _('Database Error')
sectext = _('The database file (%s) cannot be read. Try to '
'repair it (see http://trac.gajim.org/wiki/DatabaseBackup) '
'or remove it (all history will be lost).') % \
common.logger.LOG_DB_PATH
- self.dispatch('ERROR', (pritext, sectext))
+ self.dispatch('DB_ERROR', (pritext, sectext))
our_jid = gajim.get_jid_from_account(self.name)
if jid_stripped == our_jid and resource == self.server_resource:
# We got our own presence
@@ -2281,6 +2426,8 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream,
common.xmpp.NS_MUC_ADMIN)
con.RegisterHandler('iq', self._PrivateCB, 'result',
common.xmpp.NS_PRIVATE)
+ con.RegisterHandler('iq', self._SecLabelCB, 'result',
+ common.xmpp.NS_SECLABEL_CATALOG)
con.RegisterHandler('iq', self._HttpAuthCB, 'get',
common.xmpp.NS_HTTP_AUTH)
con.RegisterHandler('iq', self._CommandExecuteCB, 'set',
diff --git a/src/common/contacts.py b/src/common/contacts.py
index edd9c22e3..e115ef46d 100644
--- a/src/common/contacts.py
+++ b/src/common/contacts.py
@@ -4,7 +4,7 @@
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Travis Shirk <travis AT pobox.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
## Tomasz Melcer <liori AT exroot.org>
@@ -496,11 +496,8 @@ class Contacts():
return self._contacts.keys()
def get_contacts_jid_list(self):
- contacts = self._contacts.keys()
- for jid in self._contacts.keys():
- if self._contacts[jid][0].is_groupchat():
- contacts.remove(jid)
- return contacts
+ return [jid for jid, contact in self._contacts.iteritems() if not
+ contact[0].is_groupchat()]
def get_contact_from_full_jid(self, fjid):
"""
diff --git a/src/common/dataforms.py b/src/common/dataforms.py
index 4620556c4..fea7187ea 100644
--- a/src/common/dataforms.py
+++ b/src/common/dataforms.py
@@ -3,7 +3,7 @@
## src/common/dataforms.py
##
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
##
## This file is part of Gajim.
@@ -227,6 +227,86 @@ class DataField(ExtendedNode):
return locals()
+ @nested_property
+ def media():
+ """
+ Media data
+ """
+ def fget(self):
+ media = self.getTag('media', namespace=xmpp.NS_DATA_MEDIA)
+ if media:
+ return Media(media)
+
+ def fset(self, value):
+ fdel(self)
+ self.addChild(node=value)
+
+ def fdel(self):
+ t = self.getTag('media')
+ if t is not None:
+ self.delChild(t)
+
+ return locals()
+
+class Uri(xmpp.Node):
+ def __init__(self, uri_tag):
+ xmpp.Node.__init__(self, node=uri_tag)
+
+ @nested_property
+ def type_():
+ """
+ uri type
+ """
+ def fget(self):
+ return self.getAttr('type')
+
+ def fset(self, value):
+ self.setAttr('type', value)
+
+ def fdel(self):
+ self.delAttr('type')
+
+ return locals()
+
+ @nested_property
+ def uri_data():
+ """
+ uri data
+ """
+ def fget(self):
+ return self.getData()
+
+ def fset(self, value):
+ self.setData(value)
+
+ def fdel(self):
+ self.setData(None)
+
+ return locals()
+
+class Media(xmpp.Node):
+ def __init__(self, media_tag):
+ xmpp.Node.__init__(self, node=media_tag)
+
+ @nested_property
+ def uris():
+ """
+ URIs of the media element.
+ """
+ def fget(self):
+ return map(Uri, self.getTags('uri'))
+
+ def fset(self, value):
+ fdel(self)
+ for uri in values:
+ self.addChild(node=uri)
+
+ def fdel(self, value):
+ for element in self.getTags('uri'):
+ self.delChild(element)
+
+ return locals()
+
class BooleanField(DataField):
@nested_property
def value():
@@ -543,12 +623,13 @@ class SimpleDataForm(DataForm, DataRecord):
if f.required:
# Keep all required fields
continue
- if (hasattr(f, 'value') and not f.value) or (hasattr(f, 'values') and \
- len(f.values) == 0):
+ if (hasattr(f, 'value') and not f.value) or (hasattr(f, 'values') \
+ and len(f.values) == 0):
to_be_removed.append(f)
else:
del f.label
del f.description
+ del f.media
for f in to_be_removed:
c.delChild(f)
return c
diff --git a/src/common/dbus_support.py b/src/common/dbus_support.py
index d1e8fe381..389c16f18 100644
--- a/src/common/dbus_support.py
+++ b/src/common/dbus_support.py
@@ -4,7 +4,7 @@
## Copyright (C) 2005 Andrew Sayman <lorien420 AT myrealbox.com>
## Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
## Stefan Bethge <stefan AT lanpartei.de>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
@@ -42,6 +42,7 @@ except ImportError:
else:
try:
# test if dbus-x11 is installed
+ bus = dbus.SystemBus()
bus = dbus.SessionBus()
supported = True # does user have D-Bus bindings?
except dbus.DBusException:
@@ -49,6 +50,12 @@ else:
if not os.name == 'nt': # only say that to non Windows users
print _('D-Bus does not run correctly on this machine')
print _('D-Bus capabilities of Gajim cannot be used')
+ except exceptions.SystemBusNotPresent:
+ print _('D-Bus does not run correctly on this machine: system bus not '
+ 'present')
+ except exceptions.SessionBusNotPresent:
+ print _('D-Bus does not run correctly on this machine: session bus not '
+ 'present')
class SystemBus:
"""
diff --git a/src/common/defs.py b/src/common/defs.py
index 348010bff..5eb2a8084 100644
--- a/src/common/defs.py
+++ b/src/common/defs.py
@@ -2,8 +2,8 @@
## src/common/defs.py
##
## Copyright (C) 2006 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
-## Jean-Marie Traissard <jim AT lapin.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
## Tomasz Melcer <liori AT exroot.org>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
diff --git a/src/common/dh.py b/src/common/dh.py
index 29f59a083..827d87806 100644
--- a/src/common/dh.py
+++ b/src/common/dh.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/common/dh.py
##
-## Copyright (C) 2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
##
## This file is part of Gajim.
diff --git a/src/common/events.py b/src/common/events.py
index 9e8ae059f..f51d64fb4 100644
--- a/src/common/events.py
+++ b/src/common/events.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 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>
diff --git a/src/common/exceptions.py b/src/common/exceptions.py
index 6e1df2e36..122262418 100644
--- a/src/common/exceptions.py
+++ b/src/common/exceptions.py
@@ -2,7 +2,7 @@
## src/common/exceptions.py
##
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Brendan Taylor <whateley AT gmail.com>
##
diff --git a/src/common/fuzzyclock.py b/src/common/fuzzyclock.py
index f84d63c3d..e997ec6bf 100755
--- a/src/common/fuzzyclock.py
+++ b/src/common/fuzzyclock.py
@@ -2,7 +2,7 @@
## src/common/fuzzyclock.py
##
## Copyright (C) 2006 Christoph Neuroth <delmonico AT gmx.net>
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2009 Benjamin Richter <br AT waldteufel-online.net>
##
diff --git a/src/common/gajim.py b/src/common/gajim.py
index c8646aec3..cfd4419af 100644
--- a/src/common/gajim.py
+++ b/src/common/gajim.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/common/gajim.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Travis Shirk <travis AT pobox.com>
## Nikos Kouremenos <kourem AT gmail.com>
@@ -91,7 +91,7 @@ DATA_DIR = gajimpaths['DATA']
ICONS_DIR = gajimpaths['ICONS']
HOME_DIR = gajimpaths['HOME']
PLUGINS_DIRS = [gajimpaths['PLUGINS_BASE'],
- gajimpaths['PLUGINS_USER']]
+ gajimpaths['PLUGINS_USER']]
PLUGINS_CONFIG_DIR = gajimpaths['PLUGINS_CONFIG_DIR']
try:
@@ -186,12 +186,6 @@ else:
# read.
HAVE_LATEX = False
-HAVE_INDICATOR = True
-try:
- import indicate
-except ImportError:
- HAVE_INDICATOR = False
-
HAVE_FARSIGHT = True
try:
import farsight, gst
@@ -204,7 +198,7 @@ gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI, xmpp.NS_FILE,
'jabber:iq:gateway', xmpp.NS_LAST, xmpp.NS_PRIVACY, xmpp.NS_PRIVATE,
xmpp.NS_REGISTER, xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED, 'msglog',
'sslc2s', 'stringprep', xmpp.NS_PING, xmpp.NS_TIME_REVISED, xmpp.NS_SSN,
- xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK, xmpp.NS_ROSTERX]
+ xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK, xmpp.NS_ROSTERX, xmpp.NS_SECLABEL]
# Optional features gajim supports per account
gajim_optional_features = {}
diff --git a/src/common/helpers.py b/src/common/helpers.py
index 51b6f49ad..367520d75 100644
--- a/src/common/helpers.py
+++ b/src/common/helpers.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/common/helpers.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 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>
@@ -29,6 +29,7 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
+import sys
import re
import locale
import os
@@ -220,9 +221,9 @@ def get_uf_show(show, use_mnemonic = False):
uf_show = _('Free for Chat')
elif show == 'online':
if use_mnemonic:
- uf_show = _('_Available')
+ uf_show = Q_('?user status:_Available')
else:
- uf_show = _('Available')
+ uf_show = Q_('?user status:Available')
elif show == 'connecting':
uf_show = _('Connecting')
elif show == 'away':
@@ -1341,11 +1342,11 @@ def get_subscription_request_msg(account=None):
our_jid = gajim.get_jid_from_account(account)
vcard = gajim.connections[account].get_cached_vcard(our_jid)
name = ''
- if 'N' in vcard:
- if 'GIVEN' in vcard['N'] and 'FAMILY' in vcard['N']:
- name = vcard['N']['GIVEN'] + ' ' + vcard['N']['FAMILY']
- if not name:
- if 'FN' in vcard:
+ if vcard:
+ if 'N' in vcard:
+ if 'GIVEN' in vcard['N'] and 'FAMILY' in vcard['N']:
+ name = vcard['N']['GIVEN'] + ' ' + vcard['N']['FAMILY']
+ if not name and 'FN' in vcard:
name = vcard['FN']
nick = gajim.nicks[account]
if name and nick:
@@ -1353,4 +1354,4 @@ def get_subscription_request_msg(account=None):
elif nick:
name = nick
s = Template(s).safe_substitute({'name': name})
- return s
+ return s
diff --git a/src/common/i18n.py b/src/common/i18n.py
index 4497a56f0..3ff764c99 100644
--- a/src/common/i18n.py
+++ b/src/common/i18n.py
@@ -1,9 +1,8 @@
# -*- coding:utf-8 -*-
## src/common/i18n.py
##
-## Copyright (C) 2003-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2004 Vincent Hanquez <tab AT snarc.org>
-## Copyright (C) 2004-2007 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2009 Benjamin Richter <br AT waldteufel-online.net>
##
@@ -68,20 +67,25 @@ if gettext._translations:
else:
_translation = gettext.NullTranslations()
-def Q_(s):
- # Qualified translatable strings
- # Some strings are too ambiguous to be easily translated.
- # so we must use as:
- # s = Q_('?vcard:Unknown')
- # widget.set_text(s)
- # Q_() removes the ?vcard:
- # but gettext while parsing the file detects ?vcard:Unknown as a whole string.
- # translator can either put the ?vcard: part or no (easier for him or her to no)
- # nothing fails
- s = _(s)
- if s[0] == '?':
- s = s[s.find(':')+1:] # remove ?abc: part
- return s
+def Q_(text):
+ """
+ Translate the given text, optionally qualified with a special
+ construction, which will help translators to disambiguate between
+ same terms, but in different contexts.
+
+ When translated text is returned - this rudimentary construction
+ will be stripped off, if it's present.
+
+ Here is the construction to use:
+ Q_("?vcard:Unknown")
+
+ Everything between ? and : - is the qualifier to convey the context
+ to the translators. Everything after : - is the text itself.
+ """
+ text = _(text)
+ if text.startswith('?'):
+ qualifier, text = text.split(':', 1)
+ return text
def ngettext(s_sing, s_plural, n, replace_sing = None, replace_plural = None):
"""
diff --git a/src/common/jingle.py b/src/common/jingle.py
index 8f87504c8..da2a5d031 100644
--- a/src/common/jingle.py
+++ b/src/common/jingle.py
@@ -33,7 +33,7 @@ Handles the jingle signalling protocol
import xmpp
import helpers
-from jingle_session import JingleSession
+from jingle_session import JingleSession, JingleStates
from jingle_rtp import JingleAudio, JingleVideo
@@ -43,32 +43,23 @@ class ConnectionJingle(object):
"""
def __init__(self):
- # dictionary: (jid, sessionid) => JingleSession object
+ # dictionary: sessionid => JingleSession object
self.__sessions = {}
# dictionary: (jid, iq stanza id) => JingleSession object,
# one time callbacks
self.__iq_responses = {}
- def add_jingle(self, jingle):
- """
- Add a jingle session to a jingle stanza dispatcher
-
- jingle - a JingleSession object.
- """
- self.__sessions[(jingle.peerjid, jingle.sid)] = jingle
-
- def delete_jingle_session(self, peerjid, sid):
+ def delete_jingle_session(self, sid):
"""
Remove a jingle session from a jingle stanza dispatcher
"""
- key = (peerjid, sid)
- if key in self.__sessions:
+ if sid in self.__sessions:
#FIXME: Move this elsewhere?
- for content in self.__sessions[key].contents.values():
+ for content in self.__sessions[sid].contents.values():
content.destroy()
- self.__sessions[key].callbacks = []
- del self.__sessions[key]
+ self.__sessions[sid].callbacks = []
+ del self.__sessions[sid]
def _JingleCB(self, con, stanza):
"""
@@ -94,13 +85,16 @@ class ConnectionJingle(object):
sid = jingle.getAttr('sid')
# do we need to create a new jingle object
- if (jid, sid) not in self.__sessions:
+ if sid not in self.__sessions:
#TODO: tie-breaking and other things...
newjingle = JingleSession(con=self, weinitiate=False, jid=jid, sid=sid)
- self.add_jingle(newjingle)
+ self.__sessions[sid] = newjingle
# we already have such session in dispatcher...
- self.__sessions[(jid, sid)].on_stanza(stanza)
+ self.__sessions[sid].on_stanza(stanza)
+ # Delete invalid/unneeded sessions
+ if sid in self.__sessions and self.__sessions[sid].state == JingleStates.ended:
+ self.delete_jingle_session(sid)
raise xmpp.NodeProcessed
@@ -112,7 +106,7 @@ class ConnectionJingle(object):
jingle.add_content('voice', JingleAudio(jingle))
else:
jingle = JingleSession(self, weinitiate=True, jid=jid)
- self.add_jingle(jingle)
+ self.__sessions[jingle.sid] = jingle
jingle.add_content('voice', JingleAudio(jingle))
jingle.start_session()
return jingle.sid
@@ -125,15 +119,29 @@ class ConnectionJingle(object):
jingle.add_content('video', JingleVideo(jingle))
else:
jingle = JingleSession(self, weinitiate=True, jid=jid)
- self.add_jingle(jingle)
+ self.__sessions[jingle.sid] = jingle
jingle.add_content('video', JingleVideo(jingle))
jingle.start_session()
return jingle.sid
+
+ def iter_jingle_sessions(self, jid, sid=None, media=None):
+ if sid:
+ return (session for session in self.__sessions.values() if session.sid == sid)
+ sessions = (session for session in self.__sessions.values() if session.peerjid == jid)
+ if media:
+ if media not in ('audio', 'video'):
+ return tuple()
+ else:
+ return (session for session in sessions if session.get_content(media))
+ else:
+ return sessions
+
+
def get_jingle_session(self, jid, sid=None, media=None):
if sid:
- if (jid, sid) in self.__sessions:
- return self.__sessions[(jid, sid)]
+ if sid in self.__sessions:
+ return self.__sessions[sid]
else:
return None
elif media:
diff --git a/src/common/jingle_content.py b/src/common/jingle_content.py
index f4022cc8e..af94561c2 100644
--- a/src/common/jingle_content.py
+++ b/src/common/jingle_content.py
@@ -127,6 +127,11 @@ class JingleContent(object):
content.addChild(node=self.transport.make_transport([candidate]))
self.session.send_transport_info(content)
+ def send_description_info(self):
+ content = self.__content()
+ self._fill_content(content)
+ self.session.send_description_info(content)
+
def __fill_jingle_stanza(self, stanza, content, error, action):
"""
Add our things to session-initiate stanza
diff --git a/src/common/jingle_rtp.py b/src/common/jingle_rtp.py
index 32d9398a9..536fda8d6 100644
--- a/src/common/jingle_rtp.py
+++ b/src/common/jingle_rtp.py
@@ -15,6 +15,8 @@
Handles Jingle RTP sessions (XEP 0167)
"""
+from collections import deque
+
import gobject
import socket
@@ -28,6 +30,10 @@ from jingle_transport import JingleTransportICEUDP
from jingle_content import contents, JingleContent, JingleContentSetupException
+import logging
+log = logging.getLogger('gajim.c.jingle_rtp')
+
+
class JingleRTPContent(JingleContent):
def __init__(self, session, media, transport=None):
if transport is None:
@@ -38,6 +44,10 @@ class JingleRTPContent(JingleContent):
self.farsight_media = {'audio': farsight.MEDIA_TYPE_AUDIO,
'video': farsight.MEDIA_TYPE_VIDEO}[media]
+ self.pipeline = None
+ self.src_bin = None
+ self.stream_failed_once = False
+
self.candidates_ready = False # True when local candidates are prepared
self.callbacks['session-initiate'] += [self.__on_remote_codecs]
@@ -89,8 +99,7 @@ class JingleRTPContent(JingleContent):
farsight.DIRECTION_RECV, 'nice', params)
def is_ready(self):
- return (JingleContent.is_ready(self) and self.candidates_ready
- and self.p2psession.get_property('codecs-ready'))
+ return (JingleContent.is_ready(self) and self.candidates_ready)
def make_bin_from_config(self, config_key, pipeline, text):
pipeline = pipeline % gajim.config.get(config_key)
@@ -117,15 +126,16 @@ class JingleRTPContent(JingleContent):
Send several DTMF tones
"""
if self._dtmf_running:
- raise Exception # TODO: Proper exception
+ raise Exception("There is a DTMF batch already running")
+ events = deque(events)
self._dtmf_running = True
- self._start_dtmf(events.pop(0))
+ self._start_dtmf(events.popleft())
gobject.timeout_add(500, self._next_dtmf, events)
def _next_dtmf(self, events):
self._stop_dtmf()
if events:
- self._start_dtmf(events.pop(0))
+ self._start_dtmf(events.popleft())
gobject.timeout_add(500, self._next_dtmf, events)
else:
self._dtmf_running = False
@@ -161,14 +171,14 @@ class JingleRTPContent(JingleContent):
def _on_gst_message(self, bus, message):
if message.type == gst.MESSAGE_ELEMENT:
name = message.structure.get_name()
+ log.debug('gst element message: %s: %s' % (name, message))
if name == 'farsight-new-active-candidate-pair':
pass
elif name == 'farsight-recv-codecs-changed':
pass
elif name == 'farsight-codecs-changed':
- if self.is_ready():
- self.session.on_session_state_changed(self)
- # TODO: description-info
+ if self.sent and self.p2psession.get_property('codecs-ready'):
+ self.send_description_info()
elif name == 'farsight-local-candidates-prepared':
self.candidates_ready = True
if self.is_ready():
@@ -176,22 +186,53 @@ class JingleRTPContent(JingleContent):
elif name == 'farsight-new-local-candidate':
candidate = message.structure['candidate']
self.transport.candidates.append(candidate)
- if self.candidates_ready:
+ if self.sent:
# FIXME: Is this case even possible?
self.send_candidate(candidate)
elif name == 'farsight-component-state-changed':
state = message.structure['state']
- print message.structure['component'], state
if state == farsight.STREAM_STATE_FAILED:
reason = xmpp.Node('reason')
reason.setTag('failed-transport')
- self.session._session_terminate(reason)
+ self.session.remove_content(self.creator, self.name, reason)
elif name == 'farsight-error':
- print 'Farsight error #%d!' % message.structure['error-no']
- print 'Message: %s' % message.structure['error-msg']
- print 'Debug: %s' % message.structure['debug-msg']
+ log.error('Farsight error #%d!\nMessage: %s\nDebug: %s' % (
+ message.structure['error-no'],
+ message.structure['error-msg'],
+ message.structure['debug-msg']))
+ elif message.type == gst.MESSAGE_ERROR:
+ # TODO: Fix it to fallback to videotestsrc anytime an error occur,
+ # or raise an error, Jingle way
+ # or maybe one-sided stream?
+ if not self.stream_failed_once:
+ self.session.connection.dispatch('ERROR',
+ (_("GStreamer error"),
+ _("Error: %s\nDebug: %s" % (message.structure['gerror'],
+ message.structure['debug']))))
+
+ sink_pad = self.p2psession.get_property('sink-pad')
+
+ # Remove old source
+ self.src_bin.get_pad('src').unlink(sink_pad)
+ self.src_bin.set_state(gst.STATE_NULL)
+ self.pipeline.remove(self.src_bin)
+
+ if not self.stream_failed_once:
+ # Add fallback source
+ self.src_bin = self.get_fallback_src()
+ self.pipeline.add(self.src_bin)
+ self.src_bin.get_pad('src').link(sink_pad)
+ self.stream_failed_once = True
else:
- print name
+ reason = xmpp.Node('reason')
+ reason.setTag('failed-application')
+ self.session.remove_content(self.creator, self.name, reason)
+
+ # Start playing again
+ self.pipeline.set_state(gst.STATE_PLAYING)
+
+ def get_fallback_src(self):
+ return gst.element_factory_make('fakesrc')
def __on_content_accept(self, stanza, content, error, action):
if self.accepted:
@@ -291,19 +332,19 @@ class JingleAudio(JingleRTPContent):
# the local parts
# TODO: Add queues?
- src_bin = self.make_bin_from_config('audio_input_device',
+ self.src_bin = self.make_bin_from_config('audio_input_device',
'%s ! audioconvert', _("audio input"))
self.sink = self.make_bin_from_config('audio_output_device',
'audioconvert ! volume name=gajim_out_vol ! %s', _("audio output"))
- self.mic_volume = src_bin.get_by_name('gajim_vol')
+ self.mic_volume = self.src_bin.get_by_name('gajim_vol')
self.out_volume = self.sink.get_by_name('gajim_out_vol')
# link gst elements
- self.pipeline.add(self.sink, src_bin)
+ self.pipeline.add(self.sink, self.src_bin)
- src_bin.get_pad('src').link(self.p2psession.get_property(
+ self.src_bin.get_pad('src').link(self.p2psession.get_property(
'sink-pad'))
self.p2pstream.connect('src-pad-added', self._on_src_pad_added)
@@ -323,24 +364,42 @@ class JingleVideo(JingleRTPContent):
JingleRTPContent.setup_stream(self)
# the local parts
- src_bin = self.make_bin_from_config('video_input_device',
- '%s ! videoscale ! ffmpegcolorspace', _("video input"))
+ if gajim.config.get('video_framerate'):
+ framerate = 'videorate ! video/x-raw-yuv,framerate=%s ! ' % \
+ gajim.config.get('video_framerate')
+ else:
+ framerate = ''
+ try:
+ w, h = gajim.config.get('video_size').split('x')
+ except:
+ w = h = None
+ if w and h:
+ video_size = 'video/x-raw-yuv,width=%s,height=%s ! ' % (w, h)
+ else:
+ video_size = ''
+ self.src_bin = self.make_bin_from_config('video_input_device',
+ '%%s ! %svideoscale ! %sffmpegcolorspace' % (framerate, video_size),
+ _("video input"))
#caps = gst.element_factory_make('capsfilter')
#caps.set_property('caps', gst.caps_from_string('video/x-raw-yuv, width=320, height=240'))
- self.pipeline.add(src_bin)#, caps)
+ self.pipeline.add(self.src_bin)#, caps)
#src_bin.link(caps)
self.sink = self.make_bin_from_config('video_output_device',
'videoscale ! ffmpegcolorspace ! %s', _("video output"))
self.pipeline.add(self.sink)
- src_bin.get_pad('src').link(self.p2psession.get_property('sink-pad'))
+ self.src_bin.get_pad('src').link(self.p2psession.get_property('sink-pad'))
self.p2pstream.connect('src-pad-added', self._on_src_pad_added)
# The following is needed for farsight to process ICE requests:
self.pipeline.set_state(gst.STATE_PLAYING)
+ def get_fallback_src(self):
+ # TODO: Use avatar?
+ pipeline = 'videotestsrc is-live=true ! video/x-raw-yuv,framerate=10/1 ! ffmpegcolorspace'
+ return gst.parse_bin_from_description(pipeline, True)
def get_content(desc):
if desc['media'] == 'audio':
diff --git a/src/common/jingle_session.py b/src/common/jingle_session.py
index fd5fec1ef..4849f7fa8 100644
--- a/src/common/jingle_session.py
+++ b/src/common/jingle_session.py
@@ -68,8 +68,9 @@ class JingleSession(object):
self.connection = con # connection to use
# our full jid
#FIXME: Get rid of gajim here?
- self.ourjid = gajim.get_jid_from_account(self.connection.name) + '/' + \
- con.server_resource
+ self.ourjid = gajim.get_jid_from_account(self.connection.name)
+ if con.server_resource:
+ self.ourjid = self.ourjid + '/' + con.server_resource
self.peerjid = jid # jid we connect to
# jid we use as the initiator
self.initiator = weinitiate and self.ourjid or self.peerjid
@@ -183,17 +184,18 @@ class JingleSession(object):
# The content is from us, accept it
content.accepted = True
- def remove_content(self, creator, name):
+ def remove_content(self, creator, name, reason=None):
"""
- We do not need this now
+ Remove the content `name` created by `creator`
+ by sending content-remove, or by sending session-terminate if
+ there is no content left.
"""
- #TODO:
if (creator, name) in self.contents:
content = self.contents[(creator, name)]
if len(self.contents) > 1:
- self.__content_remove(content)
+ self.__content_remove(content, reason)
self.contents[(creator, name)].destroy()
- if len(self.contents) == 0:
+ if not self.contents:
self.end_session()
def modify_content(self, creator, name, *someother):
@@ -262,6 +264,12 @@ class JingleSession(object):
jingle.addChild(node=content)
self.connection.connection.send(stanza)
+ def send_description_info(self, content):
+ assert self.state != JingleStates.ended
+ stanza, jingle = self.__make_jingle('description-info')
+ jingle.addChild(node=content)
+ self.connection.connection.send(stanza)
+
def on_stanza(self, stanza):
"""
A callback for ConnectionJingle. It gets stanza, then tries to send it to
@@ -277,7 +285,7 @@ class JingleSession(object):
# it's a jingle action
action = jingle.getAttr('action')
if action not in self.callbacks:
- self.__send_error(stanza, 'bad_request')
+ self.__send_error(stanza, 'bad-request')
return
# FIXME: If we aren't initiated and it's not a session-initiate...
if action != 'session-initiate' and self.state == JingleStates.ended:
@@ -310,17 +318,15 @@ class JingleSession(object):
def __on_error(self, stanza, jingle, error, action):
# FIXME
text = error.getTagData('text')
- jingle_error = None
- xmpp_error = None
+ error_name = None
for child in error.getChildren():
if child.getNamespace() == xmpp.NS_JINGLE_ERRORS:
- jingle_error = child.getName()
+ error_name = child.getName()
+ break
elif child.getNamespace() == xmpp.NS_STANZAS:
- xmpp_error = child.getName()
- self.__dispatch_error(xmpp_error, jingle_error, text)
+ error_name = child.getName()
+ self.__dispatch_error(error_name, text, error.getAttribute('type'))
# FIXME: Not sure when we would want to do that...
- if xmpp_error == 'item-not-found':
- self.connection.delete_jingle_session(self.peerjid, self.sid)
def __on_transport_replace(self, stanza, jingle, error, action):
for content in jingle.iterTags('content'):
@@ -354,7 +360,7 @@ class JingleSession(object):
# TODO: ringing, active, (un)hold, (un)mute
payload = jingle.getPayload()
if payload:
- self.__send_error(stanza, 'feature-not-implemented', 'unsupported-info')
+ self.__send_error(stanza, 'feature-not-implemented', 'unsupported-info', type_='modify')
raise xmpp.NodeProcessed
def __on_content_remove(self, stanza, jingle, error, action):
@@ -425,14 +431,24 @@ class JingleSession(object):
# Jingle with unknown entities, it SHOULD return a <service-unavailable/>
# error.
+ # Check if there's already a session with this user:
+ for session in self.connection.iter_jingle_sessions(self.peerjid):
+ if not session is self:
+ reason = xmpp.Node('reason')
+ alternative_session = reason.setTag('alternative-session')
+ alternative_session.setTagData('sid', session.sid)
+ self.__ack(stanza, jingle, error, action)
+ self._session_terminate(reason)
+ raise xmpp.NodeProcessed
+
# Lets check what kind of jingle session does the peer want
- contents, contents_rejected, reason = self.__parse_contents(jingle)
+ contents, contents_rejected, reason_txt = self.__parse_contents(jingle)
# If there's no content we understand...
if not contents:
# TODO: http://xmpp.org/extensions/xep-0166.html#session-terminate
reason = xmpp.Node('reason')
- reason.setTag(reason)
+ reason.setTag(reason_txt)
self.__ack(stanza, jingle, error, action)
self._session_terminate(reason)
raise xmpp.NodeProcessed
@@ -450,14 +466,19 @@ class JingleSession(object):
for content in jingle.iterTags('content'):
name = content['name']
creator = content['creator']
- cn = self.contents[(creator, name)]
- cn.on_stanza(stanza, content, error, action)
+ if (creator, name) not in self.contents:
+ text = 'Content %s (created by %s) does not exist' % (name, creator)
+ self.__send_error(stanza, 'bad-request', text=text, type_='_modify')
+ raise xmpp.NodeProcessed
+ else:
+ cn = self.contents[(creator, name)]
+ cn.on_stanza(stanza, content, error, action)
def __on_session_terminate(self, stanza, jingle, error, action):
- self.connection.delete_jingle_session(self.peerjid, self.sid)
+ self.connection.delete_jingle_session(self.sid)
reason, text = self.__reason_from_stanza(jingle)
if reason not in ('success', 'cancel', 'decline'):
- self.__dispatch_error(reason, reason, text)
+ self.__dispatch_error(reason, text)
if text:
text = '%s (%s)' % (reason, text)
else:
@@ -496,7 +517,7 @@ class JingleSession(object):
reasons.add('failed-application')
else:
contents_rejected.append((element['name'], 'peer'))
- failed.add('unsupported-applications')
+ reasons.add('unsupported-applications')
failure_reason = None
@@ -509,16 +530,15 @@ class JingleSession(object):
return (contents, contents_rejected, failure_reason)
- def __dispatch_error(self, error, jingle_error=None, text=None):
- if jingle_error:
- error = jingle_error
+ def __dispatch_error(self, error=None, text=None, type_=None):
if text:
text = '%s (%s)' % (error, text)
- else:
- text = error
- self.connection.dispatch('JINGLE_ERROR', (self.peerjid, self.sid, text))
+ if type_ != 'modify':
+ self.connection.dispatch('JINGLE_ERROR',
+ (self.peerjid, self.sid, text or error))
def __reason_from_stanza(self, stanza):
+ # TODO: Move to GUI?
reason = 'success'
reasons = ['success', 'busy', 'cancel', 'connectivity-error',
'decline', 'expired', 'failed-application', 'failed-transport',
@@ -534,7 +554,7 @@ class JingleSession(object):
break
return (reason, text)
- def __make_jingle(self, action):
+ def __make_jingle(self, action, reason=None):
stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.peerjid))
attrs = {'action': action,
'sid': self.sid}
@@ -543,16 +563,21 @@ class JingleSession(object):
elif action == 'session-accept':
attrs['responder'] = self.responder
jingle = stanza.addChild('jingle', attrs=attrs, namespace=xmpp.NS_JINGLE)
+ if reason is not None:
+ jingle.addChild(node=reason)
return stanza, jingle
- def __send_error(self, stanza, error, jingle_error=None, text=None):
- err = xmpp.Error(stanza, '%s %s' % (xmpp.NS_STANZAS, error))
+ def __send_error(self, stanza, error, jingle_error=None, text=None, type_=None):
+ err_stanza = xmpp.Error(stanza, '%s %s' % (xmpp.NS_STANZAS, error))
+ err = err_stanza.getTag('error')
+ if type_:
+ err.setAttr('type', type_)
if jingle_error:
err.setTag(jingle_error, namespace=xmpp.NS_JINGLE_ERRORS)
if text:
err.setTagData('text', text)
- self.connection.connection.send(err)
- self.__dispatch_error(error, jingle_error, text)
+ self.connection.connection.send(err_stanza)
+ self.__dispatch_error(jingle_error or error, text, type_)
def __append_content(self, jingle, content):
"""
@@ -596,19 +621,19 @@ class JingleSession(object):
def _session_terminate(self, reason=None):
assert self.state != JingleStates.ended
- stanza, jingle = self.__make_jingle('session-terminate')
- if reason is not None:
- jingle.addChild(node=reason)
+ stanza, jingle = self.__make_jingle('session-terminate', reason=reason)
self.__broadcast_all(stanza, jingle, None, 'session-terminate-sent')
- self.connection.connection.send(stanza)
+ if self.connection.connection and self.connection.connected >= 2:
+ self.connection.connection.send(stanza)
+ # TODO: Move to GUI?
reason, text = self.__reason_from_stanza(jingle)
if reason not in ('success', 'cancel', 'decline'):
- self.__dispatch_error(reason, reason, text)
+ self.__dispatch_error(reason, text)
if text:
text = '%s (%s)' % (reason, text)
else:
text = reason
- self.connection.delete_jingle_session(self.peerjid, self.sid)
+ self.connection.delete_jingle_session(self.sid)
self.connection.dispatch('JINGLE_DISCONNECTED',
(self.peerjid, self.sid, None, text))
@@ -640,9 +665,9 @@ class JingleSession(object):
def __content_modify(self):
assert self.state != JingleStates.ended
- def __content_remove(self, content):
+ def __content_remove(self, content, reason=None):
assert self.state != JingleStates.ended
- stanza, jingle = self.__make_jingle('content-remove')
+ stanza, jingle = self.__make_jingle('content-remove', reason=reason)
self.__append_content(jingle, content)
self.connection.connection.send(stanza)
# TODO: this will fail if content is not an RTP content
diff --git a/src/common/latex.py b/src/common/latex.py
index a95f1f9f7..95e0b09de 100644
--- a/src/common/latex.py
+++ b/src/common/latex.py
@@ -5,7 +5,7 @@
## Copyright (C) 2005-2006 Alex Mauer <hawke AT hawkesnest.net>
## Travis Shirk <travis AT pobox.com>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
diff --git a/src/common/location_listener.py b/src/common/location_listener.py
index 37a2e4612..af0aca6eb 100644
--- a/src/common/location_listener.py
+++ b/src/common/location_listener.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
## src/common/location_listener.py
##
-## Copyright (C) 2009 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2009-2010 Yann Leboulanger <asterix AT lagaule.org>
##
## This file is part of Gajim.
##
@@ -18,6 +18,8 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
+from datetime import datetime
+
from common import gajim
from common import pep
from common import dbus_support
@@ -37,47 +39,47 @@ class LocationListener:
self._data = {}
def get_data(self):
- self._get_address()
- self._get_position()
-
- def _get_address(self):
bus = dbus.SessionBus()
- if 'org.freedesktop.Geoclue.Master' not in bus.list_names():
- self._on_geoclue_address_changed()
+ try:
+ # Initializes Geoclue.
+ obj = bus.get_object('org.freedesktop.Geoclue.Master',
+ '/org/freedesktop/Geoclue/Master')
+ # get MasterClient path
+ path = obj.Create()
+ # get MasterClient
+ cli = bus.get_object('org.freedesktop.Geoclue.Master', path)
+ cli.SetRequirements(1, 0, True, 1023)
+
+ self._get_address(cli)
+ self._get_position(cli)
+ except:
+ self._on_geoclue_position_changed()
return
- obj = bus.get_object('org.freedesktop.Geoclue.Master',
- '/org/freedesktop/Geoclue/Master')
- # get MasterClient path
- path = obj.Create()
- # get MasterClient
- cli = bus.get_object('org.freedesktop.Geoclue.Master', path)
+
+
+ def _get_address(self, cli):
+ bus = dbus.SessionBus()
cli.AddressStart()
# Check that there is a provider
name, description, service, path = cli.GetAddressProvider()
if path:
- timestamp, address, accuracy = cli.GetAddress()
+ provider = bus.get_object(service, path)
+ timestamp, address, accuracy = provider.GetAddress()
self._on_geoclue_address_changed(timestamp, address, accuracy)
- def _get_position(self):
+ def _get_position(self, cli):
bus = dbus.SessionBus()
- if 'org.freedesktop.Geoclue.Master' not in bus.list_names():
- self._on_geoclue_position_changed()
- return
- obj = bus.get_object('org.freedesktop.Geoclue.Master',
- '/org/freedesktop/Geoclue/Master')
- # get MasterClient path
- path = obj.Create()
- # get MasterClient
- cli = bus.get_object('org.freedesktop.Geoclue.Master', path)
cli.PositionStart()
# Check that there is a provider
name, description, service, path = cli.GetPositionProvider()
if path:
- fields, timestamp, lat, lon, alt, accuray = cli.GetPosition()
+ provider = bus.get_object(service, path)
+ fields, timestamp, lat, lon, alt, accuracy = provider.GetPosition()
self._on_geoclue_position_changed(fields, timestamp, lat, lon, alt,
accuracy)
def start(self):
+ self.location_info = {}
self.get_data()
bus = dbus.SessionBus()
# Geoclue
@@ -96,7 +98,7 @@ class LocationListener:
'region', 'street']:
self._data[field] = address.get(field, None)
if timestamp:
- self._data['timestamp'] = timestamp
+ self._data['timestamp'] = self._timestamp_to_utc(timestamp)
if accuracy:
# in PEP it's horizontal accuracy
self._data['accuracy'] = accuracy[1]
@@ -110,7 +112,7 @@ class LocationListener:
if _dict[field] is not None:
self._data[field] = _dict[field]
if timestamp:
- self._data['timestamp'] = timestamp
+ self._data['timestamp'] = self._timestamp_to_utc(timestamp)
if accuracy:
# in PEP it's horizontal accuracy
self._data['accuracy'] = accuracy[1]
@@ -123,10 +125,21 @@ class LocationListener:
continue
if not gajim.config.get_per('accounts', acct, 'publish_location'):
continue
- if gajim.connections[acct].location_info == self._data:
+ if self.location_info == self._data:
continue
+ if 'timestamp' in self.location_info and 'timestamp' in self._data:
+ last_data = self.location_info.copy()
+ del last_data['timestamp']
+ new_data = self._data.copy()
+ del new_data['timestamp']
+ if last_data == new_data:
+ continue
gajim.connections[acct].send_location(self._data)
- gajim.connections[acct].location_info = self._data
+ self.location_info = self._data.copy()
+
+ def _timestamp_to_utc(self, timestamp):
+ time = datetime.utcfromtimestamp(timestamp)
+ return time.strftime('%Y-%m-%dT%H:%MZ')
def enable():
listener = LocationListener.get()
diff --git a/src/common/logger.py b/src/common/logger.py
index b1079690a..c1228c639 100644
--- a/src/common/logger.py
+++ b/src/common/logger.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/common/logger.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
diff --git a/src/common/multimedia_helpers.py b/src/common/multimedia_helpers.py
index 1707e1961..8678b919b 100644
--- a/src/common/multimedia_helpers.py
+++ b/src/common/multimedia_helpers.py
@@ -31,11 +31,14 @@ class DeviceManager(object):
element = gst.element_factory_make(name, '%spresencetest' % name)
if isinstance(element, gst.interfaces.PropertyProbe):
element.set_state(gst.STATE_READY)
+ element.probe_property_name('device')
devices = element.probe_get_values_name('device')
if devices:
self.devices[text % _(' Default device')] = pipe % name
for device in devices:
+ element.set_state(gst.STATE_NULL)
element.set_property('device', device)
+ element.set_state(gst.STATE_READY)
device_name = element.get_property('device-name')
self.devices[text % device_name] = pipe % '%s device=%s' % (name, device)
element.set_state(gst.STATE_NULL)
@@ -76,10 +79,10 @@ class VideoInputManager(DeviceManager):
self.devices = {}
# Test src
self.detect_element('videotestsrc', _('Video test'),
- '%s is-live=true')
+ '%s is-live=true ! video/x-raw-yuv,framerate=10/1')
# Auto src
self.detect_element('autovideosrc', _('Autodetect'))
- # V4L2 src ; TODO: Figure out why it doesn't work
+ # V4L2 src
self.detect_element('v4l2src', _('V4L2: %s'))
# Funny things, just to test...
# self.devices['GOOM'] = 'audiotestsrc ! goom'
@@ -92,8 +95,7 @@ class VideoOutputManager(DeviceManager):
# Fake video output
self.detect_element('fakesink', _('Fake audio output'))
# Auto sink
- self.detect_element('autovideosink', _('Autodetect'))
- # xvimage sink
self.detect_element('xvimagesink', _('X Window System (X11/XShm/Xv): %s'))
# ximagesink
self.detect_element('ximagesink', _('X Window System (without Xv)'))
+ self.detect_element('autovideosink', _('Autodetect'))
diff --git a/src/common/optparser.py b/src/common/optparser.py
index b90304b4d..e89548163 100644
--- a/src/common/optparser.py
+++ b/src/common/optparser.py
@@ -2,7 +2,7 @@
## src/common/optparser.py
##
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 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-2008 Jean-Marie Traissard <jim AT lapin.org>
@@ -115,13 +115,16 @@ class OptionsParser:
gajim.config.foreach(self.write_line, f)
except IOError, e:
return str(e)
+ f.flush()
+ os.fsync(f.fileno())
f.close()
if os.path.exists(self.__filename):
- # win32 needs this
- try:
- os.remove(self.__filename)
- except Exception:
- pass
+ if os.name == 'nt':
+ # win32 needs this
+ try:
+ os.remove(self.__filename)
+ except Exception:
+ pass
try:
os.rename(self.__tempfile, self.__filename)
except IOError, e:
@@ -704,7 +707,9 @@ class OptionsParser:
"""
Remove hardcoded ../data/sounds from config
"""
- dirs = ('../data', gajim.gajimpaths.root, gajim.DATA_DIR)
+ dirs = ['../data', gajim.gajimpaths.data_root, gajim.DATA_DIR]
+ if os.name != 'nt':
+ dirs.append(os.path.expanduser(u'~/.gajim'))
for evt in gajim.config.get_per('soundevents'):
path = gajim.config.get_per('soundevents', evt, 'path')
# absolute and relative passes are necessary
diff --git a/src/common/passwords.py b/src/common/passwords.py
index 15607976d..1cebfee55 100644
--- a/src/common/passwords.py
+++ b/src/common/passwords.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Gustavo J. A. M. Carneiro <gjcarneiro AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
## Julien Pivotto <roidelapluie AT gmail.com>
## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
diff --git a/src/common/pep.py b/src/common/pep.py
index b417b77e0..255d0fe3a 100644
--- a/src/common/pep.py
+++ b/src/common/pep.py
@@ -2,7 +2,7 @@
## src/common/pep.py
##
## Copyright (C) 2007 Piotr Gaczkowski <doomhammerng AT gmail.com>
-## Copyright (C) 2007-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
## Jean-Marie Traissard <jim AT lapin.org>
## Jonathan Schleifer <js-common.gajim AT webkeks.org>
@@ -544,7 +544,7 @@ class ConnectionPEP(object):
items = event_tag.getTag('items')
if items:
for item in items.getTags('item'):
- entry = item.getTag('entry')
+ entry = item.getTag('entry', namespace=xmpp.NS_ATOM)
if entry:
# for each entry in feed (there shouldn't be more than one,
# but to be sure...
diff --git a/src/common/protocol/bytestream.py b/src/common/protocol/bytestream.py
index f70b50aa5..8277ed710 100644
--- a/src/common/protocol/bytestream.py
+++ b/src/common/protocol/bytestream.py
@@ -6,7 +6,7 @@
## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
## Travis Shirk <travis AT pobox.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
## Jean-Marie Traissard <jim AT lapin.org>
@@ -75,6 +75,190 @@ class ConnectionBytestream:
def __init__(self):
self.files_props = {}
+ def _ft_get_our_jid(self):
+ our_jid = gajim.get_jid_from_account(self.name)
+ resource = self.server_resource
+ return our_jid + '/' + resource
+
+ def _ft_get_receiver_jid(self, file_props):
+ return file_props['receiver'].jid + '/' + file_props['receiver'].resource
+
+ def _ft_get_from(self, iq_obj):
+ return helpers.get_full_jid_from_iq(iq_obj)
+
+ def _ft_get_streamhost_jid_attr(self, streamhost):
+ return helpers.parse_jid(streamhost.getAttr('jid'))
+
+ def send_file_request(self, file_props):
+ """
+ Send iq for new FT request
+ """
+ if not self.connection or self.connected < 2:
+ return
+ file_props['sender'] = self._ft_get_our_jid()
+ fjid = self._ft_get_receiver_jid(file_props)
+ iq = xmpp.Iq(to=fjid, typ='set')
+ iq.setID(file_props['sid'])
+ self.files_props[file_props['sid']] = file_props
+ si = iq.setTag('si', namespace=xmpp.NS_SI)
+ si.setAttr('profile', xmpp.NS_FILE)
+ si.setAttr('id', file_props['sid'])
+ file_tag = si.setTag('file', namespace=xmpp.NS_FILE)
+ file_tag.setAttr('name', file_props['name'])
+ file_tag.setAttr('size', file_props['size'])
+ desc = file_tag.setTag('desc')
+ if 'desc' in file_props:
+ desc.setData(file_props['desc'])
+ file_tag.setTag('range')
+ feature = si.setTag('feature', namespace=xmpp.NS_FEATURE)
+ _feature = xmpp.DataForm(typ='form')
+ feature.addChild(node=_feature)
+ field = _feature.setField('stream-method')
+ field.setAttr('type', 'list-single')
+ field.addOption(xmpp.NS_BYTESTREAM)
+ self.connection.send(iq)
+
+ def send_file_approval(self, file_props):
+ """
+ Send iq, confirming that we want to download the file
+ """
+ # user response to ConfirmationDialog may come after we've disconneted
+ if not self.connection or self.connected < 2:
+ return
+ iq = xmpp.Iq(to=unicode(file_props['sender']), typ='result')
+ iq.setAttr('id', file_props['request-id'])
+ si = iq.setTag('si', namespace=xmpp.NS_SI)
+ if 'offset' in file_props and file_props['offset']:
+ file_tag = si.setTag('file', namespace=xmpp.NS_FILE)
+ range_tag = file_tag.setTag('range')
+ range_tag.setAttr('offset', file_props['offset'])
+ feature = si.setTag('feature', namespace=xmpp.NS_FEATURE)
+ _feature = xmpp.DataForm(typ='submit')
+ feature.addChild(node=_feature)
+ field = _feature.setField('stream-method')
+ field.delAttr('type')
+ field.setValue(xmpp.NS_BYTESTREAM)
+ self.connection.send(iq)
+
+ def send_file_rejection(self, file_props, code='403', typ=None):
+ """
+ Inform sender that we refuse to download the file
+
+ typ is used when code = '400', in this case typ can be 'strean' for
+ invalid stream or 'profile' for invalid profile
+ """
+ # user response to ConfirmationDialog may come after we've disconneted
+ if not self.connection or self.connected < 2:
+ return
+ iq = xmpp.Iq(to=unicode(file_props['sender']), typ='error')
+ iq.setAttr('id', file_props['request-id'])
+ if code == '400' and typ in ('stream', 'profile'):
+ name = 'bad-request'
+ text = ''
+ else:
+ name = 'forbidden'
+ text = 'Offer Declined'
+ err = xmpp.ErrorNode(code=code, typ='cancel', name=name, text=text)
+ if code == '400' and typ in ('stream', 'profile'):
+ if typ == 'stream':
+ err.setTag('no-valid-streams', namespace=xmpp.NS_SI)
+ else:
+ err.setTag('bad-profile', namespace=xmpp.NS_SI)
+ iq.addChild(node=err)
+ self.connection.send(iq)
+
+ def _siResultCB(self, con, iq_obj):
+ file_props = self.files_props.get(iq_obj.getAttr('id'))
+ if not file_props:
+ return
+ if 'request-id' in file_props:
+ # we have already sent streamhosts info
+ return
+ file_props['receiver'] = self._ft_get_from(iq_obj)
+ si = iq_obj.getTag('si')
+ file_tag = si.getTag('file')
+ range_tag = None
+ if file_tag:
+ range_tag = file_tag.getTag('range')
+ if range_tag:
+ offset = range_tag.getAttr('offset')
+ if offset:
+ file_props['offset'] = int(offset)
+ length = range_tag.getAttr('length')
+ if length:
+ file_props['length'] = int(length)
+ feature = si.setTag('feature')
+ if feature.getNamespace() != xmpp.NS_FEATURE:
+ return
+ form_tag = feature.getTag('x')
+ form = xmpp.DataForm(node=form_tag)
+ field = form.getField('stream-method')
+ if field.getValue() != xmpp.NS_BYTESTREAM:
+ return
+ self._send_socks5_info(file_props)
+ raise xmpp.NodeProcessed
+
+ def _siSetCB(self, con, iq_obj):
+ jid = self._ft_get_from(iq_obj)
+ file_props = {'type': 'r'}
+ file_props['sender'] = jid
+ file_props['request-id'] = unicode(iq_obj.getAttr('id'))
+ si = iq_obj.getTag('si')
+ profile = si.getAttr('profile')
+ mime_type = si.getAttr('mime-type')
+ if profile != xmpp.NS_FILE:
+ self.send_file_rejection(file_props, code='400', typ='profile')
+ raise xmpp.NodeProcessed
+ feature_tag = si.getTag('feature', namespace=xmpp.NS_FEATURE)
+ if not feature_tag:
+ return
+ form_tag = feature_tag.getTag('x', namespace=xmpp.NS_DATA)
+ if not form_tag:
+ return
+ form = dataforms.ExtendForm(node=form_tag)
+ for f in form.iter_fields():
+ if f.var == 'stream-method' and f.type == 'list-single':
+ values = [o[1] for o in f.options]
+ if xmpp.NS_BYTESTREAM in values:
+ break
+ else:
+ self.send_file_rejection(file_props, code='400', typ='stream')
+ raise xmpp.NodeProcessed
+ file_tag = si.getTag('file')
+ for attribute in file_tag.getAttrs():
+ if attribute in ('name', 'size', 'hash', 'date'):
+ val = file_tag.getAttr(attribute)
+ if val is None:
+ continue
+ file_props[attribute] = val
+ file_desc_tag = file_tag.getTag('desc')
+ if file_desc_tag is not None:
+ file_props['desc'] = file_desc_tag.getData()
+
+ if mime_type is not None:
+ file_props['mime-type'] = mime_type
+ file_props['receiver'] = self._ft_get_our_jid()
+ file_props['sid'] = unicode(si.getAttr('id'))
+ file_props['transfered_size'] = []
+ gajim.socks5queue.add_file_props(self.name, file_props)
+ self.dispatch('FILE_REQUEST', (jid, file_props))
+ raise xmpp.NodeProcessed
+
+ def _siErrorCB(self, con, iq_obj):
+ si = iq_obj.getTag('si')
+ profile = si.getAttr('profile')
+ if profile != xmpp.NS_FILE:
+ return
+ file_props = self.files_props.get(iq_obj.getAttr('id'))
+ if not file_props:
+ return
+ jid = self._ft_get_from(iq_obj)
+ file_props['error'] = -3
+ self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
+ raise xmpp.NodeProcessed
+
+class ConnectionSocks5Bytestream(ConnectionBytestream):
+
def send_success_connect_reply(self, streamhost):
"""
Send reply to the initiator of FT that we made a connection
@@ -252,92 +436,6 @@ class ConnectionBytestream:
else:
return []
- def send_file_rejection(self, file_props, code='403', typ=None):
- """
- Inform sender that we refuse to download the file
-
- typ is used when code = '400', in this case typ can be 'strean' for
- invalid stream or 'profile' for invalid profile
- """
- # user response to ConfirmationDialog may come after we've disconneted
- if not self.connection or self.connected < 2:
- return
- iq = xmpp.Iq(to=unicode(file_props['sender']), typ='error')
- iq.setAttr('id', file_props['request-id'])
- if code == '400' and typ in ('stream', 'profile'):
- name = 'bad-request'
- text = ''
- else:
- name = 'forbidden'
- text = 'Offer Declined'
- err = xmpp.ErrorNode(code=code, typ='cancel', name=name, text=text)
- if code == '400' and typ in ('stream', 'profile'):
- if typ == 'stream':
- err.setTag('no-valid-streams', namespace=xmpp.NS_SI)
- else:
- err.setTag('bad-profile', namespace=xmpp.NS_SI)
- iq.addChild(node=err)
- self.connection.send(iq)
-
- def send_file_approval(self, file_props):
- """
- Send iq, confirming that we want to download the file
- """
- # user response to ConfirmationDialog may come after we've disconneted
- if not self.connection or self.connected < 2:
- return
- iq = xmpp.Iq(to=unicode(file_props['sender']), typ='result')
- iq.setAttr('id', file_props['request-id'])
- si = iq.setTag('si', namespace=xmpp.NS_SI)
- if 'offset' in file_props and file_props['offset']:
- file_tag = si.setTag('file', namespace=xmpp.NS_FILE)
- range_tag = file_tag.setTag('range')
- range_tag.setAttr('offset', file_props['offset'])
- feature = si.setTag('feature', namespace=xmpp.NS_FEATURE)
- _feature = xmpp.DataForm(typ='submit')
- feature.addChild(node=_feature)
- field = _feature.setField('stream-method')
- field.delAttr('type')
- field.setValue(xmpp.NS_BYTESTREAM)
- self.connection.send(iq)
-
- def _ft_get_our_jid(self):
- our_jid = gajim.get_jid_from_account(self.name)
- resource = self.server_resource
- return our_jid + '/' + resource
-
- def _ft_get_receiver_jid(self, file_props):
- return file_props['receiver'].jid + '/' + file_props['receiver'].resource
-
- def send_file_request(self, file_props):
- """
- Send iq for new FT request
- """
- if not self.connection or self.connected < 2:
- return
- file_props['sender'] = self._ft_get_our_jid()
- fjid = self._ft_get_receiver_jid(file_props)
- iq = xmpp.Iq(to=fjid, typ='set')
- iq.setID(file_props['sid'])
- self.files_props[file_props['sid']] = file_props
- si = iq.setTag('si', namespace=xmpp.NS_SI)
- si.setAttr('profile', xmpp.NS_FILE)
- si.setAttr('id', file_props['sid'])
- file_tag = si.setTag('file', namespace=xmpp.NS_FILE)
- file_tag.setAttr('name', file_props['name'])
- file_tag.setAttr('size', file_props['size'])
- desc = file_tag.setTag('desc')
- if 'desc' in file_props:
- desc.setData(file_props['desc'])
- file_tag.setTag('range')
- feature = si.setTag('feature', namespace=xmpp.NS_FEATURE)
- _feature = xmpp.DataForm(typ='form')
- feature.addChild(node=_feature)
- field = _feature.setField('stream-method')
- field.setAttr('type', 'list-single')
- field.addOption(xmpp.NS_BYTESTREAM)
- self.connection.send(iq)
-
def _result_socks5_sid(self, sid, hash_id):
"""
Store the result of SHA message from auth
@@ -406,9 +504,6 @@ class ConnectionBytestream:
self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
raise xmpp.NodeProcessed
- def _ft_get_from(self, iq_obj):
- return helpers.get_full_jid_from_iq(iq_obj)
-
def _bytestreamSetCB(self, con, iq_obj):
target = unicode(iq_obj.getAttr('to'))
id_ = unicode(iq_obj.getAttr('id'))
@@ -466,9 +561,6 @@ class ConnectionBytestream:
gajim.socks5queue.activate_proxy(host['idx'])
raise xmpp.NodeProcessed
- def _ft_get_streamhost_jid_attr(self, streamhost):
- return helpers.parse_jid(streamhost.getAttr('jid'))
-
def _bytestreamResultCB(self, con, iq_obj):
frm = self._ft_get_from(iq_obj)
real_id = unicode(iq_obj.getAttr('id'))
@@ -504,7 +596,7 @@ class ConnectionBytestream:
raise xmpp.NodeProcessed
if real_id.startswith('au_'):
- if 'stopped' in file and file_props['stopped']:
+ if 'stopped' in file_props and file_props['stopped']:
self.remove_transfer(file_props)
else:
gajim.socks5queue.send_file(file_props, self.name)
@@ -516,6 +608,9 @@ class ConnectionBytestream:
if proxyhost['jid'] == jid:
proxy = proxyhost
+ if 'stopped' in file_props and file_props['stopped']:
+ self.remove_transfer(file_props)
+ raise xmpp.NodeProcessed
if proxy is not None:
file_props['streamhost-used'] = True
if 'streamhosts' not in file_props:
@@ -530,10 +625,7 @@ class ConnectionBytestream:
raise xmpp.NodeProcessed
else:
- if 'stopped' in file_props and file_props['stopped']:
- self.remove_transfer(file_props)
- else:
- gajim.socks5queue.send_file(file_props, self.name)
+ gajim.socks5queue.send_file(file_props, self.name)
if 'fast' in file_props:
fasts = file_props['fast']
if len(fasts) > 0:
@@ -542,98 +634,7 @@ class ConnectionBytestream:
raise xmpp.NodeProcessed
- def _siResultCB(self, con, iq_obj):
- file_props = self.files_props.get(iq_obj.getAttr('id'))
- if not file_props:
- return
- if 'request-id' in file_props:
- # we have already sent streamhosts info
- return
- file_props['receiver'] = self._ft_get_from(iq_obj)
- si = iq_obj.getTag('si')
- file_tag = si.getTag('file')
- range_tag = None
- if file_tag:
- range_tag = file_tag.getTag('range')
- if range_tag:
- offset = range_tag.getAttr('offset')
- if offset:
- file_props['offset'] = int(offset)
- length = range_tag.getAttr('length')
- if length:
- file_props['length'] = int(length)
- feature = si.setTag('feature')
- if feature.getNamespace() != xmpp.NS_FEATURE:
- return
- form_tag = feature.getTag('x')
- form = xmpp.DataForm(node=form_tag)
- field = form.getField('stream-method')
- if field.getValue() != xmpp.NS_BYTESTREAM:
- return
- self._send_socks5_info(file_props)
- raise xmpp.NodeProcessed
-
- def _siSetCB(self, con, iq_obj):
- jid = self._ft_get_from(iq_obj)
- file_props = {'type': 'r'}
- file_props['sender'] = jid
- file_props['request-id'] = unicode(iq_obj.getAttr('id'))
- si = iq_obj.getTag('si')
- profile = si.getAttr('profile')
- mime_type = si.getAttr('mime-type')
- if profile != xmpp.NS_FILE:
- self.send_file_rejection(file_props, code='400', typ='profile')
- raise xmpp.NodeProcessed
- feature_tag = si.getTag('feature', namespace=xmpp.NS_FEATURE)
- if not feature_tag:
- return
- form_tag = feature_tag.getTag('x', namespace=xmpp.NS_DATA)
- if not form_tag:
- return
- form = dataforms.ExtendForm(node=form_tag)
- for f in form.iter_fields():
- if f.var == 'stream-method' and f.type == 'list-single':
- values = [o[1] for o in f.options]
- if xmpp.NS_BYTESTREAM in values:
- break
- else:
- self.send_file_rejection(file_props, code='400', typ='stream')
- raise xmpp.NodeProcessed
- file_tag = si.getTag('file')
- for attribute in file_tag.getAttrs():
- if attribute in ('name', 'size', 'hash', 'date'):
- val = file_tag.getAttr(attribute)
- if val is None:
- continue
- file_props[attribute] = val
- file_desc_tag = file_tag.getTag('desc')
- if file_desc_tag is not None:
- file_props['desc'] = file_desc_tag.getData()
-
- if mime_type is not None:
- file_props['mime-type'] = mime_type
- file_props['receiver'] = self._ft_get_our_jid()
- file_props['sid'] = unicode(si.getAttr('id'))
- file_props['transfered_size'] = []
- gajim.socks5queue.add_file_props(self.name, file_props)
- self.dispatch('FILE_REQUEST', (jid, file_props))
- raise xmpp.NodeProcessed
-
- def _siErrorCB(self, con, iq_obj):
- si = iq_obj.getTag('si')
- profile = si.getAttr('profile')
- if profile != xmpp.NS_FILE:
- return
- file_props = self.files_props.get(iq_obj.getAttr('id'))
- if not file_props:
- return
- jid = self._ft_get_from(iq_obj)
- file_props['error'] = -3
- self.dispatch('FILE_REQUEST_ERROR', (jid, file_props, ''))
- raise xmpp.NodeProcessed
-
-
-class ConnectionBytestreamZeroconf(ConnectionBytestream):
+class ConnectionSocks5BytestreamZeroconf(ConnectionSocks5Bytestream):
def _ft_get_from(self, iq_obj):
return unicode(iq_obj.getFrom())
diff --git a/src/common/proxy65_manager.py b/src/common/proxy65_manager.py
index e30af586d..5176b460d 100644
--- a/src/common/proxy65_manager.py
+++ b/src/common/proxy65_manager.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Jean-Marie Traissard <jim AT lapin.org>
-## Copyright (C) 2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
##
## This file is part of Gajim.
##
@@ -83,6 +83,8 @@ class Proxy65Manager:
host = item.getAttr('host')
port = item.getAttr('port')
jid = item.getAttr('jid')
+ if not host or not port or not jid:
+ self.proxies[proxy]._on_connect_failure()
self.proxies[proxy].resolve_result(host, port, jid)
# we can have only one streamhost
raise common.xmpp.NodeProcessed
@@ -113,8 +115,7 @@ class ProxyResolver:
self.host = str(host)
self.port = int(port)
self.jid = unicode(jid)
- self.state = S_RESOLVED
- #FIXME: re-enable proxy testing
+ self.state = S_INITIAL
log.info('start resolving %s:%s' % (self.host, self.port))
self.receiver_tester = ReceiverTester(self.host, self.port, self.jid,
self.sid, self.sender_jid, self._on_receiver_success,
diff --git a/src/common/pubsub.py b/src/common/pubsub.py
index 2a75d34a9..7cd83ff0f 100644
--- a/src/common/pubsub.py
+++ b/src/common/pubsub.py
@@ -2,7 +2,7 @@
## src/common/pubsub.py
##
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
##
@@ -109,25 +109,37 @@ class ConnectionPubSub:
self.connection.send(query)
- def send_pb_delete(self, jid, node):
+ def send_pb_purge(self, jid, node):
+ """
+ Purge node: Remove all items
+ """
+ if not self.connection or self.connected < 2:
+ return
+ query = xmpp.Iq('set', to=jid)
+ d = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB_OWNER)
+ d = d.addChild('purge', {'node': node})
+
+ self.connection.send(query)
+
+ def send_pb_delete(self, jid, node, on_ok=None, on_fail=None):
"""
Delete node
"""
if not self.connection or self.connected < 2:
return
query = xmpp.Iq('set', to=jid)
- d = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB)
+ d = query.addChild('pubsub', namespace=xmpp.NS_PUBSUB_OWNER)
d = d.addChild('delete', {'node': node})
def response(con, resp, jid, node):
- if resp.getType() == 'result':
- self.dispatch('PUBSUB_NODE_REMOVED', (jid, node))
- else:
+ if resp.getType() == 'result' and on_ok:
+ on_ok(jid, node)
+ elif on_fail:
msg = resp.getErrorMsg()
- self.dispatch('PUBSUB_NODE_NOT_REMOVED', (jid, node, msg))
+ on_fail(jid, node, msg)
self.connection.SendAndCallForResponse(query, response, {'jid': jid,
- 'node': node})
+ 'node': node})
def send_pb_create(self, jid, node, configure = False, configure_form = None):
"""
diff --git a/src/common/rst_xhtml_generator.py b/src/common/rst_xhtml_generator.py
index 09f28a504..45e07c72b 100644
--- a/src/common/rst_xhtml_generator.py
+++ b/src/common/rst_xhtml_generator.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Santiago Gala
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
##
## This file is part of Gajim.
diff --git a/src/common/sleepy.py b/src/common/sleepy.py
index 7f115da21..f32ebf807 100644
--- a/src/common/sleepy.py
+++ b/src/common/sleepy.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/common/sleepy.py
##
-## Copyright (C) 2003-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Mateusz Biliński <mateusz AT bilinski.it>
diff --git a/src/common/socks5.py b/src/common/socks5.py
index bfeda862e..4d99cdb21 100644
--- a/src/common/socks5.py
+++ b/src/common/socks5.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
diff --git a/src/common/stanza_session.py b/src/common/stanza_session.py
index 129f60ea3..028bee596 100644
--- a/src/common/stanza_session.py
+++ b/src/common/stanza_session.py
@@ -1,9 +1,9 @@
# -*- coding:utf-8 -*-
## src/common/stanza_session.py
##
+## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
-## Copyright (C) 2007-2008 Yann Leboulanger <asterix AT lagaule.org>
-## Brendan Taylor <whateley AT gmail.com>
+## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
## Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
diff --git a/src/common/xmpp/auth_nb.py b/src/common/xmpp/auth_nb.py
index 2b982d091..670526bf9 100644
--- a/src/common/xmpp/auth_nb.py
+++ b/src/common/xmpp/auth_nb.py
@@ -29,6 +29,8 @@ import random
import itertools
import dispatcher_nb
import hashlib
+import hmac
+import hashlib
import logging
log = logging.getLogger('gajim.c.x.auth_nb')
@@ -113,6 +115,8 @@ def challenge_splitter(data):
quotes_open = False
return dict_
+def scram_parse(chatter):
+ return dict(s.split('=', 1) for s in chatter.split(','))
class SASL(PlugIn):
"""
@@ -187,7 +191,7 @@ class SASL(PlugIn):
"""
if not feats.getTag('mechanisms', namespace=NS_SASL):
self.startsasl='not-supported'
- log.error('SASL not supported by server')
+ log.info('SASL not supported by server')
return
self.mecs = []
for mec in feats.getTag('mechanisms', namespace=NS_SASL).getTags(
@@ -209,9 +213,12 @@ class SASL(PlugIn):
raise NodeProcessed
if "EXTERNAL" in self.mecs:
self.mecs.remove('EXTERNAL')
- node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'EXTERNAL'},
- payload=[base64.encodestring('%s@%s' % (self.username,
- self._owner.Server)).replace('\n', '')])
+ sasl_data = u'%s@%s' % (self.username, self._owner.Server)
+ sasl_data = sasl_data.encode('utf-8').encode('base64').replace(
+ '\n', '')
+ node = Node('auth', attrs={'xmlns': NS_SASL,
+ 'mechanism': 'EXTERNAL'}, payload=[sasl_data])
+ self.mechanism = 'EXTERNAL'
self.startsasl = SASL_IN_PROCESS
self._owner.send(str(node))
raise NodeProcessed
@@ -231,6 +238,13 @@ class SASL(PlugIn):
raise NodeProcessed
except kerberos.GSSError, e:
log.info('GSSAPI authentication failed: %s' % str(e))
+ if 'SCRAM-SHA-1' in self.mecs:
+ self.mecs.remove('SCRAM-SHA-1')
+ self.mechanism = 'SCRAM-SHA-1'
+ self._owner._caller.get_password(self.set_password, self.mechanism)
+ self.scram_step = 0
+ self.startsasl = SASL_IN_PROCESS
+ raise NodeProcessed
if 'DIGEST-MD5' in self.mecs:
self.mecs.remove('DIGEST-MD5')
node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'DIGEST-MD5'})
@@ -241,12 +255,12 @@ class SASL(PlugIn):
if 'PLAIN' in self.mecs:
self.mecs.remove('PLAIN')
self.mechanism = 'PLAIN'
- self._owner._caller.get_password(self.set_password)
+ self._owner._caller.get_password(self.set_password, self.mechanism)
self.startsasl = SASL_IN_PROCESS
raise NodeProcessed
self.startsasl = SASL_FAILURE
- log.error('I can only use EXTERNAL, DIGEST-MD5, GSSAPI and PLAIN '
- 'mecanisms.')
+ log.info('I can only use EXTERNAL, SCRAM-SHA-1, DIGEST-MD5, GSSAPI and '
+ 'PLAIN mecanisms.')
if self.on_sasl:
self.on_sasl()
return
@@ -264,7 +278,7 @@ class SASL(PlugIn):
reason = challenge.getChildren()[0]
except Exception:
reason = challenge
- log.error('Failed SASL authentification: %s' % reason)
+ log.info('Failed SASL authentification: %s' % reason)
if len(self.mecs) > 0:
# There are other mechanisms to test
self.MechanismHandler()
@@ -273,6 +287,8 @@ class SASL(PlugIn):
self.on_sasl()
raise NodeProcessed
elif challenge.getName() == 'success':
+ # TODO: Need to validate any data-with-success.
+ # TODO: Important for DIGEST-MD5 and SCRAM.
self.startsasl = SASL_SUCCESS
log.info('Successfully authenticated with remote server.')
handlers = self._owner.Dispatcher.dumpHandlers()
@@ -310,9 +326,73 @@ class SASL(PlugIn):
response = kerberos.authGSSClientResponse(self.gss_vc)
if not response:
response = ''
- self._owner.send(Node('response', attrs={'xmlns':NS_SASL},
+ self._owner.send(Node('response', attrs={'xmlns': NS_SASL},
payload=response).__str__())
raise NodeProcessed
+ if self.mechanism == 'SCRAM-SHA-1':
+ hashfn = hashlib.sha1
+
+ def HMAC(k, s):
+ return hmac.HMAC(key=k, msg=s, digestmod=hashfn).digest()
+
+ def XOR(x, y):
+ r = (chr(ord(px) ^ ord(py)) for px, py in zip(x, y))
+ return ''.join(r)
+
+ def Hi(s, salt, iters):
+ ii = 1
+ try:
+ s = s.encode('utf-8')
+ except:
+ pass
+ ui_1 = HMAC(s, salt + '\0\0\0\01')
+ ui = ui_1
+ for i in range(iters - 1):
+ ii += 1
+ ui_1 = HMAC(s, ui_1)
+ ui = XOR(ui, ui_1)
+ return ui
+
+ def H(s):
+ return hashfn(s).digest()
+
+ def scram_base64(s):
+ return ''.join(s.encode('base64').split('\n'))
+
+ if self.scram_step == 0:
+ self.scram_step = 1
+ self.scram_soup += ',' + data + ','
+ data = scram_parse(data)
+ # TODO: Should check cnonce here.
+ # TODO: Channel binding data goes in here too.
+ r = 'c=' + scram_base64(self.scram_gs2)
+ r += ',r=' + data['r']
+ self.scram_soup += r
+ salt = data['s'].decode('base64')
+ iter = int(data['i'])
+ SaltedPassword = Hi(self.password, salt, iter)
+ # TODO: Could cache this, along with salt+iter.
+ ClientKey = HMAC(SaltedPassword, 'Client Key')
+ StoredKey = H(ClientKey)
+ ClientSignature = HMAC(StoredKey, self.scram_soup)
+ ClientProof = XOR(ClientKey, ClientSignature)
+ r += ',p=' + scram_base64(ClientProof)
+ ServerKey = HMAC(SaltedPassword, 'Server Key')
+ self.scram_ServerSignature = HMAC(ServerKey, self.scram_soup)
+ sasl_data = scram_base64(r)
+ node = Node('response', attrs={'xmlns': NS_SASL},
+ payload=[sasl_data])
+ self._owner.send(str(node))
+ raise NodeProcessed
+
+ if self.scram_step == 1:
+ data = scram_parse(data)
+ if data['v'].decode('base64') != self.scram_ServerSignature:
+ # TODO: Not clear what to do here - need to abort.
+ raise Exception
+ node = Node('response', attrs={'xmlns': NS_SASL});
+ self._owner.send(str(node))
+ raise NodeProcessed
# magic foo...
chal = challenge_splitter(data)
@@ -335,31 +415,39 @@ class SASL(PlugIn):
self.resp['digest-uri'] = 'xmpp/' + self._owner.Server
self.resp['charset'] = 'utf-8'
# Password is now required
- self._owner._caller.get_password(self.set_password)
+ self._owner._caller.get_password(self.set_password, self.mechanism)
elif 'rspauth' in chal:
self._owner.send(str(Node('response', attrs={'xmlns':NS_SASL})))
else:
self.startsasl = SASL_FAILURE
- log.error('Failed SASL authentification: unknown challenge')
+ log.info('Failed SASL authentification: unknown challenge')
if self.on_sasl:
self.on_sasl()
raise NodeProcessed
+ @staticmethod
+ def _convert_to_iso88591(string):
+ try:
+ string = string.decode('utf-8').encode('iso-8859-1')
+ except UnicodeEncodeError:
+ pass
+ return string
+
def set_password(self, password):
- if password is None:
- self.password = ''
- else:
- self.password = password
- if self.mechanism == 'DIGEST-MD5':
- def convert_to_iso88591(string):
- try:
- string = string.decode('utf-8').encode('iso-8859-1')
- except UnicodeEncodeError:
- pass
- return string
- hash_username = convert_to_iso88591(self.resp['username'])
- hash_realm = convert_to_iso88591(self.resp['realm'])
- hash_password = convert_to_iso88591(self.password)
+ self.password = '' if password is None else password
+ if self.mechanism == 'SCRAM-SHA-1':
+ nonce = ''.join('%x' % randint(0, 2 ** 28) for randint in \
+ itertools.repeat(random.randint, 7))
+ self.scram_soup = 'n=' + self.username + ',r=' + nonce
+ self.scram_gs2 = 'n,,' # No CB yet.
+ sasl_data = (self.scram_gs2 + self.scram_soup).encode('base64').\
+ replace('\n', '')
+ node = Node('auth', attrs={'xmlns': NS_SASL,
+ 'mechanism': self.mechanism}, payload=[sasl_data])
+ elif self.mechanism == 'DIGEST-MD5':
+ hash_username = self._convert_to_iso88591(self.resp['username'])
+ hash_realm = self._convert_to_iso88591(self.resp['realm'])
+ hash_password = self._convert_to_iso88591(self.password)
A1 = C([H(C([hash_username, hash_realm, hash_password])),
self.resp['nonce'], self.resp['cnonce']])
A2 = C(['AUTHENTICATE', self.resp['digest-uri']])
@@ -377,8 +465,7 @@ class SASL(PlugIn):
'\r', '').replace('\n', '')
node = Node('response', attrs={'xmlns':NS_SASL}, payload=[sasl_data])
elif self.mechanism == 'PLAIN':
- sasl_data = u'%s\x00%s\x00%s' % (self.username + '@' + \
- self._owner.Server, self.username, self.password)
+ sasl_data = u'\x00%s\x00%s' % (self.username, self.password)
sasl_data = sasl_data.encode('utf-8').encode('base64').replace(
'\n', '')
node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'PLAIN'},
@@ -419,7 +506,7 @@ class NonBlockingNonSASL(PlugIn):
def _on_username(self, resp):
if not isResultNode(resp):
- log.error('No result node arrived! Aborting...')
+ log.info('No result node arrived! Aborting...')
return self.on_auth(None)
iq=Iq(typ='set', node=resp)
@@ -450,7 +537,7 @@ class NonBlockingNonSASL(PlugIn):
query.setTagData('hash', hash_)
self._method='0k'
else:
- log.warn("Sequre methods unsupported, performing plain text \
+ log.warn("Secure methods unsupported, performing plain text \
authentication")
query.setTagData('password', self.password)
self._method = 'plain'
@@ -464,7 +551,7 @@ class NonBlockingNonSASL(PlugIn):
self.owner._registered_name = self.owner.User+'@'+self.owner.Server+\
'/'+self.owner.Resource
return self.on_auth(self._method)
- log.error('Authentication failed!')
+ log.info('Authentication failed!')
return self.on_auth(None)
@@ -496,7 +583,7 @@ class NonBlockingBind(PlugIn):
attributes accordingly
"""
if not feats.getTag('bind', namespace=NS_BIND):
- log.error('Server does not requested binding.')
+ log.info('Server does not requested binding.')
# we try to bind resource anyway
#self.bound='failure'
self.bound = []
@@ -548,10 +635,10 @@ class NonBlockingBind(PlugIn):
func=self._on_session)
return
if resp:
- log.error('Binding failed: %s.' % resp.getTag('error'))
+ log.info('Binding failed: %s.' % resp.getTag('error'))
self.on_bound(None)
else:
- log.error('Binding failed: timeout expired.')
+ log.info('Binding failed: timeout expired.')
self.on_bound(None)
def _on_session(self, resp):
diff --git a/src/common/xmpp/client_nb.py b/src/common/xmpp/client_nb.py
index ea0e43fb1..44ea3ba3e 100644
--- a/src/common/xmpp/client_nb.py
+++ b/src/common/xmpp/client_nb.py
@@ -362,6 +362,9 @@ class NonBlockingClient:
supported and desired.
"""
self.stream_started = True
+ if not hasattr(self, 'onreceive'):
+ # we may already have been disconnected
+ return
self.onreceive(None)
if self.connected == 'plain':
@@ -372,7 +375,7 @@ class NonBlockingClient:
# try to negotiate TLS
if self.incoming_stream_version() != '1.0':
# if stream version is less than 1.0, we can't do more
- log.warn('While connecting with type = "tls": stream version ' +
+ log.info('While connecting with type = "tls": stream version ' +
'is less than 1.0')
self._on_connect()
return
@@ -382,7 +385,7 @@ class NonBlockingClient:
log.info('TLS supported by remote server. Requesting TLS start.')
self._tls_negotiation_handler()
else:
- log.warn('While connecting with type = "tls": TLS unsupported ' +
+ log.info('While connecting with type = "tls": TLS unsupported ' +
'by remote server')
self._on_connect()
diff --git a/src/common/xmpp/dispatcher_nb.py b/src/common/xmpp/dispatcher_nb.py
index f5972e3ce..54274928a 100644
--- a/src/common/xmpp/dispatcher_nb.py
+++ b/src/common/xmpp/dispatcher_nb.py
@@ -469,10 +469,6 @@ class XMPPDispatcher(PlugIn):
# we have released dispatcher, so self._owner has no methods
if not res:
return
- if 'remove_timeout' in self._owner.__dict__:
- # When we receive data after we started disconnecting, Transport may
- # already be plugged out
- self._owner.remove_timeout()
for (_id, _iq) in self._expected.items():
if _iq is None:
# If the expected Stanza would have arrived, ProcessNonBlocking
diff --git a/src/common/xmpp/idlequeue.py b/src/common/xmpp/idlequeue.py
index 5284ad8c7..1b0bccb04 100644
--- a/src/common/xmpp/idlequeue.py
+++ b/src/common/xmpp/idlequeue.py
@@ -215,7 +215,7 @@ class IdleQueue:
# (timeout, boolean)
# Boolean is True if timeout is specified in seconds, False means miliseconds
- PROCESS_TIMEOUT = (200, False)
+ PROCESS_TIMEOUT = (100, False)
def __init__(self):
self.queue = {}
diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py
index 26f795966..ceaa49c9a 100644
--- a/src/common/xmpp/protocol.py
+++ b/src/common/xmpp/protocol.py
@@ -28,13 +28,16 @@ NS_ADDRESS ='http://jabber.org/protocol/address'
NS_AGENTS ='jabber:iq:agents'
NS_AMP ='http://jabber.org/protocol/amp'
NS_AMP_ERRORS =NS_AMP+'#errors'
+NS_ATOM ='http://www.w3.org/2005/Atom'
NS_AUTH ='jabber:iq:auth'
NS_AVATAR ='http://www.xmpp.org/extensions/xep-0084.html#ns-metadata'
NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind'
+NS_BOB ='urn:xmpp:bob' #XEP-0231
NS_BROWSE ='jabber:iq:browse'
NS_BROWSING ='http://jabber.org/protocol/browsing' # XEP-0195
NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065
NS_CAPS ='http://jabber.org/protocol/caps' # JEP-0115
+NS_CAPTCHA ='urn:xmpp:captcha' # XEP-0158
NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # JEP-0085
NS_CHATTING ='http://jabber.org/protocol/chatting' # XEP-0194
NS_CLIENT ='jabber:client'
@@ -44,6 +47,7 @@ NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0'
NS_COMPRESS ='http://jabber.org/protocol/compress' # XEP-0138
NS_CONFERENCE ='jabber:x:conference'
NS_DATA ='jabber:x:data' # XEP-0004
+NS_DATA_MEDIA ='urn:xmpp:media-element' # XEP-0221
NS_DELAY ='jabber:x:delay'
NS_DELAY2 ='urn:xmpp:delay'
NS_DIALBACK ='jabber:server:dialback'
@@ -96,8 +100,11 @@ NS_PUBSUB_OWNER ='http://jabber.org/protocol/pubsub#owner'
NS_REGISTER ='jabber:iq:register'
NS_ROSTER ='jabber:iq:roster'
NS_ROSTERX ='http://jabber.org/protocol/rosterx' # XEP-0144
+NS_ROSTER_VER ='urn:xmpp:features:rosterver' # XEP-0273
NS_RPC ='jabber:iq:rpc' # XEP-0009
NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl'
+NS_SECLABEL ='urn:xmpp:sec-label:0'
+NS_SECLABEL_CATALOG ='urn:xmpp:sec-label:catalog:0'
NS_SEARCH ='jabber:iq:search'
NS_SERVER ='jabber:server'
NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session'
diff --git a/src/common/xmpp/roster_nb.py b/src/common/xmpp/roster_nb.py
index e90a1820a..c5a7bb463 100644
--- a/src/common/xmpp/roster_nb.py
+++ b/src/common/xmpp/roster_nb.py
@@ -38,7 +38,7 @@ class NonBlockingRoster(PlugIn):
internal representation of contacts in roster
"""
- def __init__(self, version=''):
+ def __init__(self, version=None):
"""
Init internal variables
"""
@@ -60,7 +60,8 @@ class NonBlockingRoster(PlugIn):
return
iq = Iq('get', NS_ROSTER)
- iq.setTagAttr('query', 'ver', self.version)
+ if self.version is not None:
+ iq.setTagAttr('query', 'ver', self.version)
id_ = self._owner.getAnID()
iq.setID(id_)
self._owner.send(iq)
@@ -341,6 +342,9 @@ class NonBlockingRoster(PlugIn):
self._owner.Dispatcher.ProcessNonBlocking(data)
if not self.set:
return
+ if not self._owner:
+ # Connection has been closed by receiving a <stream:error> for ex,
+ return
self._owner.onreceive(None)
if self.on_ready:
self.on_ready(self)
diff --git a/src/common/xmpp/stringprepare.py b/src/common/xmpp/stringprepare.py
index 5d0610d4b..bae16fcec 100644
--- a/src/common/xmpp/stringprepare.py
+++ b/src/common/xmpp/stringprepare.py
@@ -2,7 +2,7 @@
## src/common/xmpp/stringprepare.py
##
## Copyright (C) 2001-2005 Twisted Matrix Laboratories
-## Copyright (C) 2005-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Stefan Bethge <stefan AT lanpartei.de>
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
##
diff --git a/src/common/xmpp/tls_nb.py b/src/common/xmpp/tls_nb.py
index 9c54eb07d..1cbb99c57 100644
--- a/src/common/xmpp/tls_nb.py
+++ b/src/common/xmpp/tls_nb.py
@@ -349,8 +349,44 @@ class NonBlockingTLS(PlugIn):
def _startSSL_pyOpenSSL(self):
log.debug("_startSSL_pyOpenSSL called")
tcpsock = self._owner
- # See http://docs.python.org/dev/library/ssl.html
- tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
+ # NonBlockingHTTPBOSH instance has no attribute _owner
+ if hasattr(tcpsock, '_owner') and tcpsock._owner._caller.client_cert \
+ and os.path.exists(tcpsock._owner._caller.client_cert):
+ conn = tcpsock._owner._caller
+ # FIXME make a checkbox for Client Cert / SSLv23 / TLSv1
+ # If we are going to use a client cert/key pair for authentication,
+ # we choose TLSv1 method.
+ tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
+ log.debug('Using client cert and key from %s' % conn.client_cert)
+ try:
+ p12 = OpenSSL.crypto.load_pkcs12(open(conn.client_cert).read())
+ except OpenSSL.crypto.Error, exception_obj:
+ log.warning('Unable to load client pkcs12 certificate from '
+ 'file %s: %s ... Is it a valid PKCS12 cert?' % \
+ (conn.client_cert, exception_obj.args))
+ except:
+ log.warning('Unknown error while loading certificate from file '
+ '%s' % conn.client_cert)
+ else:
+ log.info('PKCS12 Client cert loaded OK')
+ try:
+ tcpsock._sslContext.use_certificate(p12.get_certificate())
+ tcpsock._sslContext.use_privatekey(p12.get_privatekey())
+ log.info('p12 cert and key loaded')
+ except OpenSSL.crypto.Error, exception_obj:
+ log.warning('Unable to extract client certificate from '
+ 'file %s' % conn.client_cert)
+ except Exception, msg:
+ log.warning('Unknown error extracting client certificate '
+ 'from file %s: %s' % (conn.client_cert, msg))
+ else:
+ log.info('client cert and key loaded OK')
+ else:
+ # See http://docs.python.org/dev/library/ssl.html
+ tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
+ tcpsock._sslContext.set_options(OpenSSL.SSL.OP_NO_SSLv2 | \
+ OpenSSL.SSL.OP_NO_TICKET)
+
tcpsock.ssl_errnum = 0
tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER,
self._ssl_verify_callback)
diff --git a/src/common/xmpp/transports_nb.py b/src/common/xmpp/transports_nb.py
index 8d0368abc..2b0a57c9d 100644
--- a/src/common/xmpp/transports_nb.py
+++ b/src/common/xmpp/transports_nb.py
@@ -223,7 +223,7 @@ class NonBlockingTransport(PlugIn):
if hasattr(self, '_owner') and hasattr(self._owner, 'Dispatcher'):
self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
else:
- log.warning('No Dispatcher plugged. Received data will not be processed')
+ log.warn('No Dispatcher plugged. Received data will not be processed')
self.on_receive = None
return
self.on_receive = recv_handler
@@ -342,6 +342,7 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
# variable for errno symbol that will be found from exception raised
# from connect()
errnum = 0
+ errstr = str()
# set timeout for TCP connecting - if nonblocking connect() fails, pollend
# is called. If if succeeds pollout is called.
@@ -350,8 +351,8 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
try:
self._sock.setblocking(False)
self._sock.connect((self.server, self.port))
- except Exception, (errnum, errstr):
- pass
+ except Exception, exc:
+ errnum, errstr = exc.args
if errnum in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK):
# connecting in progress
@@ -453,7 +454,7 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
self._sock.shutdown(socket.SHUT_RDWR)
self._sock.close()
except socket.error, (errnum, errstr):
- log.error('Error while disconnecting socket: %s' % errstr)
+ log.info('Error while disconnecting socket: %s' % errstr)
self.fd = -1
NonBlockingTransport.disconnect(self, do_callback)
@@ -542,7 +543,7 @@ class NonBlockingTCP(NonBlockingTransport, IdleObject):
readable=True)
self.raise_event(DATA_SENT, sent_data)
- except socket.error, e:
+ except Exception:
log.error('_do_send:', exc_info=True)
traceback.print_exc()
self.disconnect()
diff --git a/src/common/zeroconf/client_zeroconf.py b/src/common/zeroconf/client_zeroconf.py
index adc3f45d0..ffad257c3 100644
--- a/src/common/zeroconf/client_zeroconf.py
+++ b/src/common/zeroconf/client_zeroconf.py
@@ -30,6 +30,8 @@ from common.xmpp.protocol import *
import socket
import errno
import sys
+import string
+from random import Random
import logging
log = logging.getLogger('gajim.c.z.client_zeroconf')
@@ -317,6 +319,9 @@ class P2PClient(IdleObject):
common.xmpp.NS_BYTESTREAM)
self.RegisterHandler('iq', self._caller._DiscoverItemsGetCB, 'get',
common.xmpp.NS_DISCO_ITEMS)
+ self.RegisterHandler('iq', self._caller._JingleCB, 'result')
+ self.RegisterHandler('iq', self._caller._JingleCB, 'error')
+ self.RegisterHandler('iq', self._caller._JingleCB, 'set', common.xmpp.NS_JINGLE)
class P2PConnection(IdleObject, PlugIn):
def __init__(self, sock_hash, _sock, host=None, port=None, caller=None,
@@ -412,7 +417,6 @@ class P2PConnection(IdleObject, PlugIn):
If supplied data is unicode string, encode it to UTF-8.
"""
- print 'ici'
if self.state <= 0:
return
@@ -726,7 +730,8 @@ class ClientZeroconf:
def send(self, stanza, is_message=False, now=False, on_ok=None,
on_not_ok=None):
stanza.setFrom(self.roster.zeroconf.name)
- to = stanza.getTo()
+ to = unicode(stanza.getTo())
+ to = gajim.get_jid_without_resource(to)
try:
item = self.roster[to]
@@ -759,6 +764,12 @@ class ClientZeroconf:
P2PClient(None, item['address'], item['port'], self,
[(stanza, is_message)], to, on_ok=on_ok, on_not_ok=on_not_ok)
+ def getAnID(self):
+ """
+ Generate a random id
+ """
+ return ''.join(Random().sample(string.letters + string.digits, 6))
+
def RegisterDisconnectHandler(self, handler):
"""
Register handler that will be called on disconnect
diff --git a/src/common/zeroconf/connection_handlers_zeroconf.py b/src/common/zeroconf/connection_handlers_zeroconf.py
index 9d9f8356a..ace1b771e 100644
--- a/src/common/zeroconf/connection_handlers_zeroconf.py
+++ b/src/common/zeroconf/connection_handlers_zeroconf.py
@@ -35,7 +35,7 @@ from common import gajim
from common.zeroconf import zeroconf
from common.commands import ConnectionCommands
from common.pep import ConnectionPEP
-from common.protocol.bytestream import ConnectionBytestreamZeroconf
+from common.protocol.bytestream import ConnectionSocks5BytestreamZeroconf
import logging
log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf')
@@ -70,12 +70,14 @@ class ConnectionVcard(connection_handlers.ConnectionVcard):
pass
-class ConnectionHandlersZeroconf(ConnectionVcard, ConnectionBytestreamZeroconf,
-ConnectionCommands, ConnectionPEP, connection_handlers.ConnectionHandlersBase):
+class ConnectionHandlersZeroconf(ConnectionVcard,
+ConnectionSocks5BytestreamZeroconf, ConnectionCommands, ConnectionPEP,
+connection_handlers.ConnectionHandlersBase, connection_handlers.ConnectionJingle):
def __init__(self):
ConnectionVcard.__init__(self)
- ConnectionBytestreamZeroconf.__init__(self)
+ ConnectionSocks5BytestreamZeroconf.__init__(self)
ConnectionCommands.__init__(self)
+ connection_handlers.ConnectionJingle.__init__(self)
connection_handlers.ConnectionHandlersBase.__init__(self)
try:
diff --git a/src/common/zeroconf/connection_zeroconf.py b/src/common/zeroconf/connection_zeroconf.py
index 6076f8b13..484b810a4 100644
--- a/src/common/zeroconf/connection_zeroconf.py
+++ b/src/common/zeroconf/connection_zeroconf.py
@@ -7,11 +7,9 @@
## - Travis Shirk <travis@pobox.com>
## - Stefan Bethge <stefan@lanpartei.de>
##
-## Copyright (C) 2003-2004 Yann Leboulanger <asterix@lagaule.org>
-## Vincent Hanquez <tab@snarc.org>
-## Copyright (C) 2006 Yann Leboulanger <asterix@lagaule.org>
-## Vincent Hanquez <tab@snarc.org>
-## Nikos Kouremenos <nkour@jabber.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix@lagaule.org>
+## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org>
+## Copyright (C) 2006 Nikos Kouremenos <nkour@jabber.org>
## Dimitur Kirov <dkirov@gmail.com>
## Travis Shirk <travis@pobox.com>
## Norman Rasmussen <norman@rasmussen.co.za>
@@ -311,8 +309,9 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
def send_message(self, jid, msg, keyID, type_='chat', subject='',
chatstate=None, msg_id=None, composing_xep=None, resource=None,
- user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None,
- original_message=None, delayed=None, callback=None, callback_args=[]):
+ user_nick=None, xhtml=None, label=None, session=None, forward_from=None,
+ form_node=None, original_message=None, delayed=None, callback=None,
+ callback_args=[], now=True):
def on_send_ok(msg_id):
self.dispatch('MSGSENT', (jid, msg, keyID))
diff --git a/src/common/zeroconf/zeroconf_bonjour.py b/src/common/zeroconf/zeroconf_bonjour.py
index b26f984a2..1e57c0176 100644
--- a/src/common/zeroconf/zeroconf_bonjour.py
+++ b/src/common/zeroconf/zeroconf_bonjour.py
@@ -274,9 +274,9 @@ class Zeroconf:
def disconnect(self):
if self.connected:
self.connected = False
- self.browse_sdRef.close()
- self.remove_announce()
-
+ if hasattr(self, 'browse_sdRef'):
+ self.browse_sdRef.close()
+ self.remove_announce()
def browse_domain(self, domain=None):
gajim.log.debug('starting to browse')
diff --git a/src/config.py b/src/config.py
index e2253f49b..56266a07c 100644
--- a/src/config.py
+++ b/src/config.py
@@ -2,7 +2,7 @@
## src/config.py
##
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
## Stéphan Kochen <stephan AT kochen.nl>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
@@ -429,27 +429,45 @@ class PreferencesWindow:
buf.connect('changed', self.on_msg_textview_changed)
### Audio / Video tab ###
- def create_av_combobox(opt_name, device_dict):
+ def create_av_combobox(opt_name, device_dict, config_name=None,
+ key=None):
combobox = self.xml.get_object(opt_name + '_combobox')
cell = gtk.CellRendererText()
combobox.pack_start(cell, True)
combobox.add_attribute(cell, 'text', 0)
model = gtk.ListStore(str, str)
combobox.set_model(model)
+ if config_name:
+ config = gajim.config.get(config_name)
+ else:
+ config = gajim.config.get(opt_name + '_device')
- for index, (name, value) in enumerate(sorted(device_dict.iteritems())):
+ for index, (name, value) in enumerate(sorted(device_dict.\
+ iteritems(), key=key)):
model.append((name, value))
- if gajim.config.get(opt_name + '_device') == value:
+ if config == value:
combobox.set_active(index)
if HAS_GST:
create_av_combobox('audio_input', AudioInputManager().get_devices())
- create_av_combobox('audio_output', AudioOutputManager().get_devices())
+ create_av_combobox('audio_output', AudioOutputManager().get_devices(
+ ))
create_av_combobox('video_input', VideoInputManager().get_devices())
- create_av_combobox('video_output', VideoOutputManager().get_devices())
+ create_av_combobox('video_output', VideoOutputManager().get_devices(
+ ))
+
+ create_av_combobox('video_framerate', {_('Default'): '',
+ '15fps': '15/1', '10fps': '10/1', '5fps': '5/1',
+ '2.5fps': '5/2'}, 'video_framerate', key=lambda x: -1 if \
+ not x[1] else float(x[0][:-3]))
+ create_av_combobox('video_size', {_('Default'): '',
+ '800x600': '800x600', '640x480': '640x480',
+ '320x240': '320x240'}, 'video_size', key=lambda x: -1 if \
+ not x[1] else int(x[0][:3]))
+
else:
for opt_name in ('audio_input', 'audio_output', 'video_input',
- 'video_output'):
+ 'video_output', 'video_framerate', 'video_size'):
combobox = self.xml.get_object(opt_name + '_combobox')
combobox.set_sensitive(False)
@@ -1076,23 +1094,29 @@ class PreferencesWindow:
def on_msg_treemodel_row_deleted(self, model, path):
self.save_status_messages(model)
- def on_av_combobox_changed(self, combobox, opt_name):
+ def on_av_combobox_changed(self, combobox, config_name):
model = combobox.get_model()
active = combobox.get_active()
device = model[active][1].decode('utf-8')
- gajim.config.set(opt_name + '_device', device)
+ gajim.config.set(config_name, device)
def on_audio_input_combobox_changed(self, widget):
- self.on_av_combobox_changed(widget, 'audio_input')
+ self.on_av_combobox_changed(widget, 'audio_input_device')
def on_audio_output_combobox_changed(self, widget):
- self.on_av_combobox_changed(widget, 'audio_output')
+ self.on_av_combobox_changed(widget, 'audio_output_device')
def on_video_input_combobox_changed(self, widget):
- self.on_av_combobox_changed(widget, 'video_input')
+ self.on_av_combobox_changed(widget, 'video_input_device')
def on_video_output_combobox_changed(self, widget):
- self.on_av_combobox_changed(widget, 'video_output')
+ self.on_av_combobox_changed(widget, 'video_output_device')
+
+ def on_video_framerate_combobox_changed(self, widget):
+ self.on_av_combobox_changed(widget, 'video_framerate')
+
+ def on_video_size_combobox_changed(self, widget):
+ self.on_av_combobox_changed(widget, 'video_size')
def on_stun_checkbutton_toggled(self, widget):
self.on_checkbutton_toggled(widget, 'use_stun_server',
@@ -1612,13 +1636,15 @@ class AccountsWindow:
focused_widget = self.window.get_focus()
focused_widget_name = focused_widget.get_name()
if focused_widget_name in ('jid_entry1', 'resource_entry1',
- 'custom_port_entry'):
+ 'custom_port_entry', 'cert_entry1'):
if focused_widget_name == 'jid_entry1':
func = self.on_jid_entry1_focus_out_event
elif focused_widget_name == 'resource_entry1':
func = self.on_resource_entry1_focus_out_event
elif focused_widget_name == 'custom_port_entry':
func = self.on_custom_port_entry_focus_out_event
+ elif focused_widget_name == 'cert_entry1':
+ func = self.on_cert_entry1_focus_out_event
if func(focused_widget, None):
# Error detected in entry, don't change account, re-put cursor on
# previous row
@@ -1641,6 +1667,22 @@ class AccountsWindow:
self.init_account()
self.update_proxy_list()
+ def on_browse_for_client_cert_button_clicked(self, widget, data=None):
+ def on_ok(widget, path_to_clientcert_file):
+ self.dialog.destroy()
+ if not path_to_clientcert_file:
+ return
+ self.xml.get_object('cert_entry1').set_text(path_to_clientcert_file)
+ gajim.config.set_per('accounts', self.current_account,
+ 'client_cert', path_to_clientcert_file)
+
+ def on_cancel(widget):
+ self.dialog.destroy()
+
+ path_to_clientcert_file = self.xml.get_object('cert_entry1').get_text()
+ self.dialog = dialogs.ClientCertChooserDialog(path_to_clientcert_file,
+ on_ok, on_cancel)
+
def update_proxy_list(self):
if self.current_account:
our_proxy = gajim.config.get_per('accounts', self.current_account,
@@ -1796,10 +1838,14 @@ class AccountsWindow:
# Account tab
self.draw_normal_jid()
self.xml.get_object('resource_entry1').set_text(gajim.config.get_per(
- 'accounts', account, 'resource'))
+ 'accounts', account, 'resource'))
+
+ client_cert = gajim.config.get_per('accounts', account, 'client_cert')
+ self.xml.get_object('cert_entry1').set_text(client_cert)
+
self.xml.get_object('adjust_priority_with_status_checkbutton1').\
- set_active(gajim.config.get_per('accounts', account,
- 'adjust_priority_with_status'))
+ set_active(gajim.config.get_per('accounts', account,
+ 'adjust_priority_with_status'))
spinbutton = self.xml.get_object('priority_spinbutton1')
if gajim.config.get('enable_negative_priority'):
spinbutton.set_range(-128, 127)
@@ -1893,9 +1939,9 @@ class AccountsWindow:
return
win_opened = False
- if gajim.interface.msg_win_mgr.get_controls(acct = account):
+ if gajim.interface.msg_win_mgr.get_controls(acct=account):
win_opened = True
- else:
+ elif account in gajim.interface.instances:
for key in gajim.interface.instances[account]:
if gajim.interface.instances[account][key] and key != \
'remove_account':
@@ -1903,10 +1949,13 @@ class AccountsWindow:
break
# Detect if we have opened windows for this account
def remove(account):
- if 'remove_account' in gajim.interface.instances[account]:
+ if account in gajim.interface.instances and \
+ 'remove_account' in gajim.interface.instances[account]:
gajim.interface.instances[account]['remove_account'].window.\
- present()
+ present()
else:
+ if not account in gajim.interface.instances:
+ gajim.interface.instances[account] = {}
gajim.interface.instances[account]['remove_account'] = \
RemoveAccountWindow(account)
if win_opened:
@@ -2071,6 +2120,15 @@ class AccountsWindow:
gajim.config.set_per('accounts', self.current_account, 'hostname',
jid_splited[1])
+ def on_cert_entry1_focus_out_event(self, widget, event):
+ if self.ignore_events:
+ return
+ client_cert = widget.get_text()
+ if self.option_changed('client_cert', client_cert):
+ self.need_relogin = True
+ gajim.config.set_per('accounts', self.current_account, 'client_cert',
+ client_cert)
+
def on_anonymous_checkbutton1_toggled(self, widget):
if self.ignore_events:
return
@@ -2871,12 +2929,19 @@ class RemoveAccountWindow:
def on_remove_button_clicked(self, widget):
def remove():
- if gajim.connections[self.account].connected and \
+ if self.account in gajim.connections and \
+ gajim.connections[self.account].connected and \
not self.remove_and_unregister_radiobutton.get_active():
# change status to offline only if we will not remove this JID from
# server
gajim.connections[self.account].change_status('offline', 'offline')
if self.remove_and_unregister_radiobutton.get_active():
+ if not self.account in gajim.connections:
+ dialogs.ErrorDialog(
+ _('Account is disabled'),
+ _('To unregister from a server, account must be '
+ 'enabled.'))
+ return
if not gajim.connections[self.account].password:
def on_ok(passphrase, checked):
if passphrase == -1:
@@ -2896,11 +2961,12 @@ class RemoveAccountWindow:
else:
self._on_remove_success(True)
- if gajim.connections[self.account].connected:
+ if self.account in gajim.connections and \
+ gajim.connections[self.account].connected:
dialogs.ConfirmationDialog(
- _('Account "%s" is connected to the server') % self.account,
- _('If you remove it, the connection will be lost.'),
- on_response_ok=remove)
+ _('Account "%s" is connected to the server') % self.account,
+ _('If you remove it, the connection will be lost.'),
+ on_response_ok=remove)
else:
remove()
@@ -2920,29 +2986,31 @@ class RemoveAccountWindow:
on_response_ok=self.on_remove_responce_ok, is_modal=False)
return
# Close all opened windows
- gajim.interface.roster.close_all(self.account, force = True)
- gajim.connections[self.account].disconnect(on_purpose = True)
- del gajim.connections[self.account]
+ gajim.interface.roster.close_all(self.account, force=True)
+ if self.account in gajim.connections:
+ gajim.connections[self.account].disconnect(on_purpose=True)
+ del gajim.connections[self.account]
gajim.logger.remove_roster(gajim.get_jid_from_account(self.account))
gajim.config.del_per('accounts', self.account)
gajim.interface.save_config()
del gajim.interface.instances[self.account]
- del gajim.interface.minimized_controls[self.account]
- del gajim.nicks[self.account]
- del gajim.block_signed_in_notifications[self.account]
- del gajim.groups[self.account]
- gajim.contacts.remove_account(self.account)
- del gajim.gc_connected[self.account]
- del gajim.automatic_rooms[self.account]
- del gajim.to_be_removed[self.account]
- del gajim.newly_added[self.account]
- del gajim.sleeper_state[self.account]
- del gajim.encrypted_chats[self.account]
- del gajim.last_message_time[self.account]
- del gajim.status_before_autoaway[self.account]
- del gajim.transport_avatar[self.account]
- del gajim.gajim_optional_features[self.account]
- del gajim.caps_hash[self.account]
+ if self.account in gajim.nicks:
+ del gajim.interface.minimized_controls[self.account]
+ del gajim.nicks[self.account]
+ del gajim.block_signed_in_notifications[self.account]
+ del gajim.groups[self.account]
+ gajim.contacts.remove_account(self.account)
+ del gajim.gc_connected[self.account]
+ del gajim.automatic_rooms[self.account]
+ del gajim.to_be_removed[self.account]
+ del gajim.newly_added[self.account]
+ del gajim.sleeper_state[self.account]
+ del gajim.encrypted_chats[self.account]
+ del gajim.last_message_time[self.account]
+ del gajim.status_before_autoaway[self.account]
+ del gajim.transport_avatar[self.account]
+ del gajim.gajim_optional_features[self.account]
+ del gajim.caps_hash[self.account]
if len(gajim.connections) >= 2: # Do not merge accounts if only one exists
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
else:
@@ -3306,18 +3374,17 @@ class AccountCreationWizardWindow:
self.window.set_transient_for(gajim.interface.roster.window)
completion = gtk.EntryCompletion()
+ completion1 = gtk.EntryCompletion()
# Connect events from comboboxentry.child
server_comboboxentry = self.xml.get_object('server_comboboxentry')
entry = server_comboboxentry.child
entry.connect('key_press_event',
- self.on_server_comboboxentry_key_press_event, server_comboboxentry)
+ self.on_server_comboboxentry_key_press_event, server_comboboxentry)
entry.set_completion(completion)
# Do the same for the other server comboboxentry
server_comboboxentry1 = self.xml.get_object('server_comboboxentry1')
entry = server_comboboxentry1.child
- entry.connect('key_press_event',
- self.on_server_comboboxentry_key_press_event, server_comboboxentry1)
- entry.set_completion(completion)
+ entry.set_completion(completion1)
self.update_proxy_list()
@@ -3331,6 +3398,8 @@ class AccountCreationWizardWindow:
completion.set_model(servers_model)
completion.set_text_column(0)
+ completion1.set_model(servers_model)
+ completion1.set_text_column(0)
# Put servers into comboboxentries
server_comboboxentry.set_model(servers_model)
@@ -3347,9 +3416,9 @@ class AccountCreationWizardWindow:
self.advanced_button = self.xml.get_object('advanced_button')
self.finish_label = self.xml.get_object('finish_label')
self.go_online_checkbutton = self.xml.get_object(
- 'go_online_checkbutton')
+ 'go_online_checkbutton')
self.show_vcard_checkbutton = self.xml.get_object(
- 'show_vcard_checkbutton')
+ 'show_vcard_checkbutton')
self.progressbar = self.xml.get_object('progressbar')
# some vars
@@ -3359,33 +3428,33 @@ class AccountCreationWizardWindow:
self.xml.connect_signals(self)
self.window.show_all()
gajim.ged.register_event_handler('NEW_ACC_CONNECTED', ged.CORE,
- self.new_acc_connected)
+ self.new_acc_connected)
gajim.ged.register_event_handler('NEW_ACC_NOT_CONNECTED', ged.CORE,
- self.new_acc_not_connected)
+ self.new_acc_not_connected)
gajim.ged.register_event_handler('ACC_OK', ged.CORE, self.acc_is_ok)
gajim.ged.register_event_handler('ACC_NOT_OK', ged.CORE,
- self.acc_is_not_ok)
+ self.acc_is_not_ok)
def on_wizard_window_destroy(self, widget):
page = self.notebook.get_current_page()
if page in (4, 5) and self.account in gajim.connections:
- # connection instance is saved in gajim.connections and we canceled the
- # addition of the account
+ # connection instance is saved in gajim.connections and we canceled
+ # the addition of the account
del gajim.connections[self.account]
if self.account in gajim.config.get_per('accounts'):
gajim.config.del_per('accounts', self.account)
gajim.ged.remove_event_handler('NEW_ACC_CONNECTED', ged.CORE,
- self.new_acc_connected)
+ self.new_acc_connected)
gajim.ged.remove_event_handler('NEW_ACC_NOT_CONNECTED', ged.CORE,
- self.new_acc_not_connected)
+ self.new_acc_not_connected)
gajim.ged.remove_event_handler('ACC_OK', ged.CORE, self.acc_is_ok)
gajim.ged.remove_event_handler('ACC_NOT_OK', ged.CORE,
- self.acc_is_not_ok)
+ self.acc_is_not_ok)
del gajim.interface.instances['account_creation_wizard']
def on_register_server_features_button_clicked(self, widget):
helpers.launch_browser_mailer('url',
- 'http://www.jabber.org/network/oldnetwork.shtml')
+ 'http://www.jabber.org/network/oldnetwork.shtml')
def on_save_password_checkbutton_toggled(self, widget):
self.xml.get_object('password_entry').grab_focus()
@@ -3417,7 +3486,8 @@ class AccountCreationWizardWindow:
active = widget.get_active()
self.xml.get_object('username_entry').set_sensitive(not active)
self.xml.get_object('password_entry').set_sensitive(not active)
- self.xml.get_object('save_password_checkbutton').set_sensitive(not active)
+ self.xml.get_object('save_password_checkbutton').set_sensitive(
+ not active)
def show_finish_page(self):
self.cancel_button.hide()
@@ -3425,16 +3495,16 @@ class AccountCreationWizardWindow:
self.forward_button.hide()
if self.modify:
finish_text = '<big><b>%s</b></big>\n\n%s' % (
- _('Account has been added successfully'),
- _('You can set advanced account options by pressing the '
- 'Advanced button, or later by choosing the Accounts menu item '
- 'under the Edit menu from the main window.'))
+ _('Account has been added successfully'),
+ _('You can set advanced account options by pressing the '
+ 'Advanced button, or later by choosing the Accounts menu item '
+ 'under the Edit menu from the main window.'))
else:
finish_text = '<big><b>%s</b></big>\n\n%s' % (
- _('Your new account has been created successfully'),
- _('You can set advanced account options by pressing the Advanced '
- 'button, or later by choosing the Accounts menu item under the Edit'
- ' menu from the main window.'))
+ _('Your new account has been created successfully'),
+ _('You can set advanced account options by pressing the '
+ 'Advanced button, or later by choosing the Accounts menu item '
+ 'under the Edit menu from the main window.'))
self.finish_label.set_markup(finish_text)
self.finish_button.show()
self.finish_button.set_property('has-default', True)
@@ -3465,21 +3535,22 @@ class AccountCreationWizardWindow:
elif cur_page == 1:
# We are adding an existing account
- anonymous = self.xml.get_object('anonymous_checkbutton1').get_active()
+ anonymous = self.xml.get_object('anonymous_checkbutton1').\
+ get_active()
username = self.xml.get_object('username_entry').get_text().decode(
- 'utf-8').strip()
+ 'utf-8').strip()
if not username and not anonymous:
pritext = _('Invalid username')
sectext = _(
- 'You must provide a username to configure this account.')
+ 'You must provide a username to configure this account.')
dialogs.ErrorDialog(pritext, sectext)
return
- server = self.xml.get_object('server_comboboxentry').child.get_text().\
- decode('utf-8').strip()
+ server = self.xml.get_object('server_comboboxentry').child.\
+ get_text().decode('utf-8').strip()
savepass = self.xml.get_object('save_password_checkbutton').\
- get_active()
+ get_active()
password = self.xml.get_object('password_entry').get_text().decode(
- 'utf-8')
+ 'utf-8')
jid = username + '@' + server
# check if jid is conform to RFC and stringprep it
@@ -3504,12 +3575,12 @@ class AccountCreationWizardWindow:
self.show_finish_page()
elif cur_page == 2:
# We are creating a new account
- server = self.xml.get_object('server_comboboxentry1').child.get_text()\
- .decode('utf-8')
+ server = self.xml.get_object('server_comboboxentry1').child.\
+ get_text().decode('utf-8')
if not server:
dialogs.ErrorDialog(_('Invalid server'),
- _('Please provide a server on which you want to register.'))
+ _('Please provide a server on which you want to register.'))
return
self.account = server
i = 1
@@ -3527,17 +3598,17 @@ class AccountCreationWizardWindow:
config['proxy'] = proxy
config['use_custom_host'] = self.xml.get_object(
- 'custom_host_port_checkbutton').get_active()
+ 'custom_host_port_checkbutton').get_active()
custom_port = self.xml.get_object('custom_port_entry').get_text()
try:
custom_port = int(custom_port)
except Exception:
dialogs.ErrorDialog(_('Invalid entry'),
- _('Custom port must be a port number.'))
+ _('Custom port must be a port number.'))
return
config['custom_port'] = custom_port
config['custom_host'] = self.xml.get_object(
- 'custom_host_entry').get_text().decode('utf-8')
+ 'custom_host_entry').get_text().decode('utf-8')
if self.xml.get_object('anonymous_checkbutton2').get_active():
self.modify = True
@@ -3548,7 +3619,7 @@ class AccountCreationWizardWindow:
self.back_button.hide()
self.forward_button.hide()
self.update_progressbar_timeout_id = gobject.timeout_add(100,
- self.update_progressbar)
+ self.update_progressbar)
# Get form from serveur
con = connection.Connection(self.account)
gajim.connections[self.account] = con
@@ -3557,7 +3628,7 @@ class AccountCreationWizardWindow:
checked = self.xml.get_object('ssl_checkbutton').get_active()
if checked:
hostname = gajim.connections[self.account].new_account_info[
- 'hostname']
+ 'hostname']
# Check if cert is already in file
certs = ''
if os.path.isfile(gajim.MY_CACERTS):
@@ -3566,14 +3637,15 @@ class AccountCreationWizardWindow:
f.close()
if self.ssl_cert in certs:
dialogs.ErrorDialog(_('Certificate Already in File'),
- _('This certificate is already in file %s, so it\'s not added again.') % gajim.MY_CACERTS)
+ _('This certificate is already in file %s, so it\'s '
+ 'not added again.') % gajim.MY_CACERTS)
else:
f = open(gajim.MY_CACERTS, 'a')
f.write(hostname + '\n')
f.write(self.ssl_cert + '\n\n')
f.close()
gajim.connections[self.account].new_account_info[
- 'ssl_fingerprint_sha1'] = self.ssl_fingerprint
+ 'ssl_fingerprint_sha1'] = self.ssl_fingerprint
self.notebook.set_current_page(4) # show fom page
elif cur_page == 4:
if self.is_form:
@@ -3581,14 +3653,15 @@ class AccountCreationWizardWindow:
else:
form = self.data_form_widget.get_infos()
gajim.connections[self.account].send_new_account_infos(form,
- self.is_form)
+ self.is_form)
self.xml.get_object('form_vbox').remove(self.data_form_widget)
- self.xml.get_object('progressbar_label').set_markup('<b>Account is being created</b>\n\nPlease wait...')
+ self.xml.get_object('progressbar_label').set_markup(
+ '<b>Account is being created</b>\n\nPlease wait...')
self.notebook.set_current_page(5) # show creating page
self.back_button.hide()
self.forward_button.hide()
self.update_progressbar_timeout_id = gobject.timeout_add(100,
- self.update_progressbar)
+ self.update_progressbar)
def update_proxy_list(self):
proxies_combobox = self.xml.get_object('proxies_combobox')
@@ -3605,10 +3678,11 @@ class AccountCreationWizardWindow:
gajim.interface.instances['manage_proxies'].window.present()
else:
gajim.interface.instances['manage_proxies'] = \
- ManageProxiesWindow()
+ ManageProxiesWindow()
def on_custom_host_port_checkbutton_toggled(self, widget):
- self.xml.get_object('custom_host_hbox').set_sensitive(widget.get_active())
+ self.xml.get_object('custom_host_hbox').set_sensitive(widget.\
+ get_active())
def update_progressbar(self):
self.progressbar.pulse()
@@ -3639,13 +3713,16 @@ class AccountCreationWizardWindow:
if ssl_msg:
# An SSL warning occured, show it
hostname = gajim.connections[self.account].new_account_info['hostname']
- self.xml.get_object('ssl_label').set_markup(_('<b>Security Warning</b>'
- '\n\nThe authenticity of the %(hostname)s SSL certificate could be '
- 'invalid.\nSSL Error: %(error)s\n'
- 'Do you still want to connect to this server?') % {
- 'hostname': hostname, 'error': ssl_msg})
+ self.xml.get_object('ssl_label').set_markup(_(
+ '<b>Security Warning</b>'
+ '\n\nThe authenticity of the %(hostname)s SSL certificate could'
+ ' be invalid.\nSSL Error: %(error)s\n'
+ 'Do you still want to connect to this server?') % {
+ 'hostname': hostname, 'error': ssl_msg})
if ssl_err in (18, 27):
- text = _('Add this certificate to the list of trusted certificates.\nSHA1 fingerprint of the certificate:\n%s') % ssl_fingerprint
+ text = _('Add this certificate to the list of trusted '
+ 'certificates.\nSHA1 fingerprint of the certificate:\n%s') \
+ % ssl_fingerprint
self.xml.get_object('ssl_checkbutton').set_label(text)
else:
self.xml.get_object('ssl_checkbutton').set_no_show_all(True)
@@ -3675,7 +3752,7 @@ class AccountCreationWizardWindow:
img = self.xml.get_object('finish_image')
img.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
finish_text = '<big><b>%s</b></big>\n\n%s' % (
- _('An error occurred during account creation'), reason)
+ _('An error occurred during account creation'), reason)
self.finish_label.set_markup(finish_text)
self.notebook.set_current_page(6) # show finish page
@@ -3708,8 +3785,8 @@ class AccountCreationWizardWindow:
gajim.config.del_per('accounts', self.account)
img = self.xml.get_object('finish_image')
img.set_from_stock(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_DIALOG)
- finish_text = '<big><b>%s</b></big>\n\n%s' % (_('An error occurred during '
- 'account creation'), reason)
+ finish_text = '<big><b>%s</b></big>\n\n%s' % (_(
+ 'An error occurred during account creation'), reason)
self.finish_label.set_markup(finish_text)
self.notebook.set_current_page(6) # show finish page
@@ -3721,8 +3798,7 @@ class AccountCreationWizardWindow:
gajim.interface.instances['accounts'].window.present()
else:
gajim.interface.instances['accounts'] = AccountsWindow()
- gajim.interface.instances['accounts'].select_account(
- self.account)
+ gajim.interface.instances['accounts'].select_account(self.account)
self.window.destroy()
def on_finish_button_clicked(self, widget):
@@ -3776,7 +3852,7 @@ class AccountCreationWizardWindow:
def save_account(self, login, server, savepass, password, anonymous=False):
if self.account in gajim.connections:
dialogs.ErrorDialog(_('Account name is in use'),
- _('You already have an account using this name.'))
+ _('You already have an account using this name.'))
return
con = connection.Connection(self.account)
con.password = password
@@ -3800,11 +3876,11 @@ class AccountCreationWizardWindow:
# update variables
gajim.interface.instances[self.account] = {'infos': {}, 'disco': {},
- 'gc_config': {}, 'search': {}, 'online_dialog': {}}
+ 'gc_config': {}, 'search': {}, 'online_dialog': {}}
gajim.interface.minimized_controls[self.account] = {}
gajim.connections[self.account].connected = 0
gajim.connections[self.account].keepalives = gajim.config.get_per(
- 'accounts', self.account, 'keep_alive_every_foo_secs')
+ 'accounts', self.account, 'keep_alive_every_foo_secs')
gajim.groups[self.account] = {}
gajim.contacts.add_account(self.account)
gajim.gc_connected[self.account] = {}
@@ -3824,7 +3900,8 @@ class AccountCreationWizardWindow:
if 'accounts' in gajim.interface.instances:
gajim.interface.instances['accounts'].init_accounts()
# refresh roster
- if len(gajim.connections) >= 2: # Do not merge accounts if only one exists
+ if len(gajim.connections) >= 2:
+ # Do not merge accounts if only one exists
gajim.interface.roster.regroup = gajim.config.get('mergeaccounts')
else:
gajim.interface.roster.regroup = False
diff --git a/src/conversation_textview.py b/src/conversation_textview.py
index c0f5b83ed..46e60dfee 100644
--- a/src/conversation_textview.py
+++ b/src/conversation_textview.py
@@ -5,7 +5,7 @@
## Copyright (C) 2005-2006 Alex Mauer <hawke AT hawkesnest.net>
## Travis Shirk <travis AT pobox.com>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
@@ -326,6 +326,7 @@ class ConversationTextview(gobject.GObject):
tag.set_property('underline', pango.UNDERLINE_SINGLE)
buffer_.create_tag('focus-out-line', justification = gtk.JUSTIFY_CENTER)
+ self.displaymarking_tags = {}
tag = buffer_.create_tag('xep0184-warning')
@@ -703,6 +704,7 @@ class ConversationTextview(gobject.GObject):
size = 2 * size - 1
self.marks_queue = Queue.Queue(size)
self.focus_out_end_mark = None
+ self.just_cleared = True
def visit_url_from_menuitem(self, widget, link):
"""
@@ -1167,11 +1169,12 @@ class ConversationTextview(gobject.GObject):
buffer_ = self.tv.get_buffer()
end_iter = buffer_.get_end_iter()
buffer_.insert_with_tags_by_name(end_iter, '\n', 'eol')
+ self.just_cleared = False
def print_conversation_line(self, text, jid, kind, name, tim,
other_tags_for_name=[], other_tags_for_time=[],
other_tags_for_text=[], subject=None, old_kind=None, xhtml=None,
- simple=False, graphics=True):
+ simple=False, graphics=True, displaymarking=None):
"""
Print 'chat' type messages
"""
@@ -1236,6 +1239,9 @@ class ConversationTextview(gobject.GObject):
tim_format = self.get_time_to_show(tim)
buffer_.insert_with_tags_by_name(end_iter, tim_format + '\n',
'time_sometimes')
+ # If there's a displaymarking, print it here.
+ if displaymarking:
+ self.print_displaymarking(displaymarking)
# kind = info, we print things as if it was a status: same color, ...
if kind in ('error', 'info'):
kind = 'status'
@@ -1246,7 +1252,7 @@ class ConversationTextview(gobject.GObject):
text_tags.append(other_text_tag)
else: # not status nor /me
if gajim.config.get('chat_merge_consecutive_nickname'):
- if kind != old_kind:
+ if kind != old_kind or self.just_cleared:
self.print_name(name, kind, other_tags_for_name)
else:
self.print_real_text(gajim.config.get(
@@ -1269,6 +1275,7 @@ class ConversationTextview(gobject.GObject):
else:
gobject.idle_add(self.scroll_to_end)
+ self.just_cleared = False
buffer_.end_user_action()
def get_time_to_show(self, tim):
@@ -1306,6 +1313,19 @@ class ConversationTextview(gobject.GObject):
elif text.startswith('/me ') or text.startswith('/me\n'):
return kind
+ def print_displaymarking(self, displaymarking):
+ bgcolor = displaymarking.getAttr('bgcolor') or '#FFF'
+ fgcolor = displaymarking.getAttr('fgcolor') or '#000'
+ text = displaymarking.getData()
+ if text:
+ buffer_ = self.tv.get_buffer()
+ end_iter = buffer_.get_end_iter()
+ tag = self.displaymarking_tags.setdefault(bgcolor + '/' + fgcolor,
+ buffer_.create_tag(None, background=bgcolor, foreground=fgcolor))
+ buffer_.insert_with_tags(end_iter, '[' + text + ']', tag)
+ end_iter = buffer_.get_end_iter()
+ buffer_.insert_with_tags(end_iter, ' ')
+
def print_name(self, name, kind, other_tags_for_name):
if name:
buffer_ = self.tv.get_buffer()
diff --git a/src/dataforms_widget.py b/src/dataforms_widget.py
index 5fa9a2397..27466f892 100644
--- a/src/dataforms_widget.py
+++ b/src/dataforms_widget.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/dataforms_widget.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
##
@@ -27,6 +27,7 @@ multiple - these which may contain more data (with <reported/> element).'''
import gtk
import gobject
+import base64
import gtkgui_helpers
import dialogs
@@ -400,7 +401,7 @@ class SingleForm(gtk.Table, object):
check.set_active(value in field.values)
check.connect('toggled',
self.on_list_multi_checkbutton_toggled, field, value)
- widget.pack_start(check, expand=False)
+ widget.pack_start(check, expand=False)
else:
# more than 5 options: show combobox
def on_list_multi_treeview_changed(selection, f):
@@ -428,9 +429,9 @@ class SingleForm(gtk.Table, object):
commonwidget = False
xml = gtkgui_helpers.get_gtk_builder('data_form_window.ui',
- 'item_list_table')
- widget = xml.get_object('item_list_table')
- treeview = xml.get_object('item_treeview')
+ 'multiple_form_hbox')
+ widget = xml.get_object('multiple_form_hbox')
+ treeview = xml.get_object('records_treeview')
listmodel = gtk.ListStore(str)
for value in field.iter_values():
@@ -529,6 +530,25 @@ class SingleForm(gtk.Table, object):
self.attach(label, 0, 1, linecounter, linecounter+1,
xoptions=gtk.FILL, yoptions=gtk.FILL)
+ if field.media is not None:
+ for uri in field.media.uris:
+ if uri.type_.startswith('image/'):
+ try:
+ img_data = base64.decodestring(uri.uri_data)
+ pixbuf_l = gtk.gdk.PixbufLoader()
+ pixbuf_l.write(img_data)
+ pixbuf_l.close()
+ media = gtk.image_new_from_pixbuf(pixbuf_l.\
+ get_pixbuf())
+ except Exception:
+ media = gtk.Label(_('Unable to load image'))
+ else:
+ media = gtk.Label(_('Media type not supported: %s') % \
+ uri.type_)
+ linecounter += 1
+ self.attach(media, 0, 1, linecounter, linecounter+1,
+ xoptions=gtk.FILL, yoptions=gtk.FILL)
+
if commonwidget:
assert widget is not None
widget.set_sensitive(readwrite)
diff --git a/src/dialogs.py b/src/dialogs.py
index a77701b71..a1adc58bb 100644
--- a/src/dialogs.py
+++ b/src/dialogs.py
@@ -2,7 +2,7 @@
## src/dialogs.py
##
## Copyright (C) 2003-2005 Vincent Hanquez <tab AT snarc.org>
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Travis Shirk <travis AT pobox.com>
@@ -38,6 +38,7 @@ import vcard
import conversation_textview
import message_control
import dataforms_widget
+import disco
from random import randrange
from common import pep
@@ -992,7 +993,7 @@ _('Please fill in the data of the contact you want to add in account %s') % acco
if self.account:
message_buffer = self.message_textview.get_buffer()
message_buffer.set_text(helpers.get_subscription_request_msg(
- self.account))
+ self.account))
def on_add_new_contact_window_destroy(self, widget):
if self.account:
@@ -1163,7 +1164,7 @@ class AboutDialog:
dlg.set_transient_for(gajim.interface.roster.window)
dlg.set_name('Gajim')
dlg.set_version(gajim.version)
- s = u'Copyright © 2003-2009 Gajim Team'
+ s = u'Copyright © 2003-2010 Gajim Team'
dlg.set_copyright(s)
copying_file_path = self.get_path('COPYING')
if copying_file_path:
@@ -1241,8 +1242,9 @@ class AboutDialog:
class Dialog(gtk.Dialog):
def __init__(self, parent, title, buttons, default=None,
- on_response_ok=None, on_response_cancel=None):
- gtk.Dialog.__init__(self, title, parent, gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR)
+ on_response_ok=None, on_response_cancel=None):
+ gtk.Dialog.__init__(self, title, parent,
+ gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR)
self.user_response_ok = on_response_ok
self.user_response_cancel = on_response_cancel
@@ -1479,8 +1481,8 @@ class InformationDialog(HigDialog):
"""
def __init__(self, pritext, sectext=''):
- HigDialog.__init__(self, None,
- gtk.MESSAGE_INFO, gtk.BUTTONS_OK, pritext, sectext)
+ HigDialog.__init__(self, None, gtk.MESSAGE_INFO, gtk.BUTTONS_OK,
+ pritext, sectext)
self.set_modal(False)
self.set_transient_for(gajim.interface.roster.window)
self.popup()
@@ -1490,9 +1492,11 @@ class ErrorDialog(HigDialog):
HIG compliant error dialog
"""
- def __init__(self, pritext, sectext=''):
- HigDialog.__init__( self, None,
- gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, pritext, sectext)
+ def __init__(self, pritext, sectext='', on_response_ok=None,
+ on_response_cancel=None):
+ HigDialog.__init__( self, None, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK,
+ pritext, sectext, on_response_ok=on_response_ok,
+ on_response_cancel=on_response_cancel)
self.popup()
class YesNoDialog(HigDialog):
@@ -1594,7 +1598,7 @@ class ConfirmationDialogCheck(ConfirmationDialog):
"""
return self.checkbutton.get_active()
-class ConfirmationDialogDubbleCheck(ConfirmationDialog):
+class ConfirmationDialogDoubleCheck(ConfirmationDialog):
"""
HIG compliant confirmation dialog with 2 checkbuttons
"""
@@ -1869,10 +1873,15 @@ class ChangeNickDialog(InputDialogCheck):
Class for changing room nickname in case of conflict
"""
- def __init__(self, account, room_jid, title, prompt, check_text=None):
+ def __init__(self, account, room_jid, title, prompt, check_text=None,
+ change_nick=False):
+ """
+ change_nick must be set to True when we are already occupant of the room
+ and we are changing our nick
+ """
InputDialogCheck.__init__(self, title, '', checktext=check_text,
input_str='', is_modal=True, ok_handler=None, cancel_handler=None)
- self.room_queue = [(account, room_jid, prompt)]
+ self.room_queue = [(account, room_jid, prompt, change_nick)]
self.check_next()
def on_input_dialog_delete_event(self, widget, event):
@@ -1901,7 +1910,8 @@ class ChangeNickDialog(InputDialogCheck):
if 'change_nick_dialog' in gajim.interface.instances:
del gajim.interface.instances['change_nick_dialog']
return
- self.account, self.room_jid, self.prompt = self.room_queue.pop(0)
+ self.account, self.room_jid, self.prompt, self.change_nick = \
+ self.room_queue.pop(0)
self.setup_dialog()
if gajim.new_room_nick is not None and not gajim.gc_connected[
@@ -1930,7 +1940,7 @@ class ChangeNickDialog(InputDialogCheck):
if is_checked:
gajim.new_room_nick = nick
gajim.connections[self.account].join_gc(nick, self.room_jid, None,
- change_nick=True)
+ change_nick=self.change_nick)
if gajim.gc_connected[self.account][self.room_jid]:
# We are changing nick, we will change self.nick when we receive
# presence that inform that it works
@@ -1945,9 +1955,9 @@ class ChangeNickDialog(InputDialogCheck):
self.gc_control.new_nick = ''
self.check_next()
- def add_room(self, account, room_jid, prompt):
- if (account, room_jid, prompt) not in self.room_queue:
- self.room_queue.append((account, room_jid, prompt))
+ def add_room(self, account, room_jid, prompt, change_nick=False):
+ if (account, room_jid, prompt, change_nick) not in self.room_queue:
+ self.room_queue.append((account, room_jid, prompt, change_nick))
class InputTextDialog(CommonInputDialog):
"""
@@ -1969,9 +1979,9 @@ class InputTextDialog(CommonInputDialog):
start_iter, end_iter = self.input_buffer.get_bounds()
return self.input_buffer.get_text(start_iter, end_iter).decode('utf-8')
-class DubbleInputDialog:
+class DoubleInputDialog:
"""
- Class for Dubble Input dialog
+ Class for Double Input dialog
"""
def __init__(self, title, label_str1, label_str2, input_str1=None,
@@ -2156,7 +2166,6 @@ class JoinGroupchatWindow:
self._nickname_entry = self.xml.get_object('nickname_entry')
self._password_entry = self.xml.get_object('password_entry')
- self._room_jid_entry.set_text(room_jid)
self._nickname_entry.set_text(nick)
if password:
self._password_entry.set_text(password)
@@ -2171,6 +2180,13 @@ class JoinGroupchatWindow:
title = _('Join Group Chat')
self.window.set_title(title)
+ self.server_comboboxentry = self.xml.get_object('server_comboboxentry')
+ self.server_model = self.server_comboboxentry.get_model()
+ server_list = []
+ # get the muc server of our server
+ if 'jabber' in gajim.connections[account].muc_jid:
+ server_list.append(gajim.connections[account].muc_jid['jabber'])
+
self.recently_combobox = self.xml.get_object('recently_combobox')
liststore = gtk.ListStore(str)
self.recently_combobox.set_model(liststore)
@@ -2180,6 +2196,16 @@ class JoinGroupchatWindow:
self.recently_groupchat = gajim.config.get('recently_groupchat').split()
for g in self.recently_groupchat:
self.recently_combobox.append_text(g)
+ server = gajim.get_server_from_jid(g)
+ if server not in server_list and not server.startswith('irc'):
+ server_list.append(server)
+
+ for s in server_list:
+ self.server_model.append([s])
+ self.server_comboboxentry.set_active(0)
+
+ self._set_room_jid(room_jid)
+
if len(self.recently_groupchat) == 0:
self.recently_combobox.set_sensitive(False)
elif room_jid == '':
@@ -2220,8 +2246,16 @@ class JoinGroupchatWindow:
else:
if widget in self._empty_required_widgets:
self._empty_required_widgets.remove(widget)
- if len(self._empty_required_widgets) == 0 and self.account:
+ if not self._empty_required_widgets and self.account:
self.xml.get_object('join_button').set_sensitive(True)
+ text = self._room_jid_entry.get_text()
+ if widget == self._room_jid_entry and '@' in text:
+ # Don't allow @ char in room entry
+ room_jid, server = text.split('@', 1)
+ self._room_jid_entry.set_text(room_jid)
+ if server:
+ self.server_comboboxentry.child.set_text(server)
+ self.server_comboboxentry.grab_focus()
def on_account_combobox_changed(self, widget):
model = widget.get_model()
@@ -2229,11 +2263,30 @@ class JoinGroupchatWindow:
self.account = model[iter_][0].decode('utf-8')
self.on_required_entry_changed(self._nickname_entry)
+ def _set_room_jid(self, room_jid):
+ room, server = gajim.get_name_and_server_from_jid(room_jid)
+ self._room_jid_entry.set_text(room)
+ self.server_comboboxentry.child.set_text(server)
+
def on_recently_combobox_changed(self, widget):
model = widget.get_model()
iter_ = widget.get_active_iter()
room_jid = model[iter_][0].decode('utf-8')
- self._room_jid_entry.set_text(room_jid)
+ self._set_room_jid(room_jid)
+
+ def on_browse_rooms_button_clicked(self, widget):
+ server = self.server_comboboxentry.child.get_text().decode('utf-8')
+ if server in gajim.interface.instances[self.account]['disco']:
+ gajim.interface.instances[self.account]['disco'][server].window.\
+ present()
+ else:
+ try:
+ # Object will add itself to the window dict
+ disco.ServiceDiscoveryWindow(self.account, server,
+ initial_identities=[{'category': 'conference',
+ 'type': 'text'}])
+ except GajimGeneralException:
+ pass
def on_cancel_button_clicked(self, widget):
"""
@@ -2258,7 +2311,9 @@ class JoinGroupchatWindow:
'groupchat.'))
return
nickname = self._nickname_entry.get_text().decode('utf-8')
- room_jid = self._room_jid_entry.get_text().decode('utf-8')
+ server = self.server_comboboxentry.child.get_text().decode('utf-8')
+ room = self._room_jid_entry.get_text().decode('utf-8')
+ room_jid = room + '@' + server
password = self._password_entry.get_text().decode('utf-8')
try:
nickname = helpers.parse_resource(nickname)
@@ -2963,10 +3018,11 @@ class XMLConsoleWindow:
self.tagOutIq.set_property('foreground', color)
buffer_.create_tag('') # Default tag
- self.enabled = False
+ self.enabled = True
+ self.xml.get_object('enable_checkbutton').set_active(True)
self.input_textview.modify_text(
- gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
+ gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
if len(gajim.connections) > 1:
title = _('XML Console for %s') % self.account
@@ -2978,9 +3034,8 @@ class XMLConsoleWindow:
self.xml.connect_signals(self)
- def on_xml_console_window_delete_event(self, widget, event):
- self.window.hide()
- return True # do NOT destroy the window
+ def on_xml_console_window_destroy(self, widget):
+ del gajim.interface.instances[self.account]['xml_console']
def on_clear_button_clicked(self, widget):
buffer_ = self.stanzas_log_textview.get_buffer()
@@ -3059,42 +3114,41 @@ class XMLConsoleWindow:
type_ = kind # 'incoming' or 'outgoing'
if kind == 'incoming':
- buffer.insert_with_tags_by_name(end_iter, '<!-- In -->\n',
- type_)
+ buffer.insert_with_tags_by_name(end_iter, '<!-- In -->\n', type_)
elif kind == 'outgoing':
- buffer.insert_with_tags_by_name(end_iter, '<!-- Out -->\n',
- type_)
+ buffer.insert_with_tags_by_name(end_iter, '<!-- Out -->\n', type_)
end_iter = buffer.get_end_iter()
- buffer.insert_with_tags_by_name(end_iter, stanza.replace('><', '>\n<') +\
- '\n\n', type_)
+ buffer.insert_with_tags_by_name(end_iter, stanza.replace('><', '>\n<') \
+ + '\n\n', type_)
if at_the_end:
gobject.idle_add(self.scroll_to_end)
def on_send_button_clicked(self, widget):
if gajim.connections[self.account].connected <= 1:
- #if offline or connecting
+ # if offline or connecting
ErrorDialog(_('Connection not available'),
- _('Please make sure you are connected with "%s".') % self.account)
+ _('Please make sure you are connected with "%s".') % \
+ self.account)
return
begin_iter, end_iter = self.input_tv_buffer.get_bounds()
stanza = self.input_tv_buffer.get_text(begin_iter, end_iter).decode(
- 'utf-8')
+ 'utf-8')
if stanza:
gajim.connections[self.account].send_stanza(stanza)
self.input_tv_buffer.set_text('') # we sent ok, clear the textview
def on_presence_button_clicked(self, widget):
self.input_tv_buffer.set_text(
- '<presence><show></show><status></status><priority></priority>'
- '</presence>')
+ '<presence><show></show><status></status><priority></priority>'
+ '</presence>')
def on_iq_button_clicked(self, widget):
self.input_tv_buffer.set_text(
- '<iq to="" type=""><query xmlns=""></query></iq>')
+ '<iq to="" type=""><query xmlns=""></query></iq>')
def on_message_button_clicked(self, widget):
self.input_tv_buffer.set_text(
- '<message to="" type=""><body></body></message>')
+ '<message to="" type=""><body></body></message>')
def on_expander_activate(self, widget):
if not widget.get_expanded(): # it's the opposite!
@@ -3103,7 +3157,7 @@ class XMLConsoleWindow:
#Action that can be done with an incoming list of contacts
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
- 'remove': _('remove')}
+ 'remove': _('remove')}
class RosterItemExchangeWindow:
"""
Windows used when someone send you a exchange contact suggestion
@@ -3857,6 +3911,50 @@ class ProgressDialog:
return True # WM's X button or Escape key should not destroy the window
+class ClientCertChooserDialog(FileChooserDialog):
+ def __init__(self, path_to_clientcert_file='', on_response_ok=None,
+ on_response_cancel=None):
+ '''
+ optionally accepts path_to_clientcert_file so it has that as selected
+ '''
+ def on_ok(widget, callback):
+ '''
+ check if file exists and call callback
+ '''
+ path_to_clientcert_file = self.get_filename()
+ path_to_clientcert_file = \
+ gtkgui_helpers.decode_filechooser_file_paths(
+ (path_to_clientcert_file,))[0]
+ if os.path.exists(path_to_clientcert_file):
+ callback(widget, path_to_clientcert_file)
+
+ FileChooserDialog.__init__(self,
+ title_text=_('Choose Client Cert #PCKS12'),
+ action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OPEN, gtk.RESPONSE_OK),
+ current_folder='',
+ default_response=gtk.RESPONSE_OK,
+ on_response_ok=(on_ok, on_response_ok),
+ on_response_cancel=on_response_cancel)
+
+ filter_ = gtk.FileFilter()
+ filter_.set_name(_('All files'))
+ filter_.add_pattern('*')
+ self.add_filter(filter_)
+
+ filter_ = gtk.FileFilter()
+ filter_.set_name(_('PKCS12 Files'))
+ filter_.add_pattern('*.p12')
+ self.add_filter(filter_)
+ self.set_filter(filter_)
+
+ if path_to_clientcert_file:
+ # set_filename accept only absolute path
+ path_to_clientcert_file = os.path.abspath(path_to_clientcert_file)
+ self.set_filename(path_to_clientcert_file)
+
+
class SoundChooserDialog(FileChooserDialog):
def __init__(self, path_to_snd_file='', on_response_ok=None,
on_response_cancel=None):
@@ -4861,6 +4959,15 @@ class VoIPCallReceivedDialog(object):
self.content_types.add(type_)
self.set_secondary_text()
+ def remove_contents(self, content_types):
+ for type_ in content_types:
+ if type_ in self.content_types:
+ self.content_types.remove(type_)
+ if not self.content_types:
+ self.dialog.destroy()
+ else:
+ self.set_secondary_text()
+
def on_voip_call_received_messagedialog_destroy(self, dialog):
if (self.fjid, self.sid) in self.instances:
del self.instances[(self.fjid, self.sid)]
@@ -4879,17 +4986,10 @@ class VoIPCallReceivedDialog(object):
#TODO: Ensure that ctrl.contact.resource == resource
jid = gajim.get_jid_without_resource(self.fjid)
resource = gajim.get_resource_from_jid(self.fjid)
- ctrl = gajim.interface.msg_win_mgr.get_control(self.fjid, self.account)
- if not ctrl:
- ctrl = gajim.interface.msg_win_mgr.get_control(jid, self.account)
- if not ctrl:
- # open chat control
- contact = gajim.contacts.get_contact(self.account, jid, resource)
- if not contact:
- contact = gajim.contacts.get_contact(self.account, jid)
- if not contact:
- return
- ctrl = gajim.interface.new_chat(contact, self.account, resource)
+ ctrl = (gajim.interface.msg_win_mgr.get_control(self.fjid, self.account)
+ or gajim.interface.msg_win_mgr.get_control(jid, self.account)
+ or gajim.interface.new_chat_from_jid(self.account, jid))
+
# Chat control opened, update content's status
audio = session.get_content('audio')
video = session.get_content('video')
diff --git a/src/disco.py b/src/disco.py
index 46f0e82ac..37d91d93b 100644
--- a/src/disco.py
+++ b/src/disco.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2005-2006 Stéphan Kochen <stephan AT kochen.nl>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
@@ -492,8 +492,8 @@ class ServiceDiscoveryWindow(object):
Class that represents the Services Discovery window
"""
- def __init__(self, account, jid = '', node = '',
- address_entry = False, parent = None):
+ def __init__(self, account, jid='', node='', address_entry=False,
+ parent=None, initial_identities=None):
self.account = account
self.parent = parent
if not jid:
@@ -519,6 +519,9 @@ _('Without a connection, you can not browse available services'))
self.cache = ServicesCache(account)
gajim.connections[account].services_cache = self.cache
+ if initial_identities:
+ self.cache.agent_info(account, (jid, node, initial_identities, [],
+ None))
self.xml = gtkgui_helpers.get_gtk_builder('service_discovery_window.ui')
self.window = self.xml.get_object('service_discovery_window')
self.services_treeview = self.xml.get_object('services_treeview')
@@ -546,10 +549,8 @@ _('Without a connection, you can not browse available services'))
self.address_comboboxentry_entry = self.address_comboboxentry.child
self.address_comboboxentry_entry.set_activates_default(True)
- liststore = gtk.ListStore(str)
- self.address_comboboxentry.set_model(liststore)
self.latest_addresses = gajim.config.get(
- 'latest_disco_addresses').split()
+ 'latest_disco_addresses').split()
if jid in self.latest_addresses:
self.latest_addresses.remove(jid)
self.latest_addresses.insert(0, jid)
@@ -1670,6 +1671,7 @@ class ToplevelAgentBrowser(AgentBrowser):
# Search for an icon and category we can display
pix = self.cache.get_icon(identities)
+ cat, type_ = None, None
for identity in identities:
try:
cat, type_ = identity['category'], identity['type']
diff --git a/src/features_window.py b/src/features_window.py
index e15c9b21d..e9a823c16 100644
--- a/src/features_window.py
+++ b/src/features_window.py
@@ -5,7 +5,7 @@
## Julien Pivotto <roidelapluie AT gmail.com>
## Stefan Bethge <stefan AT lanpartei.de>
## Stephan Erb <steve-e AT h3c.de>
-## Copyright (C) 2007-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
## This file is part of Gajim.
@@ -31,6 +31,7 @@ import gtkgui_helpers
from common import gajim
from common import helpers
from common import kwalletbinding
+from common.i18n import Q_
class FeaturesWindow:
"""
@@ -111,7 +112,7 @@ class FeaturesWindow:
self.model = gtk.ListStore(str, bool)
treeview.set_model(self.model)
- col = gtk.TreeViewColumn(_('Available'))
+ col = gtk.TreeViewColumn(Q_('?features:Available'))
treeview.append_column(col)
cell = gtk.CellRendererToggle()
cell.set_property('radio', True)
diff --git a/src/filetransfers_window.py b/src/filetransfers_window.py
index 621386eb7..62c9948af 100644
--- a/src/filetransfers_window.py
+++ b/src/filetransfers_window.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/filetransfers_window.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
@@ -396,7 +396,7 @@ class FileTransfersWindow:
on_response_ok=(on_response_ok, account, contact, file_props),
on_response_cancel=(on_response_cancel, account, file_props))
dialog.connect('delete-event', lambda widget, event:
- on_response_cancel(widget, account, file_props))
+ on_response_cancel(account, file_props))
dialog.popup()
def get_icon(self, ident):
diff --git a/src/gajim-remote.py b/src/gajim-remote.py
index 555319a4f..ea4a064b2 100644
--- a/src/gajim-remote.py
+++ b/src/gajim-remote.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Junglecow <junglecow AT gmail.com>
## Travis Shirk <travis AT pobox.com>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
@@ -534,6 +534,8 @@ class GajimRemote:
self.arguments += ['']*(len(args)-i)
def handle_uri(self):
+ if len(sys.argv) < 3:
+ send_error(_('No uri given'))
if not sys.argv[2].startswith('xmpp:'):
send_error(_('Wrong uri'))
sys.argv[2] = sys.argv[2][5:]
diff --git a/src/gajim.py b/src/gajim.py
index e3c9bb5a2..05fbe74fe 100644
--- a/src/gajim.py
+++ b/src/gajim.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/gajim.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
## Norman Rasmussen <norman AT rasmussen.co.za>
@@ -70,32 +70,34 @@ import getopt
from common import i18n
def parseOpts():
- profile = ''
- config_path = None
+ profile_ = ''
+ config_path_ = None
try:
shortargs = 'hqvl:p:c:'
longargs = 'help quiet verbose loglevel= profile= config_path='
opts = getopt.getopt(sys.argv[1:], shortargs, longargs.split())[0]
- except getopt.error, msg:
- print msg
+ except getopt.error, msg1:
+ print msg1
print 'for help use --help'
sys.exit(2)
for o, a in opts:
if o in ('-h', '--help'):
- print 'gajim [--help] [--quiet] [--verbose] [--loglevel subsystem=level[,subsystem=level[...]]] [--profile name] [--config-path]'
+ print 'gajim [--help] [--quiet] [--verbose] ' + \
+ '[--loglevel subsystem=level[,subsystem=level[...]]] ' + \
+ '[--profile name] [--config-path]'
sys.exit()
elif o in ('-q', '--quiet'):
logging_helpers.set_quiet()
elif o in ('-v', '--verbose'):
logging_helpers.set_verbose()
elif o in ('-p', '--profile'): # gajim --profile name
- profile = a
+ profile_ = a
elif o in ('-l', '--loglevel'):
logging_helpers.set_loglevels(a)
elif o in ('-c', '--config-path'):
- config_path = a
- return profile, config_path
+ config_path_ = a
+ return profile_, config_path_
profile, config_path = parseOpts()
del parseOpts
@@ -114,8 +116,8 @@ if os.name == 'nt':
_file = None
_error = None
def write(self, text):
- fname=os.path.join(common.configpaths.gajimpaths.cache_root,
- os.path.split(sys.executable)[1]+'.log')
+ fname = os.path.join(common.configpaths.gajimpaths.cache_root,
+ os.path.split(sys.executable)[1]+'.log')
if self._file is None and self._error is None:
try:
self._file = open(fname, 'a')
@@ -133,15 +135,18 @@ if os.name == 'nt':
# PyGTK2.10+ only throws a warning
warnings.filterwarnings('error', module='gtk')
try:
+ import gobject
import gtk
-except Warning, msg:
- if str(msg) == 'could not open display':
+except Warning, msg2:
+ if str(msg2) == 'could not open display':
print >> sys.stderr, _('Gajim needs X server to run. Quiting...')
else:
- print >> sys.stderr, _('importing PyGTK failed: %s') % str(msg)
+ print >> sys.stderr, _('importing PyGTK failed: %s') % str(msg2)
sys.exit()
warnings.resetwarnings()
+gobject.set_prgname('gajim')
+
if os.name == 'nt':
warnings.filterwarnings(action='ignore')
@@ -152,12 +157,13 @@ try:
from common import gajim
except exceptions.DatabaseMalformed:
pritext = _('Database Error')
- sectext = _('The database file (%s) cannot be read. Try to repair it (see http://trac.gajim.org/wiki/DatabaseBackup) or remove it (all history will be lost).') % common.logger.LOG_DB_PATH
+ sectext = _('The database file (%s) cannot be read. Try to repair it (see '
+ 'http://trac.gajim.org/wiki/DatabaseBackup) or remove it (all history '
+ 'will be lost).') % common.logger.LOG_DB_PATH
else:
from common import dbus_support
if dbus_support.supported:
from music_track_listener import MusicTrackListener
- import dbus
from ctypes import CDLL
from ctypes.util import find_library
@@ -167,8 +173,8 @@ else:
if sysname in ('Linux', 'FreeBSD', 'OpenBSD', 'NetBSD'):
libc = CDLL(find_library('c'))
- # The constant defined in <linux/prctl.h> which is used to set the name of
- # the process.
+ # The constant defined in <linux/prctl.h> which is used to set the name
+ # of the process.
PR_SET_NAME = 15
if sysname == 'Linux':
@@ -183,11 +189,7 @@ else:
pritext = _('Gajim needs GTK 2.16 or above')
sectext = _('Gajim needs GTK 2.16 or above to run. Quiting...')
- try:
- from common import check_paths
- except exceptions.PysqliteNotAvailable, e:
- pritext = _('Gajim needs PySQLite2 to run')
- sectext = str(e)
+ from common import check_paths
if os.name == 'nt':
try:
@@ -195,7 +197,9 @@ else:
import win32api # do NOT remove. we req this module
except Exception:
pritext = _('Gajim needs pywin32 to run')
- sectext = _('Please make sure that Pywin32 is installed on your system. You can get it at %s') % 'http://sourceforge.net/project/showfiles.php?group_id=78018'
+ sectext = _('Please make sure that Pywin32 is installed on your '
+ 'system. You can get it at %s') % \
+ 'http://sourceforge.net/project/showfiles.php?group_id=78018'
if pritext:
dlg = gtk.MessageDialog(None,
@@ -211,13 +215,6 @@ del pritext
import gtkexcepthook
-import gobject
-if not hasattr(gobject, 'timeout_add_seconds'):
- def timeout_add_seconds_fake(time_sec, *args):
- return gobject.timeout_add(time_sec * 1000, *args)
- gobject.timeout_add_seconds = timeout_add_seconds_fake
-
-
import signal
import gtkgui_helpers
@@ -248,7 +245,8 @@ def pid_alive():
if os.name == 'nt':
try:
- from ctypes import (windll, c_ulong, c_int, Structure, c_char, POINTER, pointer, )
+ from ctypes import (windll, c_ulong, c_int, Structure, c_char)
+ from ctypes import (POINTER, pointer, sizeof)
except Exception:
return True
@@ -265,26 +263,26 @@ def pid_alive():
('dwFlags', c_ulong, ),
('szExeFile', c_char*512, ),
]
- def __init__(self):
- Structure.__init__(self, 512+9*4)
-
- k = windll.kernel32
- k.CreateToolhelp32Snapshot.argtypes = c_ulong, c_ulong,
- k.CreateToolhelp32Snapshot.restype = c_int
- k.Process32First.argtypes = c_int, POINTER(PROCESSENTRY32),
- k.Process32First.restype = c_int
- k.Process32Next.argtypes = c_int, POINTER(PROCESSENTRY32),
- k.Process32Next.restype = c_int
-
- def get_p(p):
- h = k.CreateToolhelp32Snapshot(2, 0) # TH32CS_SNAPPROCESS
- assert h > 0, 'CreateToolhelp32Snapshot failed'
- b = pointer(PROCESSENTRY32())
- f = k.Process32First(h, b)
- while f:
- if b.contents.th32ProcessID == p:
- return b.contents.szExeFile
- f = k.Process32Next(h, b)
+
+ kernel = windll.kernel32
+ kernel.CreateToolhelp32Snapshot.argtypes = c_ulong, c_ulong,
+ kernel.CreateToolhelp32Snapshot.restype = c_int
+ kernel.Process32First.argtypes = c_int, POINTER(PROCESSENTRY32),
+ kernel.Process32First.restype = c_int
+ kernel.Process32Next.argtypes = c_int, POINTER(PROCESSENTRY32),
+ kernel.Process32Next.restype = c_int
+
+ def get_p(pid_):
+ TH32CS_SNAPPROCESS = 2
+ CreateToolhelp32Snapshot = kernel.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
+ assert CreateToolhelp32Snapshot > 0, 'CreateToolhelp32Snapshot failed'
+ pe32 = PROCESSENTRY32()
+ pe32.dwSize = sizeof( PROCESSENTRY32 )
+ f3 = kernel.Process32First(CreateToolhelp32Snapshot, pointer(pe32))
+ while f3:
+ if pe32.th32ProcessID == pid_:
+ return pe32.szExeFile
+ f3 = kernel.Process32Next(CreateToolhelp32Snapshot, pointer(pe32))
if get_p(pid) in ('python.exe', 'gajim.exe'):
return True
@@ -294,14 +292,14 @@ def pid_alive():
return True # no /proc, assume Gajim is running
try:
- f = open('/proc/%d/cmdline'% pid)
- except IOError, e:
- if e.errno == errno.ENOENT:
+ f1 = open('/proc/%d/cmdline'% pid)
+ except IOError, e1:
+ if e1.errno == errno.ENOENT:
return False # file/pid does not exist
raise
- n = f.read().lower()
- f.close()
+ n = f1.read().lower()
+ f1.close()
if n.find('gajim') < 0:
return False
return True # Running Gajim found at pid
@@ -325,7 +323,6 @@ if pid_alive():
# run anyway, delete pid and useless global vars
if os.path.exists(pid_filename):
os.remove(pid_filename)
- del path_to_file
del pix
del pritext
del sectext
@@ -337,16 +334,15 @@ if not os.path.exists(pid_dir):
check_paths.create_path(pid_dir)
# Create pid file
try:
- f = open(pid_filename, 'w')
- f.write(str(os.getpid()))
- f.close()
-except IOError, e:
- dlg = dialogs.ErrorDialog(_('Disk Write Error'), str(e))
+ f2 = open(pid_filename, 'w')
+ f2.write(str(os.getpid()))
+ f2.close()
+except IOError, e2:
+ dlg = dialogs.ErrorDialog(_('Disk Write Error'), str(e2))
dlg.run()
dlg.destroy()
sys.exit()
del pid_dir
-del f
def on_exit():
# delete pid file on normal exit
@@ -378,7 +374,7 @@ if __name__ == '__main__':
except ImportError:
pass
else:
- def die_cb(cli):
+ def die_cb(dummy):
gajim.interface.roster.quit_gtkgui_interface()
gnome.program_init('gajim', gajim.version)
cli = gnome.ui.master_client()
@@ -389,13 +385,7 @@ if __name__ == '__main__':
if path_to_gajim_script:
argv = [path_to_gajim_script]
- # FIXME: remove this typeerror catch when gnome python is old and
- # not bad patched by distro men [2.12.0 + should not need all that
- # NORMALLY]
- try:
- cli.set_restart_command(argv)
- except AttributeError:
- cli.set_restart_command(len(argv), argv)
+ cli.set_restart_command(len(argv), argv)
check_paths.check_and_possibly_create_paths()
diff --git a/src/gajim_themes_window.py b/src/gajim_themes_window.py
index 5d83de43b..15d2952a9 100644
--- a/src/gajim_themes_window.py
+++ b/src/gajim_themes_window.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/gajim_themes_window.py
##
-## Copyright (C) 2003-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 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 Jean-Marie Traissard <jim AT lapin.org>
diff --git a/src/groupchat_control.py b/src/groupchat_control.py
index 36f84b69f..9296667f3 100644
--- a/src/groupchat_control.py
+++ b/src/groupchat_control.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/groupchat_control.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Alex Mauer <hawke AT hawkesnest.net>
@@ -40,9 +40,11 @@ import dialogs
import config
import vcard
import cell_renderer_image
+import dataforms_widget
from common import gajim
from common import helpers
+from common import dataforms
from chat_control import ChatControl
from chat_control import ChatControlBase
@@ -86,9 +88,11 @@ def tree_cell_data_func(column, renderer, model, iter_, tv=None):
renderer.set_property('xalign', 1) # align pixbuf to the right
else:
renderer.set_property('xalign', 0.5)
- if parent_iter and (model[iter_][C_AVATAR] or avatar_position == 'left'):
+ if parent_iter and (model[iter_][C_AVATAR] or avatar_position == \
+ 'left'):
renderer.set_property('visible', True)
- renderer.set_property('width', gajim.config.get('roster_avatar_width'))
+ renderer.set_property('width', gajim.config.get(
+ 'roster_avatar_width'))
else:
renderer.set_property('visible', False)
if parent_iter:
@@ -105,7 +109,7 @@ def tree_cell_data_func(column, renderer, model, iter_, tv=None):
else:
renderer.set_property('foreground', None)
renderer.set_property('font',
- gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
+ gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
else: # it is root (eg. group)
bgcolor = gajim.config.get_per('themes', theme, 'groupbgcolor')
if bgcolor:
@@ -120,7 +124,7 @@ def tree_cell_data_func(column, renderer, model, iter_, tv=None):
else:
set_renderer_color(tv, renderer, False)
renderer.set_property('font',
- gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
+ gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
class PrivateChatControl(ChatControl):
@@ -132,7 +136,8 @@ class PrivateChatControl(ChatControl):
def __init__(self, parent_win, gc_contact, contact, account, session):
room_jid = gc_contact.room_jid
- room_ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
+ room_ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
+ account)
if room_jid in gajim.interface.minimized_controls[account]:
room_ctrl = gajim.interface.minimized_controls[account][room_jid]
if room_ctrl:
@@ -161,14 +166,14 @@ class PrivateChatControl(ChatControl):
gc_contact = gajim.contacts.get_gc_contact(self.account, room, nick)
if not gc_contact:
dialogs.ErrorDialog(
- _('Sending private message failed'),
- #in second %s code replaces with nickname
- _('You are no longer in group chat "%(room)s" or "%(nick)s" has '
- 'left.') % {'room': room, 'nick': nick})
+ _('Sending private message failed'),
+ #in second %s code replaces with nickname
+ _('You are no longer in group chat "%(room)s" or '
+ '"%(nick)s" has left.') % {'room': room, 'nick': nick})
return
ChatControl.send_message(self, message, xhtml=xhtml,
- process_commands=process_commands)
+ process_commands=process_commands)
def update_ui(self):
if self.contact.show == 'offline':
@@ -185,7 +190,8 @@ class PrivateChatControl(ChatControl):
if not self.session:
fjid = self.gc_contact.get_full_jid()
- new_sess = gajim.connections[self.account].make_new_session(fjid, type_=self.type_id)
+ new_sess = gajim.connections[self.account].make_new_session(fjid,
+ type_=self.type_id)
self.set_session(new_sess)
self.session.negotiate_e2e(False)
@@ -199,9 +205,9 @@ class GroupchatControl(ChatControlBase):
def __init__(self, parent_win, contact, acct, is_continued=False):
ChatControlBase.__init__(self, self.TYPE_ID, parent_win,
- 'groupchat_control', contact, acct)
+ 'groupchat_control', contact, acct)
- self.is_continued=is_continued
+ self.is_continued = is_continued
self.is_anonymous = True
# Controls the state of autorejoin.
@@ -212,9 +218,12 @@ class GroupchatControl(ChatControlBase):
# state in got_connected()).
self.autorejoin = None
+ # Keep error dialog instance to be sure to have only once at a time
+ self.error_dialog = None
+
self.actions_button = self.xml.get_object('muc_window_actions_button')
id_ = self.actions_button.connect('clicked',
- self.on_actions_button_clicked)
+ self.on_actions_button_clicked)
self.handlers[id_] = self.actions_button
widget = self.xml.get_object('change_nick_button')
@@ -222,7 +231,8 @@ class GroupchatControl(ChatControlBase):
self.handlers[id_] = widget
widget = self.xml.get_object('change_subject_button')
- id_ = widget.connect('clicked', self._on_change_subject_menuitem_activate)
+ id_ = widget.connect('clicked',
+ self._on_change_subject_menuitem_activate)
self.handlers[id_] = widget
widget = self.xml.get_object('bookmark_button')
@@ -232,7 +242,7 @@ class GroupchatControl(ChatControlBase):
break
else:
id_ = widget.connect('clicked',
- self._on_bookmark_room_menuitem_activate)
+ self._on_bookmark_room_menuitem_activate)
self.handlers[id_] = widget
widget.show()
@@ -240,27 +250,28 @@ class GroupchatControl(ChatControlBase):
id_ = widget.connect('row_expanded', self.on_list_treeview_row_expanded)
self.handlers[id_] = widget
- id_ = widget.connect('row_collapsed', self.on_list_treeview_row_collapsed)
+ id_ = widget.connect('row_collapsed',
+ self.on_list_treeview_row_collapsed)
self.handlers[id_] = widget
id_ = widget.connect('row_activated',
- self.on_list_treeview_row_activated)
+ self.on_list_treeview_row_activated)
self.handlers[id_] = widget
id_ = widget.connect('button_press_event',
- self.on_list_treeview_button_press_event)
+ self.on_list_treeview_button_press_event)
self.handlers[id_] = widget
id_ = widget.connect('key_press_event',
- self.on_list_treeview_key_press_event)
+ self.on_list_treeview_key_press_event)
self.handlers[id_] = widget
id_ = widget.connect('motion_notify_event',
- self.on_list_treeview_motion_notify_event)
+ self.on_list_treeview_motion_notify_event)
self.handlers[id_] = widget
id_ = widget.connect('leave_notify_event',
- self.on_list_treeview_leave_notify_event)
+ self.on_list_treeview_leave_notify_event)
self.handlers[id_] = widget
self.room_jid = self.contact.jid
@@ -277,9 +288,9 @@ class GroupchatControl(ChatControlBase):
compact_view = gajim.config.get('compact_view')
self.chat_buttons_set_visible(compact_view)
self.widget_set_visible(self.xml.get_object('banner_eventbox'),
- gajim.config.get('hide_groupchat_banner'))
+ gajim.config.get('hide_groupchat_banner'))
self.widget_set_visible(self.xml.get_object('list_scrolledwindow'),
- gajim.config.get('hide_groupchat_occupants_list'))
+ gajim.config.get('hide_groupchat_occupants_list'))
self._last_selected_contact = None # None or holds jid, account tuple
@@ -301,7 +312,7 @@ class GroupchatControl(ChatControlBase):
self.gc_count_nicknames_colors = 0
self.gc_custom_colors = {}
self.number_of_colors = len(gajim.config.get('gc_nicknames_colors').\
- split(':'))
+ split(':'))
self.name_label = self.xml.get_object('banner_name_label')
self.event_box = self.xml.get_object('banner_eventbox')
@@ -314,17 +325,17 @@ class GroupchatControl(ChatControlBase):
self.list_treeview = self.xml.get_object('list_treeview')
selection = self.list_treeview.get_selection()
id_ = selection.connect('changed',
- self.on_list_treeview_selection_changed)
+ self.on_list_treeview_selection_changed)
self.handlers[id_] = selection
id_ = self.list_treeview.connect('style-set',
- self.on_list_treeview_style_set)
+ self.on_list_treeview_style_set)
self.handlers[id_] = self.list_treeview
self.resize_from_another_muc = False
# we want to know when the the widget resizes, because that is
# an indication that the hpaned has moved...
# FIXME: Find a better indicator that the hpaned has moved.
id_ = self.list_treeview.connect('size-allocate',
- self.on_treeview_size_allocate)
+ self.on_treeview_size_allocate)
self.handlers[id_] = self.list_treeview
#status_image, shown_nick, type, nickname, avatar
store = gtk.TreeStore(gtk.Image, str, str, str, gtk.gdk.Pixbuf)
@@ -343,24 +354,25 @@ class GroupchatControl(ChatControlBase):
column.pack_start(renderer_pixbuf, expand=False)
column.add_attribute(renderer_pixbuf, 'pixbuf', C_AVATAR)
column.set_cell_data_func(renderer_pixbuf, tree_cell_data_func,
- self.list_treeview)
+ self.list_treeview)
if gajim.config.get('avatar_position_in_roster') == 'left':
add_avatar_renderer()
- renderer_image = cell_renderer_image.CellRendererImage(0, 0) # status img
+ # status img
+ renderer_image = cell_renderer_image.CellRendererImage(0, 0)
renderer_image.set_property('width', 26)
column.pack_start(renderer_image, expand=False)
column.add_attribute(renderer_image, 'image', C_IMG)
column.set_cell_data_func(renderer_image, tree_cell_data_func,
- self.list_treeview)
+ self.list_treeview)
renderer_text = gtk.CellRendererText() # nickname
column.pack_start(renderer_text, expand=True)
column.add_attribute(renderer_text, 'markup', C_TEXT)
renderer_text.set_property("ellipsize", pango.ELLIPSIZE_END)
column.set_cell_data_func(renderer_text, tree_cell_data_func,
- self.list_treeview)
+ self.list_treeview)
if gajim.config.get('avatar_position_in_roster') == 'right':
add_avatar_renderer()
@@ -375,6 +387,8 @@ class GroupchatControl(ChatControlBase):
column.set_visible(False)
self.list_treeview.set_expander_column(column)
+ self.setup_seclabel(self.xml.get_object('label_selector'))
+
gajim.gc_connected[self.account][self.room_jid] = False
# disable win, we are not connected yet
ChatControlBase.got_disconnected(self)
@@ -412,7 +426,7 @@ class GroupchatControl(ChatControlBase):
if type1 == 'contact' and type2 == 'contact' and \
gajim.config.get('sort_by_show_in_muc'):
cshow = {'chat':0, 'online': 1, 'away': 2, 'xa': 3, 'dnd': 4,
- 'invisible': 5, 'offline': 6, 'error': 7}
+ 'invisible': 5, 'offline': 6, 'error': 7}
show1 = cshow[gc_contact1.show]
show2 = cshow[gc_contact2.show]
if show1 < show2:
@@ -442,7 +456,8 @@ class GroupchatControl(ChatControlBase):
self.room_jid)):
item = gtk.MenuItem(nick, use_underline=False)
submenu.append(item)
- id_ = item.connect('activate', self.append_nick_in_msg_textview, nick)
+ id_ = item.connect('activate', self.append_nick_in_msg_textview,
+ nick)
self.handlers[id_] = item
menu.show_all()
@@ -467,8 +482,10 @@ class GroupchatControl(ChatControlBase):
for account in gajim.gc_connected:
for room_jid in [i for i in gajim.gc_connected[account] if \
gajim.gc_connected[account][i] and i != self.room_jid]:
- ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid, account)
- if not ctrl:
+ ctrl = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
+ account)
+ if not ctrl and room_jid in \
+ gajim.interface.minimized_controls[account]:
ctrl = gajim.interface.minimized_controls[account][room_jid]
if ctrl:
ctrl.resize_occupant_treeview(hpaned_position)
@@ -531,8 +548,8 @@ class GroupchatControl(ChatControlBase):
self.attention_flag = False
# get active color from gtk
color = self.parent_win.notebook.style.fg[gtk.STATE_ACTIVE]
- elif chatstate == 'newmsg' and (not has_focus or not current_tab) and\
- not self.attention_flag:
+ elif chatstate == 'newmsg' and (not has_focus or not current_tab) \
+ and not self.attention_flag:
color_name = gajim.config.get_per('themes', theme,
'state_muc_msg_color')
if color_name:
@@ -580,7 +597,7 @@ class GroupchatControl(ChatControlBase):
self.change_roster_style()
def _update_banner_state_image(self):
- banner_status_img = self.xml.get_object('banner_status_image')
+ banner_status_img = self.xml.get_object('gc_banner_status_image')
images = gajim.interface.jabber_state_images
if self.room_jid in gajim.gc_connected[self.account] and \
gajim.gc_connected[self.account][self.room_jid]:
@@ -635,7 +652,8 @@ class GroupchatControl(ChatControlBase):
subject = helpers.reduce_chars_newlines(self.subject, max_lines=2)
subject = gobject.markup_escape_text(subject)
subject_text = self.urlfinder.sub(self.make_href, subject)
- subject_text = '<span %s>%s</span>' % (font_attrs_small, subject_text)
+ subject_text = '<span %s>%s</span>' % (font_attrs_small,
+ subject_text)
# tooltip must always hold ALL the subject
self.event_box.set_tooltip_text(self.subject)
@@ -688,13 +706,13 @@ class GroupchatControl(ChatControlBase):
ag = gtk.accel_groups_from_object(self.parent_win.window)[0]
change_nick_menuitem.add_accelerator('activate', ag, gtk.keysyms.n,
- gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK, gtk.ACCEL_VISIBLE)
+ gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK, gtk.ACCEL_VISIBLE)
change_subject_menuitem.add_accelerator('activate', ag,
- gtk.keysyms.t, gtk.gdk.MOD1_MASK, gtk.ACCEL_VISIBLE)
+ gtk.keysyms.t, gtk.gdk.MOD1_MASK, gtk.ACCEL_VISIBLE)
bookmark_room_menuitem.add_accelerator('activate', ag, gtk.keysyms.b,
- gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
+ gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
history_menuitem.add_accelerator('activate', ag, gtk.keysyms.h,
- gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
+ gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
if self.contact.jid in gajim.config.get_per('accounts', self.account,
'minimized_gc').split(' '):
@@ -703,7 +721,7 @@ class GroupchatControl(ChatControlBase):
bookmark_room_menuitem.set_sensitive(False)
if gajim.gc_connected[self.account][self.room_jid]:
c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
- self.nick)
+ self.nick)
if c.affiliation not in ('owner', 'admin'):
configure_room_menuitem.set_sensitive(False)
else:
@@ -723,78 +741,110 @@ class GroupchatControl(ChatControlBase):
# connect the menuitems to their respective functions
id_ = bookmark_room_menuitem.connect('activate',
- self._on_bookmark_room_menuitem_activate)
+ self._on_bookmark_room_menuitem_activate)
self.handlers[id_] = bookmark_room_menuitem
id_ = change_nick_menuitem.connect('activate',
- self._on_change_nick_menuitem_activate)
+ self._on_change_nick_menuitem_activate)
self.handlers[id_] = change_nick_menuitem
id_ = configure_room_menuitem.connect('activate',
- self._on_configure_room_menuitem_activate)
+ self._on_configure_room_menuitem_activate)
self.handlers[id_] = configure_room_menuitem
id_ = destroy_room_menuitem.connect('activate',
- self._on_destroy_room_menuitem_activate)
+ self._on_destroy_room_menuitem_activate)
self.handlers[id_] = destroy_room_menuitem
id_ = change_subject_menuitem.connect('activate',
- self._on_change_subject_menuitem_activate)
+ self._on_change_subject_menuitem_activate)
self.handlers[id_] = change_subject_menuitem
id_ = history_menuitem.connect('activate',
- self._on_history_menuitem_activate)
+ self._on_history_menuitem_activate)
self.handlers[id_] = history_menuitem
id_ = minimize_menuitem.connect('toggled',
- self.on_minimize_menuitem_toggled)
+ self.on_minimize_menuitem_toggled)
self.handlers[id_] = minimize_menuitem
menu.connect('selection-done', self.destroy_menu,
- change_nick_menuitem, change_subject_menuitem,
- bookmark_room_menuitem, history_menuitem)
+ change_nick_menuitem, change_subject_menuitem,
+ bookmark_room_menuitem, history_menuitem)
return menu
def destroy_menu(self, menu, change_nick_menuitem, change_subject_menuitem,
- bookmark_room_menuitem, history_menuitem):
+ bookmark_room_menuitem, history_menuitem):
# destroy accelerators
ag = gtk.accel_groups_from_object(self.parent_win.window)[0]
change_nick_menuitem.remove_accelerator(ag, gtk.keysyms.n,
- gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK)
+ gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK)
change_subject_menuitem.remove_accelerator(ag, gtk.keysyms.t,
- gtk.gdk.MOD1_MASK)
+ gtk.gdk.MOD1_MASK)
bookmark_room_menuitem.remove_accelerator(ag, gtk.keysyms.b,
- gtk.gdk.CONTROL_MASK)
+ gtk.gdk.CONTROL_MASK)
history_menuitem.remove_accelerator(ag, gtk.keysyms.h,
- gtk.gdk.CONTROL_MASK)
+ gtk.gdk.CONTROL_MASK)
# destroy menu
menu.destroy()
def on_message(self, nick, msg, tim, has_timestamp=False, xhtml=None,
- status_code=[]):
+ status_code=[], displaymarking=None, captcha=None):
+ if captcha:
+ dataform = dataforms.ExtendForm(node=captcha)
+ self.form_widget = dataforms_widget.DataFormWidget(dataform)
+ self.form_widget.show_all()
+ vbox = self.xml.get_object('gc_textviews_vbox')
+ vbox.pack_start(self.form_widget, expand=False, fill=False)
+
+ def on_send_dataform_clicked(widget):
+ if not self.form_widget:
+ return
+ form_node = self.form_widget.data_form.get_purged()
+ form_node.type = 'submit'
+ gajim.connections[self.account].send_captcha(self.room_jid,
+ form_node)
+ self.form_widget.hide()
+ self.form_widget.destroy()
+ self.btn_box.destroy()
+ del self.form_widget
+ del self.btn_box
+
+ valid_button = gtk.Button(stock=gtk.STOCK_OK)
+ valid_button.connect('clicked', on_send_dataform_clicked)
+ self.btn_box = gtk.HButtonBox()
+ self.btn_box.set_layout(gtk.BUTTONBOX_END)
+ self.btn_box.pack_start(valid_button)
+ self.btn_box.show_all()
+ vbox.pack_start(self.btn_box, expand=False, fill=False)
+ if self.parent_win:
+ self.parent_win.redraw_tab(self, 'attention')
+ else:
+ self.attention_flag = True
+ helpers.play_sound('muc_message_received')
if '100' in status_code:
# Room is not anonymous
self.is_anonymous = False
if not nick:
# message from server
- self.print_conversation(msg, tim=tim, xhtml=xhtml)
+ self.print_conversation(msg, tim=tim, xhtml=xhtml, displaymarking=displaymarking)
else:
# message from someone
if has_timestamp:
# don't print xhtml if it's an old message.
# Like that xhtml messages are grayed too.
- self.print_old_conversation(msg, nick, tim, None)
+ self.print_old_conversation(msg, nick, tim, None, displaymarking=displaymarking)
else:
- self.print_conversation(msg, nick, tim, xhtml)
+ self.print_conversation(msg, nick, tim, xhtml, displaymarking=displaymarking)
def on_private_message(self, nick, msg, tim, xhtml, session, msg_id=None,
- encrypted=False):
+ encrypted=False, displaymarking=None):
# Do we have a queue?
fjid = self.room_jid + '/' + nick
no_queue = len(gajim.events.get_events(self.account, fjid)) == 0
event = gajim.events.create_event('pm', (msg, '', 'incoming', tim,
- encrypted, '', msg_id, xhtml, session))
+ encrypted, '', msg_id, xhtml, session, displaymarking))
gajim.events.add_event(self.account, fjid, event)
autopopup = gajim.config.get('autopopup')
@@ -805,9 +855,9 @@ class GroupchatControl(ChatControlBase):
gajim.connections[self.account].connected > 2):
if no_queue: # We didn't have a queue: we change icons
model = self.list_treeview.get_model()
- state_images =\
- gajim.interface.roster.get_appropriate_state_images(
- self.room_jid, icon_name='event')
+ state_images = \
+ gajim.interface.roster.get_appropriate_state_images(
+ self.room_jid, icon_name='event')
image = state_images['event']
model[iter_][C_IMG] = image
if self.parent_win:
@@ -819,8 +869,8 @@ class GroupchatControl(ChatControlBase):
self.list_treeview.expand_row(path[0:1], False)
self.list_treeview.scroll_to_cell(path)
self.list_treeview.set_cursor(path)
- contact = gajim.contacts.get_contact_with_highest_priority(self.account, \
- self.room_jid)
+ contact = gajim.contacts.get_contact_with_highest_priority(
+ self.account, self.room_jid)
if contact:
gajim.interface.roster.draw_contact(self.room_jid, self.account)
@@ -837,7 +887,8 @@ class GroupchatControl(ChatControlBase):
role_iter = model.iter_next(role_iter)
return None
- def print_old_conversation(self, text, contact='', tim=None, xhtml = None):
+ def print_old_conversation(self, text, contact='', tim=None, xhtml = None,
+ displaymarking=None):
if isinstance(text, str):
text = unicode(text, 'utf-8')
if contact:
@@ -852,11 +903,12 @@ class GroupchatControl(ChatControlBase):
else:
small_attr = []
ChatControlBase.print_conversation_line(self, text, kind, contact, tim,
- small_attr, small_attr + ['restored_message'],
- small_attr + ['restored_message'], count_as_new=False, xhtml=xhtml)
+ small_attr, small_attr + ['restored_message'],
+ small_attr + ['restored_message'], count_as_new=False, xhtml=xhtml,
+ displaymarking=displaymarking)
def print_conversation(self, text, contact='', tim=None, xhtml=None,
- graphics=True):
+ graphics=True, displaymarking=None):
"""
Print a line in the conversation
@@ -887,15 +939,15 @@ class GroupchatControl(ChatControlBase):
(highlight, sound) = self.highlighting_for_message(text, tim)
if contact in self.gc_custom_colors:
other_tags_for_name.append('gc_nickname_color_' + \
- str(self.gc_custom_colors[contact]))
+ str(self.gc_custom_colors[contact]))
else:
self.gc_count_nicknames_colors += 1
if self.gc_count_nicknames_colors == self.number_of_colors:
self.gc_count_nicknames_colors = 0
self.gc_custom_colors[contact] = \
- self.gc_count_nicknames_colors
+ self.gc_count_nicknames_colors
other_tags_for_name.append('gc_nickname_color_' + \
- str(self.gc_count_nicknames_colors))
+ str(self.gc_count_nicknames_colors))
if highlight:
# muc-specific chatstate
if self.parent_win:
@@ -917,28 +969,28 @@ class GroupchatControl(ChatControlBase):
helpers.play_sound('muc_message_highlight')
if text.startswith('/me ') or text.startswith('/me\n'):
other_tags_for_text.append('gc_nickname_color_' + \
- str(self.gc_custom_colors[contact]))
+ str(self.gc_custom_colors[contact]))
self.check_and_possibly_add_focus_out_line()
ChatControlBase.print_conversation_line(self, text, kind, contact, tim,
- other_tags_for_name, [], other_tags_for_text, xhtml=xhtml,
- graphics=graphics)
+ other_tags_for_name, [], other_tags_for_text, xhtml=xhtml,
+ graphics=graphics, displaymarking=displaymarking)
def get_nb_unread(self):
type_events = ['printed_marked_gc_msg']
if gajim.config.get('notify_on_all_muc_messages'):
type_events.append('printed_gc_msg')
nb = len(gajim.events.get_events(self.account, self.room_jid,
- type_events))
+ type_events))
nb += self.get_nb_unread_pm()
return nb
def get_nb_unread_pm(self):
nb = 0
for nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
- nb += len(gajim.events.get_events(self.account, self.room_jid + '/' + \
- nick, ['pm']))
+ nb += len(gajim.events.get_events(self.account, self.room_jid + \
+ '/' + nick, ['pm']))
return nb
def highlighting_for_message(self, text, tim):
@@ -972,7 +1024,8 @@ class GroupchatControl(ChatControlBase):
does not already have it as last event. If it goes to add this line
- remove previous line first
"""
- win = gajim.interface.msg_win_mgr.get_window(self.room_jid, self.account)
+ win = gajim.interface.msg_win_mgr.get_window(self.room_jid,
+ self.account)
if win and self.room_jid == win.get_active_jid() and\
win.window.get_property('has-toplevel-focus') and\
self.parent_win.get_active_control() == self:
@@ -1031,8 +1084,8 @@ class GroupchatControl(ChatControlBase):
for nick in nick_list:
# Update pm chat window
fjid = self.room_jid + '/' + nick
- gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
- nick)
+ gc_contact = gajim.contacts.get_gc_contact(self.account,
+ self.room_jid, nick)
ctrl = gajim.interface.msg_win_mgr.get_control(fjid, self.account)
if ctrl:
@@ -1058,29 +1111,30 @@ class GroupchatControl(ChatControlBase):
if self.autorejoin is None and gajim.account_is_connected(self.account):
ar_to = gajim.config.get('muc_autorejoin_timeout')
if ar_to:
- self.autorejoin = gobject.timeout_add_seconds(ar_to, self.rejoin)
+ self.autorejoin = gobject.timeout_add_seconds(ar_to,
+ self.rejoin)
def rejoin(self):
if not self.autorejoin:
return False
password = gajim.gc_passwords.get(self.room_jid, '')
gajim.connections[self.account].join_gc(self.nick, self.room_jid,
- password)
+ password)
return True
def draw_roster(self):
self.list_treeview.get_model().clear()
for nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
- gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
- nick)
+ gc_contact = gajim.contacts.get_gc_contact(self.account,
+ self.room_jid, nick)
self.add_contact_to_roster(nick, gc_contact.show, gc_contact.role,
- gc_contact.affiliation, gc_contact.status, gc_contact.jid)
+ gc_contact.affiliation, gc_contact.status, gc_contact.jid)
self.draw_all_roles()
# Recalculate column width for ellipsizin
self.list_treeview.columns_autosize()
def on_send_pm(self, widget=None, model=None, iter_=None, nick=None,
- msg=None):
+ msg=None):
"""
Open a chat window and if msg is not None - send private message to a
contact in a room
@@ -1106,7 +1160,8 @@ class GroupchatControl(ChatControlBase):
gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
nick)
state_images = gajim.interface.jabber_state_images['16']
- if len(gajim.events.get_events(self.account, self.room_jid + '/' + nick)):
+ if len(gajim.events.get_events(self.account, self.room_jid + '/' + \
+ nick)):
image = state_images['event']
else:
image = state_images[gc_contact.show]
@@ -1125,11 +1180,13 @@ class GroupchatControl(ChatControlBase):
if status != '':
status = helpers.reduce_chars_newlines(status, max_lines=1)
# escape markup entities and make them small italic and fg color
- color = gtkgui_helpers._get_fade_color(self.list_treeview,
+ color = gtkgui_helpers.get_fade_color(self.list_treeview,
selected, focus)
- colorstring = "#%04x%04x%04x" % (color.red, color.green, color.blue)
+ colorstring = "#%04x%04x%04x" % (color.red, color.green,
+ color.blue)
name += ('\n<span size="small" style="italic" foreground="%s">'
- '%s</span>') % (colorstring, gobject.markup_escape_text(status))
+ '%s</span>') % (colorstring, gobject.markup_escape_text(
+ status))
if image.get_storage_type() == gtk.IMAGE_PIXBUF and \
gc_contact.affiliation != 'none' and gajim.config.get(
@@ -1143,8 +1200,8 @@ class GroupchatControl(ChatControlBase):
elif gc_contact.affiliation == 'member':
pixbuf2.fill(0x00ff00ff) # Green
pixbuf2.composite(pixbuf1, 12, 12, pixbuf2.get_property('width'),
- pixbuf2.get_property('height'), 0, 0, 1.0, 1.0,
- gtk.gdk.INTERP_HYPER, 127)
+ pixbuf2.get_property('height'), 0, 0, 1.0, 1.0,
+ gtk.gdk.INTERP_HYPER, 127)
image = gtk.image_new_from_pixbuf(pixbuf1)
model[iter_][C_IMG] = image
model[iter_][C_TEXT] = name
@@ -1172,7 +1229,7 @@ class GroupchatControl(ChatControlBase):
role_name = helpers.get_uf_role(role, plural=True)
if gajim.config.get('show_contacts_number'):
nbr_role, nbr_total = gajim.contacts.get_nb_role_total_gc_contacts(
- self.account, self.room_jid, role)
+ self.account, self.room_jid, role)
role_name += ' (%s/%s)' % (repr(nbr_role), repr(nbr_total))
model[role_iter][C_TEXT] = role_name
@@ -1181,7 +1238,7 @@ class GroupchatControl(ChatControlBase):
self.draw_role(role)
def chg_contact_status(self, nick, show, status, role, affiliation, jid,
- reason, actor, statusCode, new_nick, avatar_sha, tim=None):
+ reason, actor, statusCode, new_nick, avatar_sha, tim=None):
"""
When an occupant changes his or her status
"""
@@ -1205,33 +1262,35 @@ class GroupchatControl(ChatControlBase):
nick_jid += ' (%s)' % simple_jid
# statusCode
- # http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes-init
+ # http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes-\
+ # init
if statusCode:
if '100' in statusCode:
- # Can be a message (see handle_event_gc_config_change in gajim.py)
+ # Can be a message (see handle_event_gc_config_change in
+ # gajim.py)
self.print_conversation(\
- _('Any occupant is allowed to see your full JID'))
+ _('Any occupant is allowed to see your full JID'))
if '170' in statusCode:
- # Can be a message (see handle_event_gc_config_change in gajim.py)
+ # Can be a message (see handle_event_gc_config_change in
+ # gajim.py)
self.print_conversation(_('Room logging is enabled'))
if '201' in statusCode:
self.print_conversation(_('A new room has been created'))
if '210' in statusCode:
self.print_conversation(\
- _('The server has assigned or modified your roomnick'))
+ _('The server has assigned or modified your roomnick'))
if show in ('offline', 'error'):
if statusCode:
if '307' in statusCode:
if actor is None: # do not print 'kicked by None'
s = _('%(nick)s has been kicked: %(reason)s') % {
- 'nick': nick,
- 'reason': reason }
+ 'nick': nick,
+ 'reason': reason }
else:
- s = _('%(nick)s has been kicked by %(who)s: %(reason)s') % {
- 'nick': nick,
- 'who': actor,
- 'reason': reason }
+ s = _('%(nick)s has been kicked by %(who)s: '
+ '%(reason)s') % {'nick': nick, 'who': actor,
+ 'reason': reason }
self.print_conversation(s, 'info', tim=tim, graphics=False)
if nick == self.nick and not gajim.config.get(
'muc_autorejoin_on_kick'):
@@ -1239,13 +1298,11 @@ class GroupchatControl(ChatControlBase):
elif '301' in statusCode:
if actor is None: # do not print 'banned by None'
s = _('%(nick)s has been banned: %(reason)s') % {
- 'nick': nick,
- 'reason': reason }
+ 'nick': nick, 'reason': reason }
else:
- s = _('%(nick)s has been banned by %(who)s: %(reason)s') % {
- 'nick': nick,
- 'who': actor,
- 'reason': reason }
+ s = _('%(nick)s has been banned by %(who)s: '
+ '%(reason)s') % { 'nick': nick, 'who': actor,
+ 'reason': reason }
self.print_conversation(s, 'info', tim=tim, graphics=False)
if nick == self.nick:
self.autorejoin = False
@@ -1257,69 +1314,75 @@ class GroupchatControl(ChatControlBase):
s = _('You are now known as %s') % new_nick
# Stop all E2E sessions
nick_list = gajim.contacts.get_nick_list(self.account,
- self.room_jid)
+ self.room_jid)
for nick_ in nick_list:
fjid_ = self.room_jid + '/' + nick_
- ctrl = gajim.interface.msg_win_mgr.get_control(fjid_,
- self.account)
+ ctrl = gajim.interface.msg_win_mgr.get_control(
+ fjid_, self.account)
if ctrl and ctrl.session and \
ctrl.session.enable_encryption:
thread_id = ctrl.session.thread_id
ctrl.session.terminate_e2e()
- gajim.connections[self.account].delete_session(fjid_,
- thread_id)
+ gajim.connections[self.account].delete_session(
+ fjid_, thread_id)
ctrl.no_autonegotiation = False
else:
s = _('%(nick)s is now known as %(new_nick)s') % {
- 'nick': nick, 'new_nick': new_nick}
+ 'nick': nick, 'new_nick': new_nick}
# We add new nick to muc roster here, so we don't see
- # that "new_nick has joined the room" when he just changed nick.
+ # that "new_nick has joined the room" when he just changed
+ # nick.
# add_contact_to_roster will be called a second time
# after that, but that doesn't hurt
- self.add_contact_to_roster(new_nick, show, role, affiliation,
- status, jid)
+ self.add_contact_to_roster(new_nick, show, role,
+ affiliation, status, jid)
if nick in self.attention_list:
self.attention_list.remove(nick)
# keep nickname color
if nick in self.gc_custom_colors:
self.gc_custom_colors[new_nick] = \
- self.gc_custom_colors[nick]
+ self.gc_custom_colors[nick]
# rename vcard / avatar
puny_jid = helpers.sanitize_filename(self.room_jid)
puny_nick = helpers.sanitize_filename(nick)
puny_new_nick = helpers.sanitize_filename(new_nick)
- old_path = os.path.join(gajim.VCARD_PATH, puny_jid, puny_nick)
+ old_path = os.path.join(gajim.VCARD_PATH, puny_jid,
+ puny_nick)
new_path = os.path.join(gajim.VCARD_PATH, puny_jid,
- puny_new_nick)
+ puny_new_nick)
files = {old_path: new_path}
path = os.path.join(gajim.AVATAR_PATH, puny_jid)
# possible extensions
for ext in ('.png', '.jpeg', '_notif_size_bw.png',
'_notif_size_colored.png'):
files[os.path.join(path, puny_nick + ext)] = \
- os.path.join(path, puny_new_nick + ext)
+ os.path.join(path, puny_new_nick + ext)
for old_file in files:
- if os.path.exists(old_file) and old_file != files[old_file]:
- if os.path.exists(files[old_file]) and helpers.windowsify(
- old_file) != helpers.windowsify(files[old_file]):
- # Windows require this, but os.remove('test') will also
- # remove 'TEST'
+ if os.path.exists(old_file) and old_file != \
+ files[old_file]:
+ if os.path.exists(files[old_file]) and \
+ helpers.windowsify(old_file) != helpers.windowsify(
+ files[old_file]):
+ # Windows require this, but os.remove('test')
+ # will also remove 'TEST'
os.remove(files[old_file])
os.rename(old_file, files[old_file])
self.print_conversation(s, 'info', tim=tim, graphics=False)
elif '321' in statusCode:
- s = _('%(nick)s has been removed from the room (%(reason)s)') % {
- 'nick': nick, 'reason': _('affiliation changed') }
+ s = _('%(nick)s has been removed from the room '
+ '(%(reason)s)') % { 'nick': nick,
+ 'reason': _('affiliation changed') }
self.print_conversation(s, 'info', tim=tim, graphics=False)
elif '322' in statusCode:
- s = _('%(nick)s has been removed from the room (%(reason)s)') % {
- 'nick': nick,
- 'reason': _('room configuration changed to members-only') }
+ s = _('%(nick)s has been removed from the room '
+ '(%(reason)s)') % { 'nick': nick,
+ 'reason': _('room configuration changed to '
+ 'members-only') }
self.print_conversation(s, 'info', tim=tim, graphics=False)
elif '332' in statusCode:
- s = _('%(nick)s has been removed from the room (%(reason)s)') % {
- 'nick': nick,
- 'reason': _('system shutdown') }
+ s = _('%(nick)s has been removed from the room '
+ '(%(reason)s)') % {'nick': nick,
+ 'reason': _('system shutdown') }
self.print_conversation(s, 'info', tim=tim, graphics=False)
# Room has been destroyed.
elif 'destroyed' in statusCode:
@@ -1331,16 +1394,19 @@ class GroupchatControl(ChatControlBase):
self.remove_contact(nick)
self.draw_all_roles()
else:
- c = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
+ c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
+ nick)
c.show = show
c.status = status
if nick == self.nick and (not statusCode or \
'303' not in statusCode): # We became offline
self.got_disconnected()
contact = gajim.contacts.\
- get_contact_with_highest_priority(self.account, self.room_jid)
+ get_contact_with_highest_priority(self.account,
+ self.room_jid)
if contact:
- gajim.interface.roster.draw_contact(self.room_jid, self.account)
+ gajim.interface.roster.draw_contact(self.room_jid,
+ self.account)
if self.parent_win:
self.parent_win.redraw_tab(self)
else:
@@ -1351,21 +1417,24 @@ class GroupchatControl(ChatControlBase):
self.nick = nick
s = _('You are now known as %s') % nick
self.print_conversation(s, 'info', tim=tim, graphics=False)
- iter_ = self.add_contact_to_roster(nick, show, role, affiliation,
- status, jid)
+ iter_ = self.add_contact_to_roster(nick, show, role,
+ affiliation, status, jid)
newly_created = True
self.draw_all_roles()
- if statusCode and '201' in statusCode: # We just created the room
- gajim.connections[self.account].request_gc_config(self.room_jid)
+ if statusCode and '201' in statusCode:
+ # We just created the room
+ gajim.connections[self.account].request_gc_config(
+ self.room_jid)
else:
- gc_c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
- nick)
+ gc_c = gajim.contacts.get_gc_contact(self.account,
+ self.room_jid, nick)
if not gc_c:
- log.error('%s has an iter, but no gc_contact instance')
+ log.error('%s has an iter, but no gc_contact instance' % \
+ nick)
return
# Re-get vcard if avatar has changed
- # We do that here because we may request it to the real JID if we
- # knows it. connections.py doesn't know it.
+ # We do that here because we may request it to the real JID if
+ # we knows it. connections.py doesn't know it.
con = gajim.connections[self.account]
if gc_c and gc_c.jid:
real_jid = gc_c.jid
@@ -1399,12 +1468,12 @@ class GroupchatControl(ChatControlBase):
if affiliation != actual_affiliation:
if actor:
st = _('** Affiliation of %(nick)s has been set to '
- '%(affiliation)s by %(actor)s') % {'nick': nick_jid,
- 'affiliation': affiliation, 'actor': actor}
+ '%(affiliation)s by %(actor)s') % {'nick': nick_jid,
+ 'affiliation': affiliation, 'actor': actor}
else:
st = _('** Affiliation of %(nick)s has been set to '
- '%(affiliation)s') % {'nick': nick_jid,
- 'affiliation': affiliation}
+ '%(affiliation)s') % {'nick': nick_jid,
+ 'affiliation': affiliation}
if reason:
st += ' (%s)' % reason
self.print_conversation(st, tim=tim, graphics=False)
@@ -1412,24 +1481,24 @@ class GroupchatControl(ChatControlBase):
actual_role = self.get_role(nick)
if role != actual_role:
self.remove_contact(nick)
- self.add_contact_to_roster(nick, show, role,
- affiliation, status, jid)
+ self.add_contact_to_roster(nick, show, role, affiliation,
+ status, jid)
self.draw_role(actual_role)
self.draw_role(role)
if actor:
- st = _('** Role of %(nick)s has been set to %(role)s by '
- '%(actor)s') % {'nick': nick_jid, 'role': role,
- 'actor': actor}
+ st = _('** Role of %(nick)s has been set to %(role)s '
+ 'by %(actor)s') % {'nick': nick_jid, 'role': role,
+ 'actor': actor}
else:
- st = _('** Role of %(nick)s has been set to %(role)s') % {
- 'nick': nick_jid, 'role': role}
+ st = _('** Role of %(nick)s has been set to '
+ '%(role)s') % {'nick': nick_jid, 'role': role}
if reason:
st += ' (%s)' % reason
self.print_conversation(st, tim=tim, graphics=False)
right_changed = True
else:
if gc_c.show == show and gc_c.status == status and \
- gc_c.affiliation == affiliation: # no change
+ gc_c.affiliation == affiliation: # no change
return
gc_c.show = show
gc_c.affiliation = affiliation
@@ -1458,14 +1527,14 @@ class GroupchatControl(ChatControlBase):
st = _('%s has joined the group chat') % nick_jid
elif print_status == 'all':
st = _('%(nick)s is now %(status)s') % {'nick': nick_jid,
- 'status': helpers.get_uf_show(show)}
+ 'status': helpers.get_uf_show(show)}
if st:
if status:
st += ' (' + status + ')'
self.print_conversation(st, tim=tim, graphics=False)
def add_contact_to_roster(self, nick, show, role, affiliation, status,
- jid=''):
+ jid=''):
model = self.list_treeview.get_model()
role_name = helpers.get_uf_role(role, plural=True)
@@ -1483,14 +1552,16 @@ class GroupchatControl(ChatControlBase):
role_iter = self.get_role_iter(role)
if not role_iter:
role_iter = model.append(None,
- (gajim.interface.jabber_state_images['16']['closed'], role,
- 'role', role_name, None))
+ (gajim.interface.jabber_state_images['16']['closed'], role,
+ 'role', role_name, None))
self.draw_all_roles()
iter_ = model.append(role_iter, (None, nick, 'contact', name, None))
- if not nick in gajim.contacts.get_nick_list(self.account, self.room_jid):
- gc_contact = gajim.contacts.create_gc_contact(room_jid=self.room_jid, account=self.account,
- name=nick, show=show, status=status, role=role,
- affiliation=affiliation, jid=j, resource=resource)
+ if not nick in gajim.contacts.get_nick_list(self.account,
+ self.room_jid):
+ gc_contact = gajim.contacts.create_gc_contact(
+ room_jid=self.room_jid, account=self.account,
+ name=nick, show=show, status=status, role=role,
+ affiliation=affiliation, jid=j, resource=resource)
gajim.contacts.add_gc_contact(self.account, gc_contact)
self.draw_contact(nick)
self.draw_avatar(nick)
@@ -1501,13 +1572,11 @@ class GroupchatControl(ChatControlBase):
fake_jid = self.room_jid + '/' + nick
pixbuf = gtkgui_helpers.get_avatar_pixbuf_from_cache(fake_jid)
if pixbuf == 'ask':
- if j:
- fjid = j
- if resource:
- fjid += '/' + resource
- gajim.connections[self.account].request_vcard(fjid, fake_jid)
+ if j and not self.is_anonymous:
+ gajim.connections[self.account].request_vcard(j, fake_jid)
else:
- gajim.connections[self.account].request_vcard(fake_jid, fake_jid)
+ gajim.connections[self.account].request_vcard(fake_jid,
+ fake_jid)
if nick == self.nick: # we became online
self.got_connected()
self.list_treeview.expand_row((model.get_path(role_iter)), False)
@@ -1557,12 +1626,13 @@ class GroupchatControl(ChatControlBase):
if not message:
return
+ label = self.get_seclabel()
if message != '' or message != '\n':
self.save_sent_message(message)
# Send the message
gajim.connections[self.account].send_gc_message(self.room_jid,
- message, xhtml=xhtml)
+ message, xhtml=xhtml, label=label)
self.msg_textview.get_buffer().set_text('')
self.msg_textview.grab_focus()
@@ -1594,12 +1664,12 @@ class GroupchatControl(ChatControlBase):
ctrl.parent_win = None
gajim.interface.roster.add_groupchat(self.contact.jid, self.account,
- status = self.subject)
+ status = self.subject)
del win._controls[self.account][self.contact.jid]
def shutdown(self, status='offline'):
- # PluginSystem: calling shutdown of super class (ChatControlBase)
+ # PluginSystem: calling shutdown of super class (ChatControlBase)
# to let it remove it's GUI extension points
super(GroupchatControl, self).shutdown()
@@ -1618,10 +1688,11 @@ class GroupchatControl(ChatControlBase):
for nick in nick_list:
# Update pm chat window
fjid = self.room_jid + '/' + nick
- ctrl = gajim.interface.msg_win_mgr.get_gc_control(fjid, self.account)
+ ctrl = gajim.interface.msg_win_mgr.get_gc_control(fjid,
+ self.account)
if ctrl:
- contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
- nick)
+ contact = gajim.contacts.get_gc_contact(self.account,
+ self.room_jid, nick)
contact.show = 'offline'
contact.status = ''
ctrl.update_ui()
@@ -1632,7 +1703,7 @@ class GroupchatControl(ChatControlBase):
if sess.enable_encryption:
sess.terminate_e2e()
gajim.connections[self.account].delete_session(fjid,
- sess.thread_id)
+ sess.thread_id)
# They can already be removed by the destroy function
if self.room_jid in gajim.contacts.get_gc_list(self.account):
gajim.contacts.remove_room(self.account, self.room_jid)
@@ -1654,9 +1725,9 @@ class GroupchatControl(ChatControlBase):
includes = gajim.config.get('confirm_close_muc_rooms').split(' ')
excludes = gajim.config.get('noconfirm_close_muc_rooms').split(' ')
# whether to ask for comfirmation before closing muc
- if (gajim.config.get('confirm_close_muc') or self.room_jid in includes) \
- and gajim.gc_connected[self.account][self.room_jid] and self.room_jid not\
- in excludes:
+ if (gajim.config.get('confirm_close_muc') or self.room_jid in includes)\
+ and gajim.gc_connected[self.account][self.room_jid] and self.room_jid \
+ not in excludes:
return False
return True
@@ -1673,9 +1744,9 @@ class GroupchatControl(ChatControlBase):
includes = gajim.config.get('confirm_close_muc_rooms').split(' ')
excludes = gajim.config.get('noconfirm_close_muc_rooms').split(' ')
# whether to ask for comfirmation before closing muc
- if (gajim.config.get('confirm_close_muc') or self.room_jid in includes) \
- and gajim.gc_connected[self.account][self.room_jid] and self.room_jid not\
- in excludes:
+ if (gajim.config.get('confirm_close_muc') or self.room_jid in includes)\
+ and gajim.gc_connected[self.account][self.room_jid] and self.room_jid \
+ not in excludes:
def on_ok(clicked):
if clicked:
@@ -1690,13 +1761,13 @@ class GroupchatControl(ChatControlBase):
on_no(self)
pritext = _('Are you sure you want to leave group chat "%s"?')\
- % self.name
+ % self.name
sectext = _('If you close this window, you will be disconnected '
- 'from this group chat.')
+ 'from this group chat.')
dialogs.ConfirmationDialogCheck(pritext, sectext,
- _('Do _not ask me again'), on_response_ok=on_ok,
- on_response_cancel=on_cancel)
+ _('_Do not ask me again'), on_response_ok=on_ok,
+ on_response_cancel=on_cancel)
return
on_yes(self)
@@ -1726,11 +1797,12 @@ class GroupchatControl(ChatControlBase):
def on_ok(subject):
# Note, we don't update self.subject since we don't know whether it
# will work yet
- gajim.connections[self.account].send_gc_subject(self.room_jid, subject)
+ gajim.connections[self.account].send_gc_subject(self.room_jid,
+ subject)
dialogs.InputTextDialog(_('Changing Subject'),
- _('Please specify the new subject:'), input_str=self.subject,
- ok_handler=on_ok)
+ _('Please specify the new subject:'), input_str=self.subject,
+ ok_handler=on_ok)
def _on_change_nick_menuitem_activate(self, widget):
if 'change_nick_dialog' in gajim.interface.instances:
@@ -1739,18 +1811,20 @@ class GroupchatControl(ChatControlBase):
title = _('Changing Nickname')
prompt = _('Please specify the new nickname you want to use:')
gajim.interface.instances['change_nick_dialog'] = \
- dialogs.ChangeNickDialog(self.account, self.room_jid, title,
- prompt)
+ dialogs.ChangeNickDialog(self.account, self.room_jid, title,
+ prompt, change_nick=True)
def _on_configure_room_menuitem_activate(self, widget):
- c = gajim.contacts.get_gc_contact(self.account, self.room_jid, self.nick)
+ c = gajim.contacts.get_gc_contact(self.account, self.room_jid,
+ self.nick)
if c.affiliation == 'owner':
gajim.connections[self.account].request_gc_config(self.room_jid)
elif c.affiliation == 'admin':
if self.room_jid not in gajim.interface.instances[self.account][
'gc_config']:
- gajim.interface.instances[self.account]['gc_config'][self.room_jid]\
- = config.GroupchatConfigWindow(self.account, self.room_jid)
+ gajim.interface.instances[self.account]['gc_config'][
+ self.room_jid] = config.GroupchatConfigWindow(self.account,
+ self.room_jid)
def _on_destroy_room_menuitem_activate(self, widget):
def on_ok(reason, jid):
@@ -1762,25 +1836,25 @@ class GroupchatControl(ChatControlBase):
dialogs.ErrorDialog(_('Invalid group chat Jabber ID'),
_('The group chat Jabber ID has not allowed characters.'))
return
- gajim.connections[self.account].destroy_gc_room(self.room_jid, reason,
- jid)
+ gajim.connections[self.account].destroy_gc_room(self.room_jid,
+ reason, jid)
# Ask for a reason
- dialogs.DubbleInputDialog(_('Destroying %s') % self.room_jid,
- _('You are going to definitively destroy this room.\n'
- 'You may specify a reason below:'),
- _('You may also enter an alternate venue:'), ok_handler=on_ok)
+ dialogs.DoubleInputDialog(_('Destroying %s') % self.room_jid,
+ _('You are going to definitively destroy this room.\n'
+ 'You may specify a reason below:'),
+ _('You may also enter an alternate venue:'), ok_handler=on_ok)
def _on_bookmark_room_menuitem_activate(self, widget):
"""
Bookmark the room, without autojoin and not minimized
"""
password = gajim.gc_passwords.get(self.room_jid, '')
- gajim.interface.add_gc_bookmark(self.account, self.name, self.room_jid, \
- '0', '0', password, self.nick)
+ gajim.interface.add_gc_bookmark(self.account, self.name, self.room_jid,\
+ '0', '0', password, self.nick)
def _on_drag_data_received(self, widget, context, x, y, selection,
- target_type, timestamp):
+ target_type, timestamp):
# Invite contact to groupchat
treeview = gajim.interface.roster.tree
model = treeview.get_model()
@@ -1797,7 +1871,7 @@ class GroupchatControl(ChatControlBase):
gajim.connections[self.account].send_invite(self.room_jid, contact_jid)
def handle_message_textview_mykey_press(self, widget, event_keyval,
- event_keymod):
+ event_keymod):
# NOTE: handles mykeypress which is custom signal connected to this
# CB in new_room(). for this singal see message_textview.py
@@ -1814,16 +1888,17 @@ class GroupchatControl(ChatControlBase):
cursor_position = message_buffer.get_insert()
end_iter = message_buffer.get_iter_at_mark(cursor_position)
text = message_buffer.get_text(start_iter, end_iter, False).decode(
- 'utf-8')
+ 'utf-8')
splitted_text = text.split()
# HACK: Not the best soltution.
if (text.startswith(self.COMMAND_PREFIX) and not
- text.startswith(self.COMMAND_PREFIX * 2) and len(splitted_text) == 1):
- return super(GroupchatControl,
- self).handle_message_textview_mykey_press(widget, event_keyval,
- event_keymod)
+ text.startswith(self.COMMAND_PREFIX * 2) and \
+ len(splitted_text) == 1):
+ return super(GroupchatControl, self).\
+ handle_message_textview_mykey_press(widget, event_keyval,
+ event_keymod)
# nick completion
# check if tab is pressed with empty message
@@ -1844,14 +1919,14 @@ class GroupchatControl(ChatControlBase):
if len(self.nick_hits) and self.last_key_tabs and \
text[:-after_nick_len].endswith(self.nick_hits[0]):
# we should cycle
- # Previous nick in list may had a space inside, so we check text and
- # not splitted_text and store it into 'begin' var
+ # Previous nick in list may had a space inside, so we check text
+ # and not splitted_text and store it into 'begin' var
self.nick_hits.append(self.nick_hits[0])
begin = self.nick_hits.pop(0)
else:
self.nick_hits = [] # clear the hit list
list_nick = gajim.contacts.get_nick_list(self.account,
- self.room_jid)
+ self.room_jid)
list_nick.sort(key=unicode.lower) # case-insensitive sort
if begin == '':
# empty message, show lasts nicks that highlighted us first
@@ -1884,14 +1959,14 @@ class GroupchatControl(ChatControlBase):
# have to accomodate for the added space from last
# completion
start_iter.backward_chars(len(begin) + \
- len(gc_refer_to_nick_char))
+ len(gc_refer_to_nick_char))
else:
start_iter.backward_chars(len(begin))
message_buffer.delete(start_iter, end_iter)
# get a shell-like completion
- # if there's more than one nick for this completion, complete only
- # the part that all these nicks have in common
+ # if there's more than one nick for this completion, complete
+ # only the part that all these nicks have in common
if gajim.config.get('shell_like_completion') and \
len(self.nick_hits) > 1:
end = False
@@ -1905,13 +1980,14 @@ class GroupchatControl(ChatControlBase):
completion = completion[:-1]
break
# if the current nick matches a COMPLETE existing nick,
- # and if the user tab TWICE, complete that nick (with the "add")
+ # and if the user tab TWICE, complete that nick (with the
+ # "add")
if self.last_key_tabs:
for nick in self.nick_hits:
if nick == completion:
- # The user seems to want this nick, so
- # complete it as if it were the only nick
- # available
+ # The user seems to want this nick, so
+ # complete it as if it were the only nick
+ # available
add = gc_refer_to_nick_char + ' '
else:
completion = self.nick_hits[0]
@@ -1950,11 +2026,11 @@ class GroupchatControl(ChatControlBase):
"""
def on_ok(reason):
gajim.connections[self.account].gc_set_role(self.room_jid, nick,
- 'none', reason)
+ 'none', reason)
# ask for reason
dialogs.InputDialog(_('Kicking %s') % nick,
- _('You may specify a reason below:'), ok_handler=on_ok)
+ _('You may specify a reason below:'), ok_handler=on_ok)
def mk_menu(self, event, iter_):
"""
@@ -1971,7 +2047,7 @@ class GroupchatControl(ChatControlBase):
# looking for user's affiliation and role
user_nick = self.nick
user_affiliation = gajim.contacts.get_gc_contact(self.account,
- self.room_jid, user_nick).affiliation
+ self.room_jid, user_nick).affiliation
user_role = self.get_role(user_nick)
# making menu from gtk builder
@@ -1996,7 +2072,7 @@ class GroupchatControl(ChatControlBase):
target_affiliation in ('admin', 'owner'):
item.set_sensitive(False)
id_ = item.connect('activate', self.on_voice_checkmenuitem_activate,
- nick)
+ nick)
self.handlers[id_] = item
item = xml.get_object('moderator_checkmenuitem')
@@ -2005,7 +2081,7 @@ class GroupchatControl(ChatControlBase):
target_affiliation in ('admin', 'owner'):
item.set_sensitive(False)
id_ = item.connect('activate', self.on_moderator_checkmenuitem_activate,
- nick)
+ nick)
self.handlers[id_] = item
item = xml.get_object('ban_menuitem')
@@ -2019,23 +2095,27 @@ class GroupchatControl(ChatControlBase):
item = xml.get_object('member_checkmenuitem')
item.set_active(target_affiliation != 'none')
if not user_affiliation in ('admin', 'owner') or \
- (user_affiliation != 'owner' and target_affiliation in ('admin', 'owner')):
+ (user_affiliation != 'owner' and target_affiliation in ('admin',
+ 'owner')):
item.set_sensitive(False)
- id_ = item.connect('activate', self.on_member_checkmenuitem_activate, jid)
+ id_ = item.connect('activate', self.on_member_checkmenuitem_activate,
+ jid)
self.handlers[id_] = item
item = xml.get_object('admin_checkmenuitem')
item.set_active(target_affiliation in ('admin', 'owner'))
if not user_affiliation == 'owner':
item.set_sensitive(False)
- id_ = item.connect('activate', self.on_admin_checkmenuitem_activate, jid)
+ id_ = item.connect('activate', self.on_admin_checkmenuitem_activate,
+ jid)
self.handlers[id_] = item
item = xml.get_object('owner_checkmenuitem')
item.set_active(target_affiliation == 'owner')
if not user_affiliation == 'owner':
item.set_sensitive(False)
- id_ = item.connect('activate', self.on_owner_checkmenuitem_activate, jid)
+ id_ = item.connect('activate', self.on_owner_checkmenuitem_activate,
+ jid)
self.handlers[id_] = item
item = xml.get_object('information_menuitem')
@@ -2212,8 +2292,8 @@ class GroupchatControl(ChatControlBase):
self.tooltip.id = row
nick = model[iter_][C_NICK].decode('utf-8')
self.tooltip.timeout = gobject.timeout_add(500,
- self.show_tooltip, gajim.contacts.get_gc_contact(account,
- self.room_jid, nick))
+ self.show_tooltip, gajim.contacts.get_gc_contact(
+ account, self.room_jid, nick))
def on_list_treeview_leave_notify_event(self, widget, event):
props = widget.get_path_at_pos(int(event.x), int(event.y))
@@ -2233,7 +2313,7 @@ class GroupchatControl(ChatControlBase):
rect = self.list_treeview.get_cell_area(props[0], props[1])
position = self.list_treeview.window.get_origin()
self.tooltip.show_tooltip(contact, rect.height,
- position[1] + rect.y)
+ position[1] + rect.y)
else:
self.tooltip.hide_tooltip()
@@ -2242,97 +2322,98 @@ class GroupchatControl(ChatControlBase):
Grant voice privilege to a user
"""
gajim.connections[self.account].gc_set_role(self.room_jid, nick,
- 'participant')
+ 'participant')
def revoke_voice(self, widget, nick):
"""
Revoke voice privilege to a user
"""
gajim.connections[self.account].gc_set_role(self.room_jid, nick,
- 'visitor')
+ 'visitor')
def grant_moderator(self, widget, nick):
"""
Grant moderator privilege to a user
"""
gajim.connections[self.account].gc_set_role(self.room_jid, nick,
- 'moderator')
+ 'moderator')
def revoke_moderator(self, widget, nick):
"""
Revoke moderator privilege to a user
"""
gajim.connections[self.account].gc_set_role(self.room_jid, nick,
- 'participant')
+ 'participant')
def ban(self, widget, jid):
"""
Ban a user
"""
def on_ok(reason):
- gajim.connections[self.account].gc_set_affiliation(self.room_jid, jid,
- 'outcast', reason)
+ gajim.connections[self.account].gc_set_affiliation(self.room_jid,
+ jid, 'outcast', reason)
# to ban we know the real jid. so jid is not fakejid
nick = gajim.get_nick_from_jid(jid)
# ask for reason
dialogs.InputDialog(_('Banning %s') % nick,
- _('You may specify a reason below:'), ok_handler=on_ok)
+ _('You may specify a reason below:'), ok_handler=on_ok)
def grant_membership(self, widget, jid):
"""
Grant membership privilege to a user
"""
gajim.connections[self.account].gc_set_affiliation(self.room_jid, jid,
- 'member')
+ 'member')
def revoke_membership(self, widget, jid):
"""
Revoke membership privilege to a user
"""
gajim.connections[self.account].gc_set_affiliation(self.room_jid, jid,
- 'none')
+ 'none')
def grant_admin(self, widget, jid):
"""
Grant administrative privilege to a user
"""
gajim.connections[self.account].gc_set_affiliation(self.room_jid, jid,
- 'admin')
+ 'admin')
def revoke_admin(self, widget, jid):
"""
Revoke administrative privilege to a user
"""
gajim.connections[self.account].gc_set_affiliation(self.room_jid, jid,
- 'member')
+ 'member')
def grant_owner(self, widget, jid):
"""
Grant owner privilege to a user
"""
gajim.connections[self.account].gc_set_affiliation(self.room_jid, jid,
- 'owner')
+ 'owner')
def revoke_owner(self, widget, jid):
"""
Revoke owner privilege to a user
"""
gajim.connections[self.account].gc_set_affiliation(self.room_jid, jid,
- 'admin')
+ 'admin')
def on_info(self, widget, nick):
"""
Call vcard_information_window class to display user's information
"""
- gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid, nick)
+ gc_contact = gajim.contacts.get_gc_contact(self.account, self.room_jid,
+ nick)
contact = gc_contact.as_contact()
if contact.jid in gajim.interface.instances[self.account]['infos']:
- gajim.interface.instances[self.account]['infos'][contact.jid].window.\
- present()
+ gajim.interface.instances[self.account]['infos'][contact.jid].\
+ window.present()
else:
gajim.interface.instances[self.account]['infos'][contact.jid] = \
- vcard.VcardWindow(contact, self.account, gc_contact)
+ vcard.VcardWindow(contact, self.account, gc_contact)
def on_history(self, widget, nick):
jid = gajim.construct_fjid(self.room_jid, nick)
@@ -2347,7 +2428,7 @@ class GroupchatControl(ChatControlBase):
if fjid in connection.blocked_contacts:
return
new_rule = {'order': u'1', 'type': u'jid', 'action': u'deny',
- 'value' : fjid, 'child': [u'message', u'iq', u'presence-out']}
+ 'value' : fjid, 'child': [u'message', u'iq', u'presence-out']}
connection.blocked_list.append(new_rule)
connection.blocked_contacts.append(fjid)
self.draw_contact(nick)
@@ -2381,7 +2462,7 @@ class GroupchatControl(ChatControlBase):
connection.del_privacy_list('block')
if 'blocked_contacts' in gajim.interface.instances[self.account]:
gajim.interface.instances[self.account]['blocked_contacts'].\
- privacy_list_received([])
+ privacy_list_received([])
def on_voice_checkmenuitem_activate(self, widget, nick):
if widget.get_active():
diff --git a/src/groups.py b/src/groups.py
index 298e1f4b6..f38cce238 100644
--- a/src/groups.py
+++ b/src/groups.py
@@ -1,8 +1,8 @@
# -*- coding:utf-8 -*-
## src/groups.py
##
-## Copyright (C) 2006 Yann Leboulanger <asterix AT lagaule.org>
-## Tomasz Melcer <liori AT exroot.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
##
## This file is part of Gajim.
##
diff --git a/src/gtkexcepthook.py b/src/gtkexcepthook.py
index f4d6d73c1..1b85e93bb 100644
--- a/src/gtkexcepthook.py
+++ b/src/gtkexcepthook.py
@@ -2,7 +2,7 @@
## src/gtkexcepthook.py
##
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
##
## This file is part of Gajim.
diff --git a/src/gtkgui_helpers.py b/src/gtkgui_helpers.py
index 9d5188207..217b8ba51 100644
--- a/src/gtkgui_helpers.py
+++ b/src/gtkgui_helpers.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/gtkgui_helpers.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
@@ -189,7 +189,8 @@ def get_default_font():
xdg_config_home = os.environ.get('XDG_CONFIG_HOME', '')
if xdg_config_home == '':
xdg_config_home = os.path.expanduser('~/.config') # default
- xfce_config_file = os.path.join(xdg_config_home, 'xfce4/mcs_settings/gtk.xml')
+ xfce_config_file = os.path.join(xdg_config_home,
+ 'xfce4/mcs_settings/gtk.xml')
kde_config_file = os.path.expanduser('~/.kde/share/config/kdeglobals')
@@ -201,7 +202,8 @@ def get_default_font():
return line[start:line.find('"', start)].decode('utf-8')
except Exception:
#we talk about file
- print >> sys.stderr, _('Error: cannot open %s for reading') % xfce_config_file
+ print >> sys.stderr, _('Error: cannot open %s for reading') % \
+ xfce_config_file
elif os.path.exists(kde_config_file):
try:
@@ -216,7 +218,8 @@ def get_default_font():
return font_string.decode('utf-8')
except Exception:
#we talk about file
- print >> sys.stderr, _('Error: cannot open %s for reading') % kde_config_file
+ print >> sys.stderr, _('Error: cannot open %s for reading') % \
+ kde_config_file
return None
@@ -261,7 +264,7 @@ def get_running_processes():
files = os.listdir('/proc')
# files that doesn't have only digits in names...
- files = filter(str.isdigit, files)
+ files = [f for f in files if f.isdigit()]
# files that aren't directories...
files = [f for f in files if os.path.isdir('/proc/' + f)]
@@ -275,7 +278,8 @@ def get_running_processes():
files = [f for f in files if os.path.islink('/proc/' + f + '/exe')]
# list of processes
- processes = [os.path.basename(os.readlink('/proc/' + f +'/exe')) for f in files]
+ processes = [os.path.basename(os.readlink('/proc/' + f +'/exe')) for f \
+ in files]
return processes
return []
@@ -314,7 +318,8 @@ class HashDigest:
def cleanID(self, id_):
id_ = id_.strip().lower()
- for strip in (' :.-_'): id_ = id_.replace(strip, '')
+ for strip in (' :.-_'):
+ id_ = id_.replace(strip, '')
return id_
def __eq__(self, other):
@@ -436,7 +441,8 @@ def get_abspath_for_script(scriptname, want_type = False):
os.chmod(path_to_script, 0700)
except OSError: # do not traceback (could be a permission problem)
#we talk about a file here
- s = _('Could not write to %s. Session Management support will not work') % path_to_script
+ s = _('Could not write to %s. Session Management support will '
+ 'not work') % path_to_script
print >> sys.stderr, s
else: # normal user (not svn user)
@@ -541,13 +547,13 @@ def file_is_locked(path_to_file):
try:
# try make a handle for READING the file
hfile = win32file.CreateFile(
- path_to_file, # path to file
- win32con.GENERIC_READ, # open for reading
- 0, # do not share with other proc
+ path_to_file, # path to file
+ win32con.GENERIC_READ, # open for reading
+ 0, # do not share with other proc
secur_att,
- win32con.OPEN_EXISTING, # existing file only
+ win32con.OPEN_EXISTING, # existing file only
win32con.FILE_ATTRIBUTE_NORMAL, # normal file
- 0 # no attr. template
+ 0 # no attr. template
)
except pywintypes.error:
return True
@@ -555,7 +561,7 @@ def file_is_locked(path_to_file):
hfile.Close()
return False
-def _get_fade_color(treeview, selected, focused):
+def get_fade_color(treeview, selected, focused):
"""
Get a gdk color that is between foreground and background in 0.3
0.7 respectively colors of the cell for the given treeview
@@ -621,7 +627,8 @@ def get_avatar_pixbuf_from_cache(fjid, use_local=True):
# don't show avatar for the transport itself
return None
- if any(jid in gajim.contacts.get_gc_list(acc) for acc in gajim.connections):
+ if any(jid in gajim.contacts.get_gc_list(acc) for acc in \
+ gajim.contacts.get_accounts()):
is_groupchat_contact = True
else:
is_groupchat_contact = False
@@ -756,8 +763,10 @@ def possibly_set_gajim_as_xmpp_handler():
# setting for GNOME/Gconf
client.set_bool('/desktop/gnome/url-handlers/xmpp/enabled', True)
- client.set_string('/desktop/gnome/url-handlers/xmpp/command', command)
- client.set_bool('/desktop/gnome/url-handlers/xmpp/needs_terminal', False)
+ client.set_string('/desktop/gnome/url-handlers/xmpp/command',
+ command)
+ client.set_bool('/desktop/gnome/url-handlers/xmpp/needs_terminal',
+ False)
# setting for KDE
if path_to_kde_file is not None: # user has run kde at least once
@@ -780,7 +789,8 @@ Description=xmpp
''' % command)
f.close()
except IOError:
- log.debug("I/O Error writing settings to %s", repr(path_to_kde_file), exc_info=True)
+ log.debug("I/O Error writing settings to %s",
+ repr(path_to_kde_file), exc_info=True)
else: # no gajim remote, stop ask user everytime
gajim.config.set('check_if_gajim_is_default', False)
@@ -807,12 +817,12 @@ Description=xmpp
# xmpp: is currently handled by another program, so ask the user
pritext = _('Gajim is not the default Jabber client')
sectext = _('Would you like to make Gajim the default Jabber client?')
- checktext = _('Always check to see if Gajim is the default Jabber client '
- 'on startup')
+ checktext = _('Always check to see if Gajim is the default Jabber '
+ 'client on startup')
def on_cancel(checked):
gajim.config.set('check_if_gajim_is_default', checked)
dlg = dialogs.ConfirmationDialogCheck(pritext, sectext, checktext,
- set_gajim_as_xmpp_handler, on_cancel)
+ set_gajim_as_xmpp_handler, on_cancel)
if gajim.config.get('check_if_gajim_is_default'):
dlg.checkbutton.set_active(True)
@@ -849,12 +859,12 @@ def get_possible_button_event(event):
def destroy_widget(widget):
widget.destroy()
-def on_avatar_save_as_menuitem_activate(widget, jid, account, default_name=''):
+def on_avatar_save_as_menuitem_activate(widget, jid, default_name=''):
def on_continue(response, file_path):
if response < 0:
return
pixbuf = get_avatar_pixbuf_from_cache(jid)
- path, extension = os.path.splitext(file_path)
+ extension = os.path.splitext(file_path)[1]
if not extension:
# Silently save as Jpeg image
image_format = 'jpeg'
@@ -875,9 +885,10 @@ def on_avatar_save_as_menuitem_activate(widget, jid, account, default_name=''):
def on_ok(file_path, pixbuf):
pixbuf.save(file_path, 'jpeg')
dialogs.ConfirmationDialog(_('Extension not supported'),
- _('Image cannot be saved in %(type)s format. Save as %(new_filename)s?'
- ) % {'type': image_format, 'new_filename': new_file_path},
- on_response_ok = (on_ok, new_file_path, pixbuf))
+ _('Image cannot be saved in %(type)s format. Save as '
+ '%(new_filename)s?') % {'type': image_format,
+ 'new_filename': new_file_path},
+ on_response_ok = (on_ok, new_file_path, pixbuf))
else:
dialog.destroy()
@@ -888,22 +899,21 @@ def on_avatar_save_as_menuitem_activate(widget, jid, account, default_name=''):
# check if we have write permissions
if not os.access(file_path, os.W_OK):
file_name = os.path.basename(file_path)
- dialogs.ErrorDialog(_('Cannot overwrite existing file "%s"' %
- file_name),
- _('A file with this name already exists and you do not have '
- 'permission to overwrite it.'))
+ dialogs.ErrorDialog(_('Cannot overwrite existing file "%s"') % \
+ file_name, _('A file with this name already exists and you '
+ 'do not have permission to overwrite it.'))
return
dialog2 = dialogs.FTOverwriteConfirmationDialog(
- _('This file already exists'), _('What do you want to do?'),
- propose_resume=False, on_response=(on_continue, file_path))
+ _('This file already exists'), _('What do you want to do?'),
+ propose_resume=False, on_response=(on_continue, file_path))
dialog2.set_transient_for(dialog)
dialog2.set_destroy_with_parent(True)
else:
dirname = os.path.dirname(file_path)
if not os.access(dirname, os.W_OK):
dialogs.ErrorDialog(_('Directory "%s" is not writable') % \
- dirname, _('You do not have permission to create files in this'
- ' directory.'))
+ dirname, _('You do not have permission to create files in '
+ 'this directory.'))
return
on_continue(0, file_path)
@@ -912,15 +922,15 @@ def on_avatar_save_as_menuitem_activate(widget, jid, account, default_name=''):
dialog.destroy()
dialog = dialogs.FileChooserDialog(title_text=_('Save Image as...'),
- action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL,
- gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK),
- default_response=gtk.RESPONSE_OK,
- current_folder=gajim.config.get('last_save_dir'), on_response_ok=on_ok,
- on_response_cancel=on_cancel)
+ action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL,
+ gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK),
+ default_response=gtk.RESPONSE_OK,
+ current_folder=gajim.config.get('last_save_dir'), on_response_ok=on_ok,
+ on_response_cancel=on_cancel)
dialog.set_current_name(default_name + '.jpeg')
dialog.connect('delete-event', lambda widget, event:
- on_cancel(widget))
+ on_cancel(widget))
def on_bm_header_changed_state(widget, event):
widget.set_state(gtk.STATE_NORMAL) #do not allow selected_state
@@ -1124,7 +1134,7 @@ def __label_size_allocate(widget, allocation):
# set wrap width to the pango.Layout of the labels ###
layout.set_width (allocation.width * pango.SCALE)
- lw, lh = layout.get_size ()
+ lh = layout.get_size()[1]
if lh_old != lh:
widget.set_size_request (-1, lh / pango.SCALE)
diff --git a/src/gui_interface.py b/src/gui_interface.py
index f62e67682..646036b51 100644
--- a/src/gui_interface.py
+++ b/src/gui_interface.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/gajim.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
## Norman Rasmussen <norman AT rasmussen.co.za>
@@ -78,7 +78,6 @@ from common import socks5
from common import helpers
from common import dataforms
from common import passwords
-from common import pep
from common import logging_helpers
import roster_window
@@ -120,6 +119,15 @@ class Interface:
#('ERROR', account, (title_text, section_text))
dialogs.ErrorDialog(data[0], data[1])
+ def handle_event_db_error(self, unused, data):
+ #('DB_ERROR', account, (title_text, section_text))
+ if self.db_error_dialog:
+ return
+ self.db_error_dialog = dialogs.ErrorDialog(data[0], data[1])
+ def destroyed(win):
+ self.db_error_dialog = None
+ self.db_error_dialog.connect('destroy', destroyed)
+
def handle_event_information(self, unused, data):
#('INFORMATION', account, (title_text, section_text))
dialogs.InformationDialog(data[0], data[1])
@@ -129,20 +137,19 @@ class Interface:
room_jid = data[0]
title = _('Unable to join group chat')
prompt = _('Your desired nickname in group chat %s is in use or '
- 'registered by another occupant.\nPlease specify another nickname '
- 'below:') % room_jid
+ 'registered by another occupant.\nPlease specify another nickname '
+ 'below:') % room_jid
check_text = _('Always use this nickname when there is a conflict')
if 'change_nick_dialog' in self.instances:
self.instances['change_nick_dialog'].add_room(account, room_jid,
- prompt)
+ prompt)
else:
self.instances['change_nick_dialog'] = dialogs.ChangeNickDialog(
- account, room_jid, title, prompt)
+ account, room_jid, title, prompt)
def handle_event_http_auth(self, account, data):
#('HTTP_AUTH', account, (method, url, transaction_id, iq_obj, msg))
def response(account, iq_obj, answer):
- self.dialog.destroy()
gajim.connections[account].build_http_auth_answer(iq_obj, answer)
def on_yes(is_checked, account, iq_obj):
@@ -153,10 +160,10 @@ class Interface:
sec_msg = _('Do you accept this request on account %s?') % account
if data[4]:
sec_msg = data[4] + '\n' + sec_msg
- self.dialog = dialogs.YesNoDialog(_('HTTP (%(method)s) Authorization for '
- '%(url)s (id: %(id)s)') % {'method': data[0], 'url': data[1],
- 'id': data[2]}, sec_msg, on_response_yes=(on_yes, account, data[3]),
- on_response_no=(response, account, data[3], 'no'))
+ dialog = dialogs.YesNoDialog(_('HTTP (%(method)s) Authorization for '
+ '%(url)s (id: %(id)s)') % {'method': data[0], 'url': data[1],
+ 'id': data[2]}, sec_msg, on_response_yes=(on_yes, account, data[3]),
+ on_response_no=(response, account, data[3], 'no'))
def handle_event_error_answer(self, account, array):
#('ERROR_ANSWER', account, (id, jid_from, errmsg, errcode))
@@ -213,10 +220,12 @@ class Interface:
model = self.roster.status_combobox.get_model()
if show in ('offline', 'error'):
for name in self.instances[account]['online_dialog'].keys():
- # .keys() is needed to not have a dictionary length changed during
- # iteration error
+ # .keys() is needed to not have a dictionary length changed
+ # during iteration error
self.instances[account]['online_dialog'][name].destroy()
- del self.instances[account]['online_dialog'][name]
+ if name in self.instances[account]['online_dialog']:
+ # destroy handler may have already removed it
+ del self.instances[account]['online_dialog'][name]
for request in self.gpg_passphrase.values():
if request:
request.interrupt()
@@ -232,7 +241,8 @@ class Interface:
# we stop blocking notifications of any kind
# this prevents from getting the roster items as 'just signed in'
# contacts. 30 seconds should be enough time
- gobject.timeout_add_seconds(30, self.unblock_signed_in_notifications, account)
+ gobject.timeout_add_seconds(30,
+ self.unblock_signed_in_notifications, account)
# sensitivity for this menuitem
model[self.roster.status_message_menuitem_iter][3] = True
@@ -284,7 +294,7 @@ class Interface:
# FIXME: Drop and rewrite...
statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
- 'invisible']
+ 'invisible']
# Ignore invalid show
if array[1] not in statuss:
return
@@ -294,6 +304,7 @@ class Interface:
jid = array[0].split('/')[0]
keyID = array[5]
contact_nickname = array[7]
+ lcontact = []
# Get the proper keyID
keyID = helpers.prepare_and_validate_gpg_keyID(account, jid, keyID)
@@ -308,8 +319,7 @@ class Interface:
else:
ji = jid
- highest = gajim.contacts. \
- get_contact_with_highest_priority(account, jid)
+ highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
was_highest = (highest and highest.resource == resource)
conn = gajim.connections[account]
@@ -335,11 +345,12 @@ class Interface:
contact1.contact_name = contact_nickname
self.roster.draw_contact(jid, account)
- if old_show == new_show and contact1.status == status_message and \
- contact1.priority == priority: # no change
+ if old_show == new_show and contact1.status == status_message \
+ and contact1.priority == priority: # no change
return
else:
- contact1 = gajim.contacts.get_first_contact_from_jid(account, ji)
+ contact1 = gajim.contacts.get_first_contact_from_jid(account,
+ ji)
if not contact1:
# Presence of another resource of our
# jid
@@ -350,8 +361,8 @@ class Interface:
if new_show < 2:
return
contact1 = gajim.contacts.create_self_contact(jid=ji,
- account=account, show=array[1], status=status_message,
- priority=priority, keyID=keyID, resource=resource)
+ account=account, show=array[1], status=status_message,
+ priority=priority, keyID=keyID, resource=resource)
old_show = 0
gajim.contacts.add_contact(account, contact1)
lcontact.append(contact1)
@@ -374,20 +385,20 @@ class Interface:
gajim.newly_added[account].append(contact1.jid)
if contact1.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].remove(contact1.jid)
- gobject.timeout_add_seconds(5, self.roster.remove_newly_added,
- contact1.jid, account)
+ gobject.timeout_add_seconds(5,
+ self.roster.remove_newly_added, contact1.jid, account)
elif old_show > 1 and new_show == 0 and conn.connected > 1:
if not contact1.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].append(contact1.jid)
if contact1.jid in gajim.newly_added[account]:
gajim.newly_added[account].remove(contact1.jid)
self.roster.draw_contact(contact1.jid, account)
- gobject.timeout_add_seconds(5, self.roster.remove_to_be_removed,
- contact1.jid, account)
+ gobject.timeout_add_seconds(5,
+ self.roster.remove_to_be_removed, contact1.jid, account)
# unset custom status
- if (old_show == 0 and new_show > 1) or (old_show > 1 and new_show == 0\
- and conn.connected > 1):
+ if (old_show == 0 and new_show > 1) or \
+ (old_show > 1 and new_show == 0 and conn.connected > 1):
if account in self.status_sent_to_users and \
jid in self.status_sent_to_users[account]:
del self.status_sent_to_users[account][jid]
@@ -420,7 +431,7 @@ class Interface:
account_ji = account + '/' + ji
gajim.block_signed_in_notifications[account_ji] = True
gobject.timeout_add_seconds(30,
- self.unblock_signed_in_notifications, account_ji)
+ self.unblock_signed_in_notifications, account_ji)
locations = (self.instances, self.instances[account])
for location in locations:
if 'add_contact' in location:
@@ -436,7 +447,7 @@ class Interface:
# (when contact signs out or has errors)
if array[1] in ('offline', 'error'):
contact1.our_chatstate = contact1.chatstate = \
- contact1.composing_xep = None
+ contact1.composing_xep = None
# TODO: This causes problems when another
# resource signs off!
@@ -450,7 +461,7 @@ class Interface:
# there won't be any sessions here if the contact terminated
# their sessions before going offline (which we do)
for sess in conn.get_sessions(ji):
- if (ji+'/'+resource) != str(sess.jid):
+ if (ji + '/' + resource) != str(sess.jid):
continue
if sess.control:
sess.control.no_autonegotiation = False
@@ -459,37 +470,86 @@ class Interface:
conn.delete_session(jid, sess.thread_id)
self.roster.chg_contact_status(contact1, array[1], status_message,
- account)
+ account)
# Notifications
if old_show < 2 and new_show > 1:
- notify.notify('contact_connected', jid, account, status_message)
+ show_notif = True
+ for c in lcontact:
+ if c.resource == resource:
+ # we look for other connected resources
+ continue
+ if c.show not in ('offline', 'error'):
+ show_notif = False
+ break
+ if show_notif:
+ # no other resource is connected, let's look in metacontacts
+ family = gajim.contacts.get_metacontacts_family(account, ji)
+ for info in family:
+ acct_ = info['account']
+ jid_ = info['jid']
+ c_ = gajim.contacts.get_contact_with_highest_priority(
+ acct_, jid_)
+ if not c_:
+ continue
+ if c_.show not in ('offline', 'error'):
+ show_notif = False
+ break
+ if show_notif:
+ notify.notify('contact_connected', jid, account,
+ status_message)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('ContactPresence', (account,
- array))
+ array))
elif old_show > 1 and new_show < 2:
- notify.notify('contact_disconnected', jid, account, status_message)
+ show_notif = True
+ for c in lcontact:
+ if c.resource == resource:
+ # we look for other connected resources
+ continue
+ if c.show not in ('offline', 'error'):
+ show_notif = False
+ break
+ if show_notif:
+ # no other resource is connected, let's look in metacontacts
+ family = gajim.contacts.get_metacontacts_family(account, ji)
+ for info in family:
+ acct_ = info['account']
+ jid_ = info['jid']
+ c_ = gajim.contacts.get_contact_with_highest_priority(
+ acct_, jid_)
+ if not c_:
+ continue
+ if c_.show not in ('offline', 'error'):
+ show_notif = False
+ break
+ if show_notif:
+ notify.notify('contact_disconnected', jid, account,
+ status_message)
if self.remote_ctrl:
- self.remote_ctrl.raise_signal('ContactAbsence', (account, array))
+ self.remote_ctrl.raise_signal('ContactAbsence', (account,
+ array))
# FIXME: stop non active file transfers
# Status change (not connected/disconnected or
# error (<1))
elif new_show > 1:
notify.notify('status_change', jid, account, [new_show,
- status_message])
+ status_message])
if self.remote_ctrl:
- self.remote_ctrl.raise_signal('ContactStatus', (account, array))
+ self.remote_ctrl.raise_signal('ContactStatus', (account,
+ array))
else:
# FIXME: MSN transport (CMSN1.2.1 and PyMSN) don't
# follow the XEP, still the case in 2008.
# It's maybe a GC_NOTIFY (specialy for MSN gc)
self.handle_event_gc_notify(account, (jid, array[1], status_message,
- array[3], None, None, None, None, None, [], None, None))
+ array[3], None, None, None, None, None, [], None, None))
highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
is_highest = (highest and highest.resource == resource)
- # disconnect the session from the ctrl if the highest resource has changed
+ # disconnect the session from the ctrl if the highest resource has
+ # changed
if (was_highest and not is_highest) or (not was_highest and is_highest):
ctrl = self.msg_win_mgr.get_control(jid, account)
@@ -499,7 +559,8 @@ class Interface:
ctrl.contact = highest
def handle_event_msgerror(self, account, array):
- #'MSGERROR' (account, (jid, error_code, error_msg, msg, time[, session]))
+ #'MSGERROR' (account, (jid, error_code, error_msg, msg, time[,
+ # session]))
full_jid_with_resource = array[0]
jids = full_jid_with_resource.split('/', 1)
jid = jids[0]
@@ -526,7 +587,8 @@ class Interface:
if session:
ctrl = session.control
else:
- ctrl = self.msg_win_mgr.get_control(full_jid_with_resource, account)
+ ctrl = self.msg_win_mgr.get_control(full_jid_with_resource,
+ account)
if not ctrl:
tv = gc_control.list_treeview
@@ -536,17 +598,18 @@ class Interface:
show = model[iter_][3]
else:
show = 'offline'
- gc_c = gajim.contacts.create_gc_contact(room_jid=jid, account=account,
- name=nick, show=show)
+ gc_c = gajim.contacts.create_gc_contact(room_jid=jid,
+ account=account, name=nick, show=show)
ctrl = self.new_private_chat(gc_c, account, session)
ctrl.print_conversation(_('Error %(code)s: %(msg)s') % {
- 'code': array[1], 'msg': array[2]}, 'status')
+ 'code': array[1], 'msg': array[2]}, 'status')
return
gc_control.print_conversation(_('Error %(code)s: %(msg)s') % {
- 'code': array[1], 'msg': array[2]}, 'status')
- if gc_control.parent_win and gc_control.parent_win.get_active_jid() == jid:
+ 'code': array[1], 'msg': array[2]}, 'status')
+ if gc_control.parent_win and \
+ gc_control.parent_win.get_active_jid() == jid:
gc_control.set_subject(gc_control.subject)
return
@@ -563,7 +626,8 @@ class Interface:
#('MSGSENT', account, (jid, msg, keyID))
msg = array[1]
# do not play sound when standalone chatstate message (eg no msg)
- if msg and gajim.config.get_per('soundevents', 'message_sent', 'enabled'):
+ if msg and gajim.config.get_per('soundevents', 'message_sent',
+ 'enabled'):
helpers.play_sound('message_sent')
def handle_event_msgnotsent(self, account, array):
@@ -571,7 +635,8 @@ class Interface:
msg = _('error while sending %(message)s ( %(error)s )') % {
'message': array[2], 'error': array[1]}
if not array[4]:
- # No session. This can happen when sending a message from gajim-remote
+ # No session. This can happen when sending a message from
+ # gajim-remote
log.warn(msg)
return
array[4].roster_message(array[0], msg, array[3], account,
@@ -592,10 +657,11 @@ class Interface:
self.add_event(account, jid, 'subscription_request', (text, nick))
if helpers.allow_showing_notification(account):
- path = gtkgui_helpers.get_icon_path('gajim-subscription_request', 48)
+ path = gtkgui_helpers.get_icon_path('gajim-subscription_request',
+ 48)
event_type = _('Subscription request')
notify.popup(event_type, jid, account, 'subscription_request', path,
- event_type, jid)
+ event_type, jid)
def handle_event_subscribed(self, account, array):
#('SUBSCRIBED', account, (jid, resource))
@@ -604,24 +670,25 @@ class Interface:
c = gajim.contacts.get_first_contact_from_jid(account, jid)
c.resource = array[1]
self.roster.remove_contact_from_groups(c.jid, account,
- [_('Not in Roster'), _('Observers')], update=False)
+ [_('Not in Roster'), _('Observers')], update=False)
else:
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
- 'attached_gpg_keys').split()
+ 'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
name = jid.split('@', 1)[0]
name = name.split('%', 1)[0]
contact1 = gajim.contacts.create_contact(jid=jid, account=account,
- name=name, groups=[], show='online', status='online',
- ask='to', resource=array[1], keyID=keyID)
+ name=name, groups=[], show='online', status='online', ask='to',
+ resource=array[1], keyID=keyID)
gajim.contacts.add_contact(account, contact1)
self.roster.add_contact(jid, account)
dialogs.InformationDialog(_('Authorization accepted'),
- _('The contact "%s" has authorized you to see his or her status.')
- % jid)
- if not gajim.config.get_per('accounts', account, 'dont_ack_subscription'):
+ _('The contact "%s" has authorized you to see his or her status.')
+ % jid)
+ if not gajim.config.get_per('accounts', account,
+ 'dont_ack_subscription'):
gajim.connections[account].ack_subscribed(jid)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('Subscribed', (account, array))
@@ -811,7 +878,8 @@ class Interface:
def handle_event_gc_notify(self, account, array):
#'GC_NOTIFY' (account, (room_jid, show, status, nick,
- # role, affiliation, jid, reason, actor, statusCode, newNick, avatar_sha))
+ # role, affiliation, jid, reason, actor, statusCode, newNick,
+ # avatar_sha))
nick = array[3]
if not nick:
return
@@ -829,14 +897,15 @@ class Interface:
room_jid in self.minimized_controls[account]:
control = self.minimized_controls[account][room_jid]
- if not control or (control and control.type_id != message_control.TYPE_GC):
+ if not control or (control and \
+ control.type_id != message_control.TYPE_GC):
return
control.chg_contact_status(nick, show, status, array[4], array[5],
- array[6], array[7], array[8], array[9], array[10], array[11])
+ array[6], array[7], array[8], array[9], array[10], array[11])
- contact = gajim.contacts.\
- get_contact_with_highest_priority(account, room_jid)
+ contact = gajim.contacts.get_contact_with_highest_priority(account,
+ room_jid)
if contact:
self.roster.draw_contact(room_jid, account)
@@ -846,9 +915,11 @@ class Interface:
statusCode = array[9]
if '303' in statusCode:
new_nick = array[10]
- ctrl.print_conversation(_('%(nick)s is now known as %(new_nick)s') \
- % {'nick': nick, 'new_nick': new_nick}, 'status')
- gc_c = gajim.contacts.get_gc_contact(account, room_jid, new_nick)
+ ctrl.print_conversation(_('%(nick)s is now known as '
+ '%(new_nick)s') % {'nick': nick, 'new_nick': new_nick},
+ 'status')
+ gc_c = gajim.contacts.get_gc_contact(account, room_jid,
+ new_nick)
c = gc_c.as_contact()
ctrl.gc_contact = gc_c
ctrl.contact = c
@@ -875,7 +946,8 @@ class Interface:
'nick': nick, 'status': uf_show}, 'status')
if status:
ctrl.print_conversation(' (', 'status', simple=True)
- ctrl.print_conversation('%s' % (status), 'status', simple=True)
+ ctrl.print_conversation('%s' % (status), 'status',
+ simple=True)
ctrl.print_conversation(')', 'status', simple=True)
ctrl.parent_win.redraw_tab(ctrl)
ctrl.update_ui()
@@ -884,7 +956,7 @@ class Interface:
def handle_event_gc_msg(self, account, array):
# ('GC_MSG', account, (jid, msg, time, has_timestamp, htmlmsg,
- # [status_codes]))
+ # [status_codes], displaymarking, captcha))
jids = array[0].split('/', 1)
room_jid = jids[0]
@@ -908,7 +980,8 @@ class Interface:
# message from someone
nick = jids[1]
- gc_control.on_message(nick, msg, array[2], array[3], xhtml, array[5])
+ gc_control.on_message(nick, msg, array[2], array[3], xhtml, array[5],
+ displaymarking=array[6], captcha=array[7])
if self.remote_ctrl:
highlight = gc_control.needs_visual_notification(msg)
@@ -988,7 +1061,8 @@ class Interface:
def handle_event_gc_config_change(self, account, array):
#('GC_CONFIG_CHANGE', account, (jid, statusCode)) statuscode is a list
# http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify
- # http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes-init
+ # http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes...
+ # -init
jid = array[0]
statusCode = array[1]
@@ -1009,8 +1083,8 @@ class Interface:
if '103' in statusCode:
changes.append(_('room now does not show unavailable members'))
if '104' in statusCode:
- changes.append(
- _('A non-privacy-related room configuration change has occurred'))
+ changes.append(_('A non-privacy-related room configuration change '
+ 'has occurred'))
if '170' in statusCode:
# Can be a presence (see chg_contact_status in groupchat_control.py)
changes.append(_('Room logging is now enabled'))
@@ -1055,8 +1129,9 @@ class Interface:
win.remove_tab(ctrl, 3)
dlg = dialogs.InputDialog(_('Password Required'),
- _('A Password is required to join the room %s. Please type it.') % \
- room_jid, is_modal=False, ok_handler=on_ok, cancel_handler=on_cancel)
+ _('A Password is required to join the room %s. Please type it.') % \
+ room_jid, is_modal=False, ok_handler=on_ok,
+ cancel_handler=on_cancel)
dlg.input_entry.set_visibility(False)
def handle_event_gc_invitation(self, account, array):
@@ -1077,6 +1152,20 @@ class Interface:
notify.popup(event_type, jid, account, 'gc-invitation', path,
event_type, room_jid)
+ def handle_event_gc_error(self, account, data):
+ #('ERROR', account, (gc_control, title_text, section_text))
+ gc_control, pritext, sectext = data
+ if gc_control:
+ if gc_control.error_dialog:
+ gc_control.error_dialog.destroy()
+ def on_close(dummy):
+ gc_control.error_dialog.destroy()
+ gc_control.error_dialog = None
+ gc_control.error_dialog = dialogs.ErrorDialog(pritext, sectext,
+ on_response_ok=on_close, on_response_cancel=on_close)
+ else:
+ dialogs.ErrorDialog(pritext, sectext)
+
def forget_gpg_passphrase(self, keyid):
if keyid in self.gpg_passphrase:
del self.gpg_passphrase[keyid]
@@ -1087,9 +1176,10 @@ class Interface:
use_gpg_agent = gajim.config.get('use_gpg_agent')
sectext = ''
if use_gpg_agent:
- sectext = _('You configured Gajim to use GPG agent, but there is no '
- 'GPG agent running or it returned a wrong passphrase.\n')
- sectext += _('You are currently connected without your OpenPGP key.')
+ sectext = _('You configured Gajim to use GPG agent, but there is no'
+ ' GPG agent running or it returned a wrong passphrase.\n')
+ sectext += _('You are currently connected without your OpenPGP '
+ 'key.')
dialogs.WarningDialog(_('Your passphrase is incorrect'), sectext)
else:
path = gtkgui_helpers.get_icon_path('gajim-warning', 48)
@@ -1121,9 +1211,9 @@ class Interface:
callback(False)
dialogs.YesNoDialog(_('GPG key not trusted'), _('The GPG key used to '
- 'encrypt this chat is not trusted. Do you really want to encrypt this '
- 'message?'), checktext=_('Do _not ask me again'),
- on_response_yes=on_yes, on_response_no=on_no)
+ 'encrypt this chat is not trusted. Do you really want to encrypt '
+ 'this message?'), checktext=_('_Do not ask me again'),
+ on_response_yes=on_yes, on_response_no=on_no)
def handle_event_password_required(self, account, array):
#('PASSWORD_REQUIRED', account, None)
@@ -1133,8 +1223,8 @@ class Interface:
if passwords.USER_HAS_GNOMEKEYRING and \
not passwords.USER_USES_GNOMEKEYRING:
text += '\n' + _('Gnome Keyring is installed but not \
- correctly started (environment variable probably not \
- correctly set)')
+ correctly started (environment variable probably not \
+ correctly set)')
def on_ok(passphrase, save):
if save:
@@ -1149,8 +1239,8 @@ class Interface:
del self.pass_dialog[account]
self.pass_dialog[account] = dialogs.PassphraseDialog(
- _('Password Required'), text, _('Save password'), ok_handler=on_ok,
- cancel_handler=on_cancel)
+ _('Password Required'), text, _('Save password'), ok_handler=on_ok,
+ cancel_handler=on_cancel)
def handle_event_roster_info(self, account, array):
#('ROSTER_INFO', account, (jid, name, sub, ask, groups))
@@ -1175,30 +1265,26 @@ class Interface:
gajim.contacts.add_contact(account, contact)
self.roster.add_contact(jid, account)
else:
- # it is an existing contact that might has changed
- re_place = False
# If contact has changed (sub, ask or group) update roster
# Mind about observer status changes:
- # According to xep 0162, a contact is not an observer anymore when
- # we asked for auth, so also remove him if ask changed
+ # According to xep 0162, a contact is not an observer anymore when
+ # we asked for auth, so also remove him if ask changed
old_groups = contacts[0].groups
if contacts[0].sub != sub or contacts[0].ask != ask\
or old_groups != groups:
- re_place = True
- # c.get_shown_groups() has changed. Reflect that in roster_winodow
+ # c.get_shown_groups() has changed. Reflect that in
+ # roster_winodow
self.roster.remove_contact(jid, account, force=True)
for contact in contacts:
contact.name = name or ''
contact.sub = sub
contact.ask = ask
contact.groups = groups or []
- if re_place:
- self.roster.add_contact(jid, account)
- # Refilter and update old groups
- for group in old_groups:
- self.roster.draw_group(group, account)
- else:
- self.roster.draw_contact(jid, account)
+ self.roster.add_contact(jid, account)
+ # Refilter and update old groups
+ for group in old_groups:
+ self.roster.draw_group(group, account)
+ self.roster.draw_contact(jid, account)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('RosterInfo', (account, array))
@@ -1232,7 +1318,7 @@ class Interface:
path = gtkgui_helpers.get_icon_path('gajim-ft_error', 48)
event_type = _('File Transfer Error')
notify.popup(event_type, jid, account, 'file-send-error', path,
- event_type, file_props['name'])
+ event_type, file_props['name'])
def handle_event_gmail_notify(self, account, array):
jid = array[0]
@@ -1249,22 +1335,23 @@ class Interface:
if gajim.config.get('notify_on_new_gmail_email_extra'):
cnt = 0
for gmessage in gmail_messages_list:
- #FIXME: emulate Gtalk client popups. find out what they parse and
- # how they decide what to show each message has a 'From',
- # 'Subject' and 'Snippet' field
- if cnt >=5:
+ # FIXME: emulate Gtalk client popups. find out what they
+ # parse and how they decide what to show each message has a
+ # 'From', 'Subject' and 'Snippet' field
+ if cnt >= 5:
break
senders = ',\n '.join(reversed(gmessage['From']))
- text += _('\n\nFrom: %(from_address)s\nSubject: %(subject)s\n%(snippet)s') % \
- {'from_address': senders, 'subject': gmessage['Subject'],
- 'snippet': gmessage['Snippet']}
+ text += _('\n\nFrom: %(from_address)s\nSubject: '
+ '%(subject)s\n%(snippet)s') % \
+ {'from_address': senders,
+ 'subject': gmessage['Subject'],
+ 'snippet': gmessage['Snippet']}
cnt += 1
if gajim.config.get_per('soundevents', 'gmail_received', 'enabled'):
helpers.play_sound('gmail_received')
notify.popup(_('New E-mail'), jid, account, 'gmail',
- path_to_image=path, title=title,
- text=text)
+ path_to_image=path, title=title, text=text)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('NewGmail', (account, array))
@@ -1362,7 +1449,8 @@ class Interface:
ft.show_stopped(jid, file_props,
error_msg=_('Remote contact stopped transfer'))
elif file_props['error'] == -6:
- ft.show_stopped(jid, file_props, error_msg=_('Error opening file'))
+ ft.show_stopped(jid, file_props,
+ error_msg=_('Error opening file'))
return
msg_type = ''
@@ -1377,9 +1465,10 @@ class Interface:
if event_type == '':
# FIXME: ugly workaround (this can happen Gajim sent, Gaim recvs)
- # this should never happen but it does. see process_result() in socks5.py
- # who calls this func (sth is really wrong unless this func is also registered
- # as progress_cb
+ # this should never happen but it does. see process_result() in
+ # socks5.py
+ # who calls this func (sth is really wrong unless this func is also
+ # registered as progress_cb
return
if msg_type:
@@ -1393,12 +1482,12 @@ class Interface:
sender).get_shown_name()
filename = os.path.basename(file_props['file-name'])
if event_type == _('File Transfer Completed'):
- txt = _('You successfully received %(filename)s from %(name)s.')\
- % {'filename': filename, 'name': name}
+ txt = _('You successfully received %(filename)s from '
+ '%(name)s.') % {'filename': filename, 'name': name}
img_name = 'gajim-ft_done'
else: # ft stopped
- txt = _('File transfer of %(filename)s from %(name)s stopped.')\
- % {'filename': filename, 'name': name}
+ txt = _('File transfer of %(filename)s from %(name)s '
+ 'stopped.') % {'filename': filename, 'name': name}
img_name = 'gajim-ft_stopped'
else:
receiver = file_props['receiver']
@@ -1414,8 +1503,8 @@ class Interface:
% {'filename': filename, 'name': name}
img_name = 'gajim-ft_done'
else: # ft stopped
- txt = _('File transfer of %(filename)s to %(name)s stopped.')\
- % {'filename': filename, 'name': name}
+ txt = _('File transfer of %(filename)s to %(name)s '
+ 'stopped.') % {'filename': filename, 'name': name}
img_name = 'gajim-ft_stopped'
path = gtkgui_helpers.get_icon_path(img_name, 48)
else:
@@ -1434,25 +1523,27 @@ class Interface:
if account not in self.instances:
return
if 'xml_console' in self.instances[account]:
- self.instances[account]['xml_console'].print_stanza(stanza, 'incoming')
+ self.instances[account]['xml_console'].print_stanza(stanza,
+ 'incoming')
def handle_event_stanza_sent(self, account, stanza):
if account not in self.instances:
return
if 'xml_console' in self.instances[account]:
- self.instances[account]['xml_console'].print_stanza(stanza, 'outgoing')
+ self.instances[account]['xml_console'].print_stanza(stanza,
+ 'outgoing')
def handle_event_vcard_published(self, account, array):
if 'profile' in self.instances[account]:
win = self.instances[account]['profile']
win.vcard_published()
- for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC) + \
- self.minimized_controls[account].values():
+ for gc_control in self.msg_win_mgr.get_controls(
+ message_control.TYPE_GC) + self.minimized_controls[account].values():
if gc_control.account == account:
show = gajim.SHOW_LIST[gajim.connections[account].connected]
status = gajim.connections[account].status
gajim.connections[account].send_gc_status(gc_control.nick,
- gc_control.room_jid, show, status)
+ gc_control.room_jid, show, status)
def handle_event_vcard_not_published(self, account, array):
if 'profile' in self.instances[account]:
@@ -1462,7 +1553,7 @@ class Interface:
def ask_offline_status(self, account):
for contact in gajim.contacts.iter_contacts(account):
gajim.connections[account].request_last_status_time(contact.jid,
- contact.resource)
+ contact.resource)
def handle_event_signed_in(self, account, empty):
"""
@@ -1484,7 +1575,8 @@ class Interface:
gajim.sleeper_state[account] = 'online'
elif not ((state == common.sleepy.STATE_AWAY and connected == 4) or \
(state == common.sleepy.STATE_XA and connected == 5)):
- # If we are autoaway/xa and come back after a disconnection, do nothing
+ # If we are autoaway/xa and come back after a disconnection, do
+ # nothing
# Else disable autoaway
gajim.sleeper_state[account] = 'off'
invisible_show = gajim.SHOW_LIST.index('invisible')
@@ -1492,13 +1584,13 @@ class Interface:
if gajim.connections[account].connected == invisible_show:
return
# join already open groupchats
- for gc_control in self.msg_win_mgr.get_controls(message_control.TYPE_GC) \
- + self.minimized_controls[account].values():
+ for gc_control in self.msg_win_mgr.get_controls(
+ message_control.TYPE_GC) + self.minimized_controls[account].values():
if account != gc_control.account:
continue
room_jid = gc_control.room_jid
if room_jid in gajim.gc_connected[account] and \
- gajim.gc_connected[account][room_jid]:
+ gajim.gc_connected[account][room_jid]:
continue
nick = gc_control.nick
password = gajim.gc_passwords.get(room_jid, '')
@@ -1546,7 +1638,8 @@ class Interface:
if account not in self.instances:
return
if 'privacy_lists' in self.instances[account]:
- self.instances[account]['privacy_lists'].privacy_lists_received(data)
+ self.instances[account]['privacy_lists'].privacy_lists_received(
+ data)
def handle_event_privacy_list_received(self, account, data):
# ('PRIVACY_LIST_RECEIVED', account, (name, rules))
@@ -1566,19 +1659,15 @@ class Interface:
if not 'type' in rule:
gajim.connections[account].blocked_all = True
elif rule['type'] == 'jid' and rule['action'] == 'deny':
- gajim.connections[account].blocked_contacts.append(rule['value'])
+ gajim.connections[account].blocked_contacts.append(
+ rule['value'])
elif rule['type'] == 'group' and rule['action'] == 'deny':
- gajim.connections[account].blocked_groups.append(rule['value'])
+ gajim.connections[account].blocked_groups.append(
+ rule['value'])
gajim.connections[account].blocked_list.append(rule)
- #elif rule['type'] == "group" and action == "deny":
- # text_item = _('%s group "%s"') % _(rule['action']), rule['value']
- # self.store.append([text_item])
- # self.global_rules.append(rule)
- #else:
- # self.global_rules_to_append.append(rule)
if 'blocked_contacts' in self.instances[account]:
self.instances[account]['blocked_contacts'].\
- privacy_list_received(rules)
+ privacy_list_received(rules)
def handle_event_privacy_lists_active_default(self, account, data):
if not data:
@@ -1598,15 +1687,17 @@ class Interface:
def handle_event_zc_name_conflict(self, account, data):
def on_ok(new_name):
gajim.config.set_per('accounts', account, 'name', new_name)
+ show = gajim.connections[account].old_show
status = gajim.connections[account].status
gajim.connections[account].username = new_name
- gajim.connections[account].change_status(status, '')
+ gajim.connections[account].change_status(show, status)
def on_cancel():
gajim.connections[account].change_status('offline', '')
dlg = dialogs.InputDialog(_('Username Conflict'),
- _('Please type a new username for your local account'), input_str=data,
- is_modal=True, ok_handler=on_ok, cancel_handler=on_cancel)
+ _('Please type a new username for your local account'),
+ input_str=data, is_modal=True, ok_handler=on_ok,
+ cancel_handler=on_cancel)
def handle_event_ping_sent(self, account, contact):
if contact.jid == contact.get_full_jid():
@@ -1664,13 +1755,15 @@ class Interface:
gajim.connections[account].status)
def on_ok(new_resource):
gajim.config.set_per('accounts', account, 'resource', new_resource)
- self.roster.send_status(account, gajim.connections[account].old_show,
- gajim.connections[account].status)
+ self.roster.send_status(account,
+ gajim.connections[account].old_show,
+ gajim.connections[account].status)
proposed_resource = gajim.connections[account].server_resource
proposed_resource += gajim.config.get('gc_proposed_nick_char')
dlg = dialogs.ResourceConflictDialog(_('Resource Conflict'),
- _('You are already connected to this account with the same resource. '
- 'Please type a new one'), resource=proposed_resource, ok_handler=on_ok)
+ _('You are already connected to this account with the same '
+ 'resource. Please type a new one'), resource=proposed_resource,
+ ok_handler=on_ok)
def handle_event_jingle_incoming(self, account, data):
# ('JINGLE_INCOMING', account, peer jid, sid, tuple-of-contents==(type,
@@ -1693,9 +1786,8 @@ class Interface:
jid = gajim.get_jid_without_resource(peerjid)
resource = gajim.get_resource_from_jid(peerjid)
- ctrl = self.msg_win_mgr.get_control(peerjid, account)
- if not ctrl:
- ctrl = self.msg_win_mgr.get_control(jid, account)
+ ctrl = (self.msg_win_mgr.get_control(peerjid, account)
+ or self.msg_win_mgr.get_control(jid, account))
if ctrl:
if 'audio' in content_types:
ctrl.set_audio_state('connection_received', sid)
@@ -1716,8 +1808,8 @@ class Interface:
if helpers.allow_showing_notification(account):
# TODO: we should use another pixmap ;-)
- txt = _('%s wants to start a voice chat.') % gajim.get_name_from_jid(
- account, peerjid)
+ txt = _('%s wants to start a voice chat.') % \
+ gajim.get_name_from_jid(account, peerjid)
path = gtkgui_helpers.get_icon_path('gajim-mic_active', 48)
event_type = _('Voice Chat Request')
notify.popup(event_type, peerjid, account, 'jingle-incoming',
@@ -1729,9 +1821,8 @@ class Interface:
if media in ('audio', 'video'):
jid = gajim.get_jid_without_resource(peerjid)
resource = gajim.get_resource_from_jid(peerjid)
- ctrl = self.msg_win_mgr.get_control(peerjid, account)
- if not ctrl:
- ctrl = self.msg_win_mgr.get_control(jid, account)
+ ctrl = (self.msg_win_mgr.get_control(peerjid, account)
+ or self.msg_win_mgr.get_control(jid, account))
if ctrl:
if media == 'audio':
ctrl.set_audio_state('connected', sid)
@@ -1743,26 +1834,29 @@ class Interface:
peerjid, sid, media, reason = data
jid = gajim.get_jid_without_resource(peerjid)
resource = gajim.get_resource_from_jid(peerjid)
- ctrl = self.msg_win_mgr.get_control(peerjid, account)
- if not ctrl:
- ctrl = self.msg_win_mgr.get_control(jid, account)
+ ctrl = (self.msg_win_mgr.get_control(peerjid, account)
+ or self.msg_win_mgr.get_control(jid, account))
if ctrl:
- if media in ('audio', None):
+ if media is None:
+ ctrl.stop_jingle(sid=sid, reason=reason)
+ elif media == 'audio':
ctrl.set_audio_state('stop', sid=sid, reason=reason)
- if media in ('video', None):
+ elif media == 'video':
ctrl.set_video_state('stop', sid=sid, reason=reason)
dialog = dialogs.VoIPCallReceivedDialog.get_dialog(peerjid, sid)
if dialog:
- dialog.dialog.destroy()
+ if media is None:
+ dialog.dialog.destroy()
+ else:
+ dialog.remove_contents((media, ))
def handle_event_jingle_error(self, account, data):
# ('JINGLE_ERROR', account, (peerjid, sid, reason))
peerjid, sid, reason = data
jid = gajim.get_jid_without_resource(peerjid)
resource = gajim.get_resource_from_jid(peerjid)
- ctrl = self.msg_win_mgr.get_control(peerjid, account)
- if not ctrl:
- ctrl = self.msg_win_mgr.get_control(jid, account)
+ ctrl = (self.msg_win_mgr.get_control(peerjid, account)
+ or self.msg_win_mgr.get_control(jid, account))
if ctrl:
ctrl.set_audio_state('error', reason=reason)
@@ -1803,14 +1897,15 @@ class Interface:
f.close()
if data[2] in certs:
dialogs.ErrorDialog(_('Certificate Already in File'),
- _('This certificate is already in file %s, so it\'s not added again.') % gajim.MY_CACERTS)
+ _('This certificate is already in file %s, so it\'s '
+ 'not added again.') % gajim.MY_CACERTS)
else:
f = open(gajim.MY_CACERTS, 'a')
f.write(server + '\n')
f.write(data[2] + '\n\n')
f.close()
- gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1',
- data[3])
+ gajim.config.set_per('accounts', account,
+ 'ssl_fingerprint_sha1', data[3])
if is_checked[1]:
ignore_ssl_errors = gajim.config.get_per('accounts', account,
'ignore_ssl_errors').split()
@@ -1825,24 +1920,27 @@ class Interface:
self.handle_event_status(account, 'offline')
pritext = _('Error verifying SSL certificate')
- sectext = _('There was an error verifying the SSL certificate of your jabber server: %(error)s\nDo you still want to connect to this server?') % {'error': data[0]}
+ sectext = _('There was an error verifying the SSL certificate of your '
+ 'jabber server: %(error)s\nDo you still want to connect to this '
+ 'server?') % {'error': data[0]}
if data[1] in (18, 27):
- checktext1 = _('Add this certificate to the list of trusted certificates.\nSHA1 fingerprint of the certificate:\n%s') % data[3]
+ checktext1 = _('Add this certificate to the list of trusted '
+ 'certificates.\nSHA1 fingerprint of the certificate:\n%s') % data[3]
else:
checktext1 = ''
checktext2 = _('Ignore this error for this certificate.')
if 'ssl_error' in self.instances[account]['online_dialog']:
self.instances[account]['online_dialog']['ssl_error'].destroy()
self.instances[account]['online_dialog']['ssl_error'] = \
- dialogs.ConfirmationDialogDubbleCheck(pritext, sectext, checktext1,
- checktext2, on_response_ok=on_ok, on_response_cancel=on_cancel)
+ dialogs.ConfirmationDialogDoubleCheck(pritext, sectext, checktext1,
+ checktext2, on_response_ok=on_ok, on_response_cancel=on_cancel)
def handle_event_fingerprint_error(self, account, data):
# ('FINGERPRINT_ERROR', account, (new_fingerprint,))
def on_yes(is_checked):
del self.instances[account]['online_dialog']['fingerprint_error']
gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1',
- data[0])
+ data[0])
# Reset the ignored ssl errors
gajim.config.set_per('accounts', account, 'ignore_ssl_errors', '')
gajim.connections[account].ssl_certificate_accepted()
@@ -1852,20 +1950,20 @@ class Interface:
self.handle_event_status(account, 'offline')
pritext = _('SSL certificate error')
sectext = _('It seems the SSL certificate of account %(account)s has '
- 'changed or your connection is being hacked.\nOld fingerprint: %(old)s'
- '\nNew fingerprint: %(new)s\n\nDo you still want to connect and update'
- ' the fingerprint of the certificate?') % {'account': account,
- 'old': gajim.config.get_per('accounts', account,
- 'ssl_fingerprint_sha1'), 'new': data[0]}
+ 'changed or your connection is being hacked.\nOld fingerprint: '
+ '%(old)s\nNew fingerprint: %(new)s\n\nDo you still want to connect '
+ 'and update the fingerprint of the certificate?') % \
+ {'account': account, 'old': gajim.config.get_per('accounts',
+ account, 'ssl_fingerprint_sha1'), 'new': data[0]}
if 'fingerprint_error' in self.instances[account]['online_dialog']:
- self.instances[account]['online_dialog']['fingerprint_error'].destroy()
+ self.instances[account]['online_dialog']['fingerprint_error'].\
+ destroy()
self.instances[account]['online_dialog']['fingerprint_error'] = \
- dialogs.YesNoDialog(pritext, sectext, on_response_yes=on_yes,
- on_response_no=on_no)
+ dialogs.YesNoDialog(pritext, sectext, on_response_yes=on_yes,
+ on_response_no=on_no)
def handle_event_plain_connection(self, account, data):
# ('PLAIN_CONNECTION', account, (connection))
- server = gajim.config.get_per('accounts', account, 'hostname')
def on_ok(is_checked):
if not is_checked[0]:
on_cancel()
@@ -1882,20 +1980,21 @@ class Interface:
gajim.connections[account].disconnect(on_purpose=True)
self.handle_event_status(account, 'offline')
pritext = _('Insecure connection')
- sectext = _('You are about to send your password on an unencrypted '
- 'connection. Are you sure you want to do that?')
+ sectext = _('You are about to connect to the server with an insecure '
+ 'connection. This means all your conversations will be '
+ 'exchanged unencrypted. Are you sure you want to do that?')
checktext1 = _('Yes, I really want to connect insecurely')
- checktext2 = _('Do _not ask me again')
+ checktext2 = _('_Do not ask me again')
if 'plain_connection' in self.instances[account]['online_dialog']:
- self.instances[account]['online_dialog']['plain_connection'].destroy()
+ self.instances[account]['online_dialog']['plain_connection'].\
+ destroy()
self.instances[account]['online_dialog']['plain_connection'] = \
- dialogs.ConfirmationDialogDubbleCheck(pritext, sectext,
- checktext1, checktext2, on_response_ok=on_ok,
- on_response_cancel=on_cancel, is_modal=False)
+ dialogs.ConfirmationDialogDoubleCheck(pritext, sectext, checktext1,
+ checktext2, on_response_ok=on_ok, on_response_cancel=on_cancel,
+ is_modal=False)
def handle_event_insecure_ssl_connection(self, account, data):
# ('INSECURE_SSL_CONNECTION', account, (connection, connection_type))
- server = gajim.config.get_per('accounts', account, 'hostname')
def on_ok(is_checked):
if not is_checked[0]:
on_cancel()
@@ -1903,9 +2002,10 @@ class Interface:
del self.instances[account]['online_dialog']['insecure_ssl']
if is_checked[1]:
gajim.config.set_per('accounts', account,
- 'warn_when_insecure_ssl_connection', False)
+ 'warn_when_insecure_ssl_connection', False)
if gajim.connections[account].connected == 0:
- # We have been disconnecting (too long time since window is opened)
+ # We have been disconnecting (too long time since window is
+ # opened)
# re-connect with auto-accept
gajim.connections[account].connection_auto_accepted = True
show, msg = gajim.connections[account].continue_connect_info[:2]
@@ -1918,28 +2018,52 @@ class Interface:
self.handle_event_status(account, 'offline')
pritext = _('Insecure connection')
sectext = _('You are about to send your password on an insecure '
- 'connection. You should install PyOpenSSL to prevent that. Are you sure you want to do that?')
+ 'connection. You should install PyOpenSSL to prevent that. Are you '
+ 'sure you want to do that?')
checktext1 = _('Yes, I really want to connect insecurely')
- checktext2 = _('Do _not ask me again')
+ checktext2 = _('_Do not ask me again')
if 'insecure_ssl' in self.instances[account]['online_dialog']:
self.instances[account]['online_dialog']['insecure_ssl'].destroy()
self.instances[account]['online_dialog']['insecure_ssl'] = \
- dialogs.ConfirmationDialogDubbleCheck(pritext, sectext,
- checktext1, checktext2, on_response_ok=on_ok,
- on_response_cancel=on_cancel, is_modal=False)
+ dialogs.ConfirmationDialogDoubleCheck(pritext, sectext, checktext1,
+ checktext2, on_response_ok=on_ok, on_response_cancel=on_cancel,
+ is_modal=False)
- def handle_event_pubsub_node_removed(self, account, data):
- # ('PUBSUB_NODE_REMOVED', account, (jid, node))
- if 'pep_services' in self.instances[account]:
- if data[0] == gajim.get_jid_from_account(account):
- self.instances[account]['pep_services'].node_removed(data[1])
-
- def handle_event_pubsub_node_not_removed(self, account, data):
- # ('PUBSUB_NODE_NOT_REMOVED', account, (jid, node, msg))
- if data[0] == gajim.get_jid_from_account(account):
- dialogs.WarningDialog(_('PEP node was not removed'),
- _('PEP node %(node)s was not removed: %(message)s') % {
- 'node': data[1], 'message': data[2]})
+ def handle_event_insecure_password(self, account, data):
+ # ('INSECURE_PASSWORD', account, ())
+ def on_ok(is_checked):
+ if not is_checked[0]:
+ on_cancel()
+ return
+ del self.instances[account]['online_dialog']['insecure_password']
+ if is_checked[1]:
+ gajim.config.set_per('accounts', account,
+ 'warn_when_insecure_password', False)
+ if gajim.connections[account].connected == 0:
+ # We have been disconnecting (too long time since window is
+ # opened)
+ # re-connect with auto-accept
+ gajim.connections[account].connection_auto_accepted = True
+ show, msg = gajim.connections[account].continue_connect_info[:2]
+ self.roster.send_status(account, show, msg)
+ return
+ gajim.connections[account].accept_insecure_password()
+ def on_cancel():
+ del self.instances[account]['online_dialog']['insecure_password']
+ gajim.connections[account].disconnect(on_purpose=True)
+ self.handle_event_status(account, 'offline')
+ pritext = _('Insecure connection')
+ sectext = _('You are about to send your password unencrypted on an '
+ 'insecure connection. Are you sure you want to do that?')
+ checktext1 = _('Yes, I really want to connect insecurely')
+ checktext2 = _('_Do not ask me again')
+ if 'insecure_password' in self.instances[account]['online_dialog']:
+ self.instances[account]['online_dialog']['insecure_password'].\
+ destroy()
+ self.instances[account]['online_dialog']['insecure_password'] = \
+ dialogs.ConfirmationDialogDoubleCheck(pritext, sectext, checktext1,
+ checktext2, on_response_ok=on_ok, on_response_cancel=on_cancel,
+ is_modal=False)
def handle_event_pep_received(self, account, data):
# ('PEP_RECEIVED', account, (jid, pep_type))
@@ -1971,89 +2095,93 @@ class Interface:
def create_core_handlers_list(self):
self.handlers = {
- 'ROSTER': [self.handle_event_roster],
- 'WARNING': [self.handle_event_warning],
- 'ERROR': [self.handle_event_error],
- 'INFORMATION': [self.handle_event_information],
- 'ERROR_ANSWER': [self.handle_event_error_answer],
- 'STATUS': [self.handle_event_status],
- 'NEW_JID': [self.handle_event_new_jid],
- 'NOTIFY': [self.handle_event_notify],
- 'MSGERROR': [self.handle_event_msgerror],
- 'MSGSENT': [self.handle_event_msgsent],
- 'MSGNOTSENT': [self.handle_event_msgnotsent],
- 'SUBSCRIBED': [self.handle_event_subscribed],
- 'UNSUBSCRIBED': [self.handle_event_unsubscribed],
- 'SUBSCRIBE': [self.handle_event_subscribe],
- 'AGENT_REMOVED': [self.handle_event_agent_removed],
- 'REGISTER_AGENT_INFO': [self.handle_event_register_agent_info],
- 'AGENT_INFO_ITEMS': [self.handle_event_agent_info_items],
- 'QUIT': [self.handle_event_quit],
- 'ACC_OK': [self.handle_event_acc_ok],
- 'MYVCARD': [self.handle_event_myvcard],
- 'VCARD': [self.handle_event_vcard],
- 'LAST_STATUS_TIME': [self.handle_event_last_status_time],
- 'OS_INFO': [self.handle_event_os_info],
- 'ENTITY_TIME': [self.handle_event_entity_time],
- 'GC_NOTIFY': [self.handle_event_gc_notify],
- 'GC_MSG': [self.handle_event_gc_msg],
- 'GC_SUBJECT': [self.handle_event_gc_subject],
- 'GC_CONFIG': [self.handle_event_gc_config],
- 'GC_CONFIG_CHANGE': [self.handle_event_gc_config_change],
- 'GC_INVITATION': [self.handle_event_gc_invitation],
- 'GC_AFFILIATION': [self.handle_event_gc_affiliation],
- 'GC_PASSWORD_REQUIRED': [self.handle_event_gc_password_required],
- 'BAD_PASSPHRASE': [self.handle_event_bad_passphrase],
- 'ROSTER_INFO': [self.handle_event_roster_info],
- 'BOOKMARKS': [self.handle_event_bookmarks],
- 'CON_TYPE': [self.handle_event_con_type],
- 'CONNECTION_LOST': [self.handle_event_connection_lost],
- 'FILE_REQUEST': [self.handle_event_file_request],
- 'GMAIL_NOTIFY': [self.handle_event_gmail_notify],
- 'FILE_REQUEST_ERROR': [self.handle_event_file_request_error],
- 'FILE_SEND_ERROR': [self.handle_event_file_send_error],
- 'STANZA_ARRIVED': [self.handle_event_stanza_arrived],
- 'STANZA_SENT': [self.handle_event_stanza_sent],
- 'HTTP_AUTH': [self.handle_event_http_auth],
- 'VCARD_PUBLISHED': [self.handle_event_vcard_published],
- 'VCARD_NOT_PUBLISHED': [self.handle_event_vcard_not_published],
- 'ASK_NEW_NICK': [self.handle_event_ask_new_nick],
- 'SIGNED_IN': [self.handle_event_signed_in],
- 'METACONTACTS': [self.handle_event_metacontacts],
- 'ATOM_ENTRY': [self.handle_atom_entry],
- 'FAILED_DECRYPT': [self.handle_event_failed_decrypt],
- 'PRIVACY_LISTS_RECEIVED': [self.handle_event_privacy_lists_received],
- 'PRIVACY_LIST_RECEIVED': [self.handle_event_privacy_list_received],
- 'PRIVACY_LISTS_ACTIVE_DEFAULT': \
- [self.handle_event_privacy_lists_active_default],
- 'PRIVACY_LIST_REMOVED': [self.handle_event_privacy_list_removed],
- 'ZC_NAME_CONFLICT': [self.handle_event_zc_name_conflict],
- 'PING_SENT': [self.handle_event_ping_sent],
- 'PING_REPLY': [self.handle_event_ping_reply],
- 'PING_ERROR': [self.handle_event_ping_error],
- 'SEARCH_FORM': [self.handle_event_search_form],
- 'SEARCH_RESULT': [self.handle_event_search_result],
- 'RESOURCE_CONFLICT': [self.handle_event_resource_conflict],
- 'ROSTERX': [self.handle_event_roster_item_exchange],
- 'PEP_CONFIG': [self.handle_event_pep_config],
- 'UNIQUE_ROOM_ID_UNSUPPORTED': \
- [self.handle_event_unique_room_id_unsupported],
- 'UNIQUE_ROOM_ID_SUPPORTED': [self.handle_event_unique_room_id_supported],
- 'GPG_PASSWORD_REQUIRED': [self.handle_event_gpg_password_required],
- 'GPG_ALWAYS_TRUST': [self.handle_event_gpg_always_trust],
- 'PASSWORD_REQUIRED': [self.handle_event_password_required],
- 'SSL_ERROR': [self.handle_event_ssl_error],
- 'FINGERPRINT_ERROR': [self.handle_event_fingerprint_error],
- 'PLAIN_CONNECTION': [self.handle_event_plain_connection],
- 'INSECURE_SSL_CONNECTION': [self.handle_event_insecure_ssl_connection],
- 'PUBSUB_NODE_REMOVED': [self.handle_event_pubsub_node_removed],
- 'PUBSUB_NODE_NOT_REMOVED': [self.handle_event_pubsub_node_not_removed],
- 'JINGLE_INCOMING': [self.handle_event_jingle_incoming],
- 'JINGLE_CONNECTED': [self.handle_event_jingle_connected],
- 'JINGLE_DISCONNECTED': [self.handle_event_jingle_disconnected],
- 'JINGLE_ERROR': [self.handle_event_jingle_error],
- 'PEP_RECEIVED': [self.handle_event_pep_received],
- 'CAPS_RECEIVED': [self.handle_event_caps_received]
+ 'ROSTER': [self.handle_event_roster],
+ 'WARNING': [self.handle_event_warning],
+ 'ERROR': [self.handle_event_error],
+ 'DB_ERROR': [self.handle_event_db_error],
+ 'INFORMATION': [self.handle_event_information],
+ 'ERROR_ANSWER': [self.handle_event_error_answer],
+ 'STATUS': [self.handle_event_status],
+ 'NEW_JID': [self.handle_event_new_jid],
+ 'NOTIFY': [self.handle_event_notify],
+ 'MSGERROR': [self.handle_event_msgerror],
+ 'MSGSENT': [self.handle_event_msgsent],
+ 'MSGNOTSENT': [self.handle_event_msgnotsent],
+ 'SUBSCRIBED': [self.handle_event_subscribed],
+ 'UNSUBSCRIBED': [self.handle_event_unsubscribed],
+ 'SUBSCRIBE': [self.handle_event_subscribe],
+ 'AGENT_REMOVED': [self.handle_event_agent_removed],
+ 'REGISTER_AGENT_INFO': [self.handle_event_register_agent_info],
+ 'AGENT_INFO_ITEMS': [self.handle_event_agent_info_items],
+ 'QUIT': [self.handle_event_quit],
+ 'ACC_OK': [self.handle_event_acc_ok],
+ 'MYVCARD': [self.handle_event_myvcard],
+ 'VCARD': [self.handle_event_vcard],
+ 'LAST_STATUS_TIME': [self.handle_event_last_status_time],
+ 'OS_INFO': [self.handle_event_os_info],
+ 'ENTITY_TIME': [self.handle_event_entity_time],
+ 'GC_NOTIFY': [self.handle_event_gc_notify],
+ 'GC_MSG': [self.handle_event_gc_msg],
+ 'GC_SUBJECT': [self.handle_event_gc_subject],
+ 'GC_CONFIG': [self.handle_event_gc_config],
+ 'GC_CONFIG_CHANGE': [self.handle_event_gc_config_change],
+ 'GC_INVITATION': [self.handle_event_gc_invitation],
+ 'GC_AFFILIATION': [self.handle_event_gc_affiliation],
+ 'GC_PASSWORD_REQUIRED': [self.handle_event_gc_password_required],
+ 'GC_ERROR': [self.handle_event_gc_error],
+ 'BAD_PASSPHRASE': [self.handle_event_bad_passphrase],
+ 'ROSTER_INFO': [self.handle_event_roster_info],
+ 'BOOKMARKS': [self.handle_event_bookmarks],
+ 'CON_TYPE': [self.handle_event_con_type],
+ 'CONNECTION_LOST': [self.handle_event_connection_lost],
+ 'FILE_REQUEST': [self.handle_event_file_request],
+ 'GMAIL_NOTIFY': [self.handle_event_gmail_notify],
+ 'FILE_REQUEST_ERROR': [self.handle_event_file_request_error],
+ 'FILE_SEND_ERROR': [self.handle_event_file_send_error],
+ 'STANZA_ARRIVED': [self.handle_event_stanza_arrived],
+ 'STANZA_SENT': [self.handle_event_stanza_sent],
+ 'HTTP_AUTH': [self.handle_event_http_auth],
+ 'VCARD_PUBLISHED': [self.handle_event_vcard_published],
+ 'VCARD_NOT_PUBLISHED': [self.handle_event_vcard_not_published],
+ 'ASK_NEW_NICK': [self.handle_event_ask_new_nick],
+ 'SIGNED_IN': [self.handle_event_signed_in],
+ 'METACONTACTS': [self.handle_event_metacontacts],
+ 'ATOM_ENTRY': [self.handle_atom_entry],
+ 'FAILED_DECRYPT': [self.handle_event_failed_decrypt],
+ 'PRIVACY_LISTS_RECEIVED': \
+ [self.handle_event_privacy_lists_received],
+ 'PRIVACY_LIST_RECEIVED': [self.handle_event_privacy_list_received],
+ 'PRIVACY_LISTS_ACTIVE_DEFAULT': \
+ [self.handle_event_privacy_lists_active_default],
+ 'PRIVACY_LIST_REMOVED': [self.handle_event_privacy_list_removed],
+ 'ZC_NAME_CONFLICT': [self.handle_event_zc_name_conflict],
+ 'PING_SENT': [self.handle_event_ping_sent],
+ 'PING_REPLY': [self.handle_event_ping_reply],
+ 'PING_ERROR': [self.handle_event_ping_error],
+ 'SEARCH_FORM': [self.handle_event_search_form],
+ 'SEARCH_RESULT': [self.handle_event_search_result],
+ 'RESOURCE_CONFLICT': [self.handle_event_resource_conflict],
+ 'ROSTERX': [self.handle_event_roster_item_exchange],
+ 'PEP_CONFIG': [self.handle_event_pep_config],
+ 'UNIQUE_ROOM_ID_UNSUPPORTED': \
+ [self.handle_event_unique_room_id_unsupported],
+ 'UNIQUE_ROOM_ID_SUPPORTED': \
+ [self.handle_event_unique_room_id_supported],
+ 'GPG_PASSWORD_REQUIRED': [self.handle_event_gpg_password_required],
+ 'GPG_ALWAYS_TRUST': [self.handle_event_gpg_always_trust],
+ 'PASSWORD_REQUIRED': [self.handle_event_password_required],
+ 'SSL_ERROR': [self.handle_event_ssl_error],
+ 'FINGERPRINT_ERROR': [self.handle_event_fingerprint_error],
+ 'PLAIN_CONNECTION': [self.handle_event_plain_connection],
+ 'INSECURE_SSL_CONNECTION': \
+ [self.handle_event_insecure_ssl_connection],
+ 'INSECURE_PASSWORD': [self.handle_event_insecure_password],
+ 'JINGLE_INCOMING': [self.handle_event_jingle_incoming],
+ 'JINGLE_CONNECTED': [self.handle_event_jingle_connected],
+ 'JINGLE_DISCONNECTED': [self.handle_event_jingle_disconnected],
+ 'JINGLE_ERROR': [self.handle_event_jingle_error],
+ 'PEP_RECEIVED': [self.handle_event_pep_received],
+ 'CAPS_RECEIVED': [self.handle_event_caps_received]
}
def register_core_handlers(self):
@@ -2065,7 +2193,7 @@ class Interface:
for event_name, event_handlers in self.handlers.iteritems():
for event_handler in event_handlers:
gajim.ged.register_event_handler(event_name, ged.CORE,
- event_handler)
+ event_handler)
################################################################################
### Methods dealing with gajim.events
@@ -2079,22 +2207,24 @@ class Interface:
# Do we have a queue?
jid = gajim.get_jid_without_resource(jid)
no_queue = len(gajim.events.get_events(account, jid)) == 0
- # type_ can be gc-invitation file-send-error file-error file-request-error
- # file-request file-completed file-stopped jingle-incoming
+ # type_ can be gc-invitation file-send-error file-error
+ # file-request-error file-request file-completed file-stopped
+ # jingle-incoming
# event_type can be in advancedNotificationWindow.events_list
event_types = {'file-request': 'ft_request',
- 'file-completed': 'ft_finished'}
+ 'file-completed': 'ft_finished'}
event_type = event_types.get(type_)
show_in_roster = notify.get_show_in_roster(event_type, account, jid)
show_in_systray = notify.get_show_in_systray(event_type, account, jid)
event = gajim.events.create_event(type_, event_args,
- show_in_roster=show_in_roster,
- show_in_systray=show_in_systray)
+ show_in_roster=show_in_roster,
+ show_in_systray=show_in_systray)
gajim.events.add_event(account, jid, event)
self.roster.show_title()
if no_queue: # We didn't have a queue: we change icons
- if not gajim.contacts.get_contact_with_highest_priority(account, jid):
+ if not gajim.contacts.get_contact_with_highest_priority(account,
+ jid):
if type_ == 'gc-invitation':
self.roster.add_groupchat(jid, account, status='offline')
else:
@@ -2108,7 +2238,8 @@ class Interface:
family = gajim.contacts.get_metacontacts_family(account, jid)
if family:
nearby_family, bb_jid, bb_account = \
- gajim.contacts.get_nearby_family_and_big_brother(family, account)
+ gajim.contacts.get_nearby_family_and_big_brother(family,
+ account)
else:
bb_jid, bb_account = jid, account
self.roster.select_contact(bb_jid, bb_account)
@@ -2148,8 +2279,8 @@ class Interface:
ctrl = self.msg_win_mgr.get_control(fjid, account)
if not ctrl:
- highest_contact = gajim.contacts.get_contact_with_highest_priority(
- account, jid)
+ highest_contact = gajim.contacts.\
+ get_contact_with_highest_priority(account, jid)
# jid can have a window if this resource was lower when he sent
# message and is now higher because the other one is offline
if resource and highest_contact.resource == resource and \
@@ -2164,14 +2295,15 @@ class Interface:
if not contact:
contact = highest_contact
- ctrl = self.new_chat(contact, account, resource = resource, session = session)
+ ctrl = self.new_chat(contact, account, resource=resource,
+ session=session)
gajim.last_message_time[account][jid] = 0 # long time ago
w = ctrl.parent_win
elif type_ in ('printed_pm', 'pm'):
- # assume that the most recently updated control we have for this party
- # is the one that this event was in
+ # assume that the most recently updated control we have for this
+ # party is the one that this event was in
event = gajim.events.get_first_event(account, fjid, type_)
if not event:
event = gajim.events.get_first_event(account, jid, type_)
@@ -2187,17 +2319,18 @@ class Interface:
room_jid = jid
nick = resource
gc_contact = gajim.contacts.get_gc_contact(account, room_jid,
- nick)
+ nick)
if gc_contact:
show = gc_contact.show
else:
show = 'offline'
gc_contact = gajim.contacts.create_gc_contact(
- room_jid=room_jid, account=account, name=nick, show=show)
+ room_jid=room_jid, account=account, name=nick,
+ show=show)
if not session:
session = gajim.connections[account].make_new_session(
- fjid, None, type_='pm')
+ fjid, None, type_='pm')
self.new_private_chat(gc_contact, account, session=session)
ctrl = session.control
@@ -2219,7 +2352,7 @@ class Interface:
# Open the window
self.roster.open_event(account, fjid, event)
elif type_ == 'gmail':
- url=gajim.connections[account].gmail_url
+ url = gajim.connections[account].gmail_url
if url:
helpers.launch_browser_mailer('url', url)
elif type_ == 'gc-invitation':
@@ -2268,36 +2401,29 @@ class Interface:
@property
def basic_pattern_re(self):
- try:
- return self._basic_pattern_re
- except AttributeError:
- self._basic_pattern_re = re.compile(self.basic_pattern, re.IGNORECASE)
- return self._basic_pattern_re
+ if not self._basic_pattern_re:
+ self._basic_pattern_re = re.compile(self.basic_pattern,
+ re.IGNORECASE)
+ return self._basic_pattern_re
@property
def emot_and_basic_re(self):
- try:
- return self._emot_and_basic_re
- except AttributeError:
+ if not self._emot_and_basic_re:
self._emot_and_basic_re = re.compile(self.emot_and_basic,
re.IGNORECASE + re.UNICODE)
- return self._emot_and_basic_re
+ return self._emot_and_basic_re
@property
def sth_at_sth_dot_sth_re(self):
- try:
- return self._sth_at_sth_dot_sth_re
- except AttributeError:
+ if not self._sth_at_sth_dot_sth_re:
self._sth_at_sth_dot_sth_re = re.compile(self.sth_at_sth_dot_sth)
- return self._sth_at_sth_dot_sth_re
+ return self._sth_at_sth_dot_sth_re
@property
def invalid_XML_chars_re(self):
- try:
- return self._invalid_XML_chars_re
- except AttributeError:
+ if not self._invalid_XML_chars_re:
self._invalid_XML_chars_re = re.compile(self.invalid_XML_chars)
- return self._invalid_XML_chars_re
+ return self._invalid_XML_chars_re
def make_regexps(self):
# regexp meta characters are: . ^ $ * + ? { } [ ] \ | ( )
@@ -2307,7 +2433,7 @@ class Interface:
# \w any alphanumeric character
# \W any non-alphanumeric character
# \b means word boundary. This is a zero-width assertion that
- # matches only at the beginning or end of a word.
+ # matches only at the beginning or end of a word.
# ^ matches at the beginning of lines
#
# * means 0 or more times
@@ -2316,38 +2442,44 @@ class Interface:
# | means or
# [^*] anything but '*' (inside [] you don't have to escape metachars)
# [^\s*] anything but whitespaces and '*'
- # (?<!\S) is a one char lookbehind assertion and asks for any leading whitespace
+ # (?<!\S) is a one char lookbehind assertion and asks for any leading
+ # whitespace
# and mathces beginning of lines so we have correct formatting detection
# even if the the text is just '*foo*'
# (?!\S) is the same thing but it's a lookahead assertion
- # \S*[^\s\W] --> in the matching string don't match ? or ) etc.. if at the end
- # so http://be) will match http://be and http://be)be) will match http://be)be
-
- legacy_prefixes = r"((?<=\()(www|ftp)\.([A-Za-z0-9\.\-_~:/\?#\[\]@!\$&'\(\)\*\+,;=]|%[A-Fa-f0-9]{2})+(?=\)))"\
- r"|((www|ftp)\.([A-Za-z0-9\.\-_~:/\?#\[\]@!\$&'\(\)\*\+,;=]|%[A-Fa-f0-9]{2})+"\
- r"\.([A-Za-z0-9\.\-_~:/\?#\[\]@!\$&'\(\)\*\+,;=]|%[A-Fa-f0-9]{2})+)"
+ # \S*[^\s\W] --> in the matching string don't match ? or ) etc.. if at
+ # the end
+ # so http://be) will match http://be and http://be)be) will match
+ # http://be)be
+
+ legacy_prefixes = r"((?<=\()(www|ftp)\.([A-Za-z0-9\.\-_~:/\?#\[\]@!\$"\
+ r"&'\(\)\*\+,;=]|%[A-Fa-f0-9]{2})+(?=\)))"\
+ r"|((www|ftp)\.([A-Za-z0-9\.\-_~:/\?#\[\]@!\$&'\(\)\*\+,;=]"\
+ r"|%[A-Fa-f0-9]{2})+"\
+ r"\.([A-Za-z0-9\.\-_~:/\?#\[\]@!\$&'\(\)\*\+,;=]|%[A-Fa-f0-9]{2})+)"
# NOTE: it's ok to catch www.gr such stuff exist!
- #FIXME: recognize xmpp: and treat it specially
+ # FIXME: recognize xmpp: and treat it specially
links = r"((?<=\()[A-Za-z][A-Za-z0-9\+\.\-]*:"\
- r"([\w\.\-_~:/\?#\[\]@!\$&'\(\)\*\+,;=]|%[A-Fa-f0-9]{2})+"\
- r"(?=\)))|([A-Za-z][A-Za-z0-9\+\.\-]*:([\w\.\-_~:/\?#\[\]@!\$&'\(\)\*\+,;=]|%[A-Fa-f0-9]{2})+)"
+ r"([\w\.\-_~:/\?#\[\]@!\$&'\(\)\*\+,;=]|%[A-Fa-f0-9]{2})+"\
+ r"(?=\)))|(\w[\w\+\.\-]*:(\S|%[A-Fa-f0-9]{2})+)"
- #2nd one: at_least_one_char@at_least_one_char.at_least_one_char
+ # 2nd one: at_least_one_char@at_least_one_char.at_least_one_char
mail = r'\bmailto:\S*[^\s\W]|' r'\b\S+@\S+\.\S*[^\s\W]'
- #detects eg. *b* *bold* *bold bold* test *bold* *bold*! (*bold*)
- #doesn't detect (it's a feature :P) * bold* *bold * * bold * test*bold*
+ # detects eg. *b* *bold* *bold bold* test *bold* *bold*! (*bold*)
+ # doesn't detect (it's a feature :P) * bold* *bold * * bold * test*bold*
formatting = r'|(?<!\w)' r'\*[^\s*]' r'([^*]*[^\s*])?' r'\*(?!\w)|'\
- r'(?<!\S)' r'/[^\s/]' r'([^/]*[^\s/])?' r'/(?!\S)|'\
- r'(?<!\w)' r'_[^\s_]' r'([^_]*[^\s_])?' r'_(?!\w)'
+ r'(?<!\S)' r'/[^\s/]' r'([^/]*[^\s/])?' r'/(?!\S)|'\
+ r'(?<!\w)' r'_[^\s_]' r'([^_]*[^\s_])?' r'_(?!\w)'
- latex = r'|\$\$[^$\\]*?([\]\[0-9A-Za-z()|+*/-]|[\\][\]\[0-9A-Za-z()|{}$])(.*?[^\\])?\$\$'
+ latex = r'|\$\$[^$\\]*?([\]\[0-9A-Za-z()|+*/-]|'\
+ r'[\\][\]\[0-9A-Za-z()|{}$])(.*?[^\\])?\$\$'
basic_pattern = links + '|' + mail + '|' + legacy_prefixes
link_pattern = basic_pattern
- self.link_pattern_re = re.compile(link_pattern, re.IGNORECASE)
+ self.link_pattern_re = re.compile(link_pattern, re.I | re.U)
if gajim.config.get('use_latex'):
basic_pattern += latex
@@ -2358,8 +2490,8 @@ class Interface:
emoticons_pattern = ''
if gajim.config.get('emoticons_theme'):
- # When an emoticon is bordered by an alpha-numeric character it is NOT
- # expanded. e.g., foo:) NO, foo :) YES, (brb) NO, (:)) YES, etc.
+ # When an emoticon is bordered by an alpha-numeric character it is
+ # NOT expanded. e.g., foo:) NO, foo :) YES, (brb) NO, (:)) YES, etc
# We still allow multiple emoticons side-by-side like :P:P:P
# sort keys by length so :qwe emot is checked before :q
keys = sorted(self.emoticons, key=len, reverse=True)
@@ -2369,21 +2501,24 @@ class Interface:
for emoticon in keys: # travel thru emoticons list
emoticon = emoticon.decode('utf-8')
emoticon_escaped = re.escape(emoticon) # espace regexp metachars
- emoticons_pattern += emoticon_escaped + '|'# | means or in regexp
+ # | means or in regexp
+ emoticons_pattern += emoticon_escaped + '|'
if (emoticon_length != len(emoticon)):
- # Build up expressions to match emoticons next to other emoticons
- emoticons_pattern_prematch = emoticons_pattern_prematch[:-1] + ')|(?<='
- emoticons_pattern_postmatch = emoticons_pattern_postmatch[:-1] + ')|(?='
+ # Build up expressions to match emoticons next to others
+ emoticons_pattern_prematch = \
+ emoticons_pattern_prematch[:-1] + ')|(?<='
+ emoticons_pattern_postmatch = \
+ emoticons_pattern_postmatch[:-1] + ')|(?='
emoticon_length = len(emoticon)
emoticons_pattern_prematch += emoticon_escaped + '|'
emoticons_pattern_postmatch += emoticon_escaped + '|'
# We match from our list of emoticons, but they must either have
# whitespace, or another emoticon next to it to match successfully
# [\w.] alphanumeric and dot (for not matching 8) in (2.8))
- emoticons_pattern = '|' + \
- '(?:(?<![\w.]' + emoticons_pattern_prematch[:-1] + '))' + \
- '(?:' + emoticons_pattern[:-1] + ')' + \
- '(?:(?![\w]' + emoticons_pattern_postmatch[:-1] + '))'
+ emoticons_pattern = '|' + '(?:(?<![\w.]' + \
+ emoticons_pattern_prematch[:-1] + '))' + '(?:' + \
+ emoticons_pattern[:-1] + ')' + '(?:(?![\w]' + \
+ emoticons_pattern_postmatch[:-1] + '))'
# because emoticons match later (in the string) they need to be after
# basic matches that may occur earlier
@@ -2396,14 +2531,15 @@ class Interface:
self.sth_at_sth_dot_sth = r'\S+@\S+\.\S*[^\s)?]'
# Invalid XML chars
- self.invalid_XML_chars = u'[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19]|[\ud800-\udfff]|[\ufffe-\uffff]'
+ self.invalid_XML_chars = u'[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x19]|'\
+ u'[\ud800-\udfff]|[\ufffe-\uffff]'
def popup_emoticons_under_button(self, button, parent_win):
"""
Popup the emoticons menu under button, located in parent_win
"""
gtkgui_helpers.popup_emoticons_under_button(self.emoticons_menu,
- button, parent_win)
+ button, parent_win)
def prepare_emoticons_menu(self):
menu = gtk.Menu()
@@ -2431,7 +2567,7 @@ class Interface:
item.connect('activate', emoticon_clicked, image[0])
#FIXME: add tooltip with ascii
menu.attach(item, counter % size, counter % size + 1,
- counter / size, counter / size + 1)
+ counter / size, counter / size + 1)
counter += 1
menu.connect('selection-done', selection_done)
menu.show_all()
@@ -2446,7 +2582,8 @@ class Interface:
sys.path.append(path)
import emoticons
if need_reload:
- # we need to reload else that doesn't work when changing emoticon set
+ # we need to reload else that doesn't work when changing emoticon
+ # set
reload(emoticons)
emots = emoticons.emoticons
for emot_filename in emots:
@@ -2455,12 +2592,14 @@ class Interface:
continue
for emot in emots[emot_filename]:
emot = emot.decode('utf-8')
- # This avoids duplicated emoticons with the same image eg. :) and :-)
+ # This avoids duplicated emoticons with the same image eg. :)
+ # and :-)
if not emot_file in self.emoticons.values():
if emot_file.endswith('.gif'):
pix = gtk.gdk.PixbufAnimation(emot_file)
else:
- pix = gtk.gdk.pixbuf_new_from_file_at_size(emot_file, 16, 16)
+ pix = gtk.gdk.pixbuf_new_from_file_at_size(emot_file,
+ 16, 16)
self.emoticons_images.append((emot, pix))
self.emoticons[emot.upper()] = emot_file
del emoticons
@@ -2475,9 +2614,11 @@ class Interface:
if not os.path.exists(path):
# It's maybe a user theme
path = os.path.join(gajim.MY_EMOTS_PATH, emot_theme)
- if not os.path.exists(path): # theme doesn't exist, disable emoticons
+ if not os.path.exists(path):
+ # theme doesn't exist, disable emoticons
dialogs.WarningDialog(_('Emoticons disabled'),
- _('Your configured emoticons theme has not been found, so emoticons have been disabled.'))
+ _('Your configured emoticons theme has not been found, so '
+ 'emoticons have been disabled.'))
gajim.config.set('emoticons_theme', '')
return
self._init_emoticons(path, need_reload)
@@ -2490,8 +2631,8 @@ class Interface:
fd = open(os.path.join(path, 'emoticons.py'), 'w')
fd.write('emoticons = ')
pprint.pprint( dict([
- (file_, [i for i in emots.keys() if emots[i] == file_])
- for file_ in set(emots.values())]), fd)
+ (file_, [i for i in emots.keys() if emots[i] == file_])
+ for file_ in set(emots.values())]), fd)
fd.close()
del emoticons
self._init_emoticons(path, need_reload=True)
@@ -2499,7 +2640,9 @@ class Interface:
pass
if len(self.emoticons) == 0:
dialogs.WarningDialog(_('Emoticons disabled'),
- _('Your configured emoticons theme cannot been loaded. You maybe need to update the format of emoticons.py file. See http://trac.gajim.org/wiki/Emoticons for more details.'))
+ _('Your configured emoticons theme cannot been loaded. You '
+ 'maybe need to update the format of emoticons.py file. See '
+ 'http://trac.gajim.org/wiki/Emoticons for more details.'))
if self.emoticons_menu:
self.emoticons_menu.destroy()
self.emoticons_menu = self.prepare_emoticons_menu()
@@ -2521,29 +2664,33 @@ class Interface:
gc_ctrl = self.msg_win_mgr.get_gc_control(room_jid, account)
win = gc_ctrl.parent_win
win.set_active_tab(gc_ctrl)
- dialogs.ErrorDialog(_('You are already in group chat %s') % room_jid)
+ dialogs.ErrorDialog(_('You are already in group chat %s') % \
+ room_jid)
return
invisible_show = gajim.SHOW_LIST.index('invisible')
if gajim.connections[account].connected == invisible_show:
dialogs.ErrorDialog(
- _('You cannot join a group chat while you are invisible'))
+ _('You cannot join a group chat while you are invisible'))
return
minimized_control = gajim.interface.minimized_controls[account].get(
- room_jid, None)
+ room_jid, None)
- if minimized_control is None and not self.msg_win_mgr.has_window(room_jid,
- account):
+ if minimized_control is None and not self.msg_win_mgr.has_window(
+ room_jid, account):
# Join new groupchat
if minimize:
- #GCMIN
- contact = gajim.contacts.create_contact(jid=room_jid, account=account, name=nick)
+ # GCMIN
+ contact = gajim.contacts.create_contact(jid=room_jid,
+ account=account, name=nick)
gc_control = GroupchatControl(None, contact, account)
- gajim.interface.minimized_controls[account][room_jid] = gc_control
+ gajim.interface.minimized_controls[account][room_jid] = \
+ gc_control
self.roster.add_groupchat(room_jid, account)
else:
- self.new_room(room_jid, nick, account, is_continued=is_continued)
+ self.new_room(room_jid, nick, account,
+ is_continued=is_continued)
elif minimized_control is None:
# We are already in that groupchat
gc_control = self.msg_win_mgr.get_gc_control(room_jid, account)
@@ -2562,20 +2709,21 @@ class Interface:
def new_room(self, room_jid, nick, account, is_continued=False):
# Get target window, create a control, and associate it with the window
# GCMIN
- contact = gajim.contacts.create_contact(jid=room_jid, account=account, name=nick)
+ contact = gajim.contacts.create_contact(jid=room_jid, account=account,
+ name=nick)
mw = self.msg_win_mgr.get_window(contact.jid, account)
if not mw:
mw = self.msg_win_mgr.create_window(contact, account,
- GroupchatControl.TYPE_ID)
+ GroupchatControl.TYPE_ID)
gc_control = GroupchatControl(mw, contact, account,
- is_continued=is_continued)
+ is_continued=is_continued)
mw.new_tab(gc_control)
def new_private_chat(self, gc_contact, account, session=None):
conn = gajim.connections[account]
if not session and gc_contact.get_full_jid() in conn.sessions:
- sessions = [s for s in conn.sessions[gc_contact.get_full_jid()].values()
- if isinstance(s, ChatControlSession)]
+ sessions = [s for s in conn.sessions[gc_contact.get_full_jid()].\
+ values() if isinstance(s, ChatControlSession)]
# look for an existing session with a chat control
for s in sessions:
@@ -2583,22 +2731,24 @@ class Interface:
session = s
break
if not session and not len(sessions) == 0:
- # there are no sessions with chat controls, just take the first one
+ # there are no sessions with chat controls, just take the first
+ # one
session = sessions[0]
if not session:
# couldn't find an existing ChatControlSession, just make a new one
- session = conn.make_new_session(gc_contact.get_full_jid(), None, 'pm')
+ session = conn.make_new_session(gc_contact.get_full_jid(), None,
+ 'pm')
contact = gc_contact.as_contact()
if not session.control:
- message_window = self.msg_win_mgr.get_window(gc_contact.get_full_jid(),
- account)
+ message_window = self.msg_win_mgr.get_window(
+ gc_contact.get_full_jid(), account)
if not message_window:
- message_window = self.msg_win_mgr.create_window(contact, account,
- message_control.TYPE_PM)
+ message_window = self.msg_win_mgr.create_window(contact,
+ account, message_control.TYPE_PM)
session.control = PrivateChatControl(message_window, gc_contact,
- contact, account, session)
+ contact, account, session)
message_window.new_tab(session.control)
if gajim.events.get_events(account, gc_contact.get_full_jid()):
@@ -2617,7 +2767,8 @@ class Interface:
mw = self.msg_win_mgr.get_window(fjid, account)
if not mw:
- mw = self.msg_win_mgr.create_window(contact, account, type_, resource)
+ mw = self.msg_win_mgr.create_window(contact, account, type_,
+ resource)
chat_control = ChatControl(mw, contact, account, session, resource)
@@ -2636,25 +2787,27 @@ class Interface:
if not contact:
added_to_roster = True
contact = self.roster.add_to_not_in_the_roster(account, jid,
- resource=resource)
+ resource=resource)
ctrl = self.msg_win_mgr.get_control(fjid, account)
if not ctrl:
ctrl = self.new_chat(contact, account,
- resource=resource)
+ resource=resource)
if len(gajim.events.get_events(account, fjid)):
ctrl.read_queue()
if message:
- buffer = ctrl.msg_textview.get_buffer()
- buffer.set_text(message)
+ buffer_ = ctrl.msg_textview.get_buffer()
+ buffer_.set_text(message)
mw = ctrl.parent_win
mw.set_active_tab(ctrl)
# For JEP-0172
if added_to_roster:
ctrl.user_nick = gajim.nicks[account]
- gobject.idle_add(lambda: mw.window.grab_focus())
+ gobject.idle_add(mw.window.grab_focus)
+
+ return ctrl
def on_open_chat_window(self, widget, contact, account, resource=None,
session=None):
@@ -2676,7 +2829,7 @@ class Interface:
if not ctrl:
ctrl = self.new_chat(contact, account, resource=resource,
- session=session)
+ session=session)
# last message is long time ago
gajim.last_message_time[account][ctrl.get_full_jid()] = 0
@@ -2694,7 +2847,7 @@ class Interface:
### Other Methods
################################################################################
- def _change_awn_icon_status(self, status):
+ def change_awn_icon_status(self, status):
if not dbus_support.supported:
# do nothing if user doesn't have D-Bus bindings
return
@@ -2724,7 +2877,7 @@ class Interface:
listener = MusicTrackListener.get()
if not self.music_track_changed_signal:
self.music_track_changed_signal = listener.connect(
- 'music-track-changed', self.music_track_changed)
+ 'music-track-changed', self.music_track_changed)
track = listener.get_playing_track()
self.music_track_changed(listener, track)
@@ -2741,7 +2894,7 @@ class Interface:
accounts = [account]
is_paused = hasattr(music_track_info, 'paused') and \
- music_track_info.paused == 0
+ music_track_info.paused == 0
if not music_track_info or is_paused:
artist = title = source = ''
else:
@@ -2761,7 +2914,7 @@ class Interface:
def get_bg_fg_colors(self):
def gdkcolor_to_rgb (gdkcolor):
return [c / 65535. for c in (gdkcolor.red, gdkcolor.green,
- gdkcolor.blue)]
+ gdkcolor.blue)]
def format_rgb (r, g, b):
return ' '.join([str(c) for c in ('rgb', r, g, b)])
@@ -2813,7 +2966,8 @@ class Interface:
'status': gajim.status_before_autoaway[account],
'time': gajim.config.get('autoawaytime')
}
- self.roster.send_status(account, 'away', auto_message, auto=True)
+ self.roster.send_status(account, 'away', auto_message,
+ auto=True)
gajim.sleeper_state[account] = 'autoaway'
elif state == common.sleepy.STATE_XA and \
gajim.sleeper_state[account] in ('online', 'autoaway',
@@ -2842,9 +2996,9 @@ class Interface:
for a in gajim.connections:
if gajim.config.get_per('accounts', a, 'autoconnect'):
if gajim.config.get_per('accounts', a, 'restore_last_status'):
- self.roster.send_status(a, gajim.config.get_per('accounts', a,
- 'last_status'), helpers.from_one_line(gajim.config.get_per(
- 'accounts', a, 'last_status_msg')))
+ self.roster.send_status(a, gajim.config.get_per('accounts',
+ a, 'last_status'), helpers.from_one_line(
+ gajim.config.get_per('accounts', a, 'last_status_msg')))
continue
show = gajim.config.get_per('accounts', a, 'autoconnect_as')
if not show in gajim.SHOW_LIST:
@@ -2896,8 +3050,8 @@ class Interface:
print >> sys.stderr, err_str
# it is good to notify the user
# in case he or she cannot see the output of the console
- dialogs.ErrorDialog(_('Could not save your settings and preferences'),
- err_str)
+ dialogs.ErrorDialog(_('Could not save your settings and '
+ 'preferences'), err_str)
sys.exit()
def save_avatar_files(self, jid, photo, puny_nick = None, local = False):
@@ -2923,39 +3077,43 @@ class Interface:
typ = 'png'
extension = '_local.png' # save local avatars as png file
else:
- pixbuf, typ = gtkgui_helpers.get_pixbuf_from_data(photo, want_type = True)
+ pixbuf, typ = gtkgui_helpers.get_pixbuf_from_data(photo,
+ want_type=True)
if pixbuf is None:
return
extension = '.' + typ
if typ not in ('jpeg', 'png'):
- gajim.log.debug('gtkpixbuf cannot save other than jpeg and png formats. saving %s\'avatar as png file (originaly %s)' % (jid, typ))
+ gajim.log.debug('gtkpixbuf cannot save other than jpeg and '\
+ 'png formats. saving %s\'avatar as png file (originaly %s)'\
+ % (jid, typ))
typ = 'png'
extension = '.png'
path_to_original_file = path_to_file + extension
try:
pixbuf.save(path_to_original_file, typ)
except Exception, e:
- log.error('Error writing avatar file %s: %s' % (path_to_original_file,
- str(e)))
+ log.error('Error writing avatar file %s: %s' % (
+ path_to_original_file, str(e)))
# Generate and save the resized, color avatar
pixbuf = gtkgui_helpers.get_scaled_pixbuf(pixbuf, 'notification')
if pixbuf:
- path_to_normal_file = path_to_file + '_notif_size_colored' + extension
+ path_to_normal_file = path_to_file + '_notif_size_colored' + \
+ extension
try:
pixbuf.save(path_to_normal_file, 'png')
except Exception, e:
log.error('Error writing avatar file %s: %s' % \
- (path_to_original_file, str(e)))
+ (path_to_original_file, str(e)))
# Generate and save the resized, black and white avatar
bwbuf = gtkgui_helpers.get_scaled_pixbuf(
- gtkgui_helpers.make_pixbuf_grayscale(pixbuf), 'notification')
+ gtkgui_helpers.make_pixbuf_grayscale(pixbuf), 'notification')
if bwbuf:
path_to_bw_file = path_to_file + '_notif_size_bw' + extension
try:
bwbuf.save(path_to_bw_file, 'png')
except Exception, e:
log.error('Error writing avatar file %s: %s' % \
- (path_to_original_file, str(e)))
+ (path_to_original_file, str(e)))
def remove_avatar_files(self, jid, puny_nick = None, local = False):
"""
@@ -2993,7 +3151,8 @@ class Interface:
elif jid in self.minimized_controls[account]:
# more or less a hack:
# On disconnect the minimized gc contact instances
- # were set to offline. Reconnect them to show up in the roster.
+ # were set to offline. Reconnect them to show up in the
+ # roster.
self.roster.add_groupchat(jid, account)
def add_gc_bookmark(self, account, name, jid, autojoin, minimize, password,
@@ -3015,8 +3174,9 @@ class Interface:
for bookmark in gajim.connections[account].bookmarks:
if bookmark['jid'] == bm['jid']:
dialogs.ErrorDialog(
- _('Bookmark already set'),
- _('Group Chat "%s" is already in your bookmarks.') % bm['jid'])
+ _('Bookmark already set'),
+ _('Group Chat "%s" is already in your bookmarks.') % \
+ bm['jid'])
return
if bookmark['name'] > bm['name']:
place_found = True
@@ -3029,8 +3189,8 @@ class Interface:
gajim.connections[account].store_bookmarks()
self.roster.set_actions_menu_needs_rebuild()
dialogs.InformationDialog(
- _('Bookmark has been added successfully'),
- _('You can manage your bookmarks via Actions menu in your roster.'))
+ _('Bookmark has been added successfully'),
+ _('You can manage your bookmarks via Actions menu in your roster.'))
# does JID exist only within a groupchat?
@@ -3100,10 +3260,6 @@ class Interface:
if resolver.USE_LIBASYNCNS:
gobject.timeout_add(200, gajim.resolver.process)
- # setup the indicator
- if gajim.HAVE_INDICATOR:
- notify.setup_indicator_server()
-
def remote_init():
if gajim.config.get('remote_control'):
try:
@@ -3132,6 +3288,7 @@ class Interface:
self.status_sent_to_groups = {}
self.gpg_passphrase = {}
self.pass_dialog = {}
+ self.db_error_dialog = None
self.default_colors = {
'inmsgcolor': gajim.config.get('inmsgcolor'),
'outmsgcolor': gajim.config.get('outmsgcolor'),
@@ -3141,6 +3298,22 @@ class Interface:
'urlmsgcolor': gajim.config.get('urlmsgcolor'),
}
+ self.handlers = {}
+ self.roster = None
+ self._invalid_XML_chars_re = None
+ self._basic_pattern_re = None
+ self._emot_and_basic_re = None
+ self._sth_at_sth_dot_sth_re = None
+ self.link_pattern_re = None
+ self.invalid_XML_chars = None
+ self.basic_pattern = None
+ self.emot_and_basic = None
+ self.sth_at_sth_dot_sth = None
+ self.emot_only = None
+ self.emoticons = []
+ self.emoticons_animations = {}
+ self.emoticons_images = {}
+
cfg_was_read = parser.read()
from common import latex
@@ -3175,24 +3348,28 @@ class Interface:
default = gajim.config.statusmsg_default
for msg in default:
gajim.config.add_per('statusmsg', msg)
- gajim.config.set_per('statusmsg', msg, 'message', default[msg][0])
- gajim.config.set_per('statusmsg', msg, 'activity', default[msg][1])
+ gajim.config.set_per('statusmsg', msg, 'message',
+ default[msg][0])
+ gajim.config.set_per('statusmsg', msg, 'activity',
+ default[msg][1])
gajim.config.set_per('statusmsg', msg, 'subactivity',
- default[msg][2])
+ default[msg][2])
gajim.config.set_per('statusmsg', msg, 'activity_text',
- default[msg][3])
- gajim.config.set_per('statusmsg', msg, 'mood', default[msg][4])
- gajim.config.set_per('statusmsg', msg, 'mood_text', default[msg][5])
+ default[msg][3])
+ gajim.config.set_per('statusmsg', msg, 'mood',
+ default[msg][4])
+ gajim.config.set_per('statusmsg', msg, 'mood_text',
+ default[msg][5])
#add default themes if there is not in the config file
theme = gajim.config.get('roster_theme')
if not theme in gajim.config.get_per('themes'):
gajim.config.set('roster_theme', _('default'))
if len(gajim.config.get_per('themes')) == 0:
d = ['accounttextcolor', 'accountbgcolor', 'accountfont',
- 'accountfontattrs', 'grouptextcolor', 'groupbgcolor', 'groupfont',
- 'groupfontattrs', 'contacttextcolor', 'contactbgcolor',
- 'contactfont', 'contactfontattrs', 'bannertextcolor',
- 'bannerbgcolor']
+ 'accountfontattrs', 'grouptextcolor', 'groupbgcolor',
+ 'groupfont', 'groupfontattrs', 'contacttextcolor',
+ 'contactbgcolor', 'contactfont', 'contactfontattrs',
+ 'bannertextcolor', 'bannerbgcolor']
default = gajim.config.themes_default
for theme_name in default:
@@ -3200,7 +3377,7 @@ class Interface:
theme = default[theme_name]
for o in d:
gajim.config.set_per('themes', theme_name, o,
- theme[d.index(o)])
+ theme[d.index(o)])
if gajim.config.get('autodetect_browser_mailer') or not cfg_was_read:
gtkgui_helpers.autodetect_browser_mailer()
@@ -3209,14 +3386,13 @@ class Interface:
# resolve and keep current record of resolved hosts
gajim.resolver = resolver.get_resolver(gajim.idlequeue)
gajim.socks5queue = socks5.SocksQueue(gajim.idlequeue,
- self.handle_event_file_rcv_completed,
- self.handle_event_file_progress,
- self.handle_event_file_error)
+ self.handle_event_file_rcv_completed,
+ self.handle_event_file_progress,
+ self.handle_event_file_error)
gajim.proxy65_manager = proxy65_manager.Proxy65Manager(gajim.idlequeue)
gajim.default_session_type = ChatControlSession
# Creating Global Events Dispatcher
- from common import ged
gajim.ged = ged.GlobalEventsDispatcher()
# Creating Network Events Controller
from common import nec
@@ -3227,11 +3403,12 @@ class Interface:
if gajim.config.get_per('accounts', gajim.ZEROCONF_ACC_NAME, 'active') \
and gajim.HAVE_ZEROCONF:
gajim.connections[gajim.ZEROCONF_ACC_NAME] = \
- connection_zeroconf.ConnectionZeroconf(gajim.ZEROCONF_ACC_NAME)
+ connection_zeroconf.ConnectionZeroconf(gajim.ZEROCONF_ACC_NAME)
for account in gajim.config.get_per('accounts'):
- if not gajim.config.get_per('accounts', account, 'is_zeroconf') and \
+ if not gajim.config.get_per('accounts', account, 'is_zeroconf') and\
gajim.config.get_per('accounts', account, 'active'):
- gajim.connections[account] = common.connection.Connection(account)
+ gajim.connections[account] = common.connection.Connection(
+ account)
# gtk hooks
gtk.about_dialog_set_email_hook(self.on_launch_browser_mailer, 'mail')
@@ -3242,9 +3419,9 @@ class Interface:
for a in gajim.connections:
self.instances[a] = {'infos': {}, 'disco': {}, 'gc_config': {},
- 'search': {}, 'online_dialog': {}}
- # online_dialog contains all dialogs that have a meaning only when we
- # are not disconnected
+ 'search': {}, 'online_dialog': {}}
+ # online_dialog contains all dialogs that have a meaning only when
+ # we are not disconnected
self.minimized_controls[a] = {}
gajim.contacts.add_account(a)
gajim.groups[a] = {}
@@ -3265,14 +3442,16 @@ class Interface:
helpers.update_optional_features()
# prepopulate data which we are sure of; note: we do not log these info
for account in gajim.connections:
- gajimcaps = caps_cache.capscache[('sha-1', gajim.caps_hash[account])]
+ gajimcaps = caps_cache.capscache[('sha-1',
+ gajim.caps_hash[account])]
gajimcaps.identities = [gajim.gajim_identity]
gajimcaps.features = gajim.gajim_common_features + \
- gajim.gajim_optional_features[account]
+ gajim.gajim_optional_features[account]
self.remote_ctrl = None
- if gajim.config.get('networkmanager_support') and dbus_support.supported:
+ if gajim.config.get('networkmanager_support') and \
+ dbus_support.supported:
import network_manager_listener
# Handle gnome screensaver
@@ -3281,9 +3460,10 @@ class Interface:
if not active:
for account in gajim.connections:
if gajim.sleeper_state[account] == 'autoaway-forced':
- # We came back online ofter gnome-screensaver autoaway
+ # We came back online ofter gnome-screensaver
+ # autoaway
self.roster.send_status(account, 'online',
- gajim.status_before_autoaway[account])
+ gajim.status_before_autoaway[account])
gajim.status_before_autoaway[account] = ''
gajim.sleeper_state[account] = 'online'
return
@@ -3298,33 +3478,35 @@ class Interface:
# we save out online status
gajim.status_before_autoaway[account] = \
gajim.connections[account].status
- # we go away (no auto status) [we pass True to auto param]
+ # we go away (no auto status) [we pass True to auto
+ # param]
auto_message = gajim.config.get('autoaway_message')
if not auto_message:
auto_message = gajim.connections[account].status
else:
- auto_message = auto_message.replace('$S', '%(status)s')
- auto_message = auto_message.replace('$T', '%(time)s')
+ auto_message = auto_message.replace('$S',
+ '%(status)s')
+ auto_message = auto_message.replace('$T',
+ '%(time)s')
auto_message = auto_message % {
- 'status': gajim.status_before_autoaway[account],
- 'time': gajim.config.get('autoxatime')
- }
+ 'status': gajim.status_before_autoaway[account],
+ 'time': gajim.config.get('autoxatime')}
self.roster.send_status(account, 'away', auto_message,
- auto=True)
+ auto=True)
gajim.sleeper_state[account] = 'autoaway-forced'
try:
bus = dbus.SessionBus()
bus.add_signal_receiver(gnome_screensaver_ActiveChanged_cb,
- 'ActiveChanged', 'org.gnome.ScreenSaver')
+ 'ActiveChanged', 'org.gnome.ScreenSaver')
except Exception:
pass
self.show_vcard_when_connect = []
self.sleeper = common.sleepy.Sleepy(
- gajim.config.get('autoawaytime') * 60, # make minutes to seconds
- gajim.config.get('autoxatime') * 60)
+ gajim.config.get('autoawaytime') * 60, # make minutes to seconds
+ gajim.config.get('autoxatime') * 60)
gtkgui_helpers.make_jabber_state_images()
@@ -3376,6 +3558,7 @@ class PassphraseRequest:
self.callbacks = []
self.dialog_created = False
self.dialog = None
+ self.passphrase = None
self.completed = False
def interrupt(self):
@@ -3398,8 +3581,8 @@ class PassphraseRequest:
self.passphrase = passphrase
self.completed = True
if passphrase is not None:
- gobject.timeout_add_seconds(30, gajim.interface.forget_gpg_passphrase,
- self.keyid)
+ gobject.timeout_add_seconds(30,
+ gajim.interface.forget_gpg_passphrase, self.keyid)
for (account, cb) in self.callbacks:
self.run_callback(account, cb)
del self.callbacks
@@ -3407,7 +3590,7 @@ class PassphraseRequest:
def create_dialog(self, account):
title = _('Passphrase Required')
second = _('Enter GPG key passphrase for key %(keyid)s (account '
- '%(account)s).') % {'keyid': self.keyid, 'account': account}
+ '%(account)s).') % {'keyid': self.keyid, 'account': account}
def _cancel():
# user cancelled, continue without GPG
@@ -3421,8 +3604,8 @@ class PassphraseRequest:
return
elif result == 'expired':
dialogs.ErrorDialog(_('GPG key expired'),
- _('Your GPG key has expired, you will be connected to %s without'
- ' OpenPGP.') % account)
+ _('Your GPG key has expired, you will be connected to %s '
+ 'without OpenPGP.') % account)
# Don't try to connect with GPG
gajim.connections[account].continue_connect_info[2] = False
self.complete(None)
@@ -3431,14 +3614,14 @@ class PassphraseRequest:
if count < 3:
# ask again
dialogs.PassphraseDialog(_('Wrong Passphrase'),
- _('Please retype your GPG passphrase or press Cancel.'),
- ok_handler=(_ok, count + 1), cancel_handler=_cancel)
+ _('Please retype your GPG passphrase or press Cancel.'),
+ ok_handler=(_ok, count + 1), cancel_handler=_cancel)
else:
# user failed 3 times, continue without GPG
self.complete(None)
- self.dialog = dialogs.PassphraseDialog(title, second, ok_handler=(_ok, 1),
- cancel_handler=_cancel)
+ self.dialog = dialogs.PassphraseDialog(title, second, ok_handler=(_ok,
+ 1), cancel_handler=_cancel)
self.dialog_created = True
diff --git a/src/gui_menu_builder.py b/src/gui_menu_builder.py
index 6652badef..fbc93d1b1 100644
--- a/src/gui_menu_builder.py
+++ b/src/gui_menu_builder.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/gui_menu_builder.py
##
-## Copyright (C) 2009 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2009-2010 Yann Leboulanger <asterix AT lagaule.org>
##
## This file is part of Gajim.
##
@@ -95,7 +95,7 @@ def build_invite_submenu(invite_menuitem, list_):
invite_to_new_room_menuitem.set_image(icon)
if len(contact_list) > 1: # several resources
invite_to_new_room_menuitem.set_submenu(build_resources_submenu(
- contact_list, account, roster.on_invite_to_new_room, cap=NS_MUC))
+ contact_list, account, roster.on_invite_to_new_room, cap=NS_MUC))
elif len(list_) == 1 and contact.supports(NS_MUC):
invite_menuitem.set_sensitive(True)
# use resource if it's self contact
@@ -104,9 +104,9 @@ def build_invite_submenu(invite_menuitem, list_):
else:
resource = None
invite_to_new_room_menuitem.connect('activate',
- roster.on_invite_to_new_room, list_, resource)
+ roster.on_invite_to_new_room, list_, resource)
else:
- invite_menuitem.set_sensitive(False)
+ invite_menuitem.set_sensitive(True)
# transform None in 'jabber'
c_t = contacts_transport or 'jabber'
muc_jid = {}
@@ -120,7 +120,8 @@ def build_invite_submenu(invite_menuitem, list_):
rooms = [] # a list of (room_jid, account) tuple
minimized_controls = []
for account in connected_accounts:
- minimized_controls += gajim.interface.minimized_controls[account].values()
+ minimized_controls += \
+ gajim.interface.minimized_controls[account].values()
for gc_control in gajim.interface.msg_win_mgr.get_controls(
message_control.TYPE_GC) + minimized_controls:
acct = gc_control.account
@@ -149,8 +150,8 @@ def build_invite_submenu(invite_menuitem, list_):
invite_to_submenu.append(menuitem)
def get_contact_menu(contact, account, use_multiple_contacts=True,
- show_start_chat=True, show_encryption=False, show_buttonbar_items=True,
- control=None):
+show_start_chat=True, show_encryption=False, show_buttonbar_items=True,
+control=None):
"""
Build contact popup menu for roster and chat window. If control is not set,
we hide invite_contacts_menuitem
diff --git a/src/history_manager.py b/src/history_manager.py
index 5c1456bde..866207050 100644
--- a/src/history_manager.py
+++ b/src/history_manager.py
@@ -4,7 +4,7 @@
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
##
diff --git a/src/history_window.py b/src/history_window.py
index 2b8922211..961b45f3b 100644
--- a/src/history_window.py
+++ b/src/history_window.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/history_window.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
@@ -337,6 +337,11 @@ class HistoryWindow:
if not self.jid:
return
year, month, day = widget.get_date() # integers
+ if year < 1900:
+ widget.select_month(0, 1900)
+ widget.select_day(1)
+ return
+
# in gtk January is 1, in python January is 0,
# I want the second
# first day of month is 1 not 0
@@ -345,7 +350,7 @@ class HistoryWindow:
days_in_this_month = calendar.monthrange(year, month)[1]
try:
log_days = gajim.logger.get_days_with_logs(self.jid, year, month,
- days_in_this_month, self.account)
+ days_in_this_month, self.account)
except exceptions.PysqliteOperationalError, e:
dialogs.ErrorDialog(_('Disk Error'), str(e))
return
diff --git a/src/htmltextview.py b/src/htmltextview.py
index e37556105..488305843 100644
--- a/src/htmltextview.py
+++ b/src/htmltextview.py
@@ -4,7 +4,7 @@
## Copyright (C) 2005 Gustavo J. A. M. Carneiro
## Copyright (C) 2006 Santiago Gala
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
## Julien Pivotto <roidelapluie AT gmail.com>
diff --git a/src/ipython_view.py b/src/ipython_view.py
index 49e98fad4..6b128da01 100644
--- a/src/ipython_view.py
+++ b/src/ipython_view.py
@@ -2,7 +2,7 @@
# -*- coding:utf-8 -*-
## src/ipython_view.py
##
-## Copyright (C) 2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2008-2010 Yann Leboulanger <asterix AT lagaule.org>
##
## This file is part of Gajim.
##
diff --git a/src/message_control.py b/src/message_control.py
index 86205c50f..700ac9485 100644
--- a/src/message_control.py
+++ b/src/message_control.py
@@ -5,7 +5,7 @@
## Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
## Travis Shirk <travis AT pobox.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
## Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
@@ -208,7 +208,7 @@ class MessageControl(object):
def send_message(self, message, keyID='', type_='chat', chatstate=None,
msg_id=None, composing_xep=None, resource=None, user_nick=None,
- xhtml=None, callback=None, callback_args=[]):
+ xhtml=None, label=None, callback=None, callback_args=[]):
# Send the given message to the active tab.
# Doesn't return None if error
jid = self.contact.jid
@@ -241,5 +241,5 @@ class MessageControl(object):
conn.send_message(jid, message, keyID, type_=type_, chatstate=chatstate,
msg_id=msg_id, composing_xep=composing_xep, resource=self.resource,
user_nick=user_nick, session=self.session,
- original_message=original_message, xhtml=xhtml, callback=callback,
+ original_message=original_message, xhtml=xhtml, label=label, callback=callback,
callback_args=callback_args)
diff --git a/src/message_textview.py b/src/message_textview.py
index 1b568af6b..2197f1b03 100644
--- a/src/message_textview.py
+++ b/src/message_textview.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/message_textview.py
##
-## Copyright (C) 2003-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2008-2009 Julien Pivotto <roidelapluie AT gmail.com>
diff --git a/src/message_window.py b/src/message_window.py
index 22c14c327..4dff5394c 100644
--- a/src/message_window.py
+++ b/src/message_window.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/message_window.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2008 Travis Shirk <travis AT pobox.com>
## Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Geobert Quach <geobert AT gmail.com>
@@ -144,6 +144,8 @@ class MessageWindow(object):
self.notebook.set_show_border(gajim.config.get('tabs_border'))
self.show_icon()
+ gobject.idle_add(self.notebook.grab_focus)
+
def change_account_name(self, old_name, new_name):
if old_name in self._controls:
self._controls[new_name] = self._controls[old_name]
@@ -219,7 +221,7 @@ class MessageWindow(object):
dialogs.YesNoDialog(
_('You are going to close several tabs'),
_('Do you really want to close them all?'),
- checktext=_('Do _not ask me again'), on_response_yes=on_yes1)
+ checktext=_('_Do not ask me again'), on_response_yes=on_yes1)
return True
def on_yes(ctrl):
@@ -685,6 +687,10 @@ _('Do you really want to close them all?'),
except KeyError:
return
+ if new_jid in self._controls[acct]:
+ self.remove_tab(self._controls[acct][new_jid],
+ self.CLOSE_CLOSE_BUTTON, force=True)
+
self._controls[acct][new_jid] = ctrl
del self._controls[acct][old_jid]
diff --git a/src/music_track_listener.py b/src/music_track_listener.py
index 9de01dd02..4d0df3bf8 100644
--- a/src/music_track_listener.py
+++ b/src/music_track_listener.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Gustavo Carneiro <gjcarneiro AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2008 Jean-Marie Traissard <jim AT lapin.org>
## Jonathan Schleifer <js-gajim AT webkeks.org>
## Stephan Erb <steve-e AT h3c.de>
diff --git a/src/negotiation.py b/src/negotiation.py
index d5d974b79..869e2fb8b 100644
--- a/src/negotiation.py
+++ b/src/negotiation.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/negotiation.py
##
-## Copyright (C) 2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
##
## This file is part of Gajim.
diff --git a/src/network_manager_listener.py b/src/network_manager_listener.py
index 5239015fe..303c1488b 100644
--- a/src/network_manager_listener.py
+++ b/src/network_manager_listener.py
@@ -4,7 +4,7 @@
## Copyright (C) 2006 Jeffrey C. Ollie <jeff AT ocjtech.us>
## Nikos Kouremenos <kourem AT gmail.com>
## Stefan Bethge <stefan AT lanpartei.de>
-## Copyright (C) 2006-2007 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
##
## This file is part of Gajim.
##
diff --git a/src/notify.py b/src/notify.py
index 92cc63b5f..9c8624b68 100644
--- a/src/notify.py
+++ b/src/notify.py
@@ -4,7 +4,7 @@
## Copyright (C) 2005 Sebastian Estienne
## Copyright (C) 2005-2006 Andrew Sayman <lorien420 AT myrealbox.com>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2007 Julien Pivotto <roidelapluie AT gmail.com>
@@ -50,24 +50,6 @@ try:
except ImportError:
USER_HAS_PYNOTIFY = False
-if gajim.HAVE_INDICATOR:
- import indicate
-
-def setup_indicator_server():
- server = indicate.indicate_server_ref_default()
- server.set_type('message.im')
- server.set_desktop_file('/usr/share/applications/gajim.desktop')
- server.connect('server-display', server_display)
- server.show()
-
-def display(indicator, account, jid, msg_type):
- gajim.interface.handle_event(account, jid, msg_type)
- indicator.hide()
-
-def server_display(server):
- win = gajim.interface.roster.window
- win.present()
-
def get_show_in_roster(event, account, contact, session=None):
"""
Return True if this event must be shown in roster, else False
@@ -115,9 +97,9 @@ def get_advanced_notification(event, account, contact):
if gajim.config.get_per('notifications', str(num), 'event') == event:
# test recipient
recipient_type = gajim.config.get_per('notifications', str(num),
- 'recipient_type')
+ 'recipient_type')
recipients = gajim.config.get_per('notifications', str(num),
- 'recipients').split()
+ 'recipients').split()
if recipient_type == 'all':
recipient_ok = True
elif recipient_type == 'contact' and contact.jid in recipients:
@@ -136,13 +118,13 @@ def get_advanced_notification(event, account, contact):
if status_ok:
# test window_opened
tab_opened = gajim.config.get_per('notifications', str(num),
- 'tab_opened')
+ 'tab_opened')
if tab_opened == 'both':
tab_opened_ok = True
else:
chat_control = helpers.get_chat_control(account, contact)
- if (chat_control and tab_opened == 'yes') or (not chat_control and \
- tab_opened == 'no'):
+ if (chat_control and tab_opened == 'yes') or (not chat_control \
+ and tab_opened == 'no'):
tab_opened_ok = True
if tab_opened_ok:
return num
@@ -152,9 +134,10 @@ def get_advanced_notification(event, account, contact):
def notify(event, jid, account, parameters, advanced_notif_num=None):
"""
- Check what type of notifications we want, depending on basic and the advanced
- configuration of notifications and do these notifications; advanced_notif_num
- holds the number of the first (top most) advanced notification
+ Check what type of notifications we want, depending on basic and the
+ advanced configuration of notifications and do these notifications;
+ advanced_notif_num holds the number of the first (top most) advanced
+ notification
"""
# First, find what notifications we want
do_popup = False
@@ -174,7 +157,8 @@ def notify(event, jid, account, parameters, advanced_notif_num=None):
gajim.block_signed_in_notifications[account_server]:
block_transport = True
if helpers.allow_showing_notification(account, 'notify_on_signin') and \
- not gajim.block_signed_in_notifications[account] and not block_transport:
+ not gajim.block_signed_in_notifications[account] and \
+ not block_transport:
do_popup = True
if gajim.config.get_per('soundevents', 'contact_connected',
'enabled') and not gajim.block_signed_in_notifications[account] and \
@@ -185,7 +169,7 @@ def notify(event, jid, account, parameters, advanced_notif_num=None):
if helpers.allow_showing_notification(account, 'notify_on_signout'):
do_popup = True
if gajim.config.get_per('soundevents', 'contact_disconnected',
- 'enabled'):
+ 'enabled'):
do_sound = True
elif event == 'new_message':
message_type = parameters[0]
@@ -206,8 +190,8 @@ def notify(event, jid, account, parameters, advanced_notif_num=None):
'first_message_received', advanced_notif_num):
do_sound = True
elif not is_first_message and focused and \
- helpers.allow_sound_notification(account, 'next_message_received_focused',
- advanced_notif_num):
+ helpers.allow_sound_notification(account,
+ 'next_message_received_focused', advanced_notif_num):
do_sound = True
elif not is_first_message and not focused and \
helpers.allow_sound_notification(account,
@@ -227,7 +211,7 @@ def notify(event, jid, account, parameters, advanced_notif_num=None):
if event == 'contact_disconnected':
show_image = 'offline.png'
suffix = '_notif_size_bw'
- else: #Status Change or Connected
+ else: # Status Change or Connected
# FIXME: for status change,
# we don't always 'online.png', but we
# first need 48x48 for all status
@@ -236,46 +220,46 @@ def notify(event, jid, account, parameters, advanced_notif_num=None):
transport_name = gajim.get_transport_name_from_jid(jid)
img_path = None
if transport_name:
- img_path = os.path.join(helpers.get_transport_path(transport_name),
- '48x48', show_image)
+ img_path = os.path.join(helpers.get_transport_path(
+ transport_name), '48x48', show_image)
if not img_path or not os.path.isfile(img_path):
iconset = gajim.config.get('iconset')
- img_path = os.path.join(helpers.get_iconset_path(iconset), '48x48',
- show_image)
- path = gtkgui_helpers.get_path_to_generic_or_avatar(img_path, jid=jid,
- suffix=suffix)
+ img_path = os.path.join(helpers.get_iconset_path(iconset),
+ '48x48', show_image)
+ path = gtkgui_helpers.get_path_to_generic_or_avatar(img_path,
+ jid=jid, suffix=suffix)
if event == 'status_change':
title = _('%(nick)s Changed Status') % \
- {'nick': gajim.get_name_from_jid(account, jid)}
+ {'nick': gajim.get_name_from_jid(account, jid)}
text = _('%(nick)s is now %(status)s') % \
- {'nick': gajim.get_name_from_jid(account, jid),\
- 'status': helpers.get_uf_show(gajim.SHOW_LIST[new_show])}
+ {'nick': gajim.get_name_from_jid(account, jid),\
+ 'status': helpers.get_uf_show(gajim.SHOW_LIST[new_show])}
if status_message:
text = text + " : " + status_message
popup(_('Contact Changed Status'), jid, account,
- path_to_image=path, title=title, text=text)
+ path_to_image=path, title=title, text=text)
elif event == 'contact_connected':
title = _('%(nickname)s Signed In') % \
- {'nickname': gajim.get_name_from_jid(account, jid)}
+ {'nickname': gajim.get_name_from_jid(account, jid)}
text = ''
if status_message:
text = status_message
popup(_('Contact Signed In'), jid, account,
- path_to_image=path, title=title, text=text)
+ path_to_image=path, title=title, text=text)
elif event == 'contact_disconnected':
title = _('%(nickname)s Signed Out') % \
- {'nickname': gajim.get_name_from_jid(account, jid)}
+ {'nickname': gajim.get_name_from_jid(account, jid)}
text = ''
if status_message:
text = status_message
popup(_('Contact Signed Out'), jid, account,
- path_to_image=path, title=title, text=text)
+ path_to_image=path, title=title, text=text)
elif event == 'new_message':
if message_type == 'normal': # single message
event_type = _('New Single Message')
img_name = 'gajim-single_msg_recv'
title = _('New Single Message from %(nickname)s') % \
- {'nickname': nickname}
+ {'nickname': nickname}
text = message
elif message_type == 'pm': # private message
event_type = _('New Private Message')
@@ -283,20 +267,21 @@ def notify(event, jid, account, parameters, advanced_notif_num=None):
img_name = 'gajim-priv_msg_recv'
title = _('New Private Message from group chat %s') % room_name
if message:
- text = _('%(nickname)s: %(message)s') % {'nickname': nickname,
- 'message': message}
+ text = _('%(nickname)s: %(message)s') % \
+ {'nickname': nickname, 'message': message}
else:
- text = _('Messaged by %(nickname)s') % {'nickname': nickname}
+ text = _('Messaged by %(nickname)s') % \
+ {'nickname': nickname}
else: # chat message
event_type = _('New Message')
img_name = 'gajim-chat_msg_recv'
title = _('New Message from %(nickname)s') % \
- {'nickname': nickname}
+ {'nickname': nickname}
text = message
img_path = gtkgui_helpers.get_icon_path(img_name, 48)
popup(event_type, jid, account, message_type,
- path_to_image=img_path, title=title, text=text)
+ path_to_image=img_path, title=title, text=text)
if do_sound:
snd_file = None
@@ -305,7 +290,7 @@ def notify(event, jid, account, parameters, advanced_notif_num=None):
if advanced_notif_num is not None and gajim.config.get_per(
'notifications', str(advanced_notif_num), 'sound') == 'yes':
snd_file = gajim.config.get_per('notifications',
- str(advanced_notif_num), 'sound_file')
+ str(advanced_notif_num), 'sound_file')
elif advanced_notif_num is not None and gajim.config.get_per(
'notifications', str(advanced_notif_num), 'sound') == 'no':
pass # do not set snd_event
@@ -324,14 +309,14 @@ def notify(event, jid, account, parameters, advanced_notif_num=None):
if do_cmd:
command = gajim.config.get_per('notifications', str(advanced_notif_num),
- 'command')
+ 'command')
try:
helpers.exec_command(command)
except Exception:
pass
def popup(event_type, jid, account, msg_type='', path_to_image=None, title=None,
- text=None):
+text=None):
"""
Notify a user of an event. It first tries to a valid implementation of
the Desktop Notification Specification. If that fails, then we fall back to
@@ -341,23 +326,11 @@ def popup(event_type, jid, account, msg_type='', path_to_image=None, title=None,
if not path_to_image:
path_to_image = gtkgui_helpers.get_icon_path('gajim-chat_msg_recv', 48)
- if gajim.HAVE_INDICATOR and event_type in (_('New Message'),
- _('New Single Message'), _('New Private Message')):
- indicator = indicate.Indicator()
- indicator.set_property('subtype', 'im')
- indicator.set_property('sender', jid)
- indicator.set_property('body', text)
- indicator.set_property_time('time', time.time())
- pixbuf = gtk.gdk.pixbuf_new_from_file(path_to_image)
- indicator.set_property_icon('icon', pixbuf)
- indicator.connect('user-display', display, account, jid, msg_type)
- indicator.show()
-
# Try to show our popup via D-Bus and notification daemon
if gajim.config.get('use_notif_daemon') and dbus_support.supported:
try:
DesktopNotification(event_type, jid, account, msg_type,
- path_to_image, title, gobject.markup_escape_text(text))
+ path_to_image, title, gobject.markup_escape_text(text))
return # sucessfully did D-Bus Notification procedure!
except dbus.DBusException, e:
# Connection to D-Bus failed
@@ -372,7 +345,7 @@ def popup(event_type, jid, account, msg_type='', path_to_image=None, title=None,
# empty text for new_message means do_preview = False
# -> default value for text
_text = gobject.markup_escape_text(
- gajim.get_name_from_jid(account, jid))
+ gajim.get_name_from_jid(account, jid))
else:
_text = gobject.markup_escape_text(text)
@@ -404,7 +377,7 @@ def popup(event_type, jid, account, msg_type='', path_to_image=None, title=None,
# Either nothing succeeded or the user wants old-style notifications
instance = dialogs.PopupNotificationWindow(event_type, jid, account,
- msg_type, path_to_image, title, text)
+ msg_type, path_to_image, title, text)
gajim.interface.roster.popup_notification_windows.append(instance)
def on_pynotify_notification_clicked(notification, action):
@@ -431,7 +404,8 @@ class NotificationResponseManager:
if self.interface is not None:
return
self.interface = dbus_support.get_notifications_interface()
- self.interface.connect_to_signal('ActionInvoked', self.on_action_invoked)
+ self.interface.connect_to_signal('ActionInvoked',
+ self.on_action_invoked)
self.interface.connect_to_signal('NotificationClosed', self.on_closed)
def on_action_invoked(self, id_, reason):
@@ -470,12 +444,12 @@ notification_response_manager = NotificationResponseManager()
class DesktopNotification:
"""
- A DesktopNotification that interfaces with D-Bus via the Desktop Notification
- specification
+ A DesktopNotification that interfaces with D-Bus via the Desktop
+ Notification Specification
"""
def __init__(self, event_type, jid, account, msg_type='',
- path_to_image=None, title=None, text=None):
+ path_to_image=None, title=None, text=None):
self.path_to_image = path_to_image
self.event_type = event_type
self.title = title
@@ -501,7 +475,7 @@ class DesktopNotification:
elif event_type == _('Contact Signed Out'):
ntype = 'presence.offline'
elif event_type in (_('New Message'), _('New Single Message'),
- _('New Private Message')):
+ _('New Private Message')):
ntype = 'im.received'
elif event_type == _('File Transfer Request'):
ntype = 'transfer'
@@ -525,7 +499,7 @@ class DesktopNotification:
else:
# default failsafe values
self.path_to_image = gtkgui_helpers.get_icon_path(
- 'gajim-chat_msg_recv', 48)
+ 'gajim-chat_msg_recv', 48)
ntype = 'im' # Notification Type
self.notif = dbus_support.get_notifications_interface(self)
@@ -546,24 +520,27 @@ class DesktopNotification:
ntype = self.ntype
if self.kde_notifications:
notification_text = ('<html><img src="%(image)s" align=left />' \
- '%(title)s<br/>%(text)s</html>') % {'title': self.title,
- 'text': self.text, 'image': self.path_to_image}
+ '%(title)s<br/>%(text)s</html>') % {'title': self.title,
+ 'text': self.text, 'image': self.path_to_image}
gajim_icon = gtkgui_helpers.get_icon_path('gajim', 48)
- self.notif.Notify(
- dbus.String(_('Gajim')), # app_name (string)
- dbus.UInt32(0), # replaces_id (uint)
- ntype, # event_id (string)
- dbus.String(gajim_icon), # app_icon (string)
- dbus.String(''), # summary (string)
+ try:
+ self.notif.Notify(
+ dbus.String(_('Gajim')), # app_name (string)
+ dbus.UInt32(0), # replaces_id (uint)
+ ntype, # event_id (string)
+ dbus.String(gajim_icon), # app_icon (string)
+ dbus.String(''), # summary (string)
dbus.String(notification_text), # body (string)
# actions (stringlist)
(dbus.String('default'), dbus.String(self.event_type),
- dbus.String('ignore'), dbus.String(_('Ignore'))),
+ dbus.String('ignore'), dbus.String(_('Ignore'))),
[], # hints (not used in KDE yet)
- dbus.UInt32(timeout*1000), # timeout (int), in ms
+ dbus.UInt32(timeout*1000), # timeout (int), in ms
reply_handler=self.attach_by_id,
error_handler=self.notify_another_way)
- return
+ return
+ except Exception:
+ pass
version = self.version
if version[:2] == [0, 2]:
actions = {}
@@ -571,22 +548,23 @@ class DesktopNotification:
actions = {'default': 0}
try:
self.notif.Notify(
- dbus.String(_('Gajim')),
- dbus.String(self.path_to_image),
- dbus.UInt32(0),
- ntype,
- dbus.Byte(0),
- dbus.String(self.title),
- dbus.String(self.text),
- [dbus.String(self.path_to_image)],
- actions,
- [''],
- True,
- dbus.UInt32(timeout),
- reply_handler=self.attach_by_id,
- error_handler=self.notify_another_way)
+ dbus.String(_('Gajim')),
+ dbus.String(self.path_to_image),
+ dbus.UInt32(0),
+ ntype,
+ dbus.Byte(0),
+ dbus.String(self.title),
+ dbus.String(self.text),
+ [dbus.String(self.path_to_image)],
+ actions,
+ [''],
+ True,
+ dbus.UInt32(timeout),
+ reply_handler=self.attach_by_id,
+ error_handler=self.notify_another_way)
except AttributeError:
- version = [0, 3, 1] # we're actually dealing with the newer version
+ # we're actually dealing with the newer version
+ version = [0, 3, 1]
if version > [0, 3]:
if gajim.interface.systray_enabled and \
gajim.config.get('attach_notifications_to_systray'):
@@ -607,10 +585,13 @@ class DesktopNotification:
text = ' '
actions = ()
if 'actions' in self.capabilities:
- actions = (dbus.String('default'), dbus.String(self.event_type))
- self.notif.Notify(
+ actions = (dbus.String('default'), dbus.String(
+ self.event_type))
+ try:
+ self.notif.Notify(
dbus.String(_('Gajim')),
- dbus.UInt32(0), # this notification does not replace other
+ # this notification does not replace other
+ dbus.UInt32(0),
dbus.String(self.path_to_image),
dbus.String(self.title),
dbus.String(text),
@@ -619,8 +600,11 @@ class DesktopNotification:
dbus.UInt32(timeout*1000),
reply_handler=self.attach_by_id,
error_handler=self.notify_another_way)
+ except Exception, e:
+ self.notify_another_way(e)
else:
- self.notif.Notify(
+ try:
+ self.notif.Notify(
dbus.String(_('Gajim')),
dbus.String(self.path_to_image),
dbus.UInt32(0),
@@ -631,6 +615,8 @@ class DesktopNotification:
dbus.UInt32(timeout*1000),
reply_handler=self.attach_by_id,
error_handler=self.notify_another_way)
+ except Exception, e:
+ self.notify_another_way(e)
def attach_by_id(self, id_):
self.id = id_
@@ -638,8 +624,12 @@ class DesktopNotification:
notification_response_manager.add_pending(self.id, self)
def notify_another_way(self, e):
- gajim.log.debug(str(e))
- gajim.log.debug('Need to implement a new way of falling back')
+ gajim.log.debug('Error when trying to use notification daemon: %s' % \
+ str(e))
+ instance = dialogs.PopupNotificationWindow(self.event_type, self.jid,
+ self.account, self.msg_type, self.path_to_image, self.title,
+ self.text)
+ gajim.interface.roster.popup_notification_windows.append(instance)
def on_action_invoked(self, id_, reason):
if self.notif is None:
@@ -668,12 +658,13 @@ class DesktopNotification:
def get_version(self):
self.notif.GetServerInfo(
- reply_handler=self.version_reply_handler,
- error_handler=self.version_error_handler_2_x_try)
+ reply_handler=self.version_reply_handler,
+ error_handler=self.version_error_handler_2_x_try)
def version_error_handler_2_x_try(self, e):
- self.notif.GetServerInformation(reply_handler=self.version_reply_handler,
- error_handler=self.version_error_handler_3_x_try)
+ self.notif.GetServerInformation(
+ reply_handler=self.version_reply_handler,
+ error_handler=self.version_error_handler_3_x_try)
def version_error_handler_3_x_try(self, e):
self.version = self.default_version
diff --git a/src/profile_window.py b/src/profile_window.py
index 577f50fa4..077ba4f24 100644
--- a/src/profile_window.py
+++ b/src/profile_window.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/profile_window.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
##
@@ -189,8 +189,8 @@ class ProfileWindow:
nick = gajim.config.get_per('accounts', self.account, 'name')
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
menuitem.connect('activate',
- gtkgui_helpers.on_avatar_save_as_menuitem_activate,
- self.jid, self.account, nick)
+ gtkgui_helpers.on_avatar_save_as_menuitem_activate,
+ self.jid, nick)
menu.append(menuitem)
# show clear
menuitem = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
@@ -203,6 +203,20 @@ class ProfileWindow:
elif event.button == 1: # left click
self.on_set_avatar_button_clicked(widget)
+ def on_BDAY_entry_focus_out_event(self, widget, event):
+ txt = widget.get_text()
+ if not txt:
+ return
+ try:
+ time.strptime(txt, '%Y-%m-%d')
+ except ValueError:
+ if not widget.is_focus():
+ pritext = _('Wrong date format')
+ dialogs.ErrorDialog(pritext, _('Format of the date must be '
+ 'YYYY-MM-DD'))
+ gobject.idle_add(lambda: widget.grab_focus())
+ return True
+
def set_value(self, entry_name, value):
try:
self.xml.get_object(entry_name).set_text(value)
diff --git a/src/remote_control.py b/src/remote_control.py
index 71cab3aa8..95411a653 100644
--- a/src/remote_control.py
+++ b/src/remote_control.py
@@ -4,7 +4,7 @@
## Copyright (C) 2005-2006 Andrew Sayman <lorien420 AT myrealbox.com>
## Dimitur Kirov <dkirov AT gmail.com>
## Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## 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>
diff --git a/src/roster_window.py b/src/roster_window.py
index d6716fb61..468bb25d0 100644
--- a/src/roster_window.py
+++ b/src/roster_window.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
## src/roster_window.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
## Stéphan Kochen <stephan AT kochen.nl>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
@@ -74,17 +74,17 @@ from common.pep import MOODS, ACTIVITIES
#(icon, name, type, jid, account, editable, second pixbuf)
(
- C_IMG, # image to show state (online, new message etc)
- C_NAME, # cellrenderer text that holds contact nickame
- C_TYPE, # account, group or contact?
- C_JID, # the jid of the row
- C_ACCOUNT, # cellrenderer text that holds account name
- C_MOOD_PIXBUF,
- C_ACTIVITY_PIXBUF,
- C_TUNE_PIXBUF,
- C_LOCATION_PIXBUF,
- C_AVATAR_PIXBUF, # avatar_pixbuf
- C_PADLOCK_PIXBUF, # use for account row only
+ C_IMG, # image to show state (online, new message etc)
+ C_NAME, # cellrenderer text that holds contact nickame
+ C_TYPE, # account, group or contact?
+ C_JID, # the jid of the row
+ C_ACCOUNT, # cellrenderer text that holds account name
+ C_MOOD_PIXBUF,
+ C_ACTIVITY_PIXBUF,
+ C_TUNE_PIXBUF,
+ C_LOCATION_PIXBUF,
+ C_AVATAR_PIXBUF, # avatar_pixbuf
+ C_PADLOCK_PIXBUF, # use for account row only
) = range(11)
class RosterWindow:
@@ -220,7 +220,8 @@ class RosterWindow:
# It's the last one.
# Go up if we are big brother
parent_iter = model.iter_parent(contact_iter)
- if parent_iter and model[parent_iter][C_TYPE] == 'contact':
+ if parent_iter and model[parent_iter][C_TYPE] == \
+ 'contact':
contact_iter = model.iter_next(parent_iter)
else:
# we tested all
@@ -305,8 +306,8 @@ class RosterWindow:
def add_account_contacts(self, account):
"""
- Add all contacts and groups of the given account to roster, draw them and
- account
+ Add all contacts and groups of the given account to roster, draw them
+ and account
"""
self.starting = True
jids = gajim.contacts.get_jid_list(account)
@@ -359,9 +360,9 @@ class RosterWindow:
contact.groups = big_brother_contact.get_shown_groups()[:]
for child_iter in parent_iters:
- it = self.model.append(child_iter, (None, contact.get_shown_name(),
- 'contact', contact.jid, account, None, None, None, None, None,
- None))
+ it = self.model.append(child_iter, (None,
+ contact.get_shown_name(), 'contact', contact.jid, account,
+ None, None, None, None, None, None))
added_iters.append(it)
else:
# We are a normal contact. Add us to our groups.
@@ -374,9 +375,10 @@ class RosterWindow:
# Group is not yet in roster, add it!
child_iterA = self._get_account_iter(account, self.model)
child_iterG = self.model.append(child_iterA,
- [gajim.interface.jabber_state_images['16']['closed'],
- gobject.markup_escape_text(group),
- 'group', group, account, None, None, None, None, None, None])
+ [gajim.interface.jabber_state_images['16']['closed'],
+ gobject.markup_escape_text(group),
+ 'group', group, account, None, None, None, None, None,
+ None])
self.draw_group(group, account)
if contact.is_transport():
@@ -402,7 +404,8 @@ class RosterWindow:
if group not in gajim.groups[account]:
gajim.groups[account][group] = {'expand': is_expanded}
- assert len(added_iters), '%s has not been added to roster!' % contact.jid
+ assert len(added_iters), '%s has not been added to roster!' % \
+ contact.jid
return added_iters
def _remove_entity(self, contact, account, groups=None):
@@ -419,7 +422,8 @@ class RosterWindow:
account -- the contacts account
groups -- list of groups to remove the contact from.
"""
- iters = self._get_contact_iter(contact.jid, account, contact, self.model)
+ iters = self._get_contact_iter(contact.jid, account, contact,
+ self.model)
assert iters, '%s shall be removed but is not in roster' % contact.jid
parent_iter = self.model.iter_parent(iters[0])
@@ -545,29 +549,28 @@ class RosterWindow:
ok = self._remove_entity(_contact, _account)
assert ok, '%s was not removed' % _jid
assert len(self._get_contact_iter(_jid, _account, _contact,
- self.model)) == 0, '%s is removed but still in roster' % _jid
+ self.model)) == 0, '%s is removed but still in roster' % _jid
if not family_in_roster:
return False
assert old_big_jid, 'No Big Brother in nearby family % (Family: %)' % \
- (nearby_family, family)
+ (nearby_family, family)
iters = self._get_contact_iter(old_big_jid, old_big_account,
- old_big_contact, self.model)
+ old_big_contact, self.model)
assert len(iters) > 0, 'Old Big Brother %s is not in roster anymore' % \
- old_big_jid
- assert not self.model.iter_children(iters[0]),\
- 'Old Big Brother %s still has children' % old_big_jid
+ old_big_jid
+ assert not self.model.iter_children(iters[0]), \
+ 'Old Big Brother %s still has children' % old_big_jid
ok = self._remove_entity(old_big_contact, old_big_account)
assert ok, "Old Big Brother %s not removed" % old_big_jid
assert len(self._get_contact_iter(old_big_jid, old_big_account,
- old_big_contact, self.model)) == 0,\
- 'Old Big Brother %s is removed but still in roster' % old_big_jid
+ old_big_contact, self.model)) == 0, \
+ 'Old Big Brother %s is removed but still in roster' % old_big_jid
return True
-
def _recalibrate_metacontact_family(self, family, account):
"""
Regroup metacontact family if necessary
@@ -575,11 +578,11 @@ class RosterWindow:
brothers = []
nearby_family, big_brother_jid, big_brother_account = \
- self._get_nearby_family_and_big_brother(family, account)
+ self._get_nearby_family_and_big_brother(family, account)
big_brother_contact = gajim.contacts.get_contact(big_brother_account,
- big_brother_jid)
- child_iters = self._get_contact_iter(big_brother_jid, big_brother_account,
- model=self.model)
+ big_brother_jid)
+ child_iters = self._get_contact_iter(big_brother_jid,
+ big_brother_account, model=self.model)
if child_iters:
parent_iter = self.model.iter_parent(child_iters[0])
parent_type = self.model[parent_iter][C_TYPE]
@@ -603,7 +606,8 @@ class RosterWindow:
_account = child['account']
if _account == big_brother_account and _jid == big_brother_jid:
continue
- child_iters = self._get_contact_iter(_jid, _account, model=self.model)
+ child_iters = self._get_contact_iter(_jid, _account,
+ model=self.model)
if not child_iters:
continue
parent_iter = self.model.iter_parent(child_iters[0])
@@ -627,13 +631,12 @@ class RosterWindow:
jid = gajim.get_jid_from_account(account)
contact = gajim.contacts.get_first_contact_from_jid(account, jid)
- assert len(self._get_contact_iter(jid, account, contact, self.model)) == \
- 0, 'Self contact %s already in roster' % jid
+ assert len(self._get_contact_iter(jid, account, contact,
+ self.model)) == 0, 'Self contact %s already in roster' % jid
child_iterA = self._get_account_iter(account, self.model)
self.model.append(child_iterA, (None, gajim.nicks[account],
- 'self_contact', jid, account, None, None, None, None,
- None, None))
+ 'self_contact', jid, account, None, None, None, None, None, None))
self.draw_completely(jid, account)
self.draw_account(account)
@@ -668,9 +671,9 @@ class RosterWindow:
show_self_contact = gajim.config.get('show_self_contact')
if show_self_contact == 'never':
return
- if (contact.resource != gajim.connections[account].server_resource and\
- show_self_contact == 'when_other_resource') or show_self_contact == \
- 'always':
+ if (contact.resource != gajim.connections[account].server_resource \
+ and show_self_contact == 'when_other_resource') or \
+ show_self_contact == 'always':
return self._add_self_contact(account)
return
@@ -691,7 +694,7 @@ class RosterWindow:
contacts = self._add_metacontact_family(family, account)
else:
# We are a normal contact
- contacts = [(contact, account),]
+ contacts = [(contact, account), ]
self._add_entity(contact, account)
# Draw the contact and its groups contact
@@ -748,18 +751,19 @@ class RosterWindow:
else:
self._remove_entity(contact, account)
- if backend and (not gajim.interface.msg_win_mgr.get_control(jid, account)\
- or force):
+ if backend and (not gajim.interface.msg_win_mgr.get_control(jid,
+ account) or force):
# If a window is still opened: don't remove contact instance
# Remove contact before redrawing, otherwise the old
# numbers will still be show
gajim.contacts.remove_jid(account, jid, remove_meta=True)
if iters:
rest_of_family = [data for data in family
- if account != data['account'] or jid != data['jid']]
+ if account != data['account'] or jid != data['jid']]
if rest_of_family:
# reshow the rest of the family
- brothers = self._add_metacontact_family(rest_of_family, account)
+ brothers = self._add_metacontact_family(rest_of_family,
+ account)
for c, acc in brothers:
self.draw_completely(c.jid, acc)
@@ -800,7 +804,8 @@ class RosterWindow:
status = ''
if contact is None:
- gc_control = gajim.interface.msg_win_mgr.get_gc_control(jid, account)
+ gc_control = gajim.interface.msg_win_mgr.get_gc_control(jid,
+ account)
if gc_control:
# there is a window that we can minimize
gajim.interface.minimized_controls[account][jid] = gc_control
@@ -810,9 +815,9 @@ class RosterWindow:
else:
name = jid.split('@')[0]
# New groupchat
- #GCMIN
- contact = gajim.contacts.create_contact(jid=jid, account=account, name=name,
- groups=[_('Groupchats')], show=show, status=status, sub='none')
+ contact = gajim.contacts.create_contact(jid=jid, account=account,
+ name=name, groups=[_('Groupchats')], show=show, status=status,
+ sub='none')
gajim.contacts.add_contact(account, contact)
self.add_contact(jid, account)
else:
@@ -849,10 +854,9 @@ class RosterWindow:
"""
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if contact is None:
- #TRANSP
- contact = gajim.contacts.create_contact(jid=jid, account=account, name=jid,
- groups=[_('Transports')], show='offline', status='offline',
- sub='from')
+ contact = gajim.contacts.create_contact(jid=jid, account=account,
+ name=jid, groups=[_('Transports')], show='offline',
+ status='offline', sub='from')
gajim.contacts.add_contact(account, contact)
self.add_contact(jid, account)
return contact
@@ -880,7 +884,7 @@ class RosterWindow:
if self.regroup:
accounts = gajim.connections.keys()
else:
- accounts = [account,]
+ accounts = [account, ]
for acc in accounts:
changed_contacts = []
@@ -895,8 +899,8 @@ class RosterWindow:
if new_name not in contact.groups:
contact.groups.append(new_name)
- changed_contacts.append({'jid':jid, 'name':contact.name,
- 'groups':contact.groups})
+ changed_contacts.append({'jid': jid, 'name': contact.name,
+ 'groups':contact.groups})
gajim.connections[acc].update_contacts(changed_contacts)
@@ -1013,10 +1017,11 @@ class RosterWindow:
num_of_accounts = gajim.get_number_of_connected_accounts()
num_of_secured = gajim.get_number_of_securely_connected_accounts()
- if gajim.account_is_securely_connected(account) and not self.regroup or \
+ if gajim.account_is_securely_connected(account) and not self.regroup or\
self.regroup and num_of_secured and num_of_secured == num_of_accounts:
- tls_pixbuf = self.window.render_icon(gtk.STOCK_DIALOG_AUTHENTICATION,
- gtk.ICON_SIZE_MENU) # the only way to create a pixbuf from stock
+ # the only way to create a pixbuf from stock
+ tls_pixbuf = self.window.render_icon(
+ gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_MENU)
self.model[child_iter][C_PADLOCK_PIXBUF] = tls_pixbuf
else:
self.model[child_iter][C_PADLOCK_PIXBUF] = None
@@ -1041,24 +1046,30 @@ class RosterWindow:
self.model[child_iter][C_NAME] = account_name
- pep = gajim.connections[account].pep
- if gajim.config.get('show_mood_in_roster') and 'mood' in pep:
- self.model[child_iter][C_MOOD_PIXBUF] = pep['mood'].asPixbufIcon()
+ pep_dict = gajim.connections[account].pep
+ if gajim.config.get('show_mood_in_roster') and 'mood' in pep_dict:
+ self.model[child_iter][C_MOOD_PIXBUF] = pep_dict['mood'].\
+ asPixbufIcon()
else:
self.model[child_iter][C_MOOD_PIXBUF] = None
- if gajim.config.get('show_activity_in_roster') and 'activity' in pep:
- self.model[child_iter][C_ACTIVITY_PIXBUF] = pep['activity'].asPixbufIcon()
+ if gajim.config.get('show_activity_in_roster') and 'activity' in \
+ pep_dict:
+ self.model[child_iter][C_ACTIVITY_PIXBUF] = pep_dict['activity'].\
+ asPixbufIcon()
else:
self.model[child_iter][C_ACTIVITY_PIXBUF] = None
- if gajim.config.get('show_tunes_in_roster') and 'tune' in pep:
- self.model[child_iter][C_TUNE_PIXBUF] = pep['tune'].asPixbufIcon()
+ if gajim.config.get('show_tunes_in_roster') and 'tune' in pep_dict:
+ self.model[child_iter][C_TUNE_PIXBUF] = pep_dict['tune'].\
+ asPixbufIcon()
else:
self.model[child_iter][C_TUNE_PIXBUF] = None
- if gajim.config.get('show_location_in_roster') and 'location' in pep:
- self.model[child_iter][C_LOCATION_PIXBUF] = pep['location'].asPixbufIcon()
+ if gajim.config.get('show_location_in_roster') and 'location' in \
+ pep_dict:
+ self.model[child_iter][C_LOCATION_PIXBUF] = pep_dict['location'].\
+ asPixbufIcon()
else:
self.model[child_iter][C_LOCATION_PIXBUF] = None
return False
@@ -1161,8 +1172,8 @@ class RosterWindow:
for jid_ in gajim.contacts.get_jid_list(account_):
contact_ = gajim.contacts.get_first_contact_from_jid(
account_, jid_)
- if contact_.get_shown_name() == contact.get_shown_name() and \
- (jid_, account_) != (jid, account):
+ if contact_.get_shown_name() == contact.get_shown_name() \
+ and (jid_, account_) != (jid, account):
add_acct = True
break
if add_acct:
@@ -1182,12 +1193,13 @@ class RosterWindow:
# escape markup entities and make them small
# italic and fg color color is calcuted to be
# always readable
- color = gtkgui_helpers._get_fade_color(self.tree, selected, focus)
- colorstring = '#%04x%04x%04x' % (color.red, color.green, color.blue)
+ color = gtkgui_helpers.get_fade_color(self.tree, selected,
+ focus)
+ colorstring = '#%04x%04x%04x' % (color.red, color.green,
+ color.blue)
name += '\n<span size="small" style="italic" ' \
- 'foreground="%s">%s</span>' % (
- colorstring,
- gobject.markup_escape_text(status))
+ 'foreground="%s">%s</span>' % (colorstring,
+ gobject.markup_escape_text(status))
icon_name = helpers.get_icon_name_to_show(contact, account)
# look if another resource has awaiting events
@@ -1203,11 +1215,11 @@ class RosterWindow:
have_visible_children = False
if family:
bb_jid, bb_account = \
- self._get_nearby_family_and_big_brother(family, account)[1:]
+ self._get_nearby_family_and_big_brother(family, account)[1:]
is_big_brother = (jid, account) == (bb_jid, bb_account)
iters = self._get_contact_iter(jid, account)
- have_visible_children = iters \
- and self.modelfilter.iter_has_child(iters[0])
+ have_visible_children = iters and \
+ self.modelfilter.iter_has_child(iters[0])
if have_visible_children:
# We are the big brother and have a visible family
@@ -1388,7 +1400,7 @@ class RosterWindow:
self.on_modelfilter_row_has_child_toggled)
self.tree.set_model(self.modelfilter)
- for acct in gajim.connections:
+ for acct in gajim.contacts.get_accounts():
self.add_account(acct)
self.add_account_contacts(acct)
# Recalculate column width for ellipsizing
@@ -1407,7 +1419,8 @@ class RosterWindow:
# Not visible in roster
return
path = self.modelfilter.get_path(iters[0])
- if self.dragging or not gajim.config.get('scroll_roster_to_last_message'):
+ if self.dragging or not gajim.config.get(
+ 'scroll_roster_to_last_message'):
# do not change selection while DND'ing
return
# Expand his parent, so this path is visible, don't expand it.
@@ -1521,12 +1534,11 @@ class RosterWindow:
self.contact_has_pending_roster_events(contact, _acc):
return True
return gajim.config.get('show_transports_group') and \
- (gajim.account_is_connected(account) or \
- gajim.config.get('showoffline'))
+ (gajim.account_is_connected(account) or \
+ gajim.config.get('showoffline'))
if gajim.config.get('showoffline'):
return True
-
if self.regroup:
# C_ACCOUNT for groups depends on the order
# accounts were connected
@@ -1536,8 +1548,7 @@ class RosterWindow:
accounts = [account]
for _acc in accounts:
for contact in gajim.contacts.iter_contacts(_acc):
- # Is this contact in this group ? (last part of if check if it's
- # self contact)
+ # Is this contact in this group?
if group in contact.get_shown_groups():
if self.contact_is_visible(contact, _acc):
return True
@@ -1557,21 +1568,21 @@ class RosterWindow:
jid = data['jid']
account = data['account']
contact = gajim.contacts.get_contact_with_highest_priority(
- account, jid)
+ account, jid)
if contact and self.contact_is_visible(contact, account):
return True
return False
else:
- contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
+ contact = gajim.contacts.get_contact_with_highest_priority(
+ account, jid)
return self.contact_is_visible(contact, account)
if type_ == 'agent':
contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
+ jid)
return self.contact_has_pending_roster_events(contact, account) or \
- (gajim.config.get('show_transports_group') and \
- (gajim.account_is_connected(account) or \
- gajim.config.get('showoffline')))
+ (gajim.config.get('show_transports_group') and \
+ (gajim.account_is_connected(account) or \
+ gajim.config.get('showoffline')))
return True
def _compareIters(self, model, iter1, iter2, data=None):
@@ -1627,12 +1638,12 @@ class RosterWindow:
if not contact2:
return 0
name2 = contact2.get_shown_name()
- # We first compare by show if sort_by_show_in_roster is True or if it's a
- # child contact
+ # We first compare by show if sort_by_show_in_roster is True or if it's
+ # a child contact
if type1 == 'contact' and type2 == 'contact' and \
gajim.config.get('sort_by_show_in_roster'):
cshow = {'chat':0, 'online': 1, 'away': 2, 'xa': 3, 'dnd': 4,
- 'invisible': 5, 'offline': 6, 'not in roster': 7, 'error': 8}
+ 'invisible': 5, 'offline': 6, 'not in roster': 7, 'error': 8}
s = self.get_show(lcontact1)
show1 = cshow.get(s, 9)
s = self.get_show(lcontact2)
@@ -1696,8 +1707,8 @@ class RosterWindow:
if gajim.contacts.get_first_contact_from_jid(account, jid) and not \
shown:
# We have this jid in our contacts list
- # XXX unread messages should probably have their session saved with
- # them
+ # XXX unread messages should probably have their session saved
+ # with them
session = gajim.connections[account].make_new_session(jid)
tim = time.localtime(float(result[2]))
@@ -1706,10 +1717,10 @@ class RosterWindow:
gajim.logger.set_shown_unread_msgs(result[0])
elif (time.time() - result[2]) > 2592000:
- # ok, here we see that we have a message in unread messages table
- # that is older than a month. It is probably from someone not in our
- # roster for accounts we usually launch, so we will delete this id
- # from unread message tables.
+ # ok, here we see that we have a message in unread messages
+ # table that is older than a month. It is probably from someone
+ # not in our roster for accounts we usually launch, so we will
+ # delete this id from unread message tables.
gajim.logger.set_read_messages([result[0]])
def fill_contacts_and_groups_dicts(self, array, account):
@@ -1727,7 +1738,8 @@ class RosterWindow:
if gajim.connections[account].server_resource:
self_jid += '/' + gajim.connections[account].server_resource
array[self_jid] = {'name': gajim.nicks[account],
- 'groups': ['self_contact'], 'subscription': 'both', 'ask': 'none'}
+ 'groups': ['self_contact'], 'subscription': 'both',
+ 'ask': 'none'}
# .keys() is needed
for jid in array.keys():
# Remove the contact in roster. It might has changed
@@ -1748,17 +1760,17 @@ class RosterWindow:
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
- 'attached_gpg_keys').split()
+ 'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
if gajim.jid_is_transport(jid):
array[jid]['groups'] = [_('Transports')]
#TRANSP - potential
- contact1 = gajim.contacts.create_contact(jid=ji, account=account, name=name,
- groups=array[jid]['groups'], show=show, status=status,
- sub=array[jid]['subscription'], ask=array[jid]['ask'],
- resource=resource, keyID=keyID)
+ contact1 = gajim.contacts.create_contact(jid=ji, account=account,
+ name=name, groups=array[jid]['groups'], show=show,
+ status=status, sub=array[jid]['subscription'],
+ ask=array[jid]['ask'], resource=resource, keyID=keyID)
gajim.contacts.add_contact(account, contact1)
if gajim.config.get('ask_avatars_on_startup'):
@@ -1769,16 +1781,19 @@ class RosterWindow:
jid_with_resource = contact1.jid
if contact1.resource:
jid_with_resource += '/' + contact1.resource
- gajim.connections[account].request_vcard(jid_with_resource)
+ gajim.connections[account].request_vcard(
+ jid_with_resource)
else:
host = gajim.get_server_from_jid(contact1.jid)
if host not in gajim.transport_avatar[account]:
- gajim.transport_avatar[account][host] = [contact1.jid]
+ gajim.transport_avatar[account][host] = \
+ [contact1.jid]
else:
- gajim.transport_avatar[account][host].append(contact1.jid)
+ gajim.transport_avatar[account][host].append(
+ contact1.jid)
- # If we already have chat windows opened, update them with new contact
- # instance
+ # If we already have chat windows opened, update them with new
+ # contact instance
chat_control = gajim.interface.msg_win_mgr.get_control(ji, account)
if chat_control:
chat_control.contact = contact1
@@ -1805,7 +1820,8 @@ class RosterWindow:
if key in self.contacts_to_be_removed.keys():
backend = self.contacts_to_be_removed[key]['backend']
del self.contacts_to_be_removed[key]
- # Remove contact will delay removal if there are more events pending
+ # Remove contact will delay removal if there are more events
+ # pending
self.remove_contact(jid, account, backend=backend)
self.show_title()
@@ -1818,8 +1834,9 @@ class RosterWindow:
event = gajim.events.get_first_event(account, jid, event.type_)
if event.type_ == 'normal':
dialogs.SingleMessageWindow(account, jid,
- action='receive', from_whom=jid, subject=data[1], message=data[0],
- resource=data[5], session=data[8], form_node=data[9])
+ action='receive', from_whom=jid, subject=data[1],
+ message=data[0], resource=data[5], session=data[8],
+ form_node=data[9])
gajim.events.remove_events(account, jid, event)
return True
elif event.type_ == 'file-request':
@@ -1887,7 +1904,8 @@ class RosterWindow:
# position of the treeview on the screen
position = self.tree.window.get_origin()
- self.tooltip.show_tooltip(contact, rect.height, position[1] + rect.y)
+ self.tooltip.show_tooltip(contact, rect.height, position[1] + \
+ rect.y)
else:
self.tooltip.hide_tooltip()
@@ -1900,32 +1918,33 @@ class RosterWindow:
dialogs.InformationDialog(_('Authorization has been sent'),
_('Now "%s" will know your status.') %jid)
- def req_sub(self, widget, jid, txt, account, groups=[], nickname=None,
+ def req_sub(self, widget, jid, txt, account, groups=None, nickname=None,
auto_auth=False):
"""
Request subscription to a contact
"""
+ groups_list = groups or []
gajim.connections[account].request_subscription(jid, txt, nickname,
- groups, auto_auth, gajim.nicks[account])
+ groups_list, auto_auth, gajim.nicks[account])
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
if not contact:
keyID = ''
attached_keys = gajim.config.get_per('accounts', account,
- 'attached_gpg_keys').split()
+ 'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
- contact = gajim.contacts.create_contact(jid=jid, account=account, name=nickname,
- groups=groups, show='requested', status='', ask='none',
- sub='subscribe', keyID=keyID)
+ contact = gajim.contacts.create_contact(jid=jid, account=account,
+ name=nickname, groups=groups_list, show='requested', status='',
+ ask='none', sub='subscribe', keyID=keyID)
gajim.contacts.add_contact(account, contact)
else:
if not _('Not in Roster') in contact.get_shown_groups():
- dialogs.InformationDialog(_('Subscription request has been sent'),
- _('If "%s" accepts this request you will know his or her status.'
- ) % jid)
+ dialogs.InformationDialog(_('Subscription request has been '
+ 'sent'), _('If "%s" accepts this request you will know his '
+ 'or her status.') % jid)
return
self.remove_contact(contact.jid, account, force=True)
- contact.groups = groups
+ contact.groups = groups_list
if nickname:
contact.name = nickname
self.add_contact(jid, account)
@@ -1950,7 +1969,6 @@ class RosterWindow:
self.set_state(account, 'connecting')
def send_status(self, account, status, txt, auto=False, to=None):
- child_iterA = self._get_account_iter(account, self.model)
if status != 'offline':
if to is None:
if status == gajim.connections[account].get_status() and \
@@ -1965,7 +1983,8 @@ class RosterWindow:
keyid = gajim.config.get_per('accounts', account, 'keyid')
if keyid and not gajim.connections[account].gpg:
dialogs.WarningDialog(_('GPG is not usable'),
- _('You will be connected to %s without OpenPGP.') % account)
+ _('You will be connected to %s without OpenPGP.') % \
+ account)
self.send_status_continue(account, status, txt, auto, to)
@@ -2027,11 +2046,11 @@ class RosterWindow:
gajim.interface.minimized_controls[account].values():
if gc_control.account == account:
if gajim.gc_connected[account][gc_control.room_jid]:
- gajim.connections[account].send_gc_status(gc_control.nick,
- gc_control.room_jid, status, txt)
+ gajim.connections[account].send_gc_status(
+ gc_control.nick, gc_control.room_jid, status, txt)
else:
- # for some reason, we are not connected to the room even if
- # tab is opened, send initial join_gc()
+ # for some reason, we are not connected to the room even
+ # if tab is opened, send initial join_gc()
gajim.connections[account].join_gc(gc_control.nick,
gc_control.room_jid, None)
if was_invisible and status != 'offline':
@@ -2129,9 +2148,10 @@ class RosterWindow:
gajim.con_types[account] = None
for jid in gajim.contacts.get_jid_list(account):
lcontact = gajim.contacts.get_contacts(account, jid)
- ctrl = gajim.interface.msg_win_mgr.get_gc_control(jid, account)
- for contact in [c for c in lcontact if ((c.show != 'offline' or \
- c.is_transport()) and not ctrl)]:
+ ctrl = gajim.interface.msg_win_mgr.get_gc_control(jid,
+ account)
+ for contact in [c for c in lcontact if (
+ (c.show != 'offline' or c.is_transport()) and not ctrl)]:
self.chg_contact_status(contact, 'offline', '', account)
self.actions_menu_needs_rebuild = True
self.update_status_combobox()
@@ -2147,7 +2167,7 @@ class RosterWindow:
show_pep can be False to hide pep things from status message or True
"""
empty_pep = {'activity': '', 'subactivity': '', 'activity_text': '',
- 'mood': '', 'mood_text': ''}
+ 'mood': '', 'mood_text': ''}
if show in gajim.config.get_per('defaultstatusmsg'):
if gajim.config.get_per('defaultstatusmsg', show, 'enabled'):
msg = gajim.config.get_per('defaultstatusmsg', show, 'message')
@@ -2175,17 +2195,17 @@ class RosterWindow:
if status == 'invisible' and self.connected_rooms(account):
dialogs.ConfirmationDialog(
- _('You are participating in one or more group chats'),
- _('Changing your status to invisible will result in disconnection '
- 'from those group chats. Are you sure you want to go invisible?'),
- on_response_ok = (change, account, status))
+ _('You are participating in one or more group chats'),
+ _('Changing your status to invisible will result in '
+ 'disconnection from those group chats. Are you sure you want '
+ 'to go invisible?'), on_response_ok = (change, account, status))
else:
change(account, status)
def update_status_combobox(self):
# table to change index in connection.connected to index in combobox
table = {'offline':9, 'connecting':9, 'online':0, 'chat':1, 'away':2,
- 'xa':3, 'dnd':4, 'invisible':5}
+ 'xa':3, 'dnd':4, 'invisible':5}
# we check if there are more options in the combobox that it should
# if yes, we remove the first ones
@@ -2204,9 +2224,9 @@ class RosterWindow:
liststore.prepend(['SEPARATOR', None, '', True])
status_combobox_text = uf_show + ' (' + _("desync'ed") +')'
liststore.prepend([status_combobox_text,
- gajim.interface.jabber_state_images['16'][show], show, False])
+ gajim.interface.jabber_state_images['16'][show], show, False])
self.status_combobox.set_active(0)
- gajim.interface._change_awn_icon_status(show)
+ gajim.interface.change_awn_icon_status(show)
self.combobox_callback_active = True
if gajim.interface.systray_enabled:
gajim.interface.systray.change_status(show)
@@ -2253,7 +2273,8 @@ class RosterWindow:
Main window X button was clicked
"""
if gajim.interface.systray_enabled and not gajim.config.get(
- 'quit_on_roster_x_button') and gajim.config.get('trayicon') != 'on_event':
+ 'quit_on_roster_x_button') and gajim.config.get('trayicon') != \
+ 'on_event':
self.tooltip.hide_tooltip()
self.window.hide()
elif gajim.config.get('quit_on_roster_x_button'):
@@ -2324,7 +2345,7 @@ class RosterWindow:
self.quit_on_next_offline = 0
accounts_to_disconnect = []
for acct in accounts:
- if gajim.connections[acct].connected:
+ if gajim.connections[acct].connected > 1:
self.quit_on_next_offline += 1
accounts_to_disconnect.append(acct)
@@ -2338,7 +2359,8 @@ class RosterWindow:
def on_continue2(message, pep_dict):
# check if there is an active file transfer
from common.protocol.bytestream import (is_transfer_active)
- files_props = gajim.interface.instances['file_transfers'].files_props
+ files_props = gajim.interface.instances['file_transfers'].\
+ files_props
transfer_active = False
for x in files_props:
for y in files_props[x]:
@@ -2348,8 +2370,8 @@ class RosterWindow:
if transfer_active:
dialogs.ConfirmationDialog(_('You have running file transfers'),
- _('If you quit now, the file(s) being transfered will be '
- 'stopped. Do you still want to quit?'),
+ _('If you quit now, the file(s) being transfered will '
+ 'be stopped. Do you still want to quit?'),
on_response_ok=(on_continue3, message, pep_dict))
return
on_continue3(message, pep_dict)
@@ -2371,8 +2393,8 @@ class RosterWindow:
for ctrl in win.controls():
fjid = ctrl.get_full_jid()
if fjid in gajim.last_message_time[ctrl.account]:
- if time.time() - gajim.last_message_time[ctrl.account][fjid] \
- < 2:
+ if time.time() - gajim.last_message_time[ctrl.account][
+ fjid] < 2:
recent = True
break
if recent:
@@ -2380,9 +2402,10 @@ class RosterWindow:
if unread or recent:
dialogs.ConfirmationDialog(_('You have unread messages'),
- _('Messages will only be available for reading them later if you'
- ' have history enabled and contact is in your roster.'),
- on_response_ok=(on_continue2, message, pep_dict))
+ _('Messages will only be available for reading them later '
+ 'if you have history enabled and contact is in your '
+ 'roster.'), on_response_ok=(on_continue2,
+ message, pep_dict))
return
on_continue2(message, pep_dict)
@@ -2544,13 +2567,14 @@ class RosterWindow:
account_name = account
if gajim.account_is_connected(account):
account_name += ' (%s/%s)' % (repr(nbr_on), repr(nbr_total))
- contact = gajim.contacts.create_self_contact(jid=jid, account=account,
- name=account_name, show=connection.get_status(),
- status=connection.status, resource=connection.server_resource,
- priority=connection.priority)
+ contact = gajim.contacts.create_self_contact(jid=jid,
+ account=account, name=account_name,
+ show=connection.get_status(), status=connection.status,
+ resource=connection.server_resource,
+ priority=connection.priority)
if gajim.connections[account].gpg:
- contact.keyID = gajim.config.get_per('accounts', connection.name,
- 'keyid')
+ contact.keyID = gajim.config.get_per('accounts',
+ connection.name, 'keyid')
contacts.append(contact)
# if we're online ...
if connection.connection:
@@ -2572,10 +2596,11 @@ class RosterWindow:
show = roster.getShow(jid+'/'+resource)
if not show:
show = 'online'
- contact = gajim.contacts.create_self_contact(jid=jid,
- account=account, show=show, status=roster.getStatus(jid + '/' + resource),
- priority=roster.getPriority(jid + '/' + resource),
- resource=resource)
+ contact = gajim.contacts.create_self_contact(
+ jid=jid, account=account, show=show,
+ status=roster.getStatus(jid + '/' + resource),
+ priority=roster.getPriority(
+ jid + '/' + resource), resource=resource)
contacts.append(contact)
if self.tooltip.timeout == 0 or self.tooltip.id != props[0]:
self.tooltip.id = row
@@ -2629,18 +2654,18 @@ class RosterWindow:
return
if len(list_) == 1:
pritext = _('Transport "%s" will be removed') % list_[0][0].jid
- sectext = _('You will no longer be able to send and receive messages '
- 'from contacts using this transport.')
+ sectext = _('You will no longer be able to send and receive '
+ 'messages from contacts using this transport.')
else:
pritext = _('Transports will be removed')
jids = ''
for (contact, account) in list_:
jids += '\n ' + contact.get_shown_name() + ','
jids = jids[:-1] + '.'
- sectext = _('You will no longer be able to send and receive messages '
- 'to contacts from these transports: %s') % jids
+ sectext = _('You will no longer be able to send and receive '
+ 'messages to contacts from these transports: %s') % jids
dialogs.ConfirmationDialog(pritext, sectext,
- on_response_ok = (remove, list_))
+ on_response_ok = (remove, list_))
def on_block(self, widget, list_, group=None):
"""
@@ -2655,22 +2680,24 @@ class RosterWindow:
if group is None:
for (contact, account) in list_:
if account not in accounts:
- if not gajim.connections[account].privacy_rules_supported:
+ if not gajim.connections[account].\
+ privacy_rules_supported:
continue
accounts.append(account)
self.send_status(account, 'offline', msg, to=contact.jid)
- new_rule = {'order': u'1', 'type': u'jid', 'action': u'deny',
- 'value' : contact.jid, 'child': [u'message', u'iq',
- u'presence-out']}
+ new_rule = {'order': u'1', 'type': u'jid',
+ 'action': u'deny', 'value' : contact.jid,
+ 'child': [u'message', u'iq', u'presence-out']}
gajim.connections[account].blocked_list.append(new_rule)
# needed for draw_contact:
gajim.connections[account].blocked_contacts.append(
- contact.jid)
+ contact.jid)
self.draw_contact(contact.jid, account)
else:
for (contact, account) in list_:
if account not in accounts:
- if not gajim.connections[account].privacy_rules_supported:
+ if not gajim.connections[account].\
+ privacy_rules_supported:
continue
accounts.append(account)
# needed for draw_group:
@@ -2679,8 +2706,10 @@ class RosterWindow:
self.send_status(account, 'offline', msg, to=contact.jid)
self.draw_contact(contact.jid, account)
new_rule = {'order': u'1', 'type': u'group', 'action': u'deny',
- 'value' : group, 'child': [u'message', u'iq', u'presence-out']}
- gajim.connections[account].blocked_list.append(new_rule)
+ 'value' : group, 'child': [u'message', u'iq',
+ u'presence-out']}
+ # account is the same for all when we block a group
+ gajim.connections[list_[0][1]].blocked_list.append(new_rule)
for account in accounts:
connection = gajim.connections[account]
connection.set_privacy_list('block', connection.blocked_list)
@@ -2702,11 +2731,11 @@ class RosterWindow:
_block_it()
return
pritext = _('You are about to block a contact. Are you sure you want'
- ' to continue?')
- sectext = _('This contact will see you offline and you will not receive '
- 'messages he will send you.')
- dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
- _('Do _not ask me again'), on_response_ok=_block_it)
+ ' to continue?')
+ sectext = _('This contact will see you offline and you will not '
+ 'receive messages he will send you.')
+ dialogs.ConfirmationDialogCheck(pritext, sectext,
+ _('_Do not ask me again'), on_response_ok=_block_it)
def on_unblock(self, widget, list_, group=None):
"""
@@ -2720,17 +2749,20 @@ class RosterWindow:
accounts.append(account)
gajim.connections[account].new_blocked_list = []
gajim.connections[account].to_unblock = []
- gajim.connections[account].to_unblock.append(contact.jid)
+ gajim.connections[account].to_unblock.append(
+ contact.jid)
else:
gajim.connections[account].to_unblock.append(contact.jid)
# needed for draw_contact:
if contact.jid in gajim.connections[account].blocked_contacts:
- gajim.connections[account].blocked_contacts.remove(contact.jid)
+ gajim.connections[account].blocked_contacts.remove(
+ contact.jid)
self.draw_contact(contact.jid, account)
for account in accounts:
for rule in gajim.connections[account].blocked_list:
if rule['action'] != 'deny' or rule['type'] != 'jid' \
- or rule['value'] not in gajim.connections[account].to_unblock:
+ or rule['value'] not in \
+ gajim.connections[account].to_unblock:
gajim.connections[account].new_blocked_list.append(rule)
else:
for (contact, account) in list_:
@@ -2739,17 +2771,19 @@ class RosterWindow:
accounts.append(account)
# needed for draw_group:
if group in gajim.connections[account].blocked_groups:
- gajim.connections[account].blocked_groups.remove(group)
+ gajim.connections[account].blocked_groups.remove(
+ group)
self.draw_group(group, account)
gajim.connections[account].new_blocked_list = []
for rule in gajim.connections[account].blocked_list:
- if rule['action'] != 'deny' or rule['type'] != 'group' \
- or rule['value'] != group:
- gajim.connections[account].new_blocked_list.append(rule)
+ if rule['action'] != 'deny' or \
+ rule['type'] != 'group' or rule['value'] != group:
+ gajim.connections[account].new_blocked_list.\
+ append(rule)
self.draw_contact(contact.jid, account)
for account in accounts:
gajim.connections[account].set_privacy_list('block',
- gajim.connections[account].new_blocked_list)
+ gajim.connections[account].new_blocked_list)
gajim.connections[account].get_privacy_list('block')
if len(gajim.connections[account].new_blocked_list) == 0:
gajim.connections[account].blocked_list = []
@@ -2760,7 +2794,7 @@ class RosterWindow:
gajim.connections[account].del_privacy_list('block')
if 'blocked_contacts' in gajim.interface.instances[account]:
gajim.interface.instances[account]['blocked_contacts'].\
- privacy_list_received([])
+ privacy_list_received([])
for (contact, account) in list_:
if not self.regroup:
show = gajim.SHOW_LIST[gajim.connections[account].connected]
@@ -2773,10 +2807,10 @@ class RosterWindow:
accounts.append(account)
if gajim.connections[account].privacy_rules_supported:
self.send_status(account, show,
- gajim.connections[account].status, to=contact.jid)
+ gajim.connections[account].status, to=contact.jid)
else:
self.send_status(account, show,
- gajim.connections[account].status, to=contact.jid)
+ gajim.connections[account].status, to=contact.jid)
def on_rename(self, widget, row_type, jid, account):
# this function is called either by F2 or by Rename menuitem
@@ -2807,13 +2841,15 @@ class RosterWindow:
if row_type in ('contact', 'agent'):
if old_text == new_text:
return
- for contact in gajim.contacts.get_contacts(account, jid):
+ contacts = gajim.contacts.get_contacts(account, jid)
+ for contact in contacts:
contact.name = new_text
gajim.connections[account].update_contact(jid, new_text, \
- contact.groups)
+ contacts[0].groups)
self.draw_contact(jid, account)
# Update opened chats
- for ctrl in gajim.interface.msg_win_mgr.get_controls(jid, account):
+ for ctrl in gajim.interface.msg_win_mgr.get_controls(jid,
+ account):
ctrl.update_ui()
win = gajim.interface.msg_win_mgr.get_window(jid, account)
win.redraw_tab(ctrl)
@@ -2826,23 +2862,25 @@ class RosterWindow:
if 'rename' in gajim.interface.instances:
del gajim.interface.instances['rename']
- gajim.interface.instances['rename'] = dialogs.InputDialog(title, message,
- old_text, False, (on_renamed, account, row_type, jid, old_text),
- on_canceled)
+ gajim.interface.instances['rename'] = dialogs.InputDialog(title,
+ message, old_text, False, (on_renamed, account, row_type, jid,
+ old_text), on_canceled)
def on_remove_group_item_activated(self, widget, group, account):
def on_ok(checked):
- for contact in gajim.contacts.get_contacts_from_group(account, group):
+ for contact in gajim.contacts.get_contacts_from_group(account,
+ group):
if not checked:
- self.remove_contact_from_groups(contact.jid, account, [group])
+ self.remove_contact_from_groups(contact.jid, account,
+ [group])
else:
gajim.connections[account].unsubscribe(contact.jid)
self.remove_contact(contact.jid, account, backend=True)
dialogs.ConfirmationDialogCheck(_('Remove Group'),
- _('Do you want to remove group %s from the roster?') % group,
- _('Also remove all contacts in this group from your roster'),
- on_response_ok=on_ok)
+ _('Do you want to remove group %s from the roster?') % group,
+ _('Also remove all contacts in this group from your roster'),
+ on_response_ok=on_ok)
def on_assign_pgp_key(self, widget, contact, account):
attached_keys = gajim.config.get_per('accounts', account,
@@ -2878,11 +2916,11 @@ class RosterWindow:
keys_str)
for u in gajim.contacts.get_contacts(account, contact.jid):
u.keyID = helpers.prepare_and_validate_gpg_keyID(account,
- contact.jid, keyID)
+ contact.jid, keyID)
dialogs.ChooseGPGKeyDialog(_('Assign OpenPGP Key'),
- _('Select a key to apply to the contact'), public_keys,
- on_key_selected, selected=keyID)
+ _('Select a key to apply to the contact'), public_keys,
+ on_key_selected, selected=keyID)
def on_set_custom_avatar_activate(self, widget, contact, account):
def on_ok(widget, path_to_file):
@@ -2922,7 +2960,7 @@ class RosterWindow:
self.update_avatar_in_gui(contact.jid, account)
dlg = dialogs.AvatarChooserDialog(on_response_ok=on_ok,
- on_response_clear=on_clear)
+ on_response_clear=on_clear)
def on_edit_groups(self, widget, list_):
dialogs.EditGroupsDialog(list_)
@@ -2936,7 +2974,7 @@ class RosterWindow:
gajim.interface.instances['logs'].open_history(contact.jid, account)
else:
gajim.interface.instances['logs'] = history_window.\
- HistoryWindow(contact.jid, account)
+ HistoryWindow(contact.jid, account)
def on_disconnect(self, widget, jid, account):
"""
@@ -2955,7 +2993,7 @@ class RosterWindow:
if jid in gajim.interface.minimized_controls[account]:
ctrl = gajim.interface.minimized_controls[account][jid]
gajim.interface.join_gc_room(account, jid, ctrl.nick,
- gajim.gc_passwords.get(jid, ''))
+ gajim.gc_passwords.get(jid, ''))
def on_send_single_message_menuitem_activate(self, widget, account,
contact=None):
@@ -2972,7 +3010,7 @@ class RosterWindow:
def on_send_file_menuitem_activate(self, widget, contact, account,
resource=None):
gajim.interface.instances['file_transfers'].show_file_send_request(
- account, contact)
+ account, contact)
def on_add_special_notification_menuitem_activate(self, widget, jid):
dialogs.AddSpecialNotificationDialog(jid)
@@ -2998,12 +3036,13 @@ class RosterWindow:
if gajim.connections[account].muc_jid[type_]:
# create the room on this muc server
if 'join_gc' in gajim.interface.instances[account]:
- gajim.interface.instances[account]['join_gc'].window.destroy()
+ gajim.interface.instances[account]['join_gc'].window.\
+ destroy()
try:
gajim.interface.instances[account]['join_gc'] = \
- dialogs.JoinGroupchatWindow(account,
- gajim.connections[account].muc_jid[type_],
- automatic = {'invities': jid_list})
+ dialogs.JoinGroupchatWindow(account,
+ gajim.connections[account].muc_jid[type_],
+ automatic = {'invities': jid_list})
except GajimGeneralException:
continue
break
@@ -3030,7 +3069,8 @@ class RosterWindow:
"""
if not jid in gajim.interface.minimized_controls[account]:
# Already opened?
- gc_control = gajim.interface.msg_win_mgr.get_gc_control(jid, account)
+ gc_control = gajim.interface.msg_win_mgr.get_gc_control(jid,
+ account)
if gc_control:
mw = gajim.interface.msg_win_mgr.get_window(jid, account)
mw.set_active_tab(gc_control)
@@ -3040,7 +3080,7 @@ class RosterWindow:
mw = gajim.interface.msg_win_mgr.get_window(jid, account)
if not mw:
mw = gajim.interface.msg_win_mgr.create_window(ctrl.contact,
- ctrl.account, ctrl.type_id)
+ ctrl.account, ctrl.type_id)
ctrl.parent_win = mw
mw.new_tab(ctrl)
mw.set_active_tab(ctrl)
@@ -3117,8 +3157,8 @@ class RosterWindow:
return
jid = model[path][C_JID].decode('utf-8')
account = model[path][C_ACCOUNT].decode('utf-8')
- contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
+ contact = gajim.contacts.get_contact_with_highest_priority(
+ account, jid)
list_.append((contact, account))
if type_ == 'contact':
self.on_req_usub(widget, list_)
@@ -3151,7 +3191,8 @@ class RosterWindow:
if event.button == 3: # Right click
try:
- model, list_of_paths = self.tree.get_selection().get_selected_rows()
+ model, list_of_paths = self.tree.get_selection().\
+ get_selected_rows()
except TypeError:
list_of_paths = []
if path not in list_of_paths:
@@ -3161,7 +3202,8 @@ class RosterWindow:
elif event.button == 2: # Middle click
try:
- model, list_of_paths = self.tree.get_selection().get_selected_rows()
+ model, list_of_paths = self.tree.get_selection().\
+ get_selected_rows()
except TypeError:
list_of_paths = []
if list_of_paths != [path]:
@@ -3188,7 +3230,7 @@ class RosterWindow:
'sync_with_global_status'):
continue
current_show = gajim.SHOW_LIST[gajim.connections[acct].\
- connected]
+ connected]
self.send_status(acct, current_show, message)
self.send_pep(acct, pep_dict)
dialogs.ChangeStatusMessageDialog(on_response, show)
@@ -3213,8 +3255,9 @@ class RosterWindow:
else:
self.tree.expand_row(path, False)
return
- # We just save on which row we press button, and open chat window on
- # button release to be able to do DND without opening chat window
+ # We just save on which row we press button, and open chat
+ # window on button release to be able to do DND without opening
+ # chat window
self.clicked_path = path
return
else:
@@ -3243,7 +3286,8 @@ class RosterWindow:
remove_auth = False
for (contact, account) in list_:
if _('Not in Roster') not in contact.get_shown_groups():
- gajim.connections[account].unsubscribe(contact.jid, remove_auth)
+ gajim.connections[account].unsubscribe(contact.jid,
+ remove_auth)
self.remove_contact(contact.jid, account, backend=True)
if not remove_auth and contact.sub == 'both':
contact.name = ''
@@ -3259,38 +3303,39 @@ class RosterWindow:
if len(list_) == 1:
contact = list_[0][0]
pritext = _('Contact "%s" will be removed from your roster') % \
- contact.get_shown_name()
- sectext = _('You are about to remove "%(name)s" (%(jid)s) from your '
- 'roster.\n') % {'name': contact.get_shown_name(),
- 'jid': contact.jid}
+ contact.get_shown_name()
+ sectext = _('You are about to remove "%(name)s" (%(jid)s) from '
+ 'your roster.\n') % {'name': contact.get_shown_name(),
+ 'jid': contact.jid}
if contact.sub == 'to':
dialogs.ConfirmationDialog(pritext, sectext + \
- _('By removing this contact you also remove authorization '
- 'resulting in him or her always seeing you as offline.'),
- on_response_ok = (on_ok2, list_))
+ _('By removing this contact you also remove authorization '
+ 'resulting in him or her always seeing you as offline.'),
+ on_response_ok=(on_ok2, list_))
elif _('Not in Roster') in contact.get_shown_groups():
# Contact is not in roster
dialogs.ConfirmationDialog(pritext, sectext + \
- _('Do you want to continue?'), on_response_ok = (on_ok2, list_))
+ _('Do you want to continue?'), on_response_ok=(on_ok2,
+ list_))
else:
dialogs.ConfirmationDialogCheck(pritext, sectext + \
- _('By removing this contact you also by default remove '
- 'authorization resulting in him or her always seeing you as '
- 'offline.'),
- _('I want this contact to know my status after removal'),
- on_response_ok = (on_ok, list_))
+ _('By removing this contact you also by default remove '
+ 'authorization resulting in him or her always seeing you as'
+ ' offline.'),
+ _('I want this contact to know my status after removal'),
+ on_response_ok=(on_ok, list_))
else:
# several contact to remove at the same time
pritext = _('Contacts will be removed from your roster')
jids = ''
for (contact, account) in list_:
- jids += '\n ' + contact.get_shown_name() + ' (%s)' % contact.jid +\
- ','
+ jids += '\n ' + contact.get_shown_name() + ' (%s)' % \
+ contact.jid + ','
sectext = _('By removing these contacts:%s\nyou also remove '
- 'authorization resulting in them always seeing you as offline.') % \
- jids
+ 'authorization resulting in them always seeing you as '
+ 'offline.') % jids
dialogs.ConfirmationDialog(pritext, sectext,
- on_response_ok = (on_ok2, list_))
+ on_response_ok=(on_ok2, list_))
def on_send_custom_status(self, widget, contact_list, show, group=None):
"""
@@ -3314,7 +3359,8 @@ class RosterWindow:
for (contact, account) in contact_list:
if account not in gajim.interface.status_sent_to_users:
gajim.interface.status_sent_to_users[account] = {}
- gajim.interface.status_sent_to_users[account][contact.jid] = show
+ gajim.interface.status_sent_to_users[account][contact.jid] \
+ = show
# 2. update privacy lists if main status is invisible
for account in account_list:
@@ -3337,19 +3383,19 @@ class RosterWindow:
else:
gajim.config.set('confirm_custom_status', 'yes')
self.get_status_message(show, on_response, show_pep=False,
- always_ask=True)
+ always_ask=True)
confirm_custom_status = gajim.config.get('confirm_custom_status')
if confirm_custom_status == 'no':
send_it()
return
- pritext = _('You are about to send a custom status. Are you sure you want'
- ' to continue?')
+ pritext = _('You are about to send a custom status. Are you sure you '
+ 'want to continue?')
sectext = _('This contact will temporarily see you as %(status)s, '
- 'but only until you change your status. Then he or she will see your '
- 'global status.') % {'status': show}
- dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
- _('Do _not ask me again'), on_response_ok=send_it)
+ 'but only until you change your status. Then he or she will see '
+ 'your global status.') % {'status': show}
+ dialogs.ConfirmationDialogCheck(pritext, sectext,
+ _('_Do not ask me again'), on_response_ok=send_it)
def on_status_combobox_changed(self, widget):
"""
@@ -3365,29 +3411,32 @@ class RosterWindow:
accounts = gajim.connections.keys()
if len(accounts) == 0:
dialogs.ErrorDialog(_('No account available'),
- _('You must create an account before you can chat with other contacts.'))
+ _('You must create an account before you can chat with other '
+ 'contacts.'))
self.update_status_combobox()
return
status = model[active][2].decode('utf-8')
- statuses_unified = helpers.statuses_unified() # status "desync'ed" or not
+ # status "desync'ed" or not
+ statuses_unified = helpers.statuses_unified()
if (active == 7 and statuses_unified) or (active == 9 and \
not statuses_unified):
# 'Change status message' selected:
# do not change show, just show change status dialog
- status = model[self.previous_status_combobox_active][2].decode('utf-8')
+ status = model[self.previous_status_combobox_active][2].decode(
+ 'utf-8')
def on_response(message, pep_dict):
if message is not None: # None if user pressed Cancel
for account in accounts:
if not gajim.config.get_per('accounts', account,
- 'sync_with_global_status'):
+ 'sync_with_global_status'):
continue
current_show = gajim.SHOW_LIST[
- gajim.connections[account].connected]
+ gajim.connections[account].connected]
self.send_status(account, current_show, message)
self.send_pep(account, pep_dict)
self.combobox_callback_active = False
self.status_combobox.set_active(
- self.previous_status_combobox_active)
+ self.previous_status_combobox_active)
self.combobox_callback_active = True
dialogs.ChangeStatusMessageDialog(on_response, status)
return
@@ -3408,14 +3457,14 @@ class RosterWindow:
'sync_with_global_status'):
global_sync_accounts.append(acct)
global_sync_connected_accounts = \
- gajim.get_number_of_connected_accounts(global_sync_accounts)
+ gajim.get_number_of_connected_accounts(global_sync_accounts)
for account in accounts:
if not gajim.config.get_per('accounts', account,
'sync_with_global_status'):
continue
# we are connected (so we wanna change show and status)
- # or no account is connected and we want to connect with new show
- # and status
+ # or no account is connected and we want to connect with new
+ # show and status
if not global_sync_connected_accounts > 0 or \
gajim.connections[account].connected > 0:
@@ -3426,9 +3475,10 @@ class RosterWindow:
if status == 'invisible':
bug_user = False
for account in accounts:
- if connected_accounts < 1 or gajim.account_is_connected(account):
+ if connected_accounts < 1 or gajim.account_is_connected(
+ account):
if not gajim.config.get_per('accounts', account,
- 'sync_with_global_status'):
+ 'sync_with_global_status'):
continue
# We're going to change our status to invisible
if self.connected_rooms(account):
@@ -3442,11 +3492,11 @@ class RosterWindow:
self.update_status_combobox()
dialogs.ConfirmationDialog(
- _('You are participating in one or more group chats'),
- _('Changing your status to invisible will result in '
- 'disconnection from those group chats. Are you sure you want to '
- 'go invisible?'), on_reponse_ok=on_ok,
- on_response_cancel=on_cancel)
+ _('You are participating in one or more group chats'),
+ _('Changing your status to invisible will result in '
+ 'disconnection from those group chats. Are you sure you '
+ 'want to go invisible?'), on_reponse_ok=on_ok,
+ on_response_cancel=on_cancel)
return
self.get_status_message(status, on_continue)
@@ -3455,7 +3505,8 @@ class RosterWindow:
if 'preferences' in gajim.interface.instances:
gajim.interface.instances['preferences'].window.present()
else:
- gajim.interface.instances['preferences'] = config.PreferencesWindow()
+ gajim.interface.instances['preferences'] = config.PreferencesWindow(
+ )
def on_plugins_menuitem_activate(self, widget):
if gajim.interface.instances.has_key('plugins'):
@@ -3500,7 +3551,7 @@ class RosterWindow:
gajim.interface.instances[account]['pep_services'].window.present()
else:
gajim.interface.instances[account]['pep_services'] = \
- config.ManagePEPServicesWindow(account)
+ config.ManagePEPServicesWindow(account)
def on_add_new_contact(self, widget, account):
dialogs.AddNewContactWindow(account)
@@ -3512,14 +3563,14 @@ class RosterWindow:
invisible_show = gajim.SHOW_LIST.index('invisible')
if gajim.connections[account].connected == invisible_show:
dialogs.ErrorDialog(_('You cannot join a group chat while you are '
- 'invisible'))
+ 'invisible'))
return
if 'join_gc' in gajim.interface.instances[account]:
gajim.interface.instances[account]['join_gc'].window.present()
else:
try:
gajim.interface.instances[account]['join_gc'] = \
- dialogs.JoinGroupchatWindow(account)
+ dialogs.JoinGroupchatWindow(account)
except GajimGeneralException:
pass
@@ -3531,7 +3582,7 @@ class RosterWindow:
def on_faq_menuitem_activate(self, widget):
helpers.launch_browser_mailer('url',
- 'http://trac.gajim.org/wiki/GajimFaq')
+ 'http://trac.gajim.org/wiki/GajimFaq')
def on_features_menuitem_activate(self, widget):
features_window.FeaturesWindow()
@@ -3557,7 +3608,7 @@ class RosterWindow:
gajim.interface.instances['logs'].window.present()
else:
gajim.interface.instances['logs'] = history_window.\
- HistoryWindow()
+ HistoryWindow()
def on_show_transports_menuitem_activate(self, widget):
gajim.config.set('show_transports_group', widget.get_active())
@@ -3572,7 +3623,8 @@ class RosterWindow:
def on_execute_command(self, widget, contact, account, resource=None):
"""
Execute command. Full JID needed; if it is other contact, resource is
- necessary. Widget is unnecessary, only to be able to make this a callback
+ necessary. Widget is unnecessary, only to be able to make this a
+ callback
"""
jid = contact.jid
if resource is not None:
@@ -3612,7 +3664,8 @@ class RosterWindow:
not gajim.config.get('quit_on_roster_x_button'):
self.tooltip.hide_tooltip()
self.window.hide()
- elif event.state & gtk.gdk.CONTROL_MASK and event.keyval == gtk.keysyms.i:
+ elif event.state & gtk.gdk.CONTROL_MASK and event.keyval == \
+ gtk.keysyms.i:
treeselection = self.tree.get_selection()
model, list_of_paths = treeselection.get_selected_rows()
for path in list_of_paths:
@@ -3620,9 +3673,11 @@ class RosterWindow:
if type_ in ('contact', 'agent'):
jid = model[path][C_JID].decode('utf-8')
account = model[path][C_ACCOUNT].decode('utf-8')
- contact = gajim.contacts.get_first_contact_from_jid(account, jid)
+ contact = gajim.contacts.get_first_contact_from_jid(account,
+ jid)
self.on_info(widget, contact, account)
- elif event.state & gtk.gdk.CONTROL_MASK and event.keyval == gtk.keysyms.h:
+ elif event.state & gtk.gdk.CONTROL_MASK and event.keyval == \
+ gtk.keysyms.h:
treeselection = self.tree.get_selection()
model, list_of_paths = treeselection.get_selected_rows()
if len(list_of_paths) != 1:
@@ -3632,7 +3687,8 @@ class RosterWindow:
if type_ in ('contact', 'agent'):
jid = model[path][C_JID].decode('utf-8')
account = model[path][C_ACCOUNT].decode('utf-8')
- contact = gajim.contacts.get_first_contact_from_jid(account, jid)
+ contact = gajim.contacts.get_first_contact_from_jid(account,
+ jid)
self.on_history(widget, contact, account)
def on_roster_window_popup_menu(self, widget):
@@ -3704,7 +3760,7 @@ class RosterWindow:
resource = contact.resource
gajim.interface.on_open_chat_window(None, contact, account, \
- resource=resource, session=session)
+ resource=resource, session=session)
def on_roster_treeview_row_activated(self, widget, path, col=0):
"""
@@ -3730,8 +3786,8 @@ class RosterWindow:
type_ = model[titer][C_TYPE]
if type_ == 'group':
group = model[titer][C_JID].decode('utf-8')
- child_model[child_iter][C_IMG] = gajim.interface.jabber_state_images[
- '16']['opened']
+ child_model[child_iter][C_IMG] = \
+ gajim.interface.jabber_state_images['16']['opened']
for account in accounts:
if group in gajim.groups[account]: # This account has this group
gajim.groups[account][group]['expand'] = True
@@ -3739,9 +3795,9 @@ class RosterWindow:
self.collapsed_rows.remove(account + group)
for contact in gajim.contacts.iter_contacts(account):
jid = contact.jid
- if group in contact.groups and gajim.contacts.is_big_brother(
- account, jid, accounts) and account + group + jid \
- not in self.collapsed_rows:
+ if group in contact.groups and \
+ gajim.contacts.is_big_brother(account, jid, accounts) and \
+ account + group + jid not in self.collapsed_rows:
titers = self._get_contact_iter(jid, account)
for titer in titers:
path = model.get_path(titer)
@@ -3768,7 +3824,7 @@ class RosterWindow:
self.collapsed_rows.remove(account + group + jid)
family = gajim.contacts.get_metacontacts_family(account, jid)
nearby_family = \
- self._get_nearby_family_and_big_brother(family, account)[0]
+ self._get_nearby_family_and_big_brother(family, account)[0]
# Redraw all brothers to show pending events
for data in nearby_family:
self.draw_contact(data['jid'], data['account'])
@@ -3791,8 +3847,8 @@ class RosterWindow:
type_ = model[titer][C_TYPE]
if type_ == 'group':
- child_model[child_iter][C_IMG] = gajim.interface.jabber_state_images[
- '16']['closed']
+ child_model[child_iter][C_IMG] = gajim.interface.\
+ jabber_state_images['16']['closed']
group = model[titer][C_JID].decode('utf-8')
for account in accounts:
if group in gajim.groups[account]: # This account has this group
@@ -3868,7 +3924,8 @@ class RosterWindow:
# if len(self._last_selected_contact):
# # update unselected rows
# for (jid, account) in self._last_selected_contact:
-# gobject.idle_add(self.draw_contact, jid, account)
+# gobject.idle_add(self.draw_contact, jid,
+# account)
# self._last_selected_contact = []
# if len(list_of_paths) == 0:
# return
@@ -3886,7 +3943,7 @@ class RosterWindow:
server_jid = gajim.config.get_per('accounts', account, 'hostname')
if server_jid in gajim.interface.instances[account]['disco']:
gajim.interface.instances[account]['disco'][server_jid].\
- window.present()
+ window.present()
else:
try:
# Object will add itself to the window dict
@@ -3942,7 +3999,8 @@ class RosterWindow:
### Drag and Drop handling
################################################################################
- def drag_data_get_data(self, treeview, context, selection, target_id, etime):
+ def drag_data_get_data(self, treeview, context, selection, target_id,
+ etime):
model, list_of_paths = self.tree.get_selection().get_selected_rows()
if len(list_of_paths) != 1:
return
@@ -3965,12 +4023,13 @@ class RosterWindow:
def on_drop_in_contact(self, widget, account_source, c_source, account_dest,
c_dest, was_big_brother, context, etime):
- if not gajim.connections[account_source].private_storage_supported or not\
- gajim.connections[account_dest].private_storage_supported:
- dialogs.WarningDialog(_('Metacontacts storage not supported by your '
- 'server'),
- _('Your server does not support storing metacontacts information. '
- 'So those information will not be saved on next reconnection.'))
+ if not gajim.connections[account_source].private_storage_supported or \
+ not gajim.connections[account_dest].private_storage_supported:
+ dialogs.WarningDialog(_('Metacontacts storage not supported by '
+ 'your server'),
+ _('Your server does not support storing metacontacts '
+ 'information. So those information will not be saved on next '
+ 'reconnection.'))
def merge_contacts(is_checked=None):
contacts = 0
@@ -3983,10 +4042,11 @@ class RosterWindow:
# We might have dropped on a metacontact.
# Remove it and readd later with updated family info
dest_family = gajim.contacts.get_metacontacts_family(account_dest,
- c_dest.jid)
+ c_dest.jid)
if dest_family:
self._remove_metacontact_family(dest_family, account_dest)
- source_family = gajim.contacts.get_metacontacts_family(account_source, c_source.jid)
+ source_family = gajim.contacts.get_metacontacts_family(
+ account_source, c_source.jid)
if dest_family == source_family:
n = contacts = len(dest_family)
for tag in source_family:
@@ -4008,7 +4068,8 @@ class RosterWindow:
# We have got little brothers. Readd them all
self._remove_metacontact_family(old_family, account_source)
else:
- # We are only a litle brother. Simply remove us from our big brother
+ # We are only a litle brother. Simply remove us from our big
+ # brother
if self._get_contact_iter(c_source.jid, account_source):
# When we have been in the group before.
# Do not try to remove us again
@@ -4027,20 +4088,21 @@ class RosterWindow:
_account = data['account']
_jid = data['jid']
- _contact = gajim.contacts.get_first_contact_from_jid(_account, _jid)
+ _contact = gajim.contacts.get_first_contact_from_jid(_account,
+ _jid)
if not _contact:
# One of the metacontacts may be not connected.
continue
_contact.groups = c_dest.groups[:]
gajim.contacts.add_metacontact(account_dest, c_dest.jid,
- _account, _contact.jid, contacts)
+ _account, _contact.jid, contacts)
gajim.connections[account_source].update_contact(_contact.jid,
- _contact.name, _contact.groups)
+ _contact.name, _contact.groups)
# Re-add all and update GUI
new_family = gajim.contacts.get_metacontacts_family(account_source,
- c_source.jid)
+ c_source.jid)
brothers = self._add_metacontact_family(new_family, account_source)
for c, acc in brothers:
@@ -4057,30 +4119,30 @@ class RosterWindow:
if confirm_metacontacts == 'no':
merge_contacts()
return
- pritext = _('You are about to create a metacontact. Are you sure you want'
- ' to continue?')
+ pritext = _('You are about to create a metacontact. Are you sure you '
+ 'want to continue?')
sectext = _('Metacontacts are a way to regroup several contacts in one '
- 'line. Generally it is used when the same person has several Jabber '
- 'accounts or transport accounts.')
+ 'line. Generally it is used when the same person has several '
+ 'Jabber accounts or transport accounts.')
dlg = dialogs.ConfirmationDialogCheck(pritext, sectext,
- _('Do _not ask me again'), on_response_ok=merge_contacts)
+ _('_Do not ask me again'), on_response_ok=merge_contacts)
if not confirm_metacontacts: # First time we see this window
dlg.checkbutton.set_active(True)
-
def on_drop_in_group(self, widget, account, c_source, grp_dest,
is_big_brother, context, etime, grp_source = None):
if is_big_brother:
# add whole metacontact to new group
- self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
+ self.add_contact_to_groups(c_source.jid, account, [grp_dest, ])
# remove afterwards so the contact is not moved to General in the
# meantime
if grp_dest != grp_source:
- self.remove_contact_from_groups(c_source.jid, account, [grp_source])
+ self.remove_contact_from_groups(c_source.jid, account,
+ [grp_source])
else:
# Normal contact or little brother
family = gajim.contacts.get_metacontacts_family(account,
- c_source.jid)
+ c_source.jid)
if family:
# Little brother
# Remove whole family. Remove us from the family.
@@ -4096,21 +4158,20 @@ class RosterWindow:
self.add_contact(data['jid'], data['account'])
break
- self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
+ self.add_contact_to_groups(c_source.jid, account, [grp_dest, ])
else:
# Normal contact
- self.add_contact_to_groups(c_source.jid, account, [grp_dest,])
- # remove afterwards so the contact is not moved to General in the
- # meantime
+ self.add_contact_to_groups(c_source.jid, account, [grp_dest, ])
+ # remove afterwards so the contact is not moved to General in
+ # the meantime
if grp_dest != grp_source:
self.remove_contact_from_groups(c_source.jid, account,
- [grp_source])
+ [grp_source])
if context.action in (gtk.gdk.ACTION_MOVE, gtk.gdk.ACTION_COPY):
context.finish(True, True, etime)
-
def drag_drop(self, treeview, context, x, y, timestamp):
target_list = treeview.drag_dest_get_target_list()
target = treeview.drag_dest_find_target(context, target_list)
@@ -4134,7 +4195,8 @@ class RosterWindow:
and path_dest[1] == 0: # dropped before the first group
return
if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 2:
- # dropped before a group: we drop it in the previous group every time
+ # dropped before a group: we drop it in the previous group every
+ # time
path_dest = (path_dest[0], path_dest[1]-1)
# destination: the row something got dropped on
iter_dest = model.get_iter(path_dest)
@@ -4155,8 +4217,8 @@ class RosterWindow:
return
if type_dest != 'contact':
return
- c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest,
- jid_dest)
+ c_dest = gajim.contacts.get_contact_with_highest_priority(
+ account_dest, jid_dest)
if not c_dest.supports(NS_FILE):
return
uri = data.strip()
@@ -4177,23 +4239,24 @@ class RosterWindow:
dialogs.ErrorDialog(_('Invalid file URI:'), '\n'.join(bad_uris))
return
def _on_send_files(account, jid, uris):
- c = gajim.contacts.get_contact_with_highest_priority(account, jid)
+ c = gajim.contacts.get_contact_with_highest_priority(account,
+ jid)
for uri in uris:
path = helpers.get_file_path_from_dnd_dropped_uri(uri)
if os.path.isfile(path): # is it file?
gajim.interface.instances['file_transfers'].send_file(
- account, c, path)
+ account, c, path)
# Popup dialog to confirm sending
prim_text = 'Send file?'
sec_text = i18n.ngettext('Do you want to send this file to %s:',
- 'Do you want to send these files to %s:', nb_uri) %\
- c_dest.get_shown_name()
+ 'Do you want to send these files to %s:', nb_uri) %\
+ c_dest.get_shown_name()
for uri in uri_splitted:
path = helpers.get_file_path_from_dnd_dropped_uri(uri)
sec_text += '\n' + os.path.basename(path)
dialog = dialogs.NonModalConfirmationDialog(prim_text, sec_text,
- on_response_ok = (_on_send_files, account_dest, jid_dest,
- uri_splitted))
+ on_response_ok=(_on_send_files, account_dest, jid_dest,
+ uri_splitted))
dialog.popup()
return
@@ -4239,7 +4302,7 @@ class RosterWindow:
return
jid_source = data.decode('utf-8')
c_source = gajim.contacts.get_contact_with_highest_priority(
- account_source, jid_source)
+ account_source, jid_source)
# Get destination group
grp_dest = None
@@ -4260,10 +4323,10 @@ class RosterWindow:
# contact drop somewhere in or on a foreign account
if (type_dest == 'account' or not self.regroup) and \
- account_source != account_dest:
+ account_source != account_dest:
# add to account in specified group
dialogs.AddNewContactWindow(account=account_dest, jid=jid_source,
- user_nick=c_source.name, group=grp_dest)
+ user_nick=c_source.name, group=grp_dest)
return
# we may not add contacts from special_groups
@@ -4273,14 +4336,14 @@ class RosterWindow:
# Is the contact we drag a meta contact?
accounts = (self.regroup and gajim.contacts.get_accounts()) or \
account_source
- is_big_brother = gajim.contacts.is_big_brother(account_source, jid_source,
- accounts)
+ is_big_brother = gajim.contacts.is_big_brother(account_source,
+ jid_source, accounts)
drop_in_middle_of_meta = False
if type_dest == 'contact':
if position == gtk.TREE_VIEW_DROP_BEFORE and len(path_dest) == 4:
drop_in_middle_of_meta = True
- if position == gtk.TREE_VIEW_DROP_AFTER and (len(path_dest) == 4 or \
+ if position == gtk.TREE_VIEW_DROP_AFTER and (len(path_dest) == 4 or\
self.modelfilter.iter_has_child(iter_dest)):
drop_in_middle_of_meta = True
# Contact drop on group row or between two contacts that are
@@ -4288,14 +4351,14 @@ class RosterWindow:
if (type_dest == 'group' or position in (gtk.TREE_VIEW_DROP_BEFORE,
gtk.TREE_VIEW_DROP_AFTER)) and not drop_in_middle_of_meta:
self.on_drop_in_group(None, account_source, c_source, grp_dest,
- is_big_brother, context, etime, grp_source)
+ is_big_brother, context, etime, grp_source)
return
# Contact drop on another contact, make meta contacts
if position == gtk.TREE_VIEW_DROP_INTO_OR_AFTER or \
position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE or drop_in_middle_of_meta:
- c_dest = gajim.contacts.get_contact_with_highest_priority(account_dest,
- jid_dest)
+ c_dest = gajim.contacts.get_contact_with_highest_priority(
+ account_dest, jid_dest)
if not c_dest:
# c_dest is None if jid_dest doesn't belong to account
return
@@ -4303,13 +4366,13 @@ class RosterWindow:
item = gtk.MenuItem(_('Send %s to %s') % (c_source.get_shown_name(),
c_dest.get_shown_name()))
item.connect('activate', self.on_drop_rosterx, account_source,
- c_source, account_dest, c_dest, is_big_brother, context, etime)
+ c_source, account_dest, c_dest, is_big_brother, context, etime)
menu.append(item)
item = gtk.MenuItem(_('Make %s and %s metacontacts') % (
c_source.get_shown_name(), c_dest.get_shown_name()))
item.connect('activate', self.on_drop_in_contact, account_source,
- c_source, account_dest, c_dest, is_big_brother, context, etime)
+ c_source, account_dest, c_dest, is_big_brother, context, etime)
menu.append(item)
@@ -4333,7 +4396,8 @@ class RosterWindow:
transport = gajim.get_transport_name_from_jid(jid)
if transport and size in self.transports_state_images:
if transport not in self.transports_state_images[size]:
- # we don't have iconset for this transport loaded yet. Let's do it
+ # we don't have iconset for this transport loaded yet. Let's do
+ # it
self.make_transport_state_images(transport)
if transport in self.transports_state_images[size] and \
icon_name in self.transports_state_images[size][transport]:
@@ -4346,18 +4410,20 @@ class RosterWindow:
"""
if gajim.config.get('use_transports_iconsets'):
folder = os.path.join(helpers.get_transport_path(transport),
- '16x16')
+ '16x16')
pixo, pixc = gtkgui_helpers.load_icons_meta()
self.transports_state_images['opened'][transport] = \
- gtkgui_helpers.load_iconset(folder, pixo, transport=True)
+ gtkgui_helpers.load_iconset(folder, pixo, transport=True)
self.transports_state_images['closed'][transport] = \
- gtkgui_helpers.load_iconset(folder, pixc, transport=True)
- folder = os.path.join(helpers.get_transport_path(transport), '32x32')
+ gtkgui_helpers.load_iconset(folder, pixc, transport=True)
+ folder = os.path.join(helpers.get_transport_path(transport),
+ '32x32')
self.transports_state_images['32'][transport] = \
- gtkgui_helpers.load_iconset(folder, transport=True)
- folder = os.path.join(helpers.get_transport_path(transport), '16x16')
+ gtkgui_helpers.load_iconset(folder, transport=True)
+ folder = os.path.join(helpers.get_transport_path(transport),
+ '16x16')
self.transports_state_images['16'][transport] = \
- gtkgui_helpers.load_iconset(folder, transport=True)
+ gtkgui_helpers.load_iconset(folder, transport=True)
def update_jabber_state_images(self):
# Update the roster
@@ -4369,8 +4435,8 @@ class RosterWindow:
if model[titer][2] != '':
# If it's not change status message iter
# eg. if it has show parameter not ''
- model[titer][1] = gajim.interface.jabber_state_images['16'][model[
- titer][2]]
+ model[titer][1] = gajim.interface.jabber_state_images['16'][
+ model[titer][2]]
titer = model.iter_next(titer)
# Update the systray
if gajim.interface.systray_enabled:
@@ -4390,10 +4456,10 @@ class RosterWindow:
return
if not self.regroup:
show = gajim.SHOW_LIST[status]
- else: # accounts merged
+ else: # accounts merged
show = helpers.get_global_show()
self.model[child_iterA][C_IMG] = gajim.interface.jabber_state_images[
- '16'][show]
+ '16'][show]
################################################################################
### Style and theme related methods
@@ -4417,9 +4483,9 @@ class RosterWindow:
# Count events in roster title only if we don't auto open them
if not helpers.allow_popup_window(account):
nb_unread += gajim.events.get_nb_events(['chat', 'normal',
- 'file-request', 'file-error', 'file-completed',
- 'file-request-error', 'file-send-error', 'file-stopped',
- 'printed_chat'], account)
+ 'file-request', 'file-error', 'file-completed',
+ 'file-request-error', 'file-send-error', 'file-stopped',
+ 'printed_chat'], account)
if nb_unread > 1:
start = '[' + str(nb_unread) + '] '
elif nb_unread == 1:
@@ -4466,7 +4532,7 @@ class RosterWindow:
"""
for contact in self._iter_contact_rows():
self.draw_contact(contact[C_JID].decode('utf-8'),
- contact[C_ACCOUNT].decode('utf-8'))
+ contact[C_ACCOUNT].decode('utf-8'))
def set_renderer_color(self, renderer, style, set_background=True):
"""
@@ -4490,7 +4556,8 @@ class RosterWindow:
elif type_ == 'group':
self._set_group_row_background_color(renderer)
renderer.set_property('xalign', 0.2)
- elif type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
+ elif type_:
+ # prevent type_ = None, see http://trac.gajim.org/ticket/2534
if not model[titer][C_JID] or not model[titer][C_ACCOUNT]:
# This can append when at the moment we add the row
return
@@ -4517,7 +4584,7 @@ class RosterWindow:
else:
self.set_renderer_color(renderer, gtk.STATE_ACTIVE, False)
renderer.set_property('font',
- gtkgui_helpers.get_theme_font_for_option(theme, 'accountfont'))
+ gtkgui_helpers.get_theme_font_for_option(theme, 'accountfont'))
renderer.set_property('xpad', 0)
renderer.set_property('width', 3)
self._set_account_row_background_color(renderer)
@@ -4528,10 +4595,11 @@ class RosterWindow:
else:
self.set_renderer_color(renderer, gtk.STATE_PRELIGHT, False)
renderer.set_property('font',
- gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
+ gtkgui_helpers.get_theme_font_for_option(theme, 'groupfont'))
renderer.set_property('xpad', 4)
self._set_group_row_background_color(renderer)
- elif type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
+ elif type_:
+ # prevent type_ = None, see http://trac.gajim.org/ticket/2534
if not model[titer][C_JID] or not model[titer][C_ACCOUNT]:
# This can append when at the moment we add the row
return
@@ -4539,20 +4607,22 @@ class RosterWindow:
account = model[titer][C_ACCOUNT].decode('utf-8')
color = None
if type_ == 'groupchat':
- ctrl = gajim.interface.minimized_controls[account].get(jid, None)
+ ctrl = gajim.interface.minimized_controls[account].get(jid,
+ None)
if ctrl and ctrl.attention_flag:
color = gajim.config.get_per('themes', theme,
- 'state_muc_directed_msg_color')
+ 'state_muc_directed_msg_color')
renderer.set_property('foreground', 'red')
if not color:
- color = gajim.config.get_per('themes', theme, 'contacttextcolor')
+ color = gajim.config.get_per('themes', theme,
+ 'contacttextcolor')
if color:
renderer.set_property('foreground', color)
else:
renderer.set_property('foreground', None)
self._set_contact_row_background_color(renderer, jid, account)
renderer.set_property('font',
- gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
+ gtkgui_helpers.get_theme_font_for_option(theme, 'contactfont'))
parent_iter = model.iter_parent(titer)
if model[parent_iter][C_TYPE] == 'contact':
renderer.set_property('xpad', 16)
@@ -4560,11 +4630,10 @@ class RosterWindow:
renderer.set_property('xpad', 8)
def _fill_pep_pixbuf_renderer(self, column, renderer, model, titer,
- data=None):
+ data=None):
"""
When a row is added, draw the respective pep icon
"""
- theme = gajim.config.get('roster_theme')
type_ = model[titer][C_TYPE]
# allocate space for the icon only if needed
@@ -4584,8 +4653,8 @@ class RosterWindow:
account = model[titer][C_ACCOUNT].decode('utf-8')
self._set_contact_row_background_color(renderer, jid, account)
- def _fill_avatar_pixbuf_renderer(self, column, renderer, model, titer, data
- = None):
+ def _fill_avatar_pixbuf_renderer(self, column, renderer, model, titer,
+ data=None):
"""
When a row is added, set properties for avatar renderer
"""
@@ -4598,7 +4667,8 @@ class RosterWindow:
if model[titer][C_AVATAR_PIXBUF] or \
gajim.config.get('avatar_position_in_roster') == 'left':
renderer.set_property('visible', True)
- if type_: # prevent type_ = None, see http://trac.gajim.org/ticket/2534
+ if type_:
+ # prevent type_ = None, see http://trac.gajim.org/ticket/2534
if not model[titer][C_JID] or not model[titer][C_ACCOUNT]:
# This can append at the moment we add the row
return
@@ -4609,12 +4679,14 @@ class RosterWindow:
renderer.set_property('visible', False)
if gajim.config.get('avatar_position_in_roster') == 'left':
- renderer.set_property('width', gajim.config.get('roster_avatar_width'))
+ renderer.set_property('width', gajim.config.get(
+ 'roster_avatar_width'))
renderer.set_property('xalign', 0.5)
else:
renderer.set_property('xalign', 1) # align pixbuf to the right
- def _fill_padlock_pixbuf_renderer(self, column, renderer, model, titer, data=None):
+ def _fill_padlock_pixbuf_renderer(self, column, renderer, model, titer,
+ data=None):
"""
When a row is added, set properties for padlock renderer
"""
@@ -4642,7 +4714,7 @@ class RosterWindow:
'just_connected_bg_color'))
elif jid in gajim.to_be_removed[account]:
renderer.set_property('cell-background', gajim.config.get(
- 'just_disconnected_bg_color'))
+ 'just_disconnected_bg_color'))
else:
color = gajim.config.get_per('themes', theme, 'contactbgcolor')
renderer.set_property('cell-background', color if color else None)
@@ -4673,7 +4745,8 @@ class RosterWindow:
muc_icon = gtkgui_helpers.load_icon('muc_active')
if muc_icon:
join_gc_menuitem.set_image(muc_icon)
- add_new_contact_menuitem = self.xml.get_object('add_new_contact_menuitem')
+ add_new_contact_menuitem = self.xml.get_object(
+ 'add_new_contact_menuitem')
service_disco_menuitem = self.xml.get_object('service_disco_menuitem')
advanced_menuitem = self.xml.get_object('advanced_menuitem')
profile_avatar_menuitem = self.xml.get_object('profile_avatar_menuitem')
@@ -4682,32 +4755,33 @@ class RosterWindow:
for m in self.advanced_menus:
m.destroy()
- # make it sensitive. it is insensitive only if no accounts are *available*
+ # make it sensitive. it is insensitive only if no accounts are
+ # *available*
advanced_menuitem.set_sensitive(True)
if self.add_new_contact_handler_id:
add_new_contact_menuitem.handler_disconnect(
- self.add_new_contact_handler_id)
+ self.add_new_contact_handler_id)
self.add_new_contact_handler_id = None
if self.service_disco_handler_id:
service_disco_menuitem.handler_disconnect(
- self.service_disco_handler_id)
+ self.service_disco_handler_id)
self.service_disco_handler_id = None
if self.new_chat_menuitem_handler_id:
new_chat_menuitem.handler_disconnect(
- self.new_chat_menuitem_handler_id)
+ self.new_chat_menuitem_handler_id)
self.new_chat_menuitem_handler_id = None
if self.single_message_menuitem_handler_id:
single_message_menuitem.handler_disconnect(
- self.single_message_menuitem_handler_id)
+ self.single_message_menuitem_handler_id)
self.single_message_menuitem_handler_id = None
if self.profile_avatar_menuitem_handler_id:
profile_avatar_menuitem.handler_disconnect(
- self.profile_avatar_menuitem_handler_id)
+ self.profile_avatar_menuitem_handler_id)
self.profile_avatar_menuitem_handler_id = None
# remove the existing submenus
@@ -4723,7 +4797,7 @@ class RosterWindow:
if self.have_new_chat_accel:
ag = gtk.accel_groups_from_object(self.window)[0]
new_chat_menuitem.remove_accelerator(ag, gtk.keysyms.n,
- gtk.gdk.CONTROL_MASK)
+ gtk.gdk.CONTROL_MASK)
self.have_new_chat_accel = False
gc_sub_menu = gtk.Menu() # gc is always a submenu
@@ -4745,10 +4819,10 @@ class RosterWindow:
# new chat
new_chat_item = gtk.MenuItem(_('using account %s') % account,
- False)
+ False)
new_chat_sub_menu.append(new_chat_item)
new_chat_item.connect('activate',
- self.on_new_chat_menuitem_activate, account)
+ self.on_new_chat_menuitem_activate, account)
new_chat_menuitem.set_submenu(new_chat_sub_menu)
new_chat_sub_menu.show_all()
@@ -4759,15 +4833,16 @@ class RosterWindow:
# new chat
if not self.new_chat_menuitem_handler_id:
self.new_chat_menuitem_handler_id = new_chat_menuitem.\
- connect('activate', self.on_new_chat_menuitem_activate,
- account)
+ connect('activate',
+ self.on_new_chat_menuitem_activate, account)
break
# menu items that don't apply to zeroconf connections
if connected_accounts == 1 or (connected_accounts == 2 and \
gajim.zeroconf_is_connected()):
- # only one 'real' (non-zeroconf) account is connected, don't need submenus
+ # only one 'real' (non-zeroconf) account is connected, don't need
+ # submenus
for account in accounts_list:
if gajim.account_is_connected(account) and \
@@ -4779,14 +4854,14 @@ class RosterWindow:
gc_sub_menu.show_all()
# add
if not self.add_new_contact_handler_id:
- self.add_new_contact_handler_id =\
- add_new_contact_menuitem.connect(
- 'activate', self.on_add_new_contact, account)
+ self.add_new_contact_handler_id = \
+ add_new_contact_menuitem.connect(
+ 'activate', self.on_add_new_contact, account)
# disco
if not self.service_disco_handler_id:
self.service_disco_handler_id = service_disco_menuitem.\
- connect('activate',
- self.on_service_disco_menuitem_activate, account)
+ connect('activate',
+ self.on_service_disco_menuitem_activate, account)
# single message
if not self.single_message_menuitem_handler_id:
@@ -4798,7 +4873,8 @@ class RosterWindow:
if not self.have_new_chat_accel:
ag = gtk.accel_groups_from_object(self.window)[0]
new_chat_menuitem.add_accelerator('activate', ag,
- gtk.keysyms.n, gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
+ gtk.keysyms.n, gtk.gdk.CONTROL_MASK,
+ gtk.ACCEL_VISIBLE)
self.have_new_chat_accel = True
break # No other account connected
@@ -4815,11 +4891,11 @@ class RosterWindow:
continue
# single message
- single_message_item = gtk.MenuItem(_('using account %s') % account,
- False)
+ single_message_item = gtk.MenuItem(_('using account %s') % \
+ account, False)
single_message_sub_menu.append(single_message_item)
single_message_item.connect('activate',
- self.on_send_single_message_menuitem_activate, account)
+ self.on_send_single_message_menuitem_activate, account)
# join gc
if gajim.connections[account].private_storage_supported:
@@ -4836,10 +4912,11 @@ class RosterWindow:
add_item.connect('activate', self.on_add_new_contact, account)
# disco
- disco_item = gtk.MenuItem(_('using %s account') % account, False)
+ disco_item = gtk.MenuItem(_('using %s account') % account,
+ False)
disco_sub_menu.append(disco_item)
disco_item.connect('activate',
- self.on_service_disco_menuitem_activate, account)
+ self.on_service_disco_menuitem_activate, account)
single_message_menuitem.set_submenu(single_message_sub_menu)
single_message_sub_menu.show_all()
@@ -4851,22 +4928,22 @@ class RosterWindow:
if connected_accounts == 0:
# no connected accounts, make the menuitems insensitive
- for item in (new_chat_menuitem, join_gc_menuitem,\
- add_new_contact_menuitem, service_disco_menuitem,\
- single_message_menuitem):
+ for item in (new_chat_menuitem, join_gc_menuitem,
+ add_new_contact_menuitem, service_disco_menuitem,
+ single_message_menuitem):
item.set_sensitive(False)
else: # we have one or more connected accounts
for item in (new_chat_menuitem, join_gc_menuitem,
- add_new_contact_menuitem, service_disco_menuitem,
- single_message_menuitem):
+ add_new_contact_menuitem, service_disco_menuitem,
+ single_message_menuitem):
item.set_sensitive(True)
# disable some fields if only local account is there
if connected_accounts == 1:
for account in gajim.connections:
if gajim.account_is_connected(account) and \
- gajim.connections[account].is_zeroconf:
+ gajim.connections[account].is_zeroconf:
for item in (join_gc_menuitem, add_new_contact_menuitem,
- service_disco_menuitem, single_message_menuitem):
+ service_disco_menuitem, single_message_menuitem):
item.set_sensitive(False)
# Manage GC bookmarks
@@ -4875,7 +4952,7 @@ class RosterWindow:
newitem = gtk.ImageMenuItem(_('_Manage Bookmarks...'))
img = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES,
- gtk.ICON_SIZE_MENU)
+ gtk.ICON_SIZE_MENU)
newitem.set_image(img)
newitem.connect('activate', self.on_manage_bookmarks_menuitem_activate)
gc_sub_menu.append(newitem)
@@ -4894,19 +4971,20 @@ class RosterWindow:
for account in connected_accounts_with_vcard:
# profile, avatar
profile_avatar_item = gtk.MenuItem(_('of account %s') % account,
- False)
+ False)
profile_avatar_sub_menu.append(profile_avatar_item)
profile_avatar_item.connect('activate',
- self.on_profile_avatar_menuitem_activate, account)
+ self.on_profile_avatar_menuitem_activate, account)
profile_avatar_menuitem.set_submenu(profile_avatar_sub_menu)
profile_avatar_sub_menu.show_all()
- elif len(connected_accounts_with_vcard) == 1: # user has only one account
+ elif len(connected_accounts_with_vcard) == 1:
+ # user has only one account
account = connected_accounts_with_vcard[0]
# profile, avatar
if not self.profile_avatar_menuitem_handler_id:
self.profile_avatar_menuitem_handler_id = \
- profile_avatar_menuitem.connect('activate',
- self.on_profile_avatar_menuitem_activate, account)
+ profile_avatar_menuitem.connect('activate',
+ self.on_profile_avatar_menuitem_activate, account)
if len(connected_accounts_with_vcard) == 0:
profile_avatar_menuitem.set_sensitive(False)
@@ -4918,8 +4996,8 @@ class RosterWindow:
advanced_menuitem.set_sensitive(False)
elif len(gajim.connections) == 1: # we have one acccount
account = gajim.connections.keys()[0]
- advanced_menuitem_menu = self.get_and_connect_advanced_menuitem_menu(
- account)
+ advanced_menuitem_menu = \
+ self.get_and_connect_advanced_menuitem_menu(account)
self.advanced_menus.append(advanced_menuitem_menu)
self.add_history_manager_menuitem(advanced_menuitem_menu)
@@ -4933,10 +5011,11 @@ class RosterWindow:
accounts.append(account)
accounts.sort()
for account in accounts:
- advanced_item = gtk.MenuItem(_('for account %s') % account, False)
+ advanced_item = gtk.MenuItem(_('for account %s') % account,
+ False)
advanced_sub_menu.append(advanced_item)
advanced_menuitem_menu = \
- self.get_and_connect_advanced_menuitem_menu(account)
+ self.get_and_connect_advanced_menuitem_menu(account)
self.advanced_menus.append(advanced_menuitem_menu)
advanced_item.set_submenu(advanced_menuitem_menu)
@@ -4960,15 +5039,18 @@ class RosterWindow:
status_menuitem = xml.get_object('status_menuitem')
start_chat_menuitem = xml.get_object('start_chat_menuitem')
- join_group_chat_menuitem = xml.get_object('join_group_chat_menuitem')
+ join_group_chat_menuitem = xml.get_object(
+ 'join_group_chat_menuitem')
muc_icon = gtkgui_helpers.load_icon('muc_active')
if muc_icon:
join_group_chat_menuitem.set_image(muc_icon)
- open_gmail_inbox_menuitem = xml.get_object('open_gmail_inbox_menuitem')
+ open_gmail_inbox_menuitem = xml.get_object(
+ 'open_gmail_inbox_menuitem')
add_contact_menuitem = xml.get_object('add_contact_menuitem')
service_discovery_menuitem = xml.get_object(
- 'service_discovery_menuitem')
- execute_command_menuitem = xml.get_object('execute_command_menuitem')
+ 'service_discovery_menuitem')
+ execute_command_menuitem = xml.get_object(
+ 'execute_command_menuitem')
edit_account_menuitem = xml.get_object('edit_account_menuitem')
sub_menu = gtk.Menu()
status_menuitem.set_submenu(sub_menu)
@@ -4993,7 +5075,7 @@ class RosterWindow:
gtkgui_helpers.add_image_to_menuitem(item, 'gajim-kbd_input')
sub_menu.append(item)
item.connect('activate', self.on_change_status_message_activate,
- account)
+ account)
if gajim.connections[account].connected < 2:
item.set_sensitive(False)
@@ -5017,14 +5099,15 @@ class RosterWindow:
if not dbus_support.supported:
item.set_sensitive(False)
else:
- activ = gajim.config.get_per('accounts', account, opt_name)
+ activ = gajim.config.get_per('accounts', account,
+ opt_name)
item.set_active(activ)
item.connect('toggled', func, account)
add_item(_('Publish Tune'), 'publish_tune',
- self.on_publish_tune_toggled)
+ self.on_publish_tune_toggled)
add_item(_('Publish Location'), 'publish_location',
- self.on_publish_location_toggled)
+ self.on_publish_location_toggled)
pep_config = gtk.ImageMenuItem(_('Configure Services...'))
item = gtk.SeparatorMenuItem()
@@ -5032,9 +5115,9 @@ class RosterWindow:
pep_config.set_sensitive(True)
pep_submenu.append(pep_config)
pep_config.connect('activate',
- self.on_pep_services_menuitem_activate, account)
+ self.on_pep_services_menuitem_activate, account)
img = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES,
- gtk.ICON_SIZE_MENU)
+ gtk.ICON_SIZE_MENU)
pep_config.set_image(img)
else:
@@ -5045,21 +5128,22 @@ class RosterWindow:
open_gmail_inbox_menuitem.hide()
else:
open_gmail_inbox_menuitem.connect('activate',
- self.on_open_gmail_inbox, account)
+ self.on_open_gmail_inbox, account)
edit_account_menuitem.connect('activate', self.on_edit_account,
- account)
+ account)
add_contact_menuitem.connect('activate', self.on_add_new_contact,
- account)
+ account)
service_discovery_menuitem.connect('activate',
- self.on_service_disco_menuitem_activate, account)
+ self.on_service_disco_menuitem_activate, account)
hostname = gajim.config.get_per('accounts', account, 'hostname')
- contact = gajim.contacts.create_contact(jid=hostname, account=account) # Fake contact
+ contact = gajim.contacts.create_contact(jid=hostname,
+ account=account) # Fake contact
execute_command_menuitem.connect('activate',
- self.on_execute_command, contact, account)
+ self.on_execute_command, contact, account)
start_chat_menuitem.connect('activate',
- self.on_new_chat_menuitem_activate, account)
+ self.on_new_chat_menuitem_activate, account)
gc_sub_menu = gtk.Menu() # gc is always a submenu
join_group_chat_menuitem.set_submenu(gc_sub_menu)
@@ -5068,8 +5152,8 @@ class RosterWindow:
# make some items insensitive if account is offline
if gajim.connections[account].connected < 2:
for widget in (add_contact_menuitem, service_discovery_menuitem,
- join_group_chat_menuitem, execute_command_menuitem, pep_menuitem,
- start_chat_menuitem):
+ join_group_chat_menuitem, execute_command_menuitem,
+ pep_menuitem, start_chat_menuitem):
widget.set_sensitive(False)
else:
xml = gtkgui_helpers.get_gtk_builder('zeroconf_context_menu.ui')
@@ -5096,7 +5180,7 @@ class RosterWindow:
gtkgui_helpers.add_image_to_menuitem(item, 'gajim-kbd_input')
sub_menu.append(item)
item.connect('activate', self.on_change_status_message_activate,
- account)
+ account)
if gajim.connections[account].connected < 2:
item.set_sensitive(False)
@@ -5108,7 +5192,7 @@ class RosterWindow:
item.connect('activate', self.change_status, account, 'offline')
zeroconf_properties_menuitem.connect('activate',
- self.on_zeroconf_properties, account)
+ self.on_zeroconf_properties, account)
return account_context_menu
@@ -5161,7 +5245,7 @@ class RosterWindow:
group = model[titer][C_JID]
for jid in gajim.contacts.get_jid_list(account):
contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
+ jid)
if group in contact.get_shown_groups():
if contact.show not in ('offline', 'error'):
list_online.append((contact, account))
@@ -5171,14 +5255,16 @@ class RosterWindow:
# Make special context menu if group is Groupchats
if group == _('Groupchats'):
maximize_menuitem = gtk.ImageMenuItem(_('_Maximize All'))
- icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
+ icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP,
+ gtk.ICON_SIZE_MENU)
maximize_menuitem.set_image(icon)
- maximize_menuitem.connect('activate', self.on_all_groupchat_maximized,\
- list_)
+ maximize_menuitem.connect('activate',
+ self.on_all_groupchat_maximized, list_)
menu.append(maximize_menuitem)
else:
# Send Group Message
- send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage'))
+ send_group_message_item = gtk.ImageMenuItem(
+ _('Send Group M_essage'))
icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
send_group_message_item.set_image(icon)
@@ -5190,13 +5276,14 @@ class RosterWindow:
send_group_message_submenu.append(group_message_to_all_item)
group_message_to_all_online_item = gtk.MenuItem(
- _('To all online users'))
+ _('To all online users'))
send_group_message_submenu.append(group_message_to_all_online_item)
group_message_to_all_online_item.connect('activate',
- self.on_send_single_message_menuitem_activate, account, list_online)
+ self.on_send_single_message_menuitem_activate, account,
+ list_online)
group_message_to_all_item.connect('activate',
- self.on_send_single_message_menuitem_activate, account, list_)
+ self.on_send_single_message_menuitem_activate, account, list_)
# Invite to
invite_menuitem = gtk.ImageMenuItem(_('In_vite to'))
@@ -5209,15 +5296,15 @@ class RosterWindow:
# Send Custom Status
send_custom_status_menuitem = gtk.ImageMenuItem(
- _('Send Cus_tom Status'))
+ _('Send Cus_tom Status'))
# add a special img for this menuitem
if helpers.group_is_blocked(account, group):
send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
- 'offline'))
+ 'offline'))
send_custom_status_menuitem.set_sensitive(False)
else:
icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
- gtk.ICON_SIZE_MENU)
+ gtk.ICON_SIZE_MENU)
send_custom_status_menuitem.set_image(icon)
status_menuitems = gtk.Menu()
send_custom_status_menuitem.set_submenu(status_menuitems)
@@ -5228,7 +5315,7 @@ class RosterWindow:
state_images = gtkgui_helpers.load_iconset(path)
status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
status_menuitem.connect('activate', self.on_send_custom_status,
- list_, s, group)
+ list_, s, group)
icon = state_images[s]
status_menuitem.set_image(icon)
status_menuitems.append(status_menuitem)
@@ -5249,7 +5336,7 @@ class RosterWindow:
gtkgui_helpers.add_image_to_menuitem(rename_item, 'gajim-kbd_input')
menu.append(rename_item)
rename_item.connect('activate', self.on_rename, 'group', group,
- account)
+ account)
# Block group
is_blocked = False
@@ -5261,15 +5348,19 @@ class RosterWindow:
if helpers.group_is_blocked(account, group):
is_blocked = True
- if is_blocked and gajim.connections[account].privacy_rules_supported:
+ if is_blocked and gajim.connections[account].\
+ privacy_rules_supported:
unblock_menuitem = gtk.ImageMenuItem(_('_Unblock'))
- icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
+ icon = gtk.image_new_from_stock(gtk.STOCK_STOP,
+ gtk.ICON_SIZE_MENU)
unblock_menuitem.set_image(icon)
- unblock_menuitem.connect('activate', self.on_unblock, list_, group)
+ unblock_menuitem.connect('activate', self.on_unblock, list_,
+ group)
menu.append(unblock_menuitem)
else:
block_menuitem = gtk.ImageMenuItem(_('_Block'))
- icon = gtk.image_new_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
+ icon = gtk.image_new_from_stock(gtk.STOCK_STOP,
+ gtk.ICON_SIZE_MENU)
block_menuitem.set_image(icon)
block_menuitem.connect('activate', self.on_block, list_, group)
menu.append(block_menuitem)
@@ -5278,11 +5369,12 @@ class RosterWindow:
# Remove group
remove_item = gtk.ImageMenuItem(_('_Remove'))
- icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)
+ icon = gtk.image_new_from_stock(gtk.STOCK_REMOVE,
+ gtk.ICON_SIZE_MENU)
remove_item.set_image(icon)
menu.append(remove_item)
remove_item.connect('activate', self.on_remove_group_item_activated,
- group, account)
+ group, account)
# unsensitive if account is not connected
if gajim.connections[account].connected < 2:
@@ -5307,7 +5399,6 @@ class RosterWindow:
"""
model = self.modelfilter
jid = model[titer][C_JID].decode('utf-8')
- tree_path = model.get_path(titer)
account = model[titer][C_ACCOUNT].decode('utf-8')
contact = gajim.contacts.get_contact_with_highest_priority(account, jid)
menu = gui_menu_builder.get_contact_menu(contact, account)
@@ -5332,7 +5423,7 @@ class RosterWindow:
if not gajim.connections[account].privacy_rules_supported:
privacy_rules_supported = False
contact = gajim.contacts.get_contact_with_highest_priority(account,
- jid)
+ jid)
if helpers.jid_is_blocked(account, jid):
is_blocked = False
list_.append((contact, account))
@@ -5346,12 +5437,13 @@ class RosterWindow:
break
account = current_account
if account is not None:
- send_group_message_item = gtk.ImageMenuItem(_('Send Group M_essage'))
+ send_group_message_item = gtk.ImageMenuItem(
+ _('Send Group M_essage'))
icon = gtk.image_new_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_MENU)
send_group_message_item.set_image(icon)
menu.append(send_group_message_item)
send_group_message_item.connect('activate',
- self.on_send_single_message_menuitem_activate, account, list_)
+ self.on_send_single_message_menuitem_activate, account, list_)
# Invite to Groupchat
invite_item = gtk.ImageMenuItem(_('In_vite to'))
@@ -5367,7 +5459,8 @@ class RosterWindow:
# Manage Transport submenu
item = gtk.ImageMenuItem(_('_Manage Contacts'))
- icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)
+ icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES,
+ gtk.ICON_SIZE_MENU)
item.set_image(icon)
manage_contacts_submenu = gtk.Menu()
item.set_submenu(manage_contacts_submenu)
@@ -5441,20 +5534,21 @@ class RosterWindow:
blocked = True
# Send Custom Status
- send_custom_status_menuitem = gtk.ImageMenuItem(_('Send Cus_tom Status'))
+ send_custom_status_menuitem = gtk.ImageMenuItem(
+ _('Send Cus_tom Status'))
# add a special img for this menuitem
if blocked:
send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
- 'offline'))
+ 'offline'))
send_custom_status_menuitem.set_sensitive(False)
else:
if account in gajim.interface.status_sent_to_users and \
jid in gajim.interface.status_sent_to_users[account]:
send_custom_status_menuitem.set_image(gtkgui_helpers.load_icon(
- gajim.interface.status_sent_to_users[account][jid]))
+ gajim.interface.status_sent_to_users[account][jid]))
else:
icon = gtk.image_new_from_stock(gtk.STOCK_NETWORK,
- gtk.ICON_SIZE_MENU)
+ gtk.ICON_SIZE_MENU)
send_custom_status_menuitem.set_image(icon)
status_menuitems = gtk.Menu()
send_custom_status_menuitem.set_submenu(status_menuitems)
@@ -5465,7 +5559,7 @@ class RosterWindow:
state_images = gtkgui_helpers.load_iconset(path)
status_menuitem = gtk.ImageMenuItem(helpers.get_uf_show(s))
status_menuitem.connect('activate', self.on_send_custom_status,
- [(contact, account)], s)
+ [(contact, account)], s)
icon = state_images[s]
status_menuitem.set_image(icon)
status_menuitems.append(status_menuitem)
@@ -5480,13 +5574,14 @@ class RosterWindow:
item.set_image(icon)
menu.append(item)
item.connect('activate', self.on_execute_command, contact, account,
- contact.resource)
+ contact.resource)
if gajim.account_is_disconnected(account):
item.set_sensitive(False)
# Manage Transport submenu
item = gtk.ImageMenuItem(_('_Manage Transport'))
- icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU)
+ icon = gtk.image_new_from_stock(gtk.STOCK_PROPERTIES,
+ gtk.ICON_SIZE_MENU)
item.set_image(icon)
manage_transport_submenu = gtk.Menu()
item.set_submenu(manage_transport_submenu)
@@ -5494,7 +5589,8 @@ class RosterWindow:
# Modify Transport
item = gtk.ImageMenuItem(_('_Modify Transport'))
- icon = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU)
+ icon = gtk.image_new_from_stock(gtk.STOCK_PREFERENCES,
+ gtk.ICON_SIZE_MENU)
item.set_image(icon)
manage_transport_submenu.append(item)
item.connect('activate', self.on_edit_agent, contact, account)
@@ -5546,7 +5642,6 @@ class RosterWindow:
menu.append(information_menuitem)
information_menuitem.connect('activate', self.on_info, contact, account)
-
event_button = gtkgui_helpers.get_possible_button_event(event)
menu.attach_to_widget(self.tree, None)
@@ -5564,24 +5659,27 @@ class RosterWindow:
if jid in gajim.interface.minimized_controls[account]:
maximize_menuitem = gtk.ImageMenuItem(_('_Maximize'))
- icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP, gtk.ICON_SIZE_MENU)
+ icon = gtk.image_new_from_stock(gtk.STOCK_GOTO_TOP,
+ gtk.ICON_SIZE_MENU)
maximize_menuitem.set_image(icon)
maximize_menuitem.connect('activate', self.on_groupchat_maximized, \
- jid, account)
+ jid, account)
menu.append(maximize_menuitem)
if not gajim.gc_connected[account].get(jid, False):
connect_menuitem = gtk.ImageMenuItem(_('_Reconnect'))
connect_icon = gtk.image_new_from_stock(gtk.STOCK_CONNECT, \
- gtk.ICON_SIZE_MENU)
+ gtk.ICON_SIZE_MENU)
connect_menuitem.set_image(connect_icon)
- connect_menuitem.connect('activate', self.on_reconnect, jid, account)
+ connect_menuitem.connect('activate', self.on_reconnect, jid,
+ account)
menu.append(connect_menuitem)
disconnect_menuitem = gtk.ImageMenuItem(_('_Disconnect'))
disconnect_icon = gtk.image_new_from_stock(gtk.STOCK_DISCONNECT, \
- gtk.ICON_SIZE_MENU)
+ gtk.ICON_SIZE_MENU)
disconnect_menuitem.set_image(disconnect_icon)
- disconnect_menuitem.connect('activate', self.on_disconnect, jid, account)
+ disconnect_menuitem.connect('activate', self.on_disconnect, jid,
+ account)
menu.append(disconnect_menuitem)
item = gtk.SeparatorMenuItem() # separator
@@ -5589,10 +5687,9 @@ class RosterWindow:
history_menuitem = gtk.ImageMenuItem(_('_History'))
history_icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL, \
- gtk.ICON_SIZE_MENU)
+ gtk.ICON_SIZE_MENU)
history_menuitem.set_image(history_icon)
- history_menuitem .connect('activate', self.on_history, \
- contact, account)
+ history_menuitem .connect('activate', self.on_history, contact, account)
menu.append(history_menuitem)
event_button = gtkgui_helpers.get_possible_button_event(event)
@@ -5613,18 +5710,18 @@ class RosterWindow:
privacy_lists_menuitem = xml.get_object('privacy_lists_menuitem')
administrator_menuitem = xml.get_object('administrator_menuitem')
send_server_message_menuitem = xml.get_object(
- 'send_server_message_menuitem')
+ 'send_server_message_menuitem')
set_motd_menuitem = xml.get_object('set_motd_menuitem')
update_motd_menuitem = xml.get_object('update_motd_menuitem')
delete_motd_menuitem = xml.get_object('delete_motd_menuitem')
xml_console_menuitem.connect('activate',
- self.on_xml_console_menuitem_activate, account)
+ self.on_xml_console_menuitem_activate, account)
if gajim.connections[account] and gajim.connections[account].\
privacy_rules_supported:
privacy_lists_menuitem.connect('activate',
- self.on_privacy_lists_menuitem_activate, account)
+ self.on_privacy_lists_menuitem_activate, account)
else:
privacy_lists_menuitem.set_sensitive(False)
@@ -5636,16 +5733,16 @@ class RosterWindow:
delete_motd_menuitem.set_sensitive(False)
else:
send_server_message_menuitem.connect('activate',
- self.on_send_server_message_menuitem_activate, account)
+ self.on_send_server_message_menuitem_activate, account)
set_motd_menuitem.connect('activate',
- self.on_set_motd_menuitem_activate, account)
+ self.on_set_motd_menuitem_activate, account)
update_motd_menuitem.connect('activate',
- self.on_update_motd_menuitem_activate, account)
+ self.on_update_motd_menuitem_activate, account)
delete_motd_menuitem.connect('activate',
- self.on_delete_motd_menuitem_activate, account)
+ self.on_delete_motd_menuitem_activate, account)
advanced_menuitem_menu.show_all()
@@ -5661,7 +5758,7 @@ class RosterWindow:
# History manager
item = gtk.ImageMenuItem(_('History Manager'))
icon = gtk.image_new_from_stock(gtk.STOCK_JUSTIFY_FILL,
- gtk.ICON_SIZE_MENU)
+ gtk.ICON_SIZE_MENU)
item.set_image(icon)
menu.append(item)
item.connect('activate', self.on_history_manager_menuitem_activate)
@@ -5755,19 +5852,20 @@ class RosterWindow:
self.hpaned = self.xml.get_object('roster_hpaned')
gajim.interface.msg_win_mgr = MessageWindowMgr(self.window, self.hpaned)
gajim.interface.msg_win_mgr.connect('window-delete',
- self.on_message_window_delete)
+ self.on_message_window_delete)
self.advanced_menus = [] # We keep them to destroy them
if gajim.config.get('roster_window_skip_taskbar'):
self.window.set_property('skip-taskbar-hint', True)
self.tree = self.xml.get_object('roster_treeview')
sel = self.tree.get_selection()
sel.set_mode(gtk.SELECTION_MULTIPLE)
- #sel.connect('changed',
+ # sel.connect('changed',
# self.on_treeview_selection_changed)
- self._last_selected_contact = [] # holds a list of (jid, account) tupples
+ # holds a list of (jid, account) tupples
+ self._last_selected_contact = []
self.transports_state_images = {'16': {}, '32': {}, 'opened': {},
- 'closed': {}}
+ 'closed': {}}
self.last_save_dir = None
self.editing_path = None # path of row with cell in edit mode
@@ -5779,17 +5877,18 @@ class RosterWindow:
self.actions_menu_needs_rebuild = True
self.regroup = gajim.config.get('mergeaccounts')
self.clicked_path = None # Used remember on wich row we clicked
- if len(gajim.connections) < 2: # Do not merge accounts if only one exists
+ if len(gajim.connections) < 2:
+ # Do not merge accounts if only one exists
self.regroup = False
#FIXME: When list_accel_closures will be wrapped in pygtk
# no need of this variable
self.have_new_chat_accel = False # Is the "Ctrl+N" shown ?
gtkgui_helpers.resize_window(self.window,
- gajim.config.get('roster_width'),
- gajim.config.get('roster_height'))
+ gajim.config.get('roster_width'),
+ gajim.config.get('roster_height'))
gtkgui_helpers.move_window(self.window,
- gajim.config.get('roster_x-position'),
- gajim.config.get('roster_y-position'))
+ gajim.config.get('roster_x-position'),
+ gajim.config.get('roster_y-position'))
self.popups_notification_height = 0
self.popup_notification_windows = []
@@ -5800,8 +5899,8 @@ class RosterWindow:
gajim.events.event_removed_subscribe(self.on_event_removed)
# when this value become 0 we quit main application. If it's more than 0
- # it means we are waiting for this number of accounts to disconnect before
- # quitting
+ # it means we are waiting for this number of accounts to disconnect
+ # before quitting
self.quit_on_next_offline = -1
# uf_show, img, show, sensitive
@@ -5830,8 +5929,8 @@ class RosterWindow:
for show in ('online', 'chat', 'away', 'xa', 'dnd', 'invisible'):
uf_show = helpers.get_uf_show(show)
- liststore.append([uf_show, gajim.interface.jabber_state_images['16'][
- show], show, True])
+ liststore.append([uf_show,
+ gajim.interface.jabber_state_images['16'][show], show, True])
# Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
liststore.append(['SEPARATOR', None, '', True])
@@ -5840,26 +5939,26 @@ class RosterWindow:
img.set_from_file(path)
# sensitivity to False because by default we're offline
self.status_message_menuitem_iter = liststore.append(
- [_('Change Status Message...'), img, '', False])
+ [_('Change Status Message...'), img, '', False])
# Add a Separator (self._iter_is_separator() checks on string SEPARATOR)
liststore.append(['SEPARATOR', None, '', True])
uf_show = helpers.get_uf_show('offline')
liststore.append([uf_show, gajim.interface.jabber_state_images['16'][
- 'offline'], 'offline', True])
+ 'offline'], 'offline', True])
status_combobox_items = ['online', 'chat', 'away', 'xa', 'dnd',
- 'invisible', 'separator1', 'change_status_msg', 'separator2',
- 'offline']
+ 'invisible', 'separator1', 'change_status_msg', 'separator2',
+ 'offline']
self.status_combobox.set_model(liststore)
# default to offline
number_of_menuitem = status_combobox_items.index('offline')
self.status_combobox.set_active(number_of_menuitem)
- # holds index to previously selected item so if "change status message..."
- # is selected we can fallback to previously selected item and not stay
- # with that item selected
+ # holds index to previously selected item so if
+ # "change status message..." is selected we can fallback to previously
+ # selected item and not stay with that item selected
self.previous_status_combobox_active = number_of_menuitem
showOffline = gajim.config.get('showoffline')
@@ -5877,7 +5976,7 @@ class RosterWindow:
show_transports_group = gajim.config.get('show_transports_group')
self.xml.get_object('show_transports_menuitem').set_active(
- show_transports_group)
+ show_transports_group)
self.xml.get_object('show_roster_menuitem').set_active(True)
@@ -5892,7 +5991,7 @@ class RosterWindow:
col.pack_start(render_pixbuf, expand=False)
col.add_attribute(render_pixbuf, 'pixbuf', C_AVATAR_PIXBUF)
col.set_cell_data_func(render_pixbuf,
- self._fill_avatar_pixbuf_renderer, None)
+ self._fill_avatar_pixbuf_renderer, None)
if gajim.config.get('avatar_position_in_roster') == 'left':
add_avatar_renderer()
@@ -5906,37 +6005,37 @@ class RosterWindow:
render_text = gtk.CellRendererText() # contact or group or account name
render_text.set_property('ellipsize', pango.ELLIPSIZE_END)
col.pack_start(render_text, expand=True)
- col.add_attribute(render_text, 'markup', C_NAME) # where we hold the name
+ col.add_attribute(render_text, 'markup', C_NAME)
+ # where we hold the name
col.set_cell_data_func(render_text, self._nameCellDataFunc, None)
render_pixbuf = gtk.CellRendererPixbuf()
col.pack_start(render_pixbuf, expand=False)
col.add_attribute(render_pixbuf, 'pixbuf', C_MOOD_PIXBUF)
col.set_cell_data_func(render_pixbuf,
- self._fill_pep_pixbuf_renderer, C_MOOD_PIXBUF)
+ self._fill_pep_pixbuf_renderer, C_MOOD_PIXBUF)
render_pixbuf = gtk.CellRendererPixbuf()
col.pack_start(render_pixbuf, expand=False)
col.add_attribute(render_pixbuf, 'pixbuf', C_ACTIVITY_PIXBUF)
col.set_cell_data_func(render_pixbuf,
- self._fill_pep_pixbuf_renderer, C_ACTIVITY_PIXBUF)
+ self._fill_pep_pixbuf_renderer, C_ACTIVITY_PIXBUF)
render_pixbuf = gtk.CellRendererPixbuf()
col.pack_start(render_pixbuf, expand=False)
col.add_attribute(render_pixbuf, 'pixbuf', C_TUNE_PIXBUF)
col.set_cell_data_func(render_pixbuf,
- self._fill_pep_pixbuf_renderer, C_TUNE_PIXBUF)
+ self._fill_pep_pixbuf_renderer, C_TUNE_PIXBUF)
render_pixbuf = gtk.CellRendererPixbuf()
col.pack_start(render_pixbuf, expand=False)
col.add_attribute(render_pixbuf, 'pixbuf', C_LOCATION_PIXBUF)
col.set_cell_data_func(render_pixbuf,
- self._fill_pep_pixbuf_renderer, C_LOCATION_PIXBUF)
+ self._fill_pep_pixbuf_renderer, C_LOCATION_PIXBUF)
self._pep_type_to_model_column = {'mood': C_MOOD_PIXBUF,
- 'activity': C_ACTIVITY_PIXBUF,
- 'tune': C_TUNE_PIXBUF,
- 'location': C_LOCATION_PIXBUF}
+ 'activity': C_ACTIVITY_PIXBUF, 'tune': C_TUNE_PIXBUF,
+ 'location': C_LOCATION_PIXBUF}
if gajim.config.get('avatar_position_in_roster') == 'right':
add_avatar_renderer()
@@ -5945,7 +6044,7 @@ class RosterWindow:
col.pack_start(render_pixbuf, expand=False)
col.add_attribute(render_pixbuf, 'pixbuf', C_PADLOCK_PIXBUF)
col.set_cell_data_func(render_pixbuf,
- self._fill_padlock_pixbuf_renderer, None)
+ self._fill_padlock_pixbuf_renderer, None)
self.tree.append_column(col)
# do not show gtk arrows workaround
@@ -5962,11 +6061,11 @@ class RosterWindow:
# signals
self.TARGET_TYPE_URI_LIST = 80
TARGETS = [('MY_TREE_MODEL_ROW',
- gtk.TARGET_SAME_APP | gtk.TARGET_SAME_WIDGET, 0)]
+ gtk.TARGET_SAME_APP | gtk.TARGET_SAME_WIDGET, 0)]
TARGETS2 = [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
- ('text/uri-list', 0, self.TARGET_TYPE_URI_LIST)]
+ ('text/uri-list', 0, self.TARGET_TYPE_URI_LIST)]
self.tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, TARGETS,
- gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE | gtk.gdk.ACTION_COPY)
+ gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE | gtk.gdk.ACTION_COPY)
self.tree.enable_model_drag_dest(TARGETS2, gtk.gdk.ACTION_DEFAULT)
self.tree.connect('drag_begin', self.drag_begin)
self.tree.connect('drag_end', self.drag_end)
@@ -5994,9 +6093,9 @@ class RosterWindow:
if len(gajim.connections) == 0: # if we have no account
def _open_wizard():
gajim.interface.instances['account_creation_wizard'] = \
- config.AccountCreationWizardWindow()
- # Open wizard only after roster is created, so we can make it transient
- # for the roster window
+ config.AccountCreationWizardWindow()
+ # Open wizard only after roster is created, so we can make it
+ # transient for the roster window
gobject.idle_add(_open_wizard)
if not gajim.ZEROCONF_ACC_NAME in gajim.config.get_per('accounts'):
# Create zeroconf in config file
diff --git a/src/search_window.py b/src/search_window.py
index 06362b651..7dc92dbb2 100644
--- a/src/search_window.py
+++ b/src/search_window.py
@@ -2,7 +2,7 @@
## src/search_window.py
##
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
-## Copyright (C) 2007-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2007-2010 Yann Leboulanger <asterix AT lagaule.org>
##
## This file is part of Gajim.
##
diff --git a/src/session.py b/src/session.py
index 4b3c530bb..9d0834ce6 100644
--- a/src/session.py
+++ b/src/session.py
@@ -1,8 +1,8 @@
# -*- coding:utf-8 -*-
## src/session.py
##
-## Copyright (C) 2008 Yann Leboulanger <asterix AT lagaule.org>
-## Brendan Taylor <whateley AT gmail.com>
+## Copyright (C) 2008-2010 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2008 Brendan Taylor <whateley AT gmail.com>
## Jonathan Schleifer <js-gajim AT webkeks.org>
## Stephan Erb <steve-e AT h3c.de>
##
@@ -95,6 +95,8 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
self.resource = resource
if self.control and self.control.resource:
self.control.change_resource(self.resource)
+ seclabel = None
+ displaymarking = None
if not msg_type or msg_type not in ('chat', 'groupchat', 'error'):
msg_type = 'normal'
@@ -113,7 +115,9 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
break
composing_xep, chatstate = self.get_chatstate(msg, msgtxt)
-
+ seclabel = msg.getTag('securitylabel')
+ if seclabel and seclabel.getNamespace() == common.xmpp.NS_SECLABEL:
+ displaymarking = seclabel.getTag('displaymarking')
xhtml = msg.getXHTML()
if msg_type == 'chat':
@@ -236,15 +240,15 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
if self.control:
# print if a control is open
self.control.print_conversation(msgtxt, tim=tim, xhtml=xhtml,
- encrypted=encrypted)
+ encrypted=encrypted, displaymarking=displaymarking)
else:
# otherwise pass it off to the control to be queued
groupchat_control.on_private_message(nickname, msgtxt, tim,
- xhtml, self, msg_id=msg_id, encrypted=encrypted)
+ xhtml, self, msg_id=msg_id, encrypted=encrypted, displaymarking=displaymarking)
else:
self.roster_message(jid, msgtxt, tim, encrypted, msg_type,
subject, resource, msg_id, user_nick, advanced_notif_num,
- xhtml=xhtml, form_node=form_node)
+ xhtml=xhtml, form_node=form_node, displaymarking=displaymarking)
nickname = gajim.get_name_from_jid(self.conn.name, jid)
@@ -268,15 +272,14 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
[full_jid_with_resource, msgtxt, tim, encrypted, msg_type, subject,
chatstate, msg_id, composing_xep, user_nick, xhtml, form_node]))
- gajim.ged.raise_event('NewMessage',
- (self.conn.name, [full_jid_with_resource, msgtxt, tim,
- encrypted, msg_type, subject, chatstate, msg_id,
- composing_xep, user_nick, xhtml, form_node]))
-
+ gajim.ged.raise_event('NewMessage',
+ (self.conn.name, [full_jid_with_resource, msgtxt, tim,
+ encrypted, msg_type, subject, chatstate, msg_id,
+ composing_xep, user_nick, xhtml, form_node]))
def roster_message(self, jid, msg, tim, encrypted=False, msg_type='',
subject=None, resource='', msg_id=None, user_nick='',
- advanced_notif_num=None, xhtml=None, form_node=None):
+ advanced_notif_num=None, xhtml=None, form_node=None, displaymarking=None):
"""
Display the message or show notification in the roster
"""
@@ -351,7 +354,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
typ = 'error'
self.control.print_conversation(msg, typ, tim=tim, encrypted=encrypted,
- subject=subject, xhtml=xhtml)
+ subject=subject, xhtml=xhtml, displaymarking=displaymarking)
if msg_id:
gajim.logger.set_read_messages([msg_id])
@@ -372,7 +375,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
contact)
event = gajim.events.create_event(type_, (msg, subject, msg_type, tim,
- encrypted, resource, msg_id, xhtml, self, form_node),
+ encrypted, resource, msg_id, xhtml, self, form_node, displaymarking),
show_in_roster=show_in_roster, show_in_systray=show_in_systray)
gajim.events.add_event(self.conn.name, fjid, event)
diff --git a/src/statusicon.py b/src/statusicon.py
index f7a97a2f3..2cd3e61be 100644
--- a/src/statusicon.py
+++ b/src/statusicon.py
@@ -3,7 +3,7 @@
##
## Copyright (C) 2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
-## Copyright (C) 2006-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2006-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
## Julien Pivotto <roidelapluie AT gmail.com>
## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
@@ -172,6 +172,7 @@ class StatusIcon:
status_menuitem = self.xml.get_object('status_menu')
join_gc_menuitem = self.xml.get_object('join_gc_menuitem')
sounds_mute_menuitem = self.xml.get_object('sounds_mute_menuitem')
+ show_roster_menuitem = self.xml.get_object('show_roster_menuitem')
if self.single_message_handler_id:
single_message_menuitem.handler_disconnect(
@@ -316,6 +317,16 @@ class StatusIcon:
sounds_mute_menuitem.set_active(not gajim.config.get('sounds_on'))
+ win = gajim.interface.roster.window
+ if win.get_property('has-toplevel-focus'):
+ show_roster_menuitem.get_children()[0].set_label(_('Hide _Roster'))
+ show_roster_menuitem.connect('activate',
+ self.on_hide_roster_menuitem_activate)
+ else:
+ show_roster_menuitem.get_children()[0].set_label(_('Show _Roster'))
+ show_roster_menuitem.connect('activate',
+ self.on_show_roster_menuitem_activate)
+
if os.name == 'nt':
if self.added_hide_menuitem is False:
self.systray_context_menu.prepend(gtk.SeparatorMenuItem())
@@ -342,6 +353,10 @@ class StatusIcon:
win = gajim.interface.roster.window
win.present()
+ def on_hide_roster_menuitem_activate(self, widget):
+ win = gajim.interface.roster.window
+ win.hide()
+
def on_preferences_menuitem_activate(self, widget):
if 'preferences' in gajim.interface.instances:
gajim.interface.instances['preferences'].window.present()
@@ -362,9 +377,16 @@ class StatusIcon:
# we could be in another VD right now. eg vd2
# and we want to show it in vd2
if not gtkgui_helpers.possibly_move_window_in_current_desktop(win):
+ x, y = win.get_position()
+ gajim.config.set('roster_x-position', x)
+ gajim.config.set('roster_y-position', y)
win.hide() # else we hide it from VD that was visible in
else:
- win.show_all()
+ if not win.get_property('visible'):
+ win.show_all()
+ gtkgui_helpers.move_window(win,
+ gajim.config.get('roster_x-position'),
+ gajim.config.get('roster_y-position'))
if not gajim.config.get('roster_window_skip_taskbar'):
win.set_property('skip-taskbar-hint', False)
win.present_with_time(gtk.get_current_event_time())
diff --git a/src/tooltips.py b/src/tooltips.py
index 08f12b4c2..6d193abe7 100644
--- a/src/tooltips.py
+++ b/src/tooltips.py
@@ -5,7 +5,7 @@
## Stéphan Kochen <stephan AT kochen.nl>
## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
## Copyright (C) 2005-2007 Nikos Kouremenos <kourem AT gmail.com>
-## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2005-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2006 Travis Shirk <travis AT pobox.com>
## Stefan Bethge <stefan AT lanpartei.de>
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
@@ -33,12 +33,15 @@ import gobject
import os
import time
import locale
+from datetime import datetime
+from datetime import timedelta
import gtkgui_helpers
from common import gajim
from common import helpers
from common.pep import MOODS, ACTIVITIES
+from common.i18n import Q_
class BaseTooltip:
"""
@@ -176,6 +179,44 @@ class BaseTooltip:
self.cur_data = None
self.check_last_time = None
+ @staticmethod
+ def colorize_status(status):
+ """
+ Colorize the status message inside the tooltip by it's
+ semantics. Color palette is the Tango.
+ """
+ formatted = "<span foreground='%s'>%s</span>"
+ if status.startswith(Q_("?user status:Available")):
+ status = formatted % ('#73D216', status)
+ elif status.startswith(_("Free for Chat")):
+ status = formatted % ('#3465A4', status)
+ elif status.startswith(_("Away")):
+ status = formatted % ('#EDD400', status)
+ elif status.startswith(_("Busy")):
+ status = formatted % ('#F57900', status)
+ elif status.startswith(_("Not Available")):
+ status = formatted % ('#CC0000', status)
+ elif status.startswith(_("Offline")):
+ status = formatted % ('#555753', status)
+ return status
+
+ @staticmethod
+ def colorize_affiliation(affiliation):
+ """
+ Color the affiliation of a MUC participant inside the tooltip by
+ it's semantics. Color palette is the Tango.
+ """
+ formatted = "<span foreground='%s'>%s</span>"
+ if affiliation.startswith(Q_("?Group Chat Contact Affiliation:None")):
+ affiliation = formatted % ('#555753', affiliation)
+ elif affiliation.startswith(_("Member")):
+ affiliation = formatted % ('#73D216', affiliation)
+ elif affiliation.startswith(_("Administrator")):
+ affiliation = formatted % ('#F57900', affiliation)
+ elif affiliation.startswith(_("Owner")):
+ affiliation = formatted % ('#CC0000', affiliation)
+ return affiliation
+
class StatusTable:
"""
Contains methods for creating status table. This is used in Roster and
@@ -342,23 +383,25 @@ class GCTooltip(BaseTooltip):
status = '<i>' +\
gobject.markup_escape_text(status) + '</i>'
properties.append((status, None))
- else: # no status message, show SHOW instead
- show = helpers.get_uf_show(contact.show)
- show = '<i>' + show + '</i>'
- properties.append((show, None))
- if contact.jid.strip() != '':
- properties.append((_('Jabber ID: '), contact.jid))
+ show = helpers.get_uf_show(contact.show)
+ show = self.colorize_status(show)
+ properties.append((show, None))
- if hasattr(contact, 'resource') and contact.resource.strip() != '':
+ if contact.jid.strip():
+ properties.append((_('Jabber ID: '), "<b>%s</b>" % contact.jid))
+
+ if hasattr(contact, 'resource') and contact.resource.strip():
properties.append((_('Resource: '),
- gobject.markup_escape_text(contact.resource) ))
+ gobject.markup_escape_text(contact.resource)))
+
if contact.affiliation != 'none':
uf_affiliation = helpers.get_uf_affiliation(contact.affiliation)
- affiliation_str = \
+ uf_affiliation =\
_('%(owner_or_admin_or_member)s of this group chat') %\
{'owner_or_admin_or_member': uf_affiliation}
- properties.append((affiliation_str, None))
+ uf_affiliation = self.colorize_affiliation(uf_affiliation)
+ properties.append((uf_affiliation, None))
# Add avatar
puny_name = helpers.sanitize_filename(contact.name)
@@ -542,8 +585,7 @@ class RosterTooltip(NotificationAreaTooltip):
show = _('Connected')
else:
show = _('Disconnected')
- show = '<i>' + show + '</i>'
- # we append show below
+ show = self.colorize_status(show)
if contact.status:
status = contact.status.strip()
@@ -559,7 +601,7 @@ class RosterTooltip(NotificationAreaTooltip):
self._append_pep_info(contact, properties)
- properties.append((_('Jabber ID: '), prim_contact.jid ))
+ properties.append((_('Jabber ID: '), "<b>%s</b>" % prim_contact.jid))
# contact has only one ressource
if num_resources == 1 and contact.resource:
@@ -584,21 +626,25 @@ class RosterTooltip(NotificationAreaTooltip):
gobject.markup_escape_text(keyID)))
if contact.last_activity_time:
- text = _(' since %s')
+ last_active = datetime(*contact.last_activity_time[:6])
+ current = datetime.now()
- if time.strftime('%j', time.localtime())== \
- time.strftime('%j', contact.last_activity_time):
- # it's today, show only the locale hour representation
- local_time = time.strftime('%I:%M %p',
- contact.last_activity_time)
+ diff = current - last_active
+ diff = timedelta(diff.days, diff.seconds)
+
+ if last_active.date() == current.date():
+ formatted = last_active.strftime("%X")
else:
- # time.strftime returns locale encoded string
- local_time = time.strftime('%c',
- contact.last_activity_time)
- local_time = local_time.decode(
- locale.getpreferredencoding())
- text = text % local_time
- properties.append(('Idle' + text, None))
+ formatted = last_active.strftime("%c")
+
+ # Do not show the "Idle since" and "Idle for" items if there
+ # is no meaningful difference between last activity time and
+ # current time.
+ if diff.days > 0 or diff.seconds > 0:
+ cs = "<span foreground='#888A85'>%s</span>"
+ properties.append((str(), None))
+ properties.append(((cs % _("Idle since %s")) % formatted, None))
+ properties.append(((cs % _("Idle for %s")) % str(diff), None))
while properties:
property_ = properties.pop(0)
@@ -649,23 +695,19 @@ class RosterTooltip(NotificationAreaTooltip):
"""
if 'mood' in contact.pep:
mood = contact.pep['mood'].asMarkupText()
- mood_string = _('Mood:') + ' %s' % mood
- properties.append((mood_string, None))
+ properties.append((_("Mood: %s") % mood, None))
if 'activity' in contact.pep:
activity = contact.pep['activity'].asMarkupText()
- activity_string = _('Activity:') + ' %s' % activity
- properties.append((activity_string, None))
+ properties.append((_("Activity: %s") % activity, None))
if 'tune' in contact.pep:
tune = contact.pep['tune'].asMarkupText()
- tune_string = _('Tune:') + ' %s' % tune
- properties.append((tune_string, None))
+ properties.append((_("Tune: %s") % tune, None))
if 'location' in contact.pep:
location = contact.pep['location'].asMarkupText()
- location_string = _('Location:') + ' %s' % location
- properties.append((location_string, None))
+ properties.append((_("Location: %s") % location, None))
class FileTransfersTooltip(BaseTooltip):
diff --git a/src/vcard.py b/src/vcard.py
index 2f30f52a6..fa2f96936 100644
--- a/src/vcard.py
+++ b/src/vcard.py
@@ -1,7 +1,7 @@
# -*- coding:utf-8 -*-
## src/vcard.py
##
-## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
+## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Vincent Hanquez <tab AT snarc.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2006 Junglecow J <junglecow AT gmail.com>
@@ -164,7 +164,7 @@ class VcardWindow:
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
menuitem.connect('activate',
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
- self.contact.jid, self.account, self.contact.get_shown_name())
+ self.contact.jid, self.contact.get_shown_name())
menu.append(menuitem)
menu.connect('selection-done', lambda w:w.destroy())
# show the menu
@@ -328,7 +328,7 @@ class VcardWindow:
subscription_label = self.xml.get_object('subscription_label')
ask_label = self.xml.get_object('ask_label')
if self.gc_contact:
- self.xml.get_object('subscription_title_label').set_markup(_("<b>Role:</b>"))
+ self.xml.get_object('subscription_title_label').set_markup(Q_("?Role in Group Chat:<b>Role:</b>"))
uf_role = helpers.get_uf_role(self.gc_contact.role)
subscription_label.set_text(uf_role)
@@ -479,7 +479,7 @@ class ZeroconfVcardWindow:
menuitem = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS)
menuitem.connect('activate',
gtkgui_helpers.on_avatar_save_as_menuitem_activate,
- self.contact.jid, self.account, self.contact.get_shown_name())
+ self.contact.jid, self.contact.get_shown_name())
menu.append(menuitem)
menu.connect('selection-done', lambda w:w.destroy())
# show the menu